Login  Register

Re: Sharing context between drawables issues.

Posted by ZooChar on Nov 04, 2015; 3:01pm
URL: https://forum.jogamp.org/Sharing-context-between-drawables-issues-tp4035716p4035733.html

The code I've written so far is very large and mature (e.g. it has big Mesh, Shader, Effect etc classes), and I am not allowed to share information about it. However, I created a much simpler example that exhibits a similar behaviour. I used the code from here to draw the triangle: http://stackoverflow.com/questions/21820644/opengl-3-2-trying-to-render-a-triangle-with-shaders-but-output-is-distorted (no need to visit the link, just giving credit).

EDIT: I managed to fix one of the two issues. I noted it in the code now.

In contrast to the previous code, I use this simple example class now to create a master context:

GLUtil

package jogltest;

import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.GLCapabilities;
import com.jogamp.opengl.GLContext;
import com.jogamp.opengl.GLDrawableFactory;
import com.jogamp.opengl.GLProfile;

public class GLUtil {
    private static GLAutoDrawable sharedDrawable;
    
    public static GLContext getSharedContext() {
        if (sharedDrawable == null) {
            GLProfile profile = GLProfile.get(GLProfile.GL3);
            GLCapabilities capabilities = new GLCapabilities(profile);
            sharedDrawable = GLDrawableFactory.getFactory(profile).createDummyAutoDrawable(null, true, capabilities, null);
            sharedDrawable.display();
        }
        
        return sharedDrawable.getContext();
    }
}

The following is a class that works just fine:

EditorComposite

package jogltest;

import java.awt.Frame;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import org.eclipse.swt.SWT;
import org.eclipse.swt.awt.SWT_AWT;
import org.eclipse.swt.widgets.Composite;

import com.jogamp.common.nio.Buffers;
import com.jogamp.opengl.GL;
import com.jogamp.opengl.GL3;
import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.GLCapabilities;
import com.jogamp.opengl.GLEventListener;
import com.jogamp.opengl.GLProfile;
import com.jogamp.opengl.awt.GLCanvas;

public class EditorComposite extends Composite {
    
    private EditorSceneListener listener;
    
    // Keeping these public for the sake of simplicity.
    public int vertexShader;
    public int fragmentShader;
    public int program;
    public int positionAttribute;
    public int vao;
    public int vbo;

    public EditorComposite(Composite parent, int style) {
        super(parent, SWT.EMBEDDED);

        GLProfile profile = GLProfile.get(GLProfile.GL3);
        GLCapabilities capabilities = new GLCapabilities(profile);
        GLCanvas canvas = new GLCanvas(capabilities);
        canvas.setSharedContext(GLUtil.getSharedContext());

        Frame frame = SWT_AWT.new_Frame(this);
        frame.add(canvas);

        listener = new EditorSceneListener();

        canvas.addGLEventListener(listener);
        canvas.display();
    }

    public class EditorSceneListener implements GLEventListener {

        private String[] vertProgram = new String[] {
            "#version 330\n" +
            "in vec2 position;\n" +
            "void main() {\n" +
            "    gl_Position = vec4(position, 0.0, 1.0);\n" +
            "}\n"};
        
        private String[] fragProgram = new String[] {
            "#version 330\n" +
            "out vec4 outColor;\n" +
            "void main() {\n" +
            "    outColor = vec4(1.0, 1.0, 1.0, 1.0);\n" +
            "}\n"};

        private FloatBuffer vertBuffer;

        @Override
        public void display(GLAutoDrawable drawable) {
            drawable.getContext().makeCurrent();
            GL3 gl = drawable.getGL().getGL3();
            gl.setSwapInterval(1);
            gl.glViewport(0, 0, 300, 300);
            gl.glDepthFunc(GL3.GL_LEQUAL);

            gl.glClearDepth(1.0f);
            gl.glClear(GL3.GL_COLOR_BUFFER_BIT | GL3.GL_DEPTH_BUFFER_BIT);
            gl.glClearColor(0f, 0f, 0f, 1f);
            
            gl.glUseProgram(program);
            gl.glBindVertexArray(vao);
            gl.glBindBuffer(GL3.GL_ARRAY_BUFFER, vbo);
            gl.glEnableVertexAttribArray(positionAttribute);
            gl.glVertexAttribPointer(positionAttribute, 2, GL.GL_FLOAT, false, 0, 0L);
            gl.glDrawArrays(GL.GL_TRIANGLES, 0, 3);

        }

