Shadow filter, anti-aliasing, and assertions

I’m getting an OpenGL error (OpenGLException: Invalid enum (1280)) when I combine DirectionalLightShadowFilter with anti-aliasing.

James Richardson reported a very similar issue back in 2015:
[SOLVED] DirectionalLightShadowFilter causing pixelation of geometry edges

His workaround was to disable assertions, but I’m unwilling to do that. Is there another solution?

Here’s my test code, which works with antialiasing disabled, but not with 2x antialiasing.

package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Sphere;
import com.jme3.shadow.DirectionalLightShadowFilter;

public class Main extends SimpleApplication {

    public static void main(String[] args) {
        Main app = new Main();
        app.start();
    }

    @Override
    public void simpleInitApp() {
        Sphere ballMesh = new Sphere(50, 50, 2f);
        Geometry ball = new Geometry("ball", ballMesh);

        Material material = new Material(assetManager,
                "Common/MatDefs/Light/Lighting.j3md");
        material.setBoolean("UseMaterialColors", true);
        material.setColor("Diffuse", ColorRGBA.Blue.clone());
        material.setColor("Specular", ColorRGBA.White.clone());
        material.setFloat("Shininess", 1f);
        ball.setMaterial(material);
        rootNode.attachChild(ball);

        Vector3f direction = new Vector3f(1f, 1f, -1f).normalizeLocal();
        DirectionalLight light
                = new DirectionalLight(direction, ColorRGBA.White.mult(2f));
        rootNode.addLight(light);

        DirectionalLightShadowFilter dlsf
                = new DirectionalLightShadowFilter(assetManager, 1024, 3);
        dlsf.setLight(light);
        dlsf.setEnabled(true);

        FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
        viewPort.addProcessor(fpp);
        int numSamples = settings.getSamples();
        System.err.printf("numSamples = %d%n", numSamples);
        if (numSamples > 1) {
            fpp.setNumSamples(numSamples);
        }

        fpp.addFilter(dlsf);
    }
}

Hey Stephen does that error also occur when combining a DirectionalLightShadowRenderer with anti-aliasing?

1 Like

I tested with a shadow renderer, and the OpenGL error did not occur.

Btw, my app uses shadow filters because a filter can be disabled dynamically using Filter.setEnable(). I haven’t figured out how to do that to a shadow renderer.

1 Like

I have always favored a Renderer instead of a Filter because I think they produce a better shadow quality and they are less resource intensive especially when less than 30 objects exist in the Scene Graph.

Anyway there is no equivalent to Filter.setEnable() for the Renderer,but you could use shadowRenderer.setShadowIntensity(0); which would make the shadows of the objects invisible.
You could also use rootNode.setShadowMode(ShadowMode.Off); which will disable shadows for the whole scene, except where overridden.

I’m not sure if any of these will prevent the system from wasting resources though.Good luck with your app!

1 Like

You can remove the processor from the viewport… that’s more the recommended way.

@sgold gonna look into the issue.

1 Like

I’ve opened an issue against the Engine:

I notice that shadow renderers don’t do any cleanup when they’re removed from the viewport. Could this be an issue?

do you have an issue?

1 Like

I have a performance issue plus inconsistencies in the rendering of the shadows. I see these issues in Maud after I add a directional-light shadow renderer, remove it, add another (with different nbShadowMaps or shadowMapSize), remove that, add another, remove that, etc.

I wonder whether there’s a need for additional re-initialization or cleanup.

Well if you don’t add the same renderer and that you create another one each time there might be a lot of framebuffers and textures created. and never cleaned up.
Maybe it should be done in the cleanup, but maybe you should recycle your renderers.

1 Like

I can only recycle the renderer if it has the same nbShadowMaps and shadowMapSize.

yeah… well I guess some cleanup should be done anyway…

1 Like

Could you maybe try to profile your app and see what’s inflating the memory/ cpu?

1 Like

I hear the disk running like crazy, so I assume it’s paging virtual memory. I don’t know how to determine the cause.

I tried BufferUtils.printCurrentDirectMemory() and that didn’t enlighten me.

I do have a test app you can try, if you wish:

package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.light.DirectionalLight;
import com.jme3.shadow.DirectionalLightShadowRenderer;
import com.jme3.util.BufferUtils;

public class TestDlsrPerf extends SimpleApplication {

    int iteration = 1;
    int nbSplits = 4;
    int size = 4096;
    DirectionalLight light;
    DirectionalLightShadowRenderer dlsr;

    public static void main(String[] args) {
        BufferUtils.setTrackDirectMemoryEnabled(true);
        TestDlsrPerf app = new TestDlsrPerf();
        app.start();
    }

    @Override
    public void simpleInitApp() {
        light = new DirectionalLight();
        rootNode.addLight(light);

        System.out.format("DLSR Stress Test with mapSize = %d%n", size);
        dlsr = new DirectionalLightShadowRenderer(assetManager, size, nbSplits);
        dlsr.setLight(light);
        viewPort.addProcessor(dlsr);
    }

    @Override
    public void simpleUpdate(float tpf) {
        System.out.format("iteration #%d:  %f sec%n", iteration, tpf);
        
        //StringBuilder stringBuilder = new StringBuilder(256);
        //BufferUtils.printCurrentDirectMemory(stringBuilder);
        //System.out.println(stringBuilder.toString());

        viewPort.removeProcessor(dlsr);
        ++iteration;

        if (tpf > 2) {
            System.exit(0);
        } else {
            nbSplits = 7 - nbSplits;
            dlsr = new DirectionalLightShadowRenderer(assetManager, size, nbSplits);
            dlsr.setLight(light);
            viewPort.addProcessor(dlsr);
        }
    }
}

