Stopping and starting JME in swing with offscreen buffer?

I wanted to be running a jme canvas while showing little previews of certain models in another JPanel. So i got it to work using the TestRenderToMemory.java example that is found idk where. Anyway, if i keep a separate thing render window running that draws on an offscreen buffer then takes a screenshot of to display it (which is what TestRenderToMemory does), it takes up tons of CPU. So in order for this to be practical, i have to draw 1 image, then stop it, then start it again when i want to see another model.

WELL IT DOESNT LIKE WHEN I START IT UP AGAIN! D=

I am completely out of idea to fix it, so this thread is like my last hope, or else i use some other engine.

Anyway, im pretty frustrated right now and i want to go eat, so sorry if im just throwing code in here.

[java]public class AssetPreview extends SimpleApplication implements SceneProcessor
{

private FrameBuffer offBuffer;
private ViewPort offView;
private Camera offCamera;
private AssetPreview.ImageDisplay display;
private int x, y, width, height;
private final ByteBuffer cpuBuf;
private final BufferedImage image;
private String model;
private boolean isStarted = false;
private boolean startRender = false;
private Thread curThread;

private class ImageDisplay extends JPanel
{

    @Override
    public void paintComponent(Graphics gfx)
    {
        super.paintComponent(gfx);
        Graphics2D g2d = (Graphics2D) gfx;

        synchronized (image)
        {
            g2d.drawImage(image, null, 0, 0);
        }
    }
}

public AssetPreview(int x, int y, int width, int height)
{
    // initializing members
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
    cpuBuf = BufferUtils.createByteBuffer(width * height * 4);
    image = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);

    // making panel
    display = new AssetPreview.ImageDisplay();

    Logger logger = Logger.getLogger(AssetPreview.class.getName());
    logger.getLogger("").setLevel(Level.OFF);
}

public void loadModel(String model)
{
    while (curThread != null && curThread.isAlive())
    {
        System.out.println("APPLES");
        try
        {
            Thread.sleep(2);
        } catch (Exception e)
        {
            e.printStackTrace();
        }
    }
    this.model = model;
    if (isStarted)
    {
        System.out.println("RESTART");
        this.restart();
    } else
    {
        //isStarted = true;
        // setting up jmonkey renderer
        this.setPauseOnLostFocus(false);
        AppSettings settings = new AppSettings(true);
        settings.setResolution(1, 1);
        this.setSettings(settings);
        this.start(JmeContext.Type.OffscreenSurface);
    }
}

public void setupOffscreenView()
{
    offCamera = new Camera(width, height);

    // create a pre-view. a view that is rendered before the main view
    offView = renderManager.createPreView("Offscreen View", offCamera);
    offView.setBackgroundColor(ColorRGBA.White);
    offView.setClearFlags(true, true, true);

    // this will let us know when the scene has been rendered to the 
    // frame buffer
    offView.addProcessor(this);

    // create offscreen framebuffer
    offBuffer = new FrameBuffer(width, height, 1);

    // setup framebuffer's cam
    offCamera.setFrustumPerspective(45f, 1f, 1f, 1000f);
    offCamera.setLocation(new Vector3f(0f, 0f, -2.6f));
    offCamera.lookAt(new Vector3f(0f, 0f, 0f), Vector3f.UNIT_Y);


    // setup framebuffer to use renderbuffer
    // this is faster for gpu -> cpu copies
    offBuffer.setDepthBuffer(Image.Format.Depth);
    offBuffer.setColorBuffer(Image.Format.RGBA8);

    // set viewport to render to offscreen framebuffer
    offView.setOutputFrameBuffer(offBuffer);
}

@Override
public void simpleInitApp()
{
    System.out.println("BEGIN");
    curThread = Thread.currentThread();
    setupOffscreenView();

    // show model
    Spatial loadedModel = assetManager.loadModel(model);
    float height = ((BoundingBox) loadedModel.getWorldBound()).getYExtent();
    float width = ((BoundingBox) loadedModel.getWorldBound()).getZExtent();

    // scale the model to size 1
    float scale = 0.0f;
    if (height > width)
    {
        scale = 1f / height;
    } else
    {
        scale = 1f / width;
    }
    loadedModel.scale(scale, scale, scale);
    System.out.println("Model: "+loadedModel.getName());

    // center the model
    loadedModel.setLocalTranslation(loadedModel.getWorldBound().getCenter().mult(-1));
    rootNode.attachChild(loadedModel);

    // create light
    DirectionalLight light = new DirectionalLight();
    light.setDirection(new Vector3f(0.0f, 0.0f, 1.0f));
    rootNode.addLight(light);       
    offView.attachScene(rootNode);      
    
    startRender = true;
}

@Override
public void simpleUpdate(float tpf)
{
    if(!startRender)
    {
        offView.clearProcessors();
        offBuffer.
        this.stop();
    }
}

public JPanel getJPanel()
{
    display.setLocation(x, y);
    display.setSize(width, height);
    return (JPanel) display;
}

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

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

public boolean isInitialized()
{
    return true;
}

public void preFrame(float tpf)
{
}

public void postQueue(RenderQueue rq)
{
}

/**
 * Update the CPU image's contents after the scene has been rendered to the
 * framebuffer.
 */
