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);
}
}