From c9f1e0877517e96b011c7c21b050ca4b32c5e326 Mon Sep 17 00:00:00 2001 From: Matin Lotfaliei Date: Sat, 16 Dec 2023 23:05:41 -0800 Subject: [PATCH 1/2] Add documentation to shaders --- src/CMakeLists.txt | 2 + src/ShapeCornersShader.h | 90 ++++++++++++++++++++++++++++++- src/kcm/CMakeLists.txt | 1 + src/kcm/ShapeCornersKCM.h | 3 +- src/shaders_110/shapecorners.frag | 89 +++++++++++++++++++++--------- src/shaders_140/shapecorners.frag | 89 +++++++++++++++++++++--------- 6 files changed, 222 insertions(+), 52 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4eafc81..4782392 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,9 @@ add_subdirectory(kcm) set(effect_SRCS + ShapeCornersEffect.h ShapeCornersEffect.cpp + ShapeCornersShader.h ShapeCornersShader.cpp plugin.cpp ) diff --git a/src/ShapeCornersShader.h b/src/ShapeCornersShader.h index 8097cda..b2d6448 100644 --- a/src/ShapeCornersShader.h +++ b/src/ShapeCornersShader.h @@ -17,27 +17,113 @@ namespace KWin { class ShapeCornersShader { public: - ShapeCornersShader(); + /** + * \brief Loads the correct shader based on the GL platform in KWin. + * Then it assigns the location references for all its uniform variables so they could be written in. + */ + explicit ShapeCornersShader(); + /** + * \return True if the GL platform version used in KWin is older than 1.40. + */ static bool IsLegacy(); - bool IsValid() const; + + /** + * \return True if the shader is valid and loaded + */ + [[nodiscard]] bool IsValid() const; + + /** + * \brief This function assigns the required variables to the shader. + * Then it pushes the shader to the stack of rendering. + * This needs to be called before each window is rendered. + * \param w The window that the effect will be rendering on + * \return A reference to the unique pointer of the loaded shader. + */ const std::unique_ptr& Bind(KWin::EffectWindow *w) const; const std::unique_ptr& Bind(QMatrix4x4 mvp, KWin::EffectWindow *w) const; + + /** + * \brief Pop the shader from the stack of rendering. + * This needs to be called after each window is rendered. + */ void Unbind() const; + + /** + * \return A reference to the unique pointer of the loaded shader. + */ std::unique_ptr& GetShader() { return m_shader; } private: + /** + * \brief A pointer to the loaded shader used to assign the value of uniform variables. + */ std::unique_ptr m_shader; + + /** + * \brief An instance of `ShaderManager` used to load shader from file and push/pop the shader for each render. + */ KWin::ShaderManager* m_manager; + + /** + * \brief Used only for its `palette()` function which holds the currently active highlight colors. + */ QWidget m_widget; + + /** + * \brief Reference to `uniform vec2 windowSize;` + * Containing `window->frameGeometry().size()` + */ int m_shader_windowSize = 0; + + /** + * \brief Reference to `uniform vec2 windowExpandedSize;` + * Containing `window->expandedGeometry().size()` + * \note When `expandedGeometry = frameGeometry`, it means there is no shadow. + */ int m_shader_windowExpandedSize = 0; + + /** + * \brief Reference to `uniform vec2 windowTopLeft;` + * Containing the distance between the top-left of `expandedGeometry` and the top-left of `frameGeometry`. + * \note When `windowTopLeft = {0,0}`, it means `expandedGeometry = frameGeometry` and there is no shadow. + */ int m_shader_windowTopLeft = 0; + + /** + * \brief Reference to `uniform vec4 shadowColor;` + * Containing the RGBA of the shadow color specified in settings. + */ int m_shader_shadowColor = 0; + + /** + * \brief Reference to `uniform float shadowSize;` + * Containing the shadow size specified in settings. + */ int m_shader_shadowSize = 0; + + /** + * \brief Reference to `uniform float radius;` + * Containing the corner radius in pixels specified in settings. + */ int m_shader_radius = 0; + + /** + * \brief Reference to `uniform vec4 outlineColor;` + * Containing the RGBA of the outline color specified in settings. + */ int m_shader_outlineColor = 0; + + /** + * \brief Reference to `uniform float outlineThickness;` + * Containing the thickness of the outline in pixels specified in settings. + */ int m_shader_outlineThickness = 0; + + /** + * \brief Reference to `uniform sampler2D front;` + * Containing the active texture. When assigned to zero, it will contain the painted contents of the window. + */ int m_shader_front = 0; }; diff --git a/src/kcm/CMakeLists.txt b/src/kcm/CMakeLists.txt index 83c0247..c0c2cc9 100644 --- a/src/kcm/CMakeLists.txt +++ b/src/kcm/CMakeLists.txt @@ -1,4 +1,5 @@ set(kcm_SRCS + ShapeCornersKCM.h ShapeCornersKCM.cpp plugin.cpp ) diff --git a/src/kcm/ShapeCornersKCM.h b/src/kcm/ShapeCornersKCM.h index fd71560..a385eea 100644 --- a/src/kcm/ShapeCornersKCM.h +++ b/src/kcm/ShapeCornersKCM.h @@ -1,4 +1,5 @@ - +#pragma once + #include #include "ui_ShapeCornersKCM.h" #include "ShapeCornersConfig.h" diff --git a/src/shaders_110/shapecorners.frag b/src/shaders_110/shapecorners.frag index 2990663..6cd9d8d 100644 --- a/src/shaders_110/shapecorners.frag +++ b/src/shaders_110/shapecorners.frag @@ -1,23 +1,37 @@ #version 110 -uniform sampler2D front; -uniform float radius; -uniform vec2 windowSize; -uniform vec2 windowExpandedSize; -uniform vec2 windowTopLeft; -uniform vec4 shadowColor; -uniform float shadowSize; -uniform vec4 outlineColor; -uniform float outlineThickness; -uniform vec4 modulation; -uniform float saturation; - -varying vec2 texcoord0; +uniform sampler2D front; // The painted contents of the window. +uniform float radius; // The thickness of the outline in pixels specified in settings. +uniform vec2 windowSize; // Containing `window->frameGeometry().size()` +uniform vec2 windowExpandedSize; // Containing `window->expandedGeometry().size()` + +uniform vec2 windowTopLeft; /* Containing the distance between the top-left of `expandedGeometry` and + * the top-left of `frameGeometry`. When `windowTopLeft = {0,0}`, it means + * `expandedGeometry = frameGeometry` and there is no shadow. */ + +uniform vec4 shadowColor; // The RGBA of the shadow color specified in settings. +uniform float shadowSize; // The shadow size specified in settings. +uniform vec4 outlineColor; // The RGBA of the outline color specified in settings. +uniform float outlineThickness; // The thickness of the outline in pixels specified in settings. + +uniform vec4 modulation; // This variable is assigned and used by KWinEffects used for proper fading. +uniform float saturation; // This variable is assigned and used by KWinEffects used for proper fading. + +varying vec2 texcoord0; // The XY location of the rendering pixel. Starting from {0.0, 0.0} to {1.0, 1.0} + +// Note: This version of GLSL uses the built-in variable `gl_FragColor` instead of `out vec4 fragColor;` bool isDrawingShadows() { return windowSize != windowExpandedSize && shadowColor.a > 0.0; } bool isDrawingOutline() { return outlineColor.a > 0.0 && outlineThickness > 0.0; } -vec4 shadowCorner(float distance_from_center) { +/* + * \brief This function generates the shadow color based on the distance_from_center + * \param distance_from_center: Distance of the rendering point and the reference point that is being used for rounding corners. + * \return The RGBA color to be used for shadow. + */ +vec4 shadowColor(float distance_from_center) { + if(!isDrawingShadows()) + return vec4(0.0, 0.0, 0.0, 0.0); float percent = -distance_from_center/shadowSize + 1.0; if (percent < 0.0) return vec4(0.0, 0.0, 0.0, 0.0); @@ -25,23 +39,33 @@ vec4 shadowCorner(float distance_from_center) { return vec4(shadowColor.rgb * shadowColor.a * percent, shadowColor.a * percent); } +/* + * \brief This function is used to choose the pixel color based on its distance to the center input. + * \param coord0: The XY point + * \param tex: The RGBA color of the pixel in XY + * \param center: The origin XY point that is being used as a reference for rounding corners. + * \return The RGBA color to be used instead of tex input. + */ vec4 shapeCorner(vec2 coord0, vec4 tex, vec2 center) { float distance_from_center = distance(coord0, center); - vec4 c = isDrawingShadows() ? shadowCorner(distance_from_center) : vec4(0.0,0.0,0.0,0.0); + vec4 c = shadowColor(distance_from_center); if(isDrawingOutline()) { vec4 outlineOverlay = vec4(mix(tex.rgb, outlineColor.rgb, outlineColor.a), 1.0); if (distance_from_center < radius - outlineThickness/2.0) { + // from the window to the outline float antialiasing = clamp(radius-outlineThickness+0.5-distance_from_center, 0.0, 1.0); return mix(outlineOverlay, tex, antialiasing); } else { + // from the outline to the shadow float antialiasing = clamp(distance_from_center-radius+0.5, 0.0, 1.0); return mix(outlineOverlay, c, antialiasing); } } else { + // from the window to the shadow float antialiasing = clamp(radius-distance_from_center, 0.0, 1.0); return mix(c, tex, antialiasing); } @@ -49,33 +73,49 @@ vec4 shapeCorner(vec2 coord0, vec4 tex, vec2 center) { void main(void) { - vec4 tex = texture2D(front, texcoord0); + vec4 tex = texture2D(front, texcoord0); // The RGBA of the XY pixel for the painted window + + /* Since `texcoord0` is ranging from {0.0, 0.0} to {1.0, 1.0} is not pixel intuitive, + * I am changing the range to become from {0.0, 0.0} to {width, height} + * in a way that {0,0} is the top-left of the window and not its shadow. + * This means areas with negative numbers and areas beyond windowSize is considered part of the shadow. */ vec2 coord0 = vec2(texcoord0.x * windowExpandedSize.x - windowTopLeft.x, (1.0-texcoord0.y)* windowExpandedSize.y - windowTopLeft.y); + /* + Split the window into these sections below. They will have a different center of circle for rounding. + + TL T T TR + L x x R + L x x R + BL B B BR + */ if (coord0.y < radius) { if (coord0.x < radius) - tex = shapeCorner(coord0, tex, vec2(radius, radius)); + tex = shapeCorner(coord0, tex, vec2(radius, radius)); // Section TL else if (coord0.x > windowSize.x - radius) - tex = shapeCorner(coord0, tex, vec2(windowSize.x - radius, radius)); + tex = shapeCorner(coord0, tex, vec2(windowSize.x - radius, radius)); // Section TR else if (coord0.y < outlineThickness) - tex = shapeCorner(coord0, tex, vec2(coord0.x, radius)); + tex = shapeCorner(coord0, tex, vec2(coord0.x, radius)); // Section T } else if (coord0.y > windowSize.y - radius) { if (coord0.x < radius) - tex = shapeCorner(coord0, tex, vec2(radius, windowSize.y - radius)); + tex = shapeCorner(coord0, tex, vec2(radius, windowSize.y - radius)); // Section BL else if (coord0.x > windowSize.x - radius) - tex = shapeCorner(coord0, tex, vec2(windowSize.x - radius, windowSize.y - radius)); + tex = shapeCorner(coord0, tex, vec2(windowSize.x - radius, windowSize.y - radius)); // Section BR else if (coord0.y > windowSize.y - outlineThickness) - tex = shapeCorner(coord0, tex, vec2(coord0.x, windowSize.y - radius)); + tex = shapeCorner(coord0, tex, vec2(coord0.x, windowSize.y - radius)); // Section B } else { if (coord0.x < radius) - tex = shapeCorner(coord0, tex, vec2(radius, coord0.y)); + tex = shapeCorner(coord0, tex, vec2(radius, coord0.y)); // Section L else if (coord0.x > windowSize.x - radius) - tex = shapeCorner(coord0, tex, vec2(windowSize.x - radius, coord0.y)); + tex = shapeCorner(coord0, tex, vec2(windowSize.x - radius, coord0.y)); // Section R + + // For section x, the tex is not changing } + // Apply the saturation and modulation. This is essential for proper fades in other KWin effects. if (saturation != 1.0) { vec3 desaturated = tex.rgb * vec3( 0.30, 0.59, 0.11 ); desaturated = vec3( dot( desaturated, tex.rgb )); @@ -83,5 +123,6 @@ void main(void) } tex *= modulation; + // Send to output gl_FragColor = tex; } diff --git a/src/shaders_140/shapecorners.frag b/src/shaders_140/shapecorners.frag index 2d296eb..c6678ff 100644 --- a/src/shaders_140/shapecorners.frag +++ b/src/shaders_140/shapecorners.frag @@ -1,24 +1,36 @@ #version 140 -uniform sampler2D front; -uniform float radius; -uniform vec2 windowSize; -uniform vec2 windowExpandedSize; -uniform vec2 windowTopLeft; -uniform vec4 shadowColor; -uniform float shadowSize; -uniform vec4 outlineColor; -uniform float outlineThickness; -uniform vec4 modulation; -uniform float saturation; - -in vec2 texcoord0; -out vec4 fragColor; +uniform sampler2D front; // The painted contents of the window. +uniform float radius; // The thickness of the outline in pixels specified in settings. +uniform vec2 windowSize; // Containing `window->frameGeometry().size()` +uniform vec2 windowExpandedSize; // Containing `window->expandedGeometry().size()` + +uniform vec2 windowTopLeft; /* The distance between the top-left of `expandedGeometry` and + * the top-left of `frameGeometry`. When `windowTopLeft = {0,0}`, it means + * `expandedGeometry = frameGeometry` and there is no shadow. */ + +uniform vec4 shadowColor; // The RGBA of the shadow color specified in settings. +uniform float shadowSize; // The shadow size specified in settings. +uniform vec4 outlineColor; // The RGBA of the outline color specified in settings. +uniform float outlineThickness; // The thickness of the outline in pixels specified in settings. + +uniform vec4 modulation; // This variable is assigned and used by KWinEffects used for proper fading. +uniform float saturation; // This variable is assigned and used by KWinEffects used for proper fading. + +in vec2 texcoord0; // The XY location of the rendering pixel. Starting from {0.0, 0.0} to {1.0, 1.0} +out vec4 fragColor; // The RGBA color that can be rendered bool isDrawingShadows() { return windowSize != windowExpandedSize && shadowColor.a > 0.0; } bool isDrawingOutline() { return outlineColor.a > 0.0 && outlineThickness > 0.0; } -vec4 shadowCorner(float distance_from_center) { +/* + * \brief This function generates the shadow color based on the distance_from_center + * \param distance_from_center: Distance of the rendering point and the reference point that is being used for rounding corners. + * \return The RGBA color to be used for shadow. + */ +vec4 shadowColor(float distance_from_center) { + if(!isDrawingShadows()) + return vec4(0,0,0,0); float percent = -distance_from_center/shadowSize + 1; if(percent < 0) return vec4(0,0,0,0); @@ -26,23 +38,33 @@ vec4 shadowCorner(float distance_from_center) { return vec4(shadowColor.rgb * shadowColor.a * percent, shadowColor.a * percent); } +/* + * \brief This function is used to choose the pixel color based on its distance to the center input. + * \param coord0: The XY point + * \param tex: The RGBA color of the pixel in XY + * \param center: The origin XY point that is being used as a reference for rounding corners. + * \return The RGBA color to be used instead of tex input. + */ vec4 shapeCorner(vec2 coord0, vec4 tex, vec2 center) { float distance_from_center = distance(coord0, center); - vec4 c = isDrawingShadows() ? shadowCorner(distance_from_center) : vec4(0,0,0,0); + vec4 c = shadowColor(distance_from_center); if(isDrawingOutline()) { vec4 outlineOverlay = vec4(mix(tex.rgb, outlineColor.rgb, outlineColor.a), 1.0); if (distance_from_center < radius - outlineThickness/2.0) { + // from the window to the outline float antialiasing = clamp(radius-outlineThickness+0.5-distance_from_center, 0.0, 1.0); return mix(outlineOverlay, tex, antialiasing); } else { + // from the outline to the shadow float antialiasing = clamp(distance_from_center-radius+0.5, 0.0, 1.0); return mix(outlineOverlay, c, antialiasing); } } else { + // from the window to the shadow float antialiasing = clamp(radius-distance_from_center, 0.0, 1.0); return mix(c, tex, antialiasing); } @@ -50,33 +72,49 @@ vec4 shapeCorner(vec2 coord0, vec4 tex, vec2 center) { void main(void) { - vec4 tex = texture(front, texcoord0); + vec4 tex = texture(front, texcoord0); // The RGBA of the XY pixel for the painted window + + /* Since `texcoord0` is ranging from {0.0, 0.0} to {1.0, 1.0} is not pixel intuitive, + * I am changing the range to become from {0.0, 0.0} to {width, height} + * in a way that {0,0} is the top-left of the window and not its shadow. + * This means areas with negative numbers and areas beyond windowSize is considered part of the shadow. */ vec2 coord0 = vec2(texcoord0.x * windowExpandedSize.x - windowTopLeft.x, (1-texcoord0.y)* windowExpandedSize.y - windowTopLeft.y); + /* + Split the window into these sections below. They will have a different center of circle for rounding. + + TL T T TR + L x x R + L x x R + BL B B BR + */ if (coord0.y < radius) { if (coord0.x < radius) - tex = shapeCorner(coord0, tex, vec2(radius, radius)); + tex = shapeCorner(coord0, tex, vec2(radius, radius)); // Section TL else if (coord0.x > windowSize.x - radius) - tex = shapeCorner(coord0, tex, vec2(windowSize.x - radius, radius)); + tex = shapeCorner(coord0, tex, vec2(windowSize.x - radius, radius)); // Section TR else if (coord0.y < outlineThickness) - tex = shapeCorner(coord0, tex, vec2(coord0.x, radius)); + tex = shapeCorner(coord0, tex, vec2(coord0.x, radius)); // Section T } else if (coord0.y > windowSize.y - radius) { if (coord0.x < radius) - tex = shapeCorner(coord0, tex, vec2(radius, windowSize.y - radius)); + tex = shapeCorner(coord0, tex, vec2(radius, windowSize.y - radius)); // Section BL else if (coord0.x > windowSize.x - radius) - tex = shapeCorner(coord0, tex, vec2(windowSize.x - radius, windowSize.y - radius)); + tex = shapeCorner(coord0, tex, vec2(windowSize.x - radius, windowSize.y - radius)); // Section BR else if (coord0.y > windowSize.y - outlineThickness) - tex = shapeCorner(coord0, tex, vec2(coord0.x, windowSize.y - radius)); + tex = shapeCorner(coord0, tex, vec2(coord0.x, windowSize.y - radius)); // Section B } else { if (coord0.x < radius) - tex = shapeCorner(coord0, tex, vec2(radius, coord0.y)); + tex = shapeCorner(coord0, tex, vec2(radius, coord0.y)); // Section L else if (coord0.x > windowSize.x - radius) - tex = shapeCorner(coord0, tex, vec2(windowSize.x - radius, coord0.y)); + tex = shapeCorner(coord0, tex, vec2(windowSize.x - radius, coord0.y)); // Section R + + // For section x, the tex is not changing } + // Apply the saturation and modulation. This is essential for proper fades in other KWin effects. if (saturation != 1.0) { vec3 desaturated = tex.rgb * vec3( 0.30, 0.59, 0.11 ); desaturated = vec3( dot( desaturated, tex.rgb )); @@ -84,5 +122,6 @@ void main(void) } tex *= modulation; + // Send to output fragColor = tex; } From 51b0562d75ebb0d9dff0436ef77f8fb1deffef82 Mon Sep 17 00:00:00 2001 From: Matin Lotfaliei Date: Sat, 16 Dec 2023 23:09:33 -0800 Subject: [PATCH 2/2] Change function name --- src/shaders_110/shapecorners.frag | 4 ++-- src/shaders_140/shapecorners.frag | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/shaders_110/shapecorners.frag b/src/shaders_110/shapecorners.frag index 6cd9d8d..eaf1741 100644 --- a/src/shaders_110/shapecorners.frag +++ b/src/shaders_110/shapecorners.frag @@ -29,7 +29,7 @@ bool isDrawingOutline() { return outlineColor.a > 0.0 && outlineThickness > 0.0 * \param distance_from_center: Distance of the rendering point and the reference point that is being used for rounding corners. * \return The RGBA color to be used for shadow. */ -vec4 shadowColor(float distance_from_center) { +vec4 getShadowColor(float distance_from_center) { if(!isDrawingShadows()) return vec4(0.0, 0.0, 0.0, 0.0); float percent = -distance_from_center/shadowSize + 1.0; @@ -48,7 +48,7 @@ vec4 shadowColor(float distance_from_center) { */ vec4 shapeCorner(vec2 coord0, vec4 tex, vec2 center) { float distance_from_center = distance(coord0, center); - vec4 c = shadowColor(distance_from_center); + vec4 c = getShadowColor(distance_from_center); if(isDrawingOutline()) { vec4 outlineOverlay = vec4(mix(tex.rgb, outlineColor.rgb, outlineColor.a), 1.0); diff --git a/src/shaders_140/shapecorners.frag b/src/shaders_140/shapecorners.frag index c6678ff..7505b45 100644 --- a/src/shaders_140/shapecorners.frag +++ b/src/shaders_140/shapecorners.frag @@ -28,7 +28,7 @@ bool isDrawingOutline() { return outlineColor.a > 0.0 && outlineThickness > 0.0 * \param distance_from_center: Distance of the rendering point and the reference point that is being used for rounding corners. * \return The RGBA color to be used for shadow. */ -vec4 shadowColor(float distance_from_center) { +vec4 getShadowColor(float distance_from_center) { if(!isDrawingShadows()) return vec4(0,0,0,0); float percent = -distance_from_center/shadowSize + 1; @@ -47,7 +47,7 @@ vec4 shadowColor(float distance_from_center) { */ vec4 shapeCorner(vec2 coord0, vec4 tex, vec2 center) { float distance_from_center = distance(coord0, center); - vec4 c = shadowColor(distance_from_center); + vec4 c = getShadowColor(distance_from_center); if(isDrawingOutline()) { vec4 outlineOverlay = vec4(mix(tex.rgb, outlineColor.rgb, outlineColor.a), 1.0);