Here’s the output from a typical run:

Jan 08, 2018 12:26:10 PM com.jme3.system.JmeDesktopSystem initialize
INFO: Running on jMonkeyEngine 3.2-stable
 * Branch: HEAD
 * Git Hash: 95d33e6
 * Build Date: 2018-01-05
Jan 08, 2018 12:26:11 PM com.jme3.system.lwjgl.LwjglContext printContextInitInfo
INFO: LWJGL 2.9.3 context running on thread jME3 Main
 * Graphics Adapter: nvd3dumx,nvwgf2umx,nvwgf2umx
 * Driver Version: 21.21.13.7633
 * Scaling Factor: 1
Jan 08, 2018 12:26:11 PM com.jme3.renderer.opengl.GLRenderer loadCapabilitiesCommon
INFO: OpenGL Renderer Information
 * Vendor: NVIDIA Corporation
 * Renderer: GeForce GT 545/PCIe/SSE2
 * OpenGL Version: 4.5.0 NVIDIA 376.33
 * GLSL Version: 4.50 NVIDIA
 * Profile: Compatibility
Jan 08, 2018 12:26:11 PM com.jme3.audio.openal.ALAudioRenderer initOpenAL
INFO: Audio Renderer Information
 * Device: OpenAL Soft
 * Vendor: OpenAL Community
 * Renderer: OpenAL Soft
 * Version: 1.1 ALSOFT 1.15.1
 * Supported channels: 64
 * ALC extensions: ALC_ENUMERATE_ALL_EXT ALC_ENUMERATION_EXT ALC_EXT_CAPTURE ALC_EXT_DEDICATED ALC_EXT_disconnect ALC_EXT_EFX ALC_EXT_thread_local_context ALC_SOFT_loopback
 * AL extensions: AL_EXT_ALAW AL_EXT_DOUBLE AL_EXT_EXPONENT_DISTANCE AL_EXT_FLOAT32 AL_EXT_IMA4 AL_EXT_LINEAR_DISTANCE AL_EXT_MCFORMATS AL_EXT_MULAW AL_EXT_MULAW_MCFORMATS AL_EXT_OFFSET AL_EXT_source_distance_model AL_LOKI_quadriphonic AL_SOFT_buffer_samples AL_SOFT_buffer_sub_data AL_SOFTX_deferred_updates AL_SOFT_direct_channels AL_SOFT_loop_points AL_SOFT_source_latency
Jan 08, 2018 12:26:11 PM com.jme3.audio.openal.ALAudioRenderer initOpenAL
WARNING: Pausing audio device not supported.
Jan 08, 2018 12:26:11 PM com.jme3.audio.openal.ALAudioRenderer initOpenAL
INFO: Audio effect extension version: 1.0
Jan 08, 2018 12:26:11 PM com.jme3.audio.openal.ALAudioRenderer initOpenAL
INFO: Audio max auxiliary sends: 4
:run
DLSR Stress Test with mapSize = 4096
iteration #1:  0.171069 sec
iteration #2:  0.105086 sec
iteration #3:  0.046529 sec
iteration #4:  0.015912 sec
iteration #5:  0.047276 sec
iteration #6:  0.078300 sec
iteration #7:  0.014673 sec
iteration #8:  0.032846 sec
iteration #9:  0.094317 sec
iteration #10:  0.011067 sec
iteration #11:  0.083999 sec
iteration #12:  0.980057 sec
iteration #13:  0.976999 sec
iteration #14:  0.899747 sec
iteration #15:  1.493412 sec
iteration #16:  1.005661 sec
iteration #17:  1.135612 sec
iteration #18:  0.936595 sec
iteration #19:  0.998461 sec
iteration #20:  1.039107 sec
iteration #21:  1.227077 sec
iteration #22:  1.035130 sec
iteration #23:  1.316993 sec
iteration #24:  1.056825 sec
iteration #25:  1.314688 sec
iteration #26:  1.037572 sec
iteration #27:  11.469026 sec
AL lib: (EE) alc_cleanup: 1 device not closed

If I set size = 8192 it typically runs only a handful of iterations before giving up:

DLSR Stress Test with mapSize = 8192
iteration #1:  0.147230 sec
iteration #2:  0.115998 sec
iteration #3:  0.168471 sec
iteration #4:  1.487428 sec
iteration #5:  1.568473 sec
iteration #6:  3.105944 sec

I tried attaching a DetailedProfilerState and got the dreaded OpenGL error:

DLSR Stress Test with mapSize = 8192
iteration #1:  0.162148 sec
iteration #2:  0.327761 sec
iteration #3:  0.109347 sec
Jan 08, 2018 1:28:11 PM com.jme3.app.LegacyApplication handleError
SEVERE: An OpenGL error has occured!
org.lwjgl.opengl.OpenGLException: Invalid operation (1282)
	at org.lwjgl.opengl.Util.checkGLError(Util.java:59)
	at com.jme3.system.lwjgl.LwjglAbstractDisplay.checkGLError(LwjglAbstractDisplay.java:136)
	at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:157)
	at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:197)
	at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:232)
	at java.lang.Thread.run(Thread.java:748)

Disabling assertions allowed me to run the test (with mapSize = 4096) for long enough to see that most of the time was spent in DLSR’s postQueue processing.