From a7c000809dbccf411ab0b4796cd7d8e287de3bd3 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Mon, 23 Feb 2026 20:01:10 -0800 Subject: [PATCH 1/4] Fix #2620 --- .../jme3/renderer/opengl/ComputeShader.java | 34 +++++++++- .../SdsmDirectionalLightShadowRenderer.java | 1 - .../main/java/com/jme3/shadow/SdsmFitter.java | 65 +++++++++++++++---- .../MatDefs/Shadow/Sdsm/FitLightFrustums.comp | 10 +-- .../MatDefs/Shadow/Sdsm/ReduceDepth.comp | 8 ++- .../MatDefs/Shadow/Sdsm/SdsmPostShadow.frag | 9 +-- .../MatDefs/Shadow/Sdsm/SdsmPostShadow.j3md | 5 +- .../Common/ShaderLib/MultiSample.glsllib | 56 ++++++++++++---- .../com/jme3/shader/plugins/GLSLLoader.java | 3 + 9 files changed, 152 insertions(+), 39 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/ComputeShader.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/ComputeShader.java index ae0b148329..594888c624 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/ComputeShader.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/ComputeShader.java @@ -50,7 +50,9 @@ public class ComputeShader extends NativeObject { private final GL4 gl; + // These need not be stored, but it helps with debugging. private final String source; + /** * Creates a new compute shader from GLSL source code. */ @@ -58,17 +60,43 @@ public ComputeShader(GL4 gl, String source) { super(); this.gl = gl; this.source = source; - //Load this upfront to surface any problems at init time + // Load this upfront to surface any problems at init time createComputeShader(); } - private ComputeShader(ComputeShader source){ + + /** + * Creates a new compute shader from GLSL source code and a set of defines. + */ + public ComputeShader(GL4 gl, String source, String[][] defines) { + super(); + this.gl = gl; + this.source = addDefines(source, defines); + System.out.println(this.source); + // Load this upfront to surface any problems at init time + createComputeShader(); + } + + private ComputeShader(ComputeShader source) { super(); this.gl = source.gl; this.id = source.id; this.source = null; } - private void createComputeShader(){ + private String addDefines(String source, String[][] defines) { + String[] sourceLines = (String[])source.split("\\r?\\n"); + StringBuilder builder = new StringBuilder(); + builder.append(sourceLines[0] + "\n"); + for (String[] pair : defines) { + builder.append("#define " + pair[0] + " " + pair[1] + "\n"); + } + for (int i = 1; i < sourceLines.length; ++i) { + builder.append(sourceLines[i] + "\n"); + } + return builder.toString(); + } + + private void createComputeShader() { // Create and compile the shader int shaderId = gl.glCreateShader(GL4.GL_COMPUTE_SHADER); if (shaderId <= 0) { diff --git a/jme3-core/src/main/java/com/jme3/shadow/SdsmDirectionalLightShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/SdsmDirectionalLightShadowRenderer.java index 44e336684c..3e3af1ffee 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/SdsmDirectionalLightShadowRenderer.java +++ b/jme3-core/src/main/java/com/jme3/shadow/SdsmDirectionalLightShadowRenderer.java @@ -147,7 +147,6 @@ private void initGL() { sdsmFitter = new SdsmFitter(gl4, renderer, assetManager); glInitialized = true; - } /** diff --git a/jme3-core/src/main/java/com/jme3/shadow/SdsmFitter.java b/jme3-core/src/main/java/com/jme3/shadow/SdsmFitter.java index 8bb2aad765..a51b82431a 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/SdsmFitter.java +++ b/jme3-core/src/main/java/com/jme3/shadow/SdsmFitter.java @@ -58,10 +58,16 @@ public class SdsmFitter { private final GL4 gl4; private final Renderer renderer; + private final AssetManager assetManager; private int maxFrameLag = 3; - private final ComputeShader depthReduceShader; - private final ComputeShader fitFrustumsShader; + private ComputeShader depthReduceShader; + private ComputeShader fitFrustumsShader; + + private ComputeShader depthReduceShaderNoMs; + private ComputeShader fitFrustumsShaderNoMs; + private ComputeShader depthReduceShaderMs; + private ComputeShader fitFrustumsShaderMs; private final LinkedList resultHoldersInFlight = new LinkedList<>(); private final LinkedList resultHoldersReady = new LinkedList<>(); @@ -313,15 +319,38 @@ void cleanup() { public SdsmFitter(GL4 gl, Renderer renderer, AssetManager assetManager) { this.gl4 = gl; this.renderer = renderer; + this.assetManager = assetManager; + } - // Load compute shaders + private ComputeShader buildShader(String source, boolean multisampling) { + ComputeShader shader = + multisampling + ? new ComputeShader(gl4, source, new String[][]{{"RESOLVE_DEPTH_MS", "1"}}) + : new ComputeShader(gl4, source); + renderer.registerNativeObject(shader); + return shader; + } + + private void initShaders(Texture depthTexture) { String reduceSource = (String)assetManager.loadAsset(REDUCE_DEPTH_SHADER); String fitSource = (String)assetManager.loadAsset(FIT_FRUSTUMS_SHADER); + + if (depthTexture.getImage().getMultiSamples() > 1) { + if (depthReduceShaderMs == null) { + depthReduceShaderMs = buildShader(reduceSource, true); + fitFrustumsShaderMs = buildShader(fitSource, true); + } + depthReduceShader = depthReduceShaderMs; + fitFrustumsShader = fitFrustumsShaderMs; - depthReduceShader = new ComputeShader(gl, reduceSource); - renderer.registerNativeObject(depthReduceShader); - fitFrustumsShader = new ComputeShader(gl, fitSource); - renderer.registerNativeObject(fitFrustumsShader); + } else if (depthTexture.getImage().getMultiSamples() == 1) { + if (depthReduceShaderNoMs == null) { + depthReduceShaderNoMs = buildShader(reduceSource, false); + fitFrustumsShaderNoMs = buildShader(fitSource, false); + } + depthReduceShader = depthReduceShaderNoMs; + fitFrustumsShader = fitFrustumsShaderNoMs; + } } /** @@ -336,6 +365,10 @@ public SdsmFitter(GL4 gl, Renderer renderer, AssetManager assetManager) { public void fit(Texture depthTexture, int splitCount, Matrix4f cameraToLight, float cameraNear, float cameraFar) { + int depthMultiSamples = depthTexture.getImage().getMultiSamples(); + + initShaders(depthTexture); + SdsmResultHolder holder = getResultHolderForUse(); holder.parameters = new FitParameters(cameraToLight, splitCount, cameraNear, cameraFar); @@ -360,6 +393,8 @@ public void fit(Texture depthTexture, int splitCount, Matrix4f cameraToLight, } catch (TextureUnitException e) { throw new RendererException(e); } + int loc = depthReduceShader.getUniformLocation("m_NumSamplesDepth"); + depthReduceShader.setUniform(loc, depthMultiSamples); depthReduceShader.bindShaderStorageBuffer(1, holder.minMaxDepthSsbo); depthReduceShader.dispatch(xGroups, yGroups, 1); gl4.glMemoryBarrier(GL4.GL_SHADER_STORAGE_BARRIER_BIT); @@ -373,6 +408,8 @@ public void fit(Texture depthTexture, int splitCount, Matrix4f cameraToLight, } catch (TextureUnitException e) { throw new RendererException(e); } + loc = fitFrustumsShader.getUniformLocation("m_NumSamplesDepth"); + fitFrustumsShader.setUniform(loc, depthMultiSamples); fitFrustumsShader.bindShaderStorageBuffer(1, holder.minMaxDepthSsbo); fitFrustumsShader.bindShaderStorageBuffer(2, holder.fitFrustumSsbo); @@ -437,11 +474,17 @@ public void cleanup() { } resultHoldersReady.clear(); - if (depthReduceShader != null) { - depthReduceShader.deleteObject(renderer); + if (depthReduceShaderMs != null) { + depthReduceShaderMs.deleteObject(renderer); + } + if (fitFrustumsShaderMs != null) { + fitFrustumsShaderMs.deleteObject(renderer); + } + if (depthReduceShaderNoMs != null) { + depthReduceShaderNoMs.deleteObject(renderer); } - if (fitFrustumsShader != null) { - fitFrustumsShader.deleteObject(renderer); + if (fitFrustumsShaderNoMs != null) { + fitFrustumsShaderNoMs.deleteObject(renderer); } } diff --git a/jme3-core/src/main/resources/Common/MatDefs/Shadow/Sdsm/FitLightFrustums.comp b/jme3-core/src/main/resources/Common/MatDefs/Shadow/Sdsm/FitLightFrustums.comp index 191da705dc..47971d1852 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Shadow/Sdsm/FitLightFrustums.comp +++ b/jme3-core/src/main/resources/Common/MatDefs/Shadow/Sdsm/FitLightFrustums.comp @@ -1,12 +1,14 @@ #version 430 +#import "Common/ShaderLib/MultiSample.glsllib" + /** * Computes tight bounding boxes for each shadow cascade via min/max on lightspace locations of depth samples that fall within each cascade. */ layout(local_size_x = 16, local_size_y = 16) in; -layout(binding = 0) uniform sampler2D inputDepth; +layout(binding = 0) uniform DEPTHTEXTURE inputDepth; layout(std430, binding = 1) readonly buffer MinMaxBuffer { uint gMin; @@ -103,7 +105,7 @@ void main() { ivec2 gid = ivec2(gl_GlobalInvocationID.xy); ivec2 lid = ivec2(gl_LocalInvocationID.xy); uint tid = gl_LocalInvocationIndex; - ivec2 inputSize = textureSize(inputDepth, 0); + ivec2 inputSize = getTextureSize(inputDepth); ivec2 baseCoord = gid * 2; // Initialize local bounds to infinity @@ -126,11 +128,11 @@ void main() { for (int x = 0; x < 2; x++) { ivec2 coord = baseCoord + ivec2(x, y); if (coord.x < inputSize.x && coord.y < inputSize.y) { - float depth = texelFetch(inputDepth, coord, 0).r; + float depth = getDepthMax(inputDepth, coord, m_NumSamplesDepth).r; // Skip background (depth == 1.0) if (depth != 1.0) { // Reconstruct clip-space position from depth - vec2 uv = (vec2(coord) + 0.5) / vec2(textureSize(inputDepth, 0)); + vec2 uv = (vec2(coord) + 0.5) / vec2(getTextureSize(inputDepth)); vec4 clipPos = vec4(uv * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0); // Transform to light view space diff --git a/jme3-core/src/main/resources/Common/MatDefs/Shadow/Sdsm/ReduceDepth.comp b/jme3-core/src/main/resources/Common/MatDefs/Shadow/Sdsm/ReduceDepth.comp index c6424c43e1..33ec1e4603 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Shadow/Sdsm/ReduceDepth.comp +++ b/jme3-core/src/main/resources/Common/MatDefs/Shadow/Sdsm/ReduceDepth.comp @@ -1,12 +1,14 @@ #version 430 +#import "Common/ShaderLib/MultiSample.glsllib" + /** * Finds the global minimum/maximum values of a depth texture. */ layout(local_size_x = 16, local_size_y = 16) in; -layout(binding = 0) uniform sampler2D inputDepth; +layout(binding = 0) uniform DEPTHTEXTURE inputDepth; layout(std430, binding = 1) buffer MinMaxBuffer { uint gMin; @@ -33,7 +35,7 @@ void main() { ivec2 gid = ivec2(gl_GlobalInvocationID.xy); ivec2 lid = ivec2(gl_LocalInvocationID.xy); uint tid = gl_LocalInvocationIndex; - ivec2 inputSize = textureSize(inputDepth, 0); + ivec2 inputSize = getTextureSize(inputDepth); // Each thread samples a 2x2 block ivec2 baseCoord = gid * 2; @@ -43,7 +45,7 @@ void main() { for (int x = 0; x < 2; x++) { ivec2 coord = baseCoord + ivec2(x, y); if (coord.x < inputSize.x && coord.y < inputSize.y) { - float depth = texelFetch(inputDepth, coord, 0).r; + float depth = getDepthMax(inputDepth, coord, m_NumSamplesDepth).r; // Discard depth == 1.0 (background/sky) if (depth != 1.0) { minMax.x = min(minMax.x, depth); diff --git a/jme3-core/src/main/resources/Common/MatDefs/Shadow/Sdsm/SdsmPostShadow.frag b/jme3-core/src/main/resources/Common/MatDefs/Shadow/Sdsm/SdsmPostShadow.frag index c8a2c64057..a6e2dda5f1 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Shadow/Sdsm/SdsmPostShadow.frag +++ b/jme3-core/src/main/resources/Common/MatDefs/Shadow/Sdsm/SdsmPostShadow.frag @@ -1,9 +1,10 @@ #import "Common/ShaderLib/GLSLCompat.glsllib" +#import "Common/ShaderLib/MultiSample.glsllib" #import "Common/ShaderLib/Shadows.glsllib" //Stripped version of the usual shadow fragment shader for SDSM; it intentionally leaves out some features. -uniform sampler2D m_Texture; -uniform sampler2D m_DepthTexture; +uniform COLORTEXTURE m_Texture; +uniform DEPTHTEXTURE m_DepthTexture; uniform mat4 m_ViewProjectionMatrixInverse; uniform vec4 m_ViewProjectionMatrixRow2; @@ -43,8 +44,8 @@ float determineShadow(int index, vec4 worldPos){ } void main() { - float depth = texture2D(m_DepthTexture,texCoord).r; - vec4 color = texture2D(m_Texture,texCoord); + float depth = getColor(m_DepthTexture,texCoord).r; + vec4 color = getDepth(m_Texture,texCoord); //Discard shadow computation on the sky if(depth == 1.0){ diff --git a/jme3-core/src/main/resources/Common/MatDefs/Shadow/Sdsm/SdsmPostShadow.j3md b/jme3-core/src/main/resources/Common/MatDefs/Shadow/Sdsm/SdsmPostShadow.j3md index 3d82b3f440..aceaa3c225 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Shadow/Sdsm/SdsmPostShadow.j3md +++ b/jme3-core/src/main/resources/Common/MatDefs/Shadow/Sdsm/SdsmPostShadow.j3md @@ -31,7 +31,8 @@ MaterialDef Post Shadow { Texture2D DepthTexture Boolean BackfaceShadows //Not used. - Int NumSamples //Not used. + Int NumSamples + Int NumSamplesDepth } Technique { @@ -47,6 +48,8 @@ MaterialDef Post Shadow { FILTER_MODE : FilterMode PCFEDGE : PCFEdge SHADOWMAP_SIZE : ShadowMapSize + RESOLVE_MS : NumSamples + RESOLVE_DEPTH_MS : NumSamplesDepth } } } \ No newline at end of file diff --git a/jme3-core/src/main/resources/Common/ShaderLib/MultiSample.glsllib b/jme3-core/src/main/resources/Common/ShaderLib/MultiSample.glsllib index 8dc1e2a728..f43b498628 100644 --- a/jme3-core/src/main/resources/Common/ShaderLib/MultiSample.glsllib +++ b/jme3-core/src/main/resources/Common/ShaderLib/MultiSample.glsllib @@ -18,12 +18,12 @@ uniform int m_NumSamplesDepth; // NOTE: Only define multisample functions if multisample is available #if defined(GL_ARB_texture_multisample) || (defined GL_ES && __VERSION__>=310) vec4 textureFetch(in sampler2DMS tex,in vec2 texC, in int numSamples){ - ivec2 iTexC = ivec2(texC * vec2(textureSize(tex))); - vec4 color = vec4(0.0); - for (int i = 0; i < numSamples; i++){ - color += texelFetch(tex, iTexC, i); - } - return color / float(numSamples); + ivec2 iTexC = ivec2(texC * vec2(textureSize(tex))); + vec4 color = vec4(0.0); + for (int i = 0; i < numSamples; i++){ + color += texelFetch(tex, iTexC, i); + } + return color / float(numSamples); } vec4 fetchTextureSample(in sampler2DMS tex,in vec2 texC,in int sampleId){ @@ -40,25 +40,57 @@ vec4 getColorSingle(in sampler2DMS tex, in vec2 texC){ return texelFetch(tex, iTexC, 0); } -vec4 getDepth(in sampler2DMS tex,in vec2 texC){ - return textureFetch(tex,texC,m_NumSamplesDepth); +vec4 getDepth(in sampler2DMS tex, in vec2 texC){ + return textureFetch(tex,texC,m_NumSamplesDepth); + } -#endif +vec4 getDepthMax(in sampler2DMS tex, in ivec2 coord, in int numSamples){ + vec4 result = vec4(0.0); + for (int i = 0; i < numSamples; i++){ + result = max(result, texelFetch(tex, coord, i)); + } + return result; +} + +ivec2 getTextureSize(in sampler2DMS tex) { + return textureSize(tex); +} +#endif // Multisampling vec4 fetchTextureSample(in sampler2D tex,in vec2 texC,in int sampleId){ +#if __VERSION__>=430 + return texture(tex,texC); +#else return texture2D(tex,texC); +#endif } vec4 getColor(in sampler2D tex, in vec2 texC){ +#if __VERSION__>=430 + return texture(tex,texC); +#else return texture2D(tex,texC); +#endif } vec4 getColorSingle(in sampler2D tex, in vec2 texC){ - return texture2D(tex, texC); +#if __VERSION__>=430 + return texture(tex,texC); +#else + return texture2D(tex,texC); +#endif } -vec4 getDepth(in sampler2D tex,in vec2 texC){ - return texture2D(tex,texC); +vec4 getDepth(in sampler2D tex, in vec2 texC){ + ivec2 iTexC = ivec2(texC * vec2(textureSize(tex, 0))); + return texelFetch(tex, iTexC, 0); +} + +vec4 getDepthMax(in sampler2D tex, in ivec2 coord, in int numSamples){ + return texelFetch(tex, coord, 0); } +ivec2 getTextureSize(in sampler2D tex){ + return textureSize(tex, 0); +} diff --git a/jme3-core/src/plugins/java/com/jme3/shader/plugins/GLSLLoader.java b/jme3-core/src/plugins/java/com/jme3/shader/plugins/GLSLLoader.java index 63b8ba68c7..2db01c47f2 100644 --- a/jme3-core/src/plugins/java/com/jme3/shader/plugins/GLSLLoader.java +++ b/jme3-core/src/plugins/java/com/jme3/shader/plugins/GLSLLoader.java @@ -108,6 +108,9 @@ private ShaderDependencyNode loadNode(Reader reader, String nodeName) { } } else if (tln.startsWith("#extension ")) { sbExt.append(ln).append('\n'); + } else if (tln.startsWith("#version ")) { + // #version must appear before the extensions, so treat it like one. + sbExt.append(ln).append('\n'); } else { sb.append(ln).append('\n'); } From 7bc9c9f8723c6c1b045f36c785b5c57183f7edab Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Wed, 25 Feb 2026 18:08:59 -0800 Subject: [PATCH 2/4] Address feedback --- .../jme3/renderer/opengl/ComputeShader.java | 18 ++-- .../main/java/com/jme3/shadow/SdsmFitter.java | 101 ++++++++++-------- 2 files changed, 67 insertions(+), 52 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/ComputeShader.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/ComputeShader.java index 594888c624..04173b0eea 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/ComputeShader.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/ComputeShader.java @@ -50,7 +50,7 @@ public class ComputeShader extends NativeObject { private final GL4 gl; - // These need not be stored, but it helps with debugging. + // The source need not be stored, but it helps with debugging. private final String source; /** @@ -60,19 +60,21 @@ public ComputeShader(GL4 gl, String source) { super(); this.gl = gl; this.source = source; - // Load this upfront to surface any problems at init time + // Load this up front to surface any problems at init time. createComputeShader(); } /** * Creates a new compute shader from GLSL source code and a set of defines. + * + * @param defines An array of string pairs. The first element of the pair + * is the macro name; the second element, the definition. */ public ComputeShader(GL4 gl, String source, String[][] defines) { super(); this.gl = gl; this.source = addDefines(source, defines); - System.out.println(this.source); - // Load this upfront to surface any problems at init time + // Load this up front to surface any problems at init time. createComputeShader(); } @@ -84,15 +86,15 @@ private ComputeShader(ComputeShader source) { } private String addDefines(String source, String[][] defines) { - String[] sourceLines = (String[])source.split("\\r?\\n"); + // The #version pragma must appear before anything else. Insert the + // defines after it. + String[] sourceLines = (String[])source.split("\\r?\\n", 2); StringBuilder builder = new StringBuilder(); builder.append(sourceLines[0] + "\n"); for (String[] pair : defines) { builder.append("#define " + pair[0] + " " + pair[1] + "\n"); } - for (int i = 1; i < sourceLines.length; ++i) { - builder.append(sourceLines[i] + "\n"); - } + builder.append(sourceLines[1] + "\n"); return builder.toString(); } diff --git a/jme3-core/src/main/java/com/jme3/shadow/SdsmFitter.java b/jme3-core/src/main/java/com/jme3/shadow/SdsmFitter.java index a51b82431a..2dce7d4a89 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/SdsmFitter.java +++ b/jme3-core/src/main/java/com/jme3/shadow/SdsmFitter.java @@ -61,13 +61,13 @@ public class SdsmFitter { private final AssetManager assetManager; private int maxFrameLag = 3; - private ComputeShader depthReduceShader; - private ComputeShader fitFrustumsShader; - - private ComputeShader depthReduceShaderNoMs; - private ComputeShader fitFrustumsShaderNoMs; - private ComputeShader depthReduceShaderMs; - private ComputeShader fitFrustumsShaderMs; + // Because the multisampling status of the depth texture may change anytime, + // we store both ms and no-ms versions of the shaders, both initialized + // lazily. This avoids recompiling the same set of shaders twice and also + // compiling unnecessary shaders if the multisampling status does not + // change. + private InternalShaders shadersNoMultisampling; + private InternalShaders shadersMultisampling; private final LinkedList resultHoldersInFlight = new LinkedList<>(); private final LinkedList resultHoldersReady = new LinkedList<>(); @@ -316,40 +316,57 @@ void cleanup() { } } + /** + * Manages the pair of depth-reduce and fit-frustums shaders. + */ + private class InternalShaders { + public final ComputeShader depthReduceShader; + public final ComputeShader fitFrustumsShader; + + InternalShaders(AssetManager assetManager, boolean multisampling) { + String reduceSource = (String)assetManager.loadAsset(REDUCE_DEPTH_SHADER); + String fitSource = (String)assetManager.loadAsset(FIT_FRUSTUMS_SHADER); + + depthReduceShader = buildShader(reduceSource, multisampling); + fitFrustumsShader = buildShader(fitSource, multisampling); + } + + private ComputeShader buildShader(String source, boolean multisampling) { + ComputeShader shader = + multisampling + ? new ComputeShader(gl4, source, new String[][]{{"RESOLVE_DEPTH_MS", "1"}}) + : new ComputeShader(gl4, source); + renderer.registerNativeObject(shader); + return shader; + } + + /** + * Cleans up GPU resources. + */ + public void cleanup(Renderer renderer) { + depthReduceShader.deleteObject(renderer); + fitFrustumsShader.deleteObject(renderer); + } + } + public SdsmFitter(GL4 gl, Renderer renderer, AssetManager assetManager) { this.gl4 = gl; this.renderer = renderer; this.assetManager = assetManager; } - - private ComputeShader buildShader(String source, boolean multisampling) { - ComputeShader shader = - multisampling - ? new ComputeShader(gl4, source, new String[][]{{"RESOLVE_DEPTH_MS", "1"}}) - : new ComputeShader(gl4, source); - renderer.registerNativeObject(shader); - return shader; - } - private void initShaders(Texture depthTexture) { - String reduceSource = (String)assetManager.loadAsset(REDUCE_DEPTH_SHADER); - String fitSource = (String)assetManager.loadAsset(FIT_FRUSTUMS_SHADER); - - if (depthTexture.getImage().getMultiSamples() > 1) { - if (depthReduceShaderMs == null) { - depthReduceShaderMs = buildShader(reduceSource, true); - fitFrustumsShaderMs = buildShader(fitSource, true); + private InternalShaders initShaders(Texture depthTexture) { + boolean multisampling = depthTexture.getImage().getMultiSamples() > 1; + if (multisampling) { + if (shadersMultisampling == null) { + shadersMultisampling = new InternalShaders(assetManager, true); } - depthReduceShader = depthReduceShaderMs; - fitFrustumsShader = fitFrustumsShaderMs; - - } else if (depthTexture.getImage().getMultiSamples() == 1) { - if (depthReduceShaderNoMs == null) { - depthReduceShaderNoMs = buildShader(reduceSource, false); - fitFrustumsShaderNoMs = buildShader(fitSource, false); + return shadersMultisampling; + } else { + if (shadersNoMultisampling == null) { + shadersNoMultisampling = new InternalShaders(assetManager, false); } - depthReduceShader = depthReduceShaderNoMs; - fitFrustumsShader = fitFrustumsShaderNoMs; + return shadersNoMultisampling; } } @@ -367,7 +384,9 @@ public void fit(Texture depthTexture, int splitCount, Matrix4f cameraToLight, int depthMultiSamples = depthTexture.getImage().getMultiSamples(); - initShaders(depthTexture); + InternalShaders shaders = initShaders(depthTexture); + ComputeShader depthReduceShader = shaders.depthReduceShader; + ComputeShader fitFrustumsShader = shaders.fitFrustumsShader; SdsmResultHolder holder = getResultHolderForUse(); holder.parameters = new FitParameters(cameraToLight, splitCount, cameraNear, cameraFar); @@ -474,17 +493,11 @@ public void cleanup() { } resultHoldersReady.clear(); - if (depthReduceShaderMs != null) { - depthReduceShaderMs.deleteObject(renderer); - } - if (fitFrustumsShaderMs != null) { - fitFrustumsShaderMs.deleteObject(renderer); - } - if (depthReduceShaderNoMs != null) { - depthReduceShaderNoMs.deleteObject(renderer); + if (shadersMultisampling != null) { + shadersMultisampling.cleanup(renderer); } - if (fitFrustumsShaderNoMs != null) { - fitFrustumsShaderNoMs.deleteObject(renderer); + if (shadersNoMultisampling != null) { + shadersNoMultisampling.cleanup(renderer); } } From 87c35e5fe489ee96b9da7af76ada0e1acb909efb Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Fri, 27 Feb 2026 09:57:01 -0800 Subject: [PATCH 3/4] Address more feedback --- .../main/resources/Common/ShaderLib/MultiSample.glsllib | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/jme3-core/src/main/resources/Common/ShaderLib/MultiSample.glsllib b/jme3-core/src/main/resources/Common/ShaderLib/MultiSample.glsllib index f43b498628..9552057aba 100644 --- a/jme3-core/src/main/resources/Common/ShaderLib/MultiSample.glsllib +++ b/jme3-core/src/main/resources/Common/ShaderLib/MultiSample.glsllib @@ -83,8 +83,11 @@ vec4 getColorSingle(in sampler2D tex, in vec2 texC){ } vec4 getDepth(in sampler2D tex, in vec2 texC){ - ivec2 iTexC = ivec2(texC * vec2(textureSize(tex, 0))); - return texelFetch(tex, iTexC, 0); +#if __VERSION__>=430 + return texture(tex,texC); +#else + return texture2D(tex,texC); +#endif } vec4 getDepthMax(in sampler2D tex, in ivec2 coord, in int numSamples){ From 995504323dc61c3218bdba056eaef9d5ec021d62 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Mon, 2 Mar 2026 15:27:58 -0800 Subject: [PATCH 4/4] Use GLSL compat --- .../MatDefs/Shadow/Sdsm/FitLightFrustums.comp | 1 + .../Common/MatDefs/Shadow/Sdsm/ReduceDepth.comp | 1 + .../Common/ShaderLib/MultiSample.glsllib | 16 ---------------- 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/jme3-core/src/main/resources/Common/MatDefs/Shadow/Sdsm/FitLightFrustums.comp b/jme3-core/src/main/resources/Common/MatDefs/Shadow/Sdsm/FitLightFrustums.comp index 47971d1852..b77e8904b4 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Shadow/Sdsm/FitLightFrustums.comp +++ b/jme3-core/src/main/resources/Common/MatDefs/Shadow/Sdsm/FitLightFrustums.comp @@ -1,5 +1,6 @@ #version 430 +#import "Common/ShaderLib/GLSLCompat.glsllib" #import "Common/ShaderLib/MultiSample.glsllib" /** diff --git a/jme3-core/src/main/resources/Common/MatDefs/Shadow/Sdsm/ReduceDepth.comp b/jme3-core/src/main/resources/Common/MatDefs/Shadow/Sdsm/ReduceDepth.comp index 33ec1e4603..14d6f3de99 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Shadow/Sdsm/ReduceDepth.comp +++ b/jme3-core/src/main/resources/Common/MatDefs/Shadow/Sdsm/ReduceDepth.comp @@ -1,5 +1,6 @@ #version 430 +#import "Common/ShaderLib/GLSLCompat.glsllib" #import "Common/ShaderLib/MultiSample.glsllib" /** diff --git a/jme3-core/src/main/resources/Common/ShaderLib/MultiSample.glsllib b/jme3-core/src/main/resources/Common/ShaderLib/MultiSample.glsllib index 9552057aba..558ede0d4a 100644 --- a/jme3-core/src/main/resources/Common/ShaderLib/MultiSample.glsllib +++ b/jme3-core/src/main/resources/Common/ShaderLib/MultiSample.glsllib @@ -59,35 +59,19 @@ ivec2 getTextureSize(in sampler2DMS tex) { #endif // Multisampling vec4 fetchTextureSample(in sampler2D tex,in vec2 texC,in int sampleId){ -#if __VERSION__>=430 - return texture(tex,texC); -#else return texture2D(tex,texC); -#endif } vec4 getColor(in sampler2D tex, in vec2 texC){ -#if __VERSION__>=430 - return texture(tex,texC); -#else return texture2D(tex,texC); -#endif } vec4 getColorSingle(in sampler2D tex, in vec2 texC){ -#if __VERSION__>=430 - return texture(tex,texC); -#else return texture2D(tex,texC); -#endif } vec4 getDepth(in sampler2D tex, in vec2 texC){ -#if __VERSION__>=430 - return texture(tex,texC); -#else return texture2D(tex,texC); -#endif } vec4 getDepthMax(in sampler2D tex, in ivec2 coord, in int numSamples){