public void postFrame(FrameBuffer out)
{
    System.out.println("POSTFRAME");
    if (startRender)
    {
        cpuBuf.clear();
        renderer.readFrameBuffer(offBuffer, cpuBuf);

        synchronized (image)
        {
            Screenshots.convertScreenShot(cpuBuf, image);
        }

        if (display != null)
        {
            display.repaint();
        }
        startRender = false;
    }
}

public void cleanup()
{
}

}
[/java]

Now for the function that uses the class first:
Note: this executes fine.

[java] private void createToolsPanel()
{
// creating asset panel
assetPanel = new JPanel(null);
assetPanel.setLocation(toolsPanelPadding, toolsPanelPadding);
assetPanel.setSize(toolsPanelWidth - 2 * toolsPanelPadding,
assetPreviewHeight+selectAssetTypeButtonSize);
editorUI.add(assetPanel);

    // creating asset preview window
    assetPreview = new AssetPreview(
            0,
            0,
            relPosX(1.0f, assetPanel),
            assetPreviewHeight);
    
    assetPreview.loadModel("Models/Oto/Oto.mesh.xml");
    JPanel assetPreviewPanel = assetPreview.getJPanel();
    assetPreviewPanel.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
    assetPanel.add(assetPreviewPanel);
    
    // creating selector buttons
    JButton button = new JButton(new ImageIcon(((new ImageIcon("model.png"))
            .getImage()).getScaledInstance(selectAssetTypeButtonSize-2,
            selectAssetTypeButtonSize-2, java.awt.Image.SCALE_SMOOTH)));
    button.setLocation(0, assetPreviewHeight);
    button.setSize(selectAssetTypeButtonSize,selectAssetTypeButtonSize);
    button.setBackground(Color.lightGray);
    button.setActionCommand("Open Browser");
    button.addActionListener(this);
    
    assetPanel.add(button);
    
         
    
    window.setVisible(true);      
}[/java]

This is where hell breaks loose:
Note: This function just recalls the “loadModel” method after a button is clicked. So probably the only thing relevant about me posting these 2 individual functions it the idea, that they are not called immediately after eachother (because somehow “example.loadModel(_this);example.loadModel(_that);” works), but im probably missing something anyway.

[java]
public void gridClicked(String path)
{
browserUI.setVisible(false);
editorUI.setVisible(true);
assetPreview.loadModel(path);
}
[/java]

And here’s the error:

Jan 28, 2013 3:06:26 PM com.jme3.app.Application handleError
SEVERE: Failed to create display
org.lwjgl.LWJGLException: Could not create context (WGL_ARB_create_context)
at org.lwjgl.opengl.WindowsContextImplementation.nCreate(Native Method)
at org.lwjgl.opengl.WindowsContextImplementation.create(WindowsContextImplementation.java:50)
at org.lwjgl.opengl.ContextGL.(ContextGL.java:132)
at org.lwjgl.opengl.Pbuffer.(Pbuffer.java:225)
at com.jme3.system.lwjgl.LwjglOffscreenBuffer.initInThread(LwjglOffscreenBuffer.java:79)
at com.jme3.system.lwjgl.LwjglOffscreenBuffer.run(LwjglOffscreenBuffer.java:144)
at java.lang.Thread.run(Thread.java:662)
Jan 28, 2013 3:06:26 PM com.jme3.app.Application handleError
SEVERE: Uncaught exception thrown in Thread[LWJGL Renderer Thread,6,main]
java.lang.NullPointerException
at com.jme3.system.lwjgl.LwjglOffscreenBuffer.deinitInThread(LwjglOffscreenBuffer.java:135)
at com.jme3.system.lwjgl.LwjglOffscreenBuffer.run(LwjglOffscreenBuffer.java:148)
at java.lang.Thread.run(Thread.java:662)
AL lib: alc_cleanup: 1 device not closed

If you’re at the bottom of this post it means you might have read it, in which case, I LOVE U! Can you help me?

Why not pause the renderer when not using it rather than shutting it down entirely?

Why not pause the renderer when not using it rather than shutting it down entirely?

Yea that’s cool. I cant find which function i would call to do that, so how do you do it? I’m thinking you might be able to just stop the thread somehow? I really don’t know. I’m still confused.

Edit:
Turns out the pause variable might actually do something, not sure why i didnt think it did. Anyway, im gonna try it with that, thank you. Im sure ill be back here if that doesnt work.

Turns out the pause variable might actually do something, not sure why i didnt think it did. Anyway, im gonna try it with that, thank you. Im sure ill be back here if that doesnt work.

The renderer paused as in not calling postFrame() and simpleUpdate(), but the cpu was still reading 25% as opposed to 10% when it is completely stopped. So obviously something is still running when i pause it that eats up the cpu. I need another way. :frowning:

Must be your own code, jme only does stuff on the update loop really (apart from terrain but when no update loop is running any threaded operations should cease quickly after).

alos keep in mind that the jit might optimize jme methods in the background, now that the load is lower

@clickity said: The renderer paused as in not calling postFrame() and simpleUpdate(), but the cpu was still reading 25% as opposed to 10% when it is completely stopped. So obviously something is still running when i pause it that eats up the cpu. I need another way. :(

Use the profiler, there is one built into the SDK.