package com.jogamp.opengl.test.bugs; import java.applet.Applet; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Frame; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.Insets; import java.awt.Rectangle; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.nio.FloatBuffer; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import com.jogamp.opengl.GL; import com.jogamp.opengl.GL2ES2; import com.jogamp.opengl.GLAutoDrawable; import com.jogamp.opengl.GLCapabilities; import com.jogamp.opengl.GLContext; import com.jogamp.opengl.GLException; import com.jogamp.opengl.GLProfile; import com.jogamp.opengl.GLRunnable; import com.jogamp.opengl.GLUniformData; import com.jogamp.opengl.awt.GLCanvas; import com.jogamp.opengl.glu.GLU; import com.jogamp.common.util.InterruptSource; import com.jogamp.newt.awt.NewtCanvasAWT; import com.jogamp.newt.opengl.GLWindow; import com.jogamp.opengl.test.junit.jogl.demos.es2.LandscapeES2; import com.jogamp.opengl.util.GLArrayDataServer; import com.jogamp.opengl.util.glsl.ShaderCode; import com.jogamp.opengl.util.glsl.ShaderProgram; import com.jogamp.opengl.util.glsl.ShaderState; /** * Difference to orig. Bug735Inv0AppletAWT: * <pre> * - MANUAL_FRAME_HANDLING: impl using pass through GLContext instead of static * </pre> * OSX Results: * <pre> * - Visible content * - Fluent animation * </pre> */ @SuppressWarnings("serial") public class Bug735Inv1AppletAWT extends Applet implements Runnable { static public final int AWT = 0; static public final int NEWT = 1; static public final int APPLET_WIDTH = 500; static public final int APPLET_HEIGHT = 290; static public final int TARGET_FPS = 120; static public final int TOOLKIT = NEWT; static public final boolean MANUAL_FRAME_HANDLING = true; ////////////////////////////////////////////////////////////////////////////// static private Frame frame; static private Bug735Inv1AppletAWT applet; private GLCanvas awtCanvas; private GLWindow newtWindow; private NewtCanvasAWT newtCanvas; private DrawRunnable drawRunnable; // JAU private GLContext context; private GLU glu; private int width; private int height; private Thread thread; private boolean doneInit = false; private boolean doneSetup = false; private final long frameRatePeriod = 1000000000L / TARGET_FPS; private long millisOffset; private int frameCount; private float frameRate; private ShaderCode vertShader; private ShaderCode fragShader; private ShaderProgram shaderProg; private ShaderState shaderState; private GLUniformData resolution; private GLUniformData time; private GLArrayDataServer vertices; private int fcount = 0, lastm = 0; private final int fint = 1; public void init() { setSize(APPLET_WIDTH, APPLET_HEIGHT); setPreferredSize(new Dimension(APPLET_WIDTH, APPLET_HEIGHT)); width = APPLET_WIDTH; height = APPLET_HEIGHT; } public void start() { thread = new InterruptSource.Thread(null, this, "Animation Thread"); thread.start(); } public void run() { int noDelays = 0; // Number of frames with a delay of 0 ms before the // animation thread yields to other running threads. final int NO_DELAYS_PER_YIELD = 15; final int TIMEOUT_SECONDS = 2; long beforeTime = System.nanoTime(); long overSleepTime = 0L; millisOffset = System.currentTimeMillis(); frameCount = 1; while (Thread.currentThread() == thread) { final CountDownLatch latch = new CountDownLatch(1); requestDraw(latch); try { latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS); } catch (final InterruptedException e) { e.printStackTrace(); } if (frameCount == 1) { EventQueue.invokeLater(new Runnable() { public void run() { requestFocusInWindow(); } }); } final long afterTime = System.nanoTime(); final long timeDiff = afterTime - beforeTime; final long sleepTime = (frameRatePeriod - timeDiff) - overSleepTime; if (sleepTime > 0) { // some time left in this cycle try { Thread.sleep(sleepTime / 1000000L, (int) (sleepTime % 1000000L)); noDelays = 0; // Got some sleep, not delaying anymore } catch (final InterruptedException ex) { } overSleepTime = (System.nanoTime() - afterTime) - sleepTime; } else { // sleepTime <= 0; the frame took longer than the period overSleepTime = 0L; noDelays++; if (noDelays > NO_DELAYS_PER_YIELD) { Thread.yield(); // give another thread a chance to run noDelays = 0; } } beforeTime = System.nanoTime(); } } public void requestDraw(final CountDownLatch latch) { if (!doneInit) { initDraw(); } if (TOOLKIT == AWT) { awtCanvas.invoke(true, drawRunnable); } else if (TOOLKIT == NEWT) { newtWindow.invoke(true, drawRunnable); } if (latch != null) { latch.countDown(); } } private class DrawRunnable implements GLRunnable { private boolean notCurrent; @Override public boolean run(final GLAutoDrawable drawable) { if (MANUAL_FRAME_HANDLING) { makeContextCurrent(drawable.getContext()); } if (!doneSetup) { setup(drawable.getGL().getGL2ES2()); } draw(drawable.getGL().getGL2ES2()); checkGLErrors(drawable.getGL()); if (MANUAL_FRAME_HANDLING) { swapBuffers(drawable.getContext()); releaseCurrentContext(drawable.getContext()); } return true; } private void makeContextCurrent(final GLContext context) { final int MAX_CONTEXT_GRAB_ATTEMPTS = 10; if (context.isCurrent()) { notCurrent = false; } else { notCurrent = true; int value = GLContext.CONTEXT_NOT_CURRENT; int attempt = 0; do { try { value = context.makeCurrent(); System.out.println("Made context current"); } catch (final GLException gle) { gle.printStackTrace(); } finally { attempt++; if (attempt == MAX_CONTEXT_GRAB_ATTEMPTS) { throw new RuntimeException("Failed to claim OpenGL context."); } } try { Thread.sleep(5); } catch (final InterruptedException e) { e.printStackTrace(); } } while (value == GLContext.CONTEXT_NOT_CURRENT); } } private void swapBuffers(final GLContext context) { final GL gl = context.getGL(); gl.glFlush(); context.getGLDrawable().swapBuffers(); } private void releaseCurrentContext(final GLContext context) { if (notCurrent) { try { context.release(); System.out.println("Released context"); } catch (final GLException gle) { gle.printStackTrace(); } } } } private void initGL() { final GLProfile profile = GLProfile.getDefault(); final GLCapabilities caps = new GLCapabilities(profile); caps.setBackgroundOpaque(true); caps.setOnscreen(true); caps.setSampleBuffers(false); if (TOOLKIT == AWT) { awtCanvas = new GLCanvas(caps); awtCanvas.setBounds(0, 0, applet.width, applet.height); awtCanvas.setBackground(new Color(0xFFCCCCCC, true)); awtCanvas.setFocusable(true); applet.setLayout(new BorderLayout()); applet.add(awtCanvas, BorderLayout.CENTER); if (MANUAL_FRAME_HANDLING) { awtCanvas.setIgnoreRepaint(true); awtCanvas.setAutoSwapBufferMode(false); } } else if (TOOLKIT == NEWT) { newtWindow = GLWindow.create(caps); newtCanvas = new NewtCanvasAWT(newtWindow); newtCanvas.setBounds(0, 0, applet.width, applet.height); newtCanvas.setBackground(new Color(0xFFCCCCCC, true)); newtCanvas.setFocusable(true); applet.setLayout(new BorderLayout()); applet.add(newtCanvas, BorderLayout.CENTER); if (MANUAL_FRAME_HANDLING) { newtCanvas.setIgnoreRepaint(true); newtWindow.setAutoSwapBufferMode(false); } } } private void initDraw() { if (TOOLKIT == AWT) { awtCanvas.setVisible(true); // Force the realization awtCanvas.display(); if (awtCanvas.getDelegatedDrawable().isRealized()) { // Request the focus here as it cannot work when the window is not visible awtCanvas.requestFocus(); } } else if (TOOLKIT == NEWT) { newtCanvas.setVisible(true); // Force the realization newtWindow.display(); if (newtWindow.isRealized()) { // Request the focus here as it cannot work when the window is not visible newtCanvas.requestFocus(); } } drawRunnable = new DrawRunnable(); doneInit = true; } private void setup(final GL2ES2 gl) { if (60 < TARGET_FPS) { // Disables vsync gl.setSwapInterval(0); } glu = new GLU(); vertShader = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, LandscapeES2.class, "shader", "shader/bin", "landscape", true); fragShader = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, LandscapeES2.class, "shader", "shader/bin", "landscape", true); vertShader.defaultShaderCustomization(gl, true, true); fragShader.defaultShaderCustomization(gl, true, true); shaderProg = new ShaderProgram(); shaderProg.add(gl, vertShader, System.err); shaderProg.add(gl, fragShader, System.err); shaderState = new ShaderState(); shaderState.attachShaderProgram(gl, shaderProg, true); resolution = new GLUniformData("iResolution", 3, FloatBuffer.wrap(new float[] {width, height, 0})); shaderState.ownUniform(resolution); shaderState.uniform(gl, resolution); time = new GLUniformData("iGlobalTime", 0.0f); shaderState.ownUniform(time); vertices = GLArrayDataServer.createGLSL("inVertex", 2, GL.GL_FLOAT, false, 4, GL.GL_STATIC_DRAW); vertices.putf(-1.0f); vertices.putf(-1.0f); vertices.putf(+1.0f); vertices.putf(-1.0f); vertices.putf(-1.0f); vertices.putf(+1.0f); vertices.putf(+1.0f); vertices.putf(+1.0f); vertices.seal(gl, true); shaderState.ownAttribute(vertices, true); shaderState.useProgram(gl, false); doneSetup = true; } private void draw(final GL2ES2 gl) { // gl.glClearColor(0.5f, 0.1f, 0.1f, 1); // gl.glClear(GL2ES2.GL_COLOR_BUFFER_BIT); shaderState.useProgram(gl, true); time.setData((System.currentTimeMillis() - millisOffset) / 1000.0f); shaderState.uniform(gl, time); vertices.enableBuffer(gl, true); gl.glDrawArrays(GL.GL_TRIANGLE_STRIP, 0, 4); vertices.enableBuffer(gl, false); shaderState.useProgram(gl, false); // Compute current framerate and printout. frameCount++; fcount += 1; final int m = (int) (System.currentTimeMillis() - millisOffset); if (m - lastm > 1000 * fint) { frameRate = (float)(fcount) / fint; fcount = 0; lastm = m; } if (frameCount % TARGET_FPS == 0) { System.out.println("FrameCount: " + frameCount + " - " + "FrameRate: " + frameRate); } } private void checkGLErrors(final GL gl) { final int err = gl.glGetError(); if (err != 0) { final String errString = glu.gluErrorString(err); System.out.println(errString); } } static public void main(final String[] args) { final GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment(); final GraphicsDevice displayDevice = environment.getDefaultScreenDevice(); frame = new Frame(displayDevice.getDefaultConfiguration()); frame.setBackground(new Color(0xCC, 0xCC, 0xCC)); frame.setTitle("TestBug735Inv1AppletAWT"); try { final Class<?> c = Thread.currentThread().getContextClassLoader(). loadClass(Bug735Inv1AppletAWT.class.getName()); applet = (Bug735Inv1AppletAWT) c.newInstance(); } catch (final Exception e) { throw new RuntimeException(e); } frame.setLayout(null); frame.add(applet); frame.pack(); frame.setResizable(false); applet.init(); final Insets insets = frame.getInsets(); final int windowW = applet.width + insets.left + insets.right; final int windowH = applet.height + insets.top + insets.bottom; frame.setSize(windowW, windowH); final Rectangle screenRect = displayDevice.getDefaultConfiguration().getBounds(); frame.setLocation(screenRect.x + (screenRect.width - applet.width) / 2, screenRect.y + (screenRect.height - applet.height) / 2); final int usableWindowH = windowH - insets.top - insets.bottom; applet.setBounds((windowW - applet.width)/2, insets.top + (usableWindowH - applet.height)/2, applet.width, applet.height); // This allows to close the frame. frame.addWindowListener(new WindowAdapter() { public void windowClosing(final WindowEvent e) { System.exit(0); } }); applet.initGL(); frame.setVisible(true); applet.start(); } }