        @Override
        public void dispose(GLAutoDrawable drawable) {
        }

        @Override
        public void init(GLAutoDrawable drawable) {
            drawable.getContext().makeCurrent();
            
            float[] verts = { 0.0f, 0.5f, 0.5f, -0.5f, -0.5f, -0.5f };

            IntBuffer vaoBuffer = Buffers.newDirectIntBuffer(1);

            GL3 gl = drawable.getGL().getGL3();
            gl.glGenVertexArrays(1, vaoBuffer);
            vao = vaoBuffer.get(0);
            gl.glBindVertexArray(vao);

            IntBuffer vboBuffer = Buffers.newDirectIntBuffer(1);
            vertBuffer = Buffers.newDirectFloatBuffer(verts);

            gl.glGenBuffers(1, vboBuffer);
            vbo = vboBuffer.get(0);

            gl.glBindBuffer(GL3.GL_ARRAY_BUFFER, vbo);
            gl.glBufferData(GL3.GL_ARRAY_BUFFER, vertBuffer.limit() * Buffers.SIZEOF_FLOAT, vertBuffer, GL3.GL_STATIC_DRAW);

            vertexShader = gl.glCreateShader(GL3.GL_VERTEX_SHADER);
            gl.glShaderSource(vertexShader, 1, vertProgram, null);
            gl.glCompileShader(vertexShader);

            fragmentShader = gl.glCreateShader(GL3.GL_FRAGMENT_SHADER);
            gl.glShaderSource(fragmentShader, 1, fragProgram, null);
            gl.glCompileShader(fragmentShader);

            program = gl.glCreateProgram();
            gl.glAttachShader(program, vertexShader);
            gl.glAttachShader(program, fragmentShader);
            gl.glBindFragDataLocation(program, 0, "outColor");
            gl.glLinkProgram(program);

            gl.glUseProgram(program);

            positionAttribute = gl.glGetAttribLocation(program, "position");
        }

        @Override
        public void reshape(GLAutoDrawable drawable, int x, int y, int w, int h) {
        }
    }
}


The following is the OffscreenRenderer which is supposed to share the same buffers and everything. One difference is that it displays a different background color (to test that the output changed). I'm having the following issues with it:
1) I get the following error: "Info: GLReadBufferUtil.readPixels: pre-exisiting GL error 0x502", though I do get an output.
2) (FIXED) If I create another offscreen drawable, it doesn't work. I either get a blank image or the image output of the very first offscreen drawable (it's not random - it apparently swaps between the two each time).

OffscreenRednerer

package jogltest;

import java.awt.image.BufferedImage;

import com.jogamp.opengl.DefaultGLCapabilitiesChooser;
import com.jogamp.opengl.GL;
import com.jogamp.opengl.GL3;
import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.GLCapabilities;
import com.jogamp.opengl.GLDrawableFactory;
import com.jogamp.opengl.GLEventListener;
import com.jogamp.opengl.GLOffscreenAutoDrawable;
import com.jogamp.opengl.GLProfile;
import com.jogamp.opengl.util.awt.AWTGLReadBufferUtil;

public class OffscreenRenderer {
    
    private float bgRed;
    private float bgGreen;
    private float bgBlue;
    private GLOffscreenAutoDrawable offScreenDrawable;
    private OffscreenListener listener;
    
    private EditorComposite parent;
    
    public void setBackground(float r, float g, float b) {
        bgRed = r; 
        bgGreen = g; 
        bgBlue = b;
    }

    public OffscreenRenderer(EditorComposite parent) {
        this.parent = parent;
        
        GLProfile profile = GLProfile.get(GLProfile.GL3);
        GLCapabilities caps = new GLCapabilities(profile);
        caps.setOnscreen(false);
        caps.setHardwareAccelerated(true);
        caps.setDoubleBuffered(true);
        caps.setBackgroundOpaque(false);
        GLDrawableFactory factory = GLDrawableFactory.getFactory(profile);
        
        offScreenDrawable = factory.createOffscreenAutoDrawable(factory.getDefaultDevice(), caps, new DefaultGLCapabilitiesChooser(), 300, 300);
        offScreenDrawable.setSharedContext(GLUtil.getSharedContext());

        listener = new OffscreenListener();
        offScreenDrawable.addGLEventListener(listener);
    }

