Render to texture - Exception when deleting framebuffer

Hi,

I’m currently working on an application using interactive genetic algorithms to evolve 3d objects.

Maybe you want to have a look at a video I’ve done before:



http://www.youtube.com/watch?v=7v5BAiXcUrs


The application shows “rooms” as textures on sliding panels. These are using offscreen rendering. When a panel is destroyed, the offscreen processor is also removed and viewport and framebuffer gets destroyed. This used to work fine some months ago (with jme svn rev. 6207), but now I often get a renderer crash on Renderer.deleteFrameBuffer(). The Exception is:



SCHWERWIEGEND: Problem FBO:

FrameBuffer[format=1024x1024x1, drawBuf=0]

Depth => BufferTarget[format=Depth]

Color(0) => TextureTarget[format=RGBA8]

04.07.2011 11:32:40 com.jme3.app.Application handleError

SCHWERWIEGEND: Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main]

java.lang.IllegalStateException: Framebuffer is missing required attachment.

at com.jme3.renderer.lwjgl.LwjglRenderer.checkFrameBufferError(LwjglRenderer.java:1269)

at com.jme3.renderer.lwjgl.LwjglRenderer.setFrameBuffer(LwjglRenderer.java:1523)

at com.jme3.renderer.RenderManager.renderViewPort(RenderManager.java:1103)

at com.jme3.renderer.RenderManager.render(RenderManager.java:1159)

at com.jme3.app.SimpleApplication.update(SimpleApplication.java:264)

at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:144)

at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:185)

at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:218)

at java.lang.Thread.run(Thread.java:662)



I’m using the following code for my offscreen processor, adapted from jme’s TestRenderToMemory:



[java]import com.jme3.asset.AssetManager;

import com.jme3.material.Material;

import com.jme3.math.ColorRGBA;

import com.jme3.post.SceneProcessor;

import com.jme3.renderer.Camera;

import com.jme3.renderer.RenderManager;

import com.jme3.renderer.ViewPort;

import com.jme3.renderer.queue.RenderQueue;

import com.jme3.scene.Node;

import com.jme3.system.AppSettings;

import com.jme3.texture.FrameBuffer;

import com.jme3.texture.Image.Format;

import com.jme3.texture.Texture2D;

public class OffscreenProcessor implements SceneProcessor {

private int renderWidth = 512;

private int renderHeight = 512;

private FrameBuffer offscreenBuffer;

private Camera offscreenCam;

private final Node offscreenScene;

private ViewPort offscreenView;

private final AssetManager assetManager;

private RenderManager rm;

private final AppSettings settings;

private ColorRGBA backgroundColor = ColorRGBA.White;

// the texture to render to

private Texture2D texture;

// the material that will be created

private final Material material;

// the key to apply the texture to the material

private final String textureKey;

/**

  • Creates a new offscreen texture processor with a SimpleTextured material.

    *
  • @param assetManager
  •      The AssetManager<br />
    
  • @param settings
  •      The application settings<br />
    
  • @param scene
  •      The scene that will be rendered<br />
    
  • @param material
  •      The material to apply the texture to<br />
    
  • @param textureKey
  •      The texture key used to apply the texture<br />
    

*/

public OffscreenProcessor(final AssetManager assetManager,

final AppSettings settings, final Node scene, final Material material,

final String textureKey) {

this.assetManager = assetManager;

this.settings = settings;

this.offscreenScene = scene;

this.material = material;

this.textureKey = textureKey;

init();

}

/**

  • Pre-initializes the processor.

    */

    private void init() {

    // update the state of the offscreen scene

    offscreenScene.updateGeometricState();

    texture = new Texture2D(renderWidth, renderHeight, Format.RGBA8);

    offscreenCam = new Camera(renderWidth, renderHeight);

    }

