From 3b0be6236c82bfcda9a21ded068699debe5fc631 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Wed, 4 May 2022 21:32:21 +0200 Subject: [PATCH] :recycle: Make it possible to reuse the hexagon shader --- extension.js | 3 +- prefs.js | 3 +- src/Hexagon.js | 191 +++++++++++++++++++++++++++++++------------------ 3 files changed, 124 insertions(+), 73 deletions(-) diff --git a/extension.js b/extension.js index d92dafb..db42dc8 100644 --- a/extension.js +++ b/extension.js @@ -37,8 +37,7 @@ const ALL_EFFECTS = [ // Me.imports.src.BrokenGlass.BrokenGlass, // Me.imports.src.EnergizeA.EnergizeA, // Me.imports.src.EnergizeB.EnergizeB, - Me.imports.src.Fire.Fire, - // Me.imports.src.Hexagon.Hexagon, + Me.imports.src.Fire.Fire, Me.imports.src.Hexagon.Hexagon, // Me.imports.src.Matrix.Matrix, // Me.imports.src.SnapOfDisintegration.SnapOfDisintegration, // Me.imports.src.TRexAttack.TRexAttack, diff --git a/prefs.js b/prefs.js index 79cf248..6c218aa 100644 --- a/prefs.js +++ b/prefs.js @@ -36,8 +36,7 @@ const ALL_EFFECTS = [ // Me.imports.src.BrokenGlass.BrokenGlass, // Me.imports.src.EnergizeA.EnergizeA, // Me.imports.src.EnergizeB.EnergizeB, - Me.imports.src.Fire.Fire, - // Me.imports.src.Hexagon.Hexagon, + Me.imports.src.Fire.Fire, Me.imports.src.Hexagon.Hexagon, // Me.imports.src.Matrix.Matrix, // Me.imports.src.SnapOfDisintegration.SnapOfDisintegration, // Me.imports.src.TRexAttack.TRexAttack, diff --git a/src/Hexagon.js b/src/Hexagon.js index 06ebcd2..08a5903 100644 --- a/src/Hexagon.js +++ b/src/Hexagon.js @@ -26,8 +26,11 @@ const utils = Me.imports.src.utils; // gradually shrink until the window is fully dissolved. // ////////////////////////////////////////////////////////////////////////////////////////// -// The shader class for this effect is registered further down in this file. -let Shader = null; +// The shader class for this effect is registered further down in this file. When this +// effect is used for the first time, shaderInstance will store one instance of +// ShaderClass which will be used whenever this effect is used. +let ShaderClass = null; +let shaderInstance = null; // The effect class is completely static. It can be used to get some metadata (like the // effect's name or supported GNOME Shell versions), to initialize the respective page of @@ -79,9 +82,16 @@ var Hexagon = class Hexagon { // ---------------------------------------------------------------- API for extension.js - // This is called from extension.js whenever a window is closed with this effect. - static createShader(actor, settings, forOpening) { - return new Shader(settings, forOpening); + // This is called from extension.js whenever a window is opened or closed with this + // effect. + static getShader(actor, settings, forOpening) { + if (shaderInstance == null) { + shaderInstance = new ShaderClass(); + } + + shaderInstance.setUniforms(settings, forOpening); + + return shaderInstance; } // The tweakTransition() is called from extension.js to tweak a window's open / close @@ -101,6 +111,12 @@ var Hexagon = class Hexagon { 'scale-y': {from: 1.0, to: 1.0, mode: 1} }; } + + // This is called from extension.js if the extension is disabled. This should free all + // static resources. + static cleanUp() { + shaderInstance = null; + } } @@ -112,34 +128,72 @@ var Hexagon = class Hexagon { if (utils.isInShellProcess()) { - const Clutter = imports.gi.Clutter; - const shaderSnippets = Me.imports.src.shaderSnippets; + const {Clutter, Shell} = imports.gi; + const shaderSnippets = Me.imports.src.shaderSnippets; - Shader = GObject.registerClass({}, class Shader extends Clutter.ShaderEffect { - _init(settings, forOpening) { - super._init({shader_type: Clutter.ShaderType.FRAGMENT_SHADER}); + ShaderClass = GObject.registerClass({}, class ShaderClass extends Shell.GLSLEffect { + // This is called when the effect is used for the first time. This can be used to + // store all required uniform locations. + _init() { + super._init(); + + this._uForOpening = this.get_uniform_location('uForOpening'); + this._uAdditiveBlending = this.get_uniform_location('uAdditiveBlending'); + this._uSeed = this.get_uniform_location('uSeed'); + this._uScale = this.get_uniform_location('uScale'); + this._uLineWidth = this.get_uniform_location('uLineWidth'); + this._uGlowColor = this.get_uniform_location('uGlowColor'); + this._uLineColor = this.get_uniform_location('uLineColor'); + } + + // This is called each time the effect is used. This can be used to retrieve the + // configuration from the settings and update all uniforms accordingly. + setUniforms(settings, forOpening) { // Get the two configurable colors. They are directly injected into the shader code // below. - const glowColor = + const glow = Clutter.Color.from_string(settings.get_string('hexagon-glow-color'))[1]; - const lineColor = + const line = Clutter.Color.from_string(settings.get_string('hexagon-line-color'))[1]; // If we are currently performing integration test, the animation uses a fixed seed. const testMode = settings.get_boolean('test-mode'); + this.set_uniform_float(this._uForOpening, 1, [forOpening]); + this.set_uniform_float(this._uAdditiveBlending, 1, + [settings.get_boolean('hexagon-additive-blending')]); + this.set_uniform_float( + this._uSeed, 2, [testMode ? 0 : Math.random(), testMode ? 0 : Math.random()]); + this.set_uniform_float(this._uScale, 1, [settings.get_double('hexagon-scale')]); + this.set_uniform_float(this._uLineWidth, 1, + [settings.get_double('hexagon-line-width')]); + this.set_uniform_float( + this._uGlowColor, 4, + [glow.red / 255, glow.green / 255, glow.blue / 255, glow.alpha / 255]); + this.set_uniform_float( + this._uLineColor, 4, + [line.red / 255, line.green / 255, line.blue / 255, line.alpha / 255]); + } + + // This is called by the constructor. This is means it's only called when the effect + // is used for the first time. + vfunc_build_pipeline() { + // Feel free to read the inline comments below to learn how this shader works. - this.set_shader_source(` + const declarations = ` // Inject some common shader snippets. ${shaderSnippets.standardUniforms()} ${shaderSnippets.noise()} - const vec2 SEED = vec2(${testMode ? 0 : Math.random()}, - ${testMode ? 0 : Math.random()}); - const float SCALE = 10.0 * ${settings.get_double('hexagon-scale')}; - const float LINE_WIDTH = 0.02 * ${settings.get_double('hexagon-line-width')}; + uniform bool uForOpening; + uniform bool uAdditiveBlending; + uniform vec2 uSeed; + uniform float uScale; + uniform float uLineWidth; + uniform vec4 uGlowColor; + uniform vec4 uLineColor; // This methods generates a procedural hexagonal pattern. It returns four values: // result.xy: This contains cell-relative coordinates for the given point. @@ -182,69 +236,68 @@ if (utils.isInShellProcess()) { return vec4(cellCoords, dist, glow); } + `; - void main() { + const code = ` + // We simply inverse the progress for opening windows. + float progress = uForOpening ? 1.0-uProgress : uProgress; - // We simply inverse the progress for opening windows. - float progress = ${forOpening ? '1.0-uProgress' : 'uProgress'}; + // Add some smooth noise to the progress so that not every tile behaves the + // same. + float noise = simplex2D(cogl_tex_coord_in[0].st + uSeed); + progress = clamp(mix(noise - 1.0, noise + 1.0, progress), 0.0, 1.0); - // Add some smooth noise to the progress so that not every tile behaves the - // same. - float noise = simplex2D(cogl_tex_coord_in[0].st + SEED); - progress = clamp(mix(noise - 1.0, noise + 1.0, progress), 0.0, 1.0); + // glowProgress fades in in the first half of the animation, tileProgress fades + // in in the second half. + float glowProgress = smoothstep(0, 1, clamp(progress / 0.5, 0, 1)); + float tileProgress = smoothstep(0, 1, clamp((progress - 0.5) / 0.5, 0, 1)); - // glowProgress fades in in the first half of the animation, tileProgress fades - // in in the second half. - float glowProgress = smoothstep(0, 1, clamp(progress / 0.5, 0, 1)); - float tileProgress = smoothstep(0, 1, clamp((progress - 0.5) / 0.5, 0, 1)); + vec2 texScale = 0.1 * vec2(uSizeX, uSizeY) / uScale; + vec4 hex = getHexagons(cogl_tex_coord_in[0].st * texScale); - vec2 texScale = vec2(uSizeX, uSizeY) / SCALE; - vec4 hex = getHexagons(cogl_tex_coord_in[0].st * texScale); + if (tileProgress > hex.z) { - if (tileProgress > hex.z) { + // Crop outer parts of the shrinking tiles. + cogl_color_out *= vec4(0.0); - // Crop outer parts of the shrinking tiles. - cogl_color_out *= vec4(0.0); + } else { + // Make the tiles shrink by offsetting the texture lookup towards the edge + // of the cell. + vec2 lookupOffset = tileProgress * hex.xy / texScale / (1.0 - tileProgress); + cogl_color_out = texture2D(uTexture, cogl_tex_coord_in[0].st + lookupOffset); + + vec4 glow = uGlowColor; + vec4 line = uLineColor; + + // For the glow, we accumulate a few exponentially scaled versions of hex.w. + glow.a *= pow(hex.w, 20.0) * 10.0 + + pow(hex.w, 10.0) * 5.0 + + pow(hex.w, 2.0) * 0.5; + + // Using step(uLineWidth, hex.z) would be simpler, but the below creates some + // fake antialiasing. + line.a *= 1.0 - smoothstep(uLineWidth*0.02*0.5, uLineWidth*0.02, hex.z); + + // Fade in the glowing lines. + glow.a *= glowProgress; + line.a *= glowProgress; + + // Do not add the hexagon lines onto transparent parts of the window. + glow *= cogl_color_out.a; + line *= cogl_color_out.a; + + if (uAdditiveBlending) { + cogl_color_out.rgb += glow.rgb * glow.a; + cogl_color_out.rgb += line.rgb * line.a; } else { - - // Make the tiles shrink by offsetting the texture lookup towards the edge - // of the cell. - vec2 lookupOffset = tileProgress * hex.xy / texScale / (1.0 - tileProgress); - cogl_color_out = texture2D(uTexture, cogl_tex_coord_in[0].st + lookupOffset); - - vec4 glow = vec4(${glowColor.red / 255}, ${glowColor.green / 255}, - ${glowColor.blue / 255}, ${glowColor.alpha / 255}); - vec4 line = vec4(${lineColor.red / 255}, ${lineColor.green / 255}, - ${lineColor.blue / 255}, ${lineColor.alpha / 255}); - - // For the glow, we accumulate a few exponentially scaled versions of hex.w. - glow.a *= pow(hex.w, 20.0) * 10.0 + - pow(hex.w, 10.0) * 5.0 + - pow(hex.w, 2.0) * 0.5; - - // Using step(LINE_WIDTH, hex.z) would be simpler, but the below creates some - // fake antialiasing. - line.a *= 1.0 - smoothstep(LINE_WIDTH*0.5, LINE_WIDTH, hex.z); - - // Fade in the glowing lines. - glow.a *= glowProgress; - line.a *= glowProgress; - - // Do not add the hexagon lines onto transparent parts of the window. - glow *= cogl_color_out.a; - line *= cogl_color_out.a; - - if (${settings.get_boolean('hexagon-additive-blending')}) { - cogl_color_out.rgb += glow.rgb * glow.a; - cogl_color_out.rgb += line.rgb * line.a; - } else { - cogl_color_out.rgb = mix(cogl_color_out.rgb, glow.rgb, glow.a); - cogl_color_out.rgb = mix(cogl_color_out.rgb, line.rgb, line.a); - } + cogl_color_out.rgb = mix(cogl_color_out.rgb, glow.rgb, glow.a); + cogl_color_out.rgb = mix(cogl_color_out.rgb, line.rgb, line.a); } } - `); + `; + + this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, declarations, code, true); }; }); } \ No newline at end of file