Summary
I have a fairly dimensional system where JFramec is GLCanvasused to render scenes (using OpenGL). The drawing surface canvascan be divided into several (for example, 4) viewports. Scene objects are displayed at different times, and several requests are canvas.display()needed before the contents of the entire scene are processed.
I installed canvas.setAutoSwapBufferMode(false);and manually call canvas.swapBuffers();, according to the documentation. I do this after the contents of each viewport have been displayed, so that the back buffer is replaced once per frame, and not once per viewport, which makes JOGL automatically by default after each pass display(GLAutoDrawable). (Note that the problem does not go away, just keeping the default behavior, but still you need to do it manually.)
The problem I am facing is that I see a strong flickering effect in some OS / GPU settings . (For an example, see the Screenshots below.) I can check my code in the following settings:
- Kubuntu 17.04 + NVIDIA GTX-960M (dev main system)
- Windows 10 + NVIDIA GTX-960M
- Kubuntu 17.04 + NVIDIA GTX-770
- Windows 7 + NVIDIA GTX-770M
, / -.
, , , OpenGL.
MCVE ( ), , . , glViewport glScissor, , display(GLAutoDrawable). (, GL_SCISSOR_TEST .)
:
.
, (, , ). , JOGL , .
, NeHe (, ), , (.. , ) , .
MCVE
JOGL OpenGL 4.0 . / .
import java.awt.*;
import java.awt.event.*;
import java.nio.*;
import java.util.*;
import java.util.Timer;
import java.util.concurrent.atomic.*;
import javax.swing.*;
import com.jogamp.opengl.*;
import com.jogamp.opengl.awt.*;
import com.jogamp.opengl.util.glsl.*;
public class ManualViewportBufferClearingTest implements GLEventListener, KeyListener {
private Timer renderLoopTimer = new Timer();
private JFrame frame = new JFrame(ManualViewportBufferClearingTest.class.getName());
private GLCanvas canvas;
private ShaderProgram shaderProgram;
private int[] vaos = new int[1];
private int[] vbos = new int[2];
private Viewport[] viewports;
private Viewport activeViewport;
private AtomicBoolean displayRequested = new AtomicBoolean(false);
private static final float[] vertexPositions = new float[] {
.25f, .25f, 0f, 1f,
-.25f, -.25f, 0f, 1f,
.25f, -.25f, 0f, 1f
};
private static final float[] vertexColors = new float[] {
1f, 1f, 1f, 1f,
1f, 1f, 1f, 1f,
1f, 1f, 1f, 1f
};
private FloatBuffer vertices = FloatBuffer.wrap(vertexPositions);
private FloatBuffer offsets = FloatBuffer.wrap(new float[] { 0, 0, 0, 0 });
private FloatBuffer colors = FloatBuffer.wrap(vertexColors);
public ManualViewportBufferClearingTest() {
final GLCapabilities caps = new GLCapabilities(GLProfile.get(GLProfile.GL4));
caps.setBackgroundOpaque(true);
caps.setDoubleBuffered(true);
caps.setRedBits(8);
caps.setGreenBits(8);
caps.setBlueBits(8);
caps.setAlphaBits(8);
canvas = new GLCanvas(caps);
canvas.addGLEventListener(this);
canvas.addKeyListener(this);
canvas.setAutoSwapBufferMode(false);
final int pixelWidth = 1024;
final int pixelHeight = 768;
frame.setSize(pixelWidth, pixelHeight);
frame.setLocationRelativeTo(null);
frame.getContentPane().setLayout(new BorderLayout());
frame.getContentPane().add(canvas, BorderLayout.CENTER);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
resetViewports(pixelWidth, pixelHeight);
frame.setVisible(true);
}
@Override
public void init(GLAutoDrawable glad) {
GL4 gl = (GL4) glad.getGL();
gl.glEnable(GL4.GL_DEPTH_TEST);
gl.glEnable(GL4.GL_SCISSOR_TEST);
gl.glGenVertexArrays(vaos.length, vaos, 0);
gl.glBindVertexArray(vaos[0]);
setupBuffers(gl);
buildProgram(gl);
shaderProgram.useProgram(gl, true);
renderLoopTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
renderToViewports();
}
}, 0, 16);
}
@Override
public void display(GLAutoDrawable glad) {
if (!displayRequested.get())
return;
final double value = System.currentTimeMillis() / 503.0;
offsets.put(0, (float) (Math.sin(value) * 0.5));
offsets.put(1, (float) (Math.cos(value) * 0.6));
GL4 gl = (GL4) glad.getGL();
gl.glViewport(activeViewport.x, activeViewport.y, activeViewport.width, activeViewport.height);
gl.glScissor(activeViewport.x, activeViewport.y, activeViewport.width, activeViewport.height);
gl.glClearBufferfv(GL4.GL_COLOR, 0, activeViewport.colorBuffer);
gl.glClearBufferfv(GL4.GL_DEPTH, 0, activeViewport.depthBuffer);
gl.glVertexAttrib4fv(1, offsets);
gl.glDrawArrays(GL4.GL_TRIANGLES, 0, 3);
}
@Override
public void dispose(GLAutoDrawable glad) {
GL4 gl = (GL4) glad.getGL();
shaderProgram.destroy(gl);
gl.glDeleteVertexArrays(vaos.length, vaos, 0);
gl.glDeleteBuffers(vbos.length, vbos, 0);
}
@Override
public void reshape(GLAutoDrawable glad, int x, int y, int width, int height) {
GL4 gl = glad.getGL().getGL4();
resetViewports(width, height);
Viewport vp = viewports[0];
gl.glViewport(vp.x, vp.y, vp.width, vp.height);
gl.glScissor(vp.x, vp.y, vp.width, vp.height);
}
@Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_ESCAPE:
cleanup();
frame.dispose();
System.exit(0);
break;
}
}
private void setupBuffers(GL4 gl) {
gl.glGenBuffers(vbos.length, vbos, 0);
gl.glBindBuffer(GL4.GL_ARRAY_BUFFER, vbos[0]);
gl.glBufferData(GL4.GL_ARRAY_BUFFER, vertices.capacity() * Float.BYTES, vertices, GL4.GL_STATIC_DRAW);
gl.glVertexAttribPointer( 0, 4 , GL4.GL_FLOAT, false, 0, 0);
gl.glEnableVertexAttribArray(0);
gl.glBindBuffer(GL4.GL_ARRAY_BUFFER, vbos[1]);
gl.glBufferData(GL4.GL_ARRAY_BUFFER, colors.capacity() * Float.BYTES, colors, GL4.GL_STATIC_DRAW);
gl.glVertexAttribPointer( 2, 4 , GL4.GL_FLOAT, false, 0, 0);
gl.glEnableVertexAttribArray(2);
}
private void resetViewports(int width, int height) {
final int halfW = width / 2;
final int halfH = height / 2;
viewports = new Viewport[] {
new Viewport(0 , 0 , halfW, halfH, Color.BLUE),
new Viewport(halfW, 0 , halfW, halfH, Color.GRAY),
new Viewport(0 , halfH, halfW, halfH, Color.RED),
new Viewport(halfW, halfH, halfW, halfH, Color.GREEN)
};
}
private void renderToViewports() {
for (int i = 0; i < viewports.length; ++i) {
activeViewport = viewports[i];
displayRequested.set(true);
canvas.display();
displayRequested.set(false);
}
canvas.swapBuffers();
}
private void cleanup() {
renderLoopTimer.cancel();
canvas.disposeGLEventListener(this, true);
vertices.clear();
offsets.clear();
colors.clear();
viewports = null;
activeViewport = null;
vertices = null;
offsets = null;
colors = null;
}
private static String getVertexSource() {
return
"#version 400 core \n"
+ " \n"
+ "layout (location = 0) in vec4 vertex_position; \n"
+ "layout (location = 1) in vec4 vertex_offset; \n"
+ "layout (location = 2) in vec4 vertex_color; \n"
+ " \n"
+ "out vertex_t { \n"
+ " vec4 color; \n"
+ "} vs; \n"
+ " \n"
+ "void main() { \n"
+ " vs.color = vertex_color; \n"
+ " gl_Position = vertex_position + vertex_offset; \n"
+ "} \n";
}
private static String getFragmentSource() {
return
"#version 400 core \n"
+ " \n"
+ "in vertex_t { \n"
+ " vec4 color; \n"
+ "} fs; \n"
+ " \n"
+ "out vec4 fragment; \n"
+ " \n"
+ "void main() { \n"
+ " fragment = fs.color; \n"
+ "} \n";
}
private void buildProgram(GL4 gl) {
shaderProgram = new ShaderProgram();
ShaderCode vs = createShader(gl, GL4.GL_VERTEX_SHADER, getVertexSource());
ShaderCode fs = createShader(gl, GL4.GL_FRAGMENT_SHADER, getFragmentSource());
shaderProgram.init(gl);
shaderProgram.add(vs);
shaderProgram.add(fs);
shaderProgram.link(gl, System.err);
if (!shaderProgram.validateProgram(gl, System.err))
throw new RuntimeException("Program failed to link");
vs.destroy(gl);
fs.destroy(gl);
}
private ShaderCode createShader(GL4 gl, int shaderType, String source) {
String[][] sources = new String[1][1];
sources[0] = new String[] { source };
ShaderCode shader = new ShaderCode(shaderType, sources.length, sources);
if (!shader.compile(gl, System.err))
throw new RuntimeException("Shader compilation failed\n" + source);
return shader;
}
@Override
public void keyReleased(KeyEvent e) {}
@Override
public void keyTyped(KeyEvent e) {}
public static void main(String[] args) {
new ManualViewportBufferClearingTest();
}
private class Viewport {
public int x, y;
public int width, height;
public FloatBuffer colorBuffer;
public FloatBuffer depthBuffer;
public Viewport(int x, int y, int width, int height, Color color, float depth) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.depthBuffer = FloatBuffer.wrap(new float[] { depth });
float[] components = color.getColorComponents(null);
colorBuffer = FloatBuffer.wrap(new float[] { components[0], components[1], components[2], 0 });
}
public Viewport(int x, int y, int width, int height, Color color) {
this(x, y, width, height, color, 1f);
}
}
}
Kubuntu 17.04 + GTX-960M ()

Windows 10 + GTX-960M ( )
. Windows 7 -, , .