    public BufferedImage renderImage() {
        offScreenDrawable.display();
        AWTGLReadBufferUtil util = new AWTGLReadBufferUtil(offScreenDrawable.getGLProfile(), true);
        BufferedImage image = util.readPixelsToBufferedImage(offScreenDrawable.getGL().getGL3(), 0, 0, 300, 300, true);
        
        //NOTE: This is the fix I added for issue (2). Releasing the context after drawing allows for more drawables to be created.
        offScreenDrawable.getContext().release();

        return image;
    }
    
    private class OffscreenListener implements GLEventListener {

        @Override
        public void display(GLAutoDrawable drawable) {
            drawable.getContext().makeCurrent();
            
            GL3 gl = drawable.getGL().getGL3();
            
            gl.setSwapInterval(1);
            gl.glViewport(0, 0, 300, 300);
            gl.glDepthFunc(GL3.GL_LEQUAL);

            gl.glClearDepth(1.0f);
            gl.glClearColor(bgRed, bgGreen, bgBlue, 1f);
            gl.glClear(GL3.GL_COLOR_BUFFER_BIT | GL3.GL_DEPTH_BUFFER_BIT);
            
            gl.glUseProgram(parent.program);
            gl.glBindVertexArray(parent.vao);
            gl.glBindBuffer(GL3.GL_ARRAY_BUFFER, parent.vbo);
            gl.glEnableVertexAttribArray(parent.positionAttribute);
            gl.glVertexAttribPointer(parent.positionAttribute, 2, GL.GL_FLOAT, false, 0, 0L);

            gl.glDrawArrays(GL.GL_TRIANGLES, 0, 3);
        }

        @Override
        public void dispose(GLAutoDrawable drawable) { }
        @Override
        public void init(GLAutoDrawable drawable) { }

        @Override
        public void reshape(GLAutoDrawable drawable, int x, int y, int w, int h) { }
    }

}

Now, here's the code that binds everything together. It creates a window with a GLCanvas on the left, and a panel on the right. By pressing "Render", a Color Dialog opens up. After you select a color, a BufferedImage is created and drawn on the right panel using the selected color as a background.

DemoWindow

package jogltest;

import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Panel;
import java.awt.image.BufferedImage;

import org.eclipse.swt.SWT;
import org.eclipse.swt.awt.SWT_AWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.ColorDialog;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class DemoWindow {

    protected Shell shell;

    public static void main(String[] args) {
        try {
            DemoWindow window = new DemoWindow();
            window.open();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void open() {
        Display display = Display.getDefault();
        createContents();
        shell.open();
        shell.layout();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
    }
    
    private static class ImagePanel extends Panel {
        private static final long serialVersionUID = 1049213893091559571L;
        
        private BufferedImage image;
        
        public void setImage(BufferedImage image) {
            this.image = image;
            this.repaint();
        }
        
        @Override
        public void paint(Graphics g) {
            g.drawImage(image, 0, 0, this.getWidth(), this.getHeight(), this);
        }   
    }

    private RGB background = new RGB(0, 0, 0);
    private ImagePanel rightPanel;
    private OffscreenRenderer renderer;

    protected void createContents() {
        shell = new Shell();
        shell.setSize(640, 384);
        shell.setText("SWT Application");
        
        EditorComposite left = new EditorComposite(shell, SWT.EMBEDDED);
        left.setBounds(10, 10, 300, 300);
        
        Button btnNewButton = new Button(shell, SWT.NONE);
        btnNewButton.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                ColorDialog dlg = new ColorDialog(shell);
                dlg.setRGB(background);
                RGB rgb = dlg.open();
                if (rgb != null) {
                    background = rgb;
                }
                
                if (renderer == null) {
                    renderer = new OffscreenRenderer(left);
                }
                
                renderer.setBackground(background.red / 255f, background.green / 255f, background.blue / 255f);
                                
                BufferedImage image = renderer.renderImage();
                
                // Uncommenting the following used to mess everything up, but not anymore (after fixing Issue #2)
                //renderer = null;
                
                rightPanel.setImage(image);
            }
        });
        btnNewButton.setBounds(559, 316, 57, 25);
        btnNewButton.setText("Render");
        
        Composite right = new Composite(shell, SWT.EMBEDDED);
        right.setBounds(316, 10, 300, 300);
        
        Frame frame = SWT_AWT.new_Frame(right);
        
        rightPanel = new ImagePanel();
        frame.add(rightPanel);
    }
}