    /**
  • Sets the size of the offscreen texture.

    *
  • @param width
  •      With of the texture<br />
    
  • @param height
  •      Height of the texture<br />
    

*/

public void setTextureSize(final int width, final int height) {

this.renderWidth = width;

this.renderHeight = height;

init();

}

@Override

public void initialize(final RenderManager rm, final ViewPort vp) {

this.rm = rm;

// create a pre-view. a view that is rendered before the main view

offscreenView = rm.createPreView("Offscreen View", offscreenCam);

offscreenCam.setFrustum(vp.getCamera().getFrustumNear(), vp.getCamera()

.getFrustumFar(), vp.getCamera().getFrustumLeft(), vp.getCamera()

.getFrustumRight(), vp.getCamera().getFrustumTop(), vp.getCamera()

.getFrustumBottom());

offscreenView.setClearFlags(true, true, true);

offscreenView.setBackgroundColor(backgroundColor);

// create offscreen framebuffer

offscreenBuffer = new FrameBuffer(renderWidth, renderHeight, 1);

// setup framebuffer to use texture

offscreenBuffer.setDepthBuffer(Format.Depth);

offscreenBuffer.setColorTexture(texture);

// set viewport to render to offscreen framebuffer

offscreenView.setOutputFrameBuffer(offscreenBuffer);

// attach the scene to the viewport to be rendered

offscreenView.attachScene(offscreenScene);

// apply the texture

material.setTexture(textureKey, texture);

// EffectsFactory.addShadowProcessor(assetManager, settings, offscreenView,

// new Vector3f(7, 2, 3));

// EffectsFactory.addLightScatteringProcessor(assetManager, settings,

// offscreenView, new Vector3f(2, .1f, 2));

// EffectsFactory.addSSAOProcessor(assetManager, settings, offscreenView);

}

@Override

public void reshape(final ViewPort vp, final int w, final int h) {

}

@Override

public boolean isInitialized() {

return rm != null;

}

@Override

public void preFrame(final float tpf) {

}

@Override

public void postQueue(final RenderQueue rq) {

}

@Override

public void postFrame(final FrameBuffer out) {

}

@Override

public void cleanup() {

offscreenBuffer.clearColorTargets();

cleanup();

offscreenView.clearScenes();

offscreenView.setOutputFrameBuffer(null);

rm.clearQueue(offscreenView);

rm.removePreView(offscreenView);

// TODO this often causes a renderer crash

rm.getRenderer().deleteFrameBuffer(offscreenBuffer);

rm.getRenderer().deleteImage(texture.getImage());

while (offscreenView.getProcessors().size() > 0) {

final SceneProcessor proc = offscreenView.getProcessors().get(0);

offscreenView.removeProcessor(proc);

}

}

/**

  • Getter for the camera.

    *
  • @return The camera of the offscreen scene

    */

    public Camera getCamera() {

    return offscreenCam;

    }

    /**
  • Getter for the material to render to.

    *
  • @return The material on which is rendered

    */

    public Material getMaterial() {

    return material;

    }

    /**
  • Getter for the offscreen scene.

    *
  • @return The scene being rendered off screen

    */

    public Node getScene() {

    return offscreenScene;

    }

    /**
  • Getter for the background color of the scene.

    *
  • @return The color of the background

    */

    public ColorRGBA getBackgroundColor() {

    return backgroundColor;

    }

    /**
  • Setter for the background color of the scene.

    *
  • @param backgroundColor
  •      The color of the background<br />
    

*/

public void setBackgroundColor(final ColorRGBA backgroundColor) {

this.backgroundColor = backgroundColor;

if (isInitialized()) {

offscreenView.setBackgroundColor(backgroundColor);

}

}

}[/java]



The ViewPort.removeProcessor() method is called from the update() method.I already tried to change the order in the cleanup() method, but it did’nt help. When I remove the Renderer.deleteFrameBuffer() call, the error disappears but I need to clean up the frame buffers. A also tried to invoke the framebuffer cleanup later using Application.enqueue, but nothing changed.



Can someone give me a hint to solve this error?

wow this is so cool application, cant you provide the whole code ? e,g the main method etc ?

No, since I develop this application for my master thesis, I can not publish the whole code, sorry.

But if you have particular questions, maybe I can answer them.

Do not use deleteFrameBuffer. Those are deleted automatically when the framebuffer object is garbage collected by Java

1 Like
Momoko_Fan said:
Do not use deleteFrameBuffer. Those are deleted automatically when the framebuffer object is garbage collected by Java

Ok, you're right. I tested it with an explicit System.gc() call and it works. Thank you!

Which steps exactly can you recommend to clean the processor up, without producing any memory or performance leaks?


Again, here is the cleanup method:
[java]offscreenBuffer.clearColorTargets();
cleanup();
offscreenView.clearScenes();
offscreenView.setOutputFrameBuffer(null);
rm.clearQueue(offscreenView);
rm.removePreView(offscreenView);
// TODO this often causes a renderer crash
// rm.getRenderer().deleteFrameBuffer(offscreenBuffer);
rm.getRenderer().deleteImage(texture.getImage());
while (offscreenView.getProcessors().size() > 0) {
final SceneProcessor proc = offscreenView.getProcessors().get(0);
offscreenView.removeProcessor(proc);
}[/java]

It all works like normal java; if all references are gone, its garbage collected. So if you remove it from the view’s list and destroy all your own references its cleaned up.

I thought I read about a leak, where the native references where not deleted here.





This is no longer an issue in JME3?

No, the assetmanager handles this. It also doesn’t load textures into memory twice if they are the same. You can manually clear the assetManagers cache using assetManager.clearCache(); but its cleared as necessary normally. So you can literally always load a texture or model or anything from the assetManager when you want to use/attach it, it will be cached for you.

1 Like

Thats nice.



Now on a garbage collect, some of my textures disappear and the panels are black, but that needs further investigation. Thanks.

I’m not sure if there is no memory leak. After running my application for some minutes, its causing some trouble.



Windows Taskmanager says the java process consumes more than 1.3 GB:



The application does not run into an OutOfMemoryError, so I did a heap dump, which looks totally normal:



11 MB heap is not much but where does the heavy memory consumption come from?



After a while, an Exception occurs:

[xml]12.07.2011 20:45:52 com.jme3.renderer.lwjgl.LwjglRenderer setFrameBuffer
SCHWERWIEGEND: Problem FBO:
FrameBuffer[format=1024x1024x1, drawBuf=0]
Depth => BufferTarget[format=Depth]
Color(0) => TextureTarget[format=RGB8]

12.07.2011 20:45:52 com.jme3.app.Application handleError
SCHWERWIEGEND: Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main]
java.lang.IllegalStateException: Framebuffer has erronous attachment.
at com.jme3.renderer.lwjgl.LwjglRenderer.checkFrameBufferError(LwjglRenderer.java:1267)
at com.jme3.renderer.lwjgl.LwjglRenderer.setFrameBuffer(LwjglRenderer.java:1523)
at com.jme3.renderer.RenderManager.renderViewPort(RenderManager.java:1103)
at com.jme3.renderer.RenderManager.render(RenderManager.java:1159)
at com.jme3.app.SimpleApplication.update(SimpleApplication.java:264)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:144)
at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:185)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:218)
at java.lang.Thread.run(Thread.java:662)
[/xml]

I had a similar issue and in my search for answers I kept on stumbling upon this thread, so for future monkeys I am leaving this here. ( My bug was self inflicted. )



In the I found that.

[java]

public void destroy(){

renderManager.removePreView( offView );

offView.removeProcessor( this );

offView.setOutputFrameBuffer( null );

offBuffer = null;

}

[/java]

Detaches the SceneProcessor and FrameBuffer which is correctly GC’ed ( Confirmed through Profiling ). I call destroy() from the main rendering threads update loop, calling it from the SceneProcessors methods may cause concurrency issues…



Link to my troubleshooting thread: http://hub.jmonkeyengine.org/groups/general-2/forum/topic/render-to-texture-framebuffer-not-gced/

1 Like

@nihal: Can you please create a test case to reproduce this? Its rather strange that it is failing with just this code …