/** * Copyright 2013 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ package com.jogamp.opengl.test.junit.jogl.acore; import java.io.IOException; import java.nio.FloatBuffer; import java.nio.ShortBuffer; import java.util.logging.Level; import java.util.logging.Logger; import com.jogamp.opengl.GL; import com.jogamp.opengl.GL2ES2; import com.jogamp.opengl.GL3; import com.jogamp.opengl.GL3bc; import com.jogamp.opengl.GLAutoDrawable; import com.jogamp.opengl.GLCapabilities; import com.jogamp.opengl.GLEventListener; import com.jogamp.opengl.GLException; import com.jogamp.opengl.GLProfile; import org.junit.Assert; import org.junit.Test; import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; import com.jogamp.common.nio.Buffers; import com.jogamp.newt.opengl.GLWindow; import com.jogamp.opengl.test.junit.util.MiscUtils; import com.jogamp.opengl.test.junit.util.QuitAdapter; import com.jogamp.opengl.test.junit.util.UITestCase; import com.jogamp.opengl.util.Animator; import com.jogamp.opengl.util.GLBuffers; /** * Test Vertex Array Object (VAO) Usage and BufferStateTracker * <p> * All combinations of CPU_SRC, VBO_ONLY and VBO_VAO are tested * and validate the fix for Bug 692, i.e. <https://jogamp.org/bugzilla/show_bug.cgi?id=692>. * </p> * <p> * Test order is important! * </p> * <p> * Note that VAO initialization does unbind the VBO .. since otherwise they are still bound * and the CPU_SRC test will fail!<br/> * The OpenGL spec does not mention that unbinding a VAO will also unbind the bound VBOs * during their setup.<br/> * Local tests here on NV and AMD proprietary driver resulted in <i>no ourput image</i> * when not unbinding said VBOs before the CPU_SRC tests.<br/> * Hence Bug 692 Comment 5 is invalid, i.e. <https://jogamp.org/bugzilla/show_bug.cgi?id=692#c5>, * and we should throw an exception to give users a hint! * </p> */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class TestBug692GL3VAONEWT extends UITestCase { static long duration = 500; // ms static class GL3VAODemo implements GLEventListener { /** Different modes of displaying the geometry */ public enum Mode { CPU_SRC { @Override void display(final GL3VAODemo t, final GL3bc gl) { t.displayCPUSourcing(gl); } }, /** Traditional one without using VAO */ VBO_ONLY { @Override void display(final GL3VAODemo t, final GL3bc gl) { t.displayVBOOnly(gl); } }, /** Using VAOs throws [incorrectly as of JOGL 2.0rc11] a GLException */ VBO_VAO { @Override void display(final GL3VAODemo t, final GL3bc gl) { t.displayVBOVAO(gl); } }; abstract void display(GL3VAODemo t, GL3bc gl); } private final Mode[] allModes; private Mode currentMode; private int currentModeIdx; public GL3VAODemo(final Mode[] modes) { allModes = modes; currentMode = allModes[0]; currentModeIdx = 0; } private final static float[] vertexColorData = new float[]{ 0.0f, 0.75f, 0.0f, 1,0,0, -0.5f, -0.75f, 0.0f, 0,1,0, 0.9f, -0.75f, 0.0f, 0,0,1 }; private final FloatBuffer vertexColorDataBuffer = GLBuffers.newDirectFloatBuffer(vertexColorData); private final short[] indices = new short[]{0, 1, 2}; private final ShortBuffer indicesBuffer = GLBuffers.newDirectShortBuffer(indices); private int ibo = -1; private int vbo = -1; private int vertID = -1; private int fragID = -1; private int progID = -1; private int vao = -1; private static int createShader(final GL3 gl, final int type, final String[] srcLines){ final int shaderID = gl.glCreateShader(type); assert shaderID > 0; final int[] lengths = new int[srcLines.length]; for (int i = 0; i < srcLines.length; i++) { lengths[i] = srcLines[i].length(); } gl.glShaderSource(shaderID, srcLines.length, srcLines, lengths, 0); gl.glCompileShader(shaderID); return shaderID; } private void initBuffers(final GL3 gl) { // IDs for 2 buffers final int[] buffArray = new int[2]; gl.glGenBuffers(buffArray.length, buffArray, 0); vbo = buffArray[0]; assert vbo > 0; // Bind buffer and upload data gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vbo); gl.glBufferData(GL.GL_ARRAY_BUFFER, vertexColorData.length * Buffers.SIZEOF_FLOAT, vertexColorDataBuffer, GL.GL_STATIC_DRAW); gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0); // Buffer with the 3 indices required for one triangle ibo = buffArray[1]; assert ibo > 0; gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, ibo); gl.glBufferData(GL.GL_ELEMENT_ARRAY_BUFFER,indices.length*Buffers.SIZEOF_SHORT, indicesBuffer, GL.GL_STATIC_DRAW); gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, 0); } private void initShaders(final GL3 gl) { final String[] vertSrc = new String[]{ "#version 150\n", "in vec4 vPosition;\n", "in vec4 vColor;\n", "out vec4 pColor;\n", "void main() {\n", " pColor = vColor;\n", " gl_Position = vPosition;\n", "}\n" }; vertID = createShader(gl, GL2ES2.GL_VERTEX_SHADER, vertSrc); final String[] fragSrc = new String[]{ "#version 150\n", "in vec4 pColor;\n", "void main() {\n", " gl_FragColor = pColor;\n", "}\n" }; fragID = createShader(gl, GL2ES2.GL_FRAGMENT_SHADER, fragSrc); // We're done with the compiler gl.glReleaseShaderCompiler(); progID = gl.glCreateProgram(); assert progID > 0; gl.glAttachShader(progID, vertID); gl.glAttachShader(progID, fragID); gl.glLinkProgram(progID); gl.glValidateProgram(progID); } private int initVAO(final GL3 gl) { final int[] buff = new int[1]; gl.glGenVertexArrays(1, buff, 0); final int vao = buff[0]; Assert.assertTrue("Invalid VAO: "+vao, vao > 0); gl.glUseProgram(progID); final int posLoc = gl.glGetAttribLocation(progID, "vPosition"); final int colorLoc = gl.glGetAttribLocation(progID, "vColor"); gl.glUseProgram(0); gl.glBindVertexArray(vao); gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vbo); gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, ibo); gl.glEnableVertexAttribArray(posLoc); gl.glEnableVertexAttribArray(colorLoc); final int stride = 6 * Buffers.SIZEOF_FLOAT; final int cOff = 3 * Buffers.SIZEOF_FLOAT; gl.glVertexAttribPointer(posLoc, 3, GL.GL_FLOAT, false, stride, 0L); gl.glVertexAttribPointer(colorLoc,3, GL.GL_FLOAT, false, stride, cOff); gl.glBindVertexArray(0); // See class documentation above! gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0); gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, 0); return vao; } @Override public void init(final GLAutoDrawable drawable) { final GL3 gl = drawable.getGL().getGL3(); gl.glEnable(GL.GL_DEPTH_TEST); gl.glDisable(GL.GL_CULL_FACE); initBuffers(gl); initShaders(gl); vao = initVAO(gl); gl.setSwapInterval(1); } @Override public void dispose(final GLAutoDrawable drawable) { final GL3 gl = drawable.getGL().getGL3(); gl.glDeleteBuffers(2, new int[]{vbo, ibo}, 0); gl.glDetachShader(progID, fragID); gl.glDetachShader(progID, vertID); gl.glDeleteProgram(progID); gl.glDeleteShader(fragID); gl.glDeleteShader(vertID); } private void displayCPUSourcing(final GL3bc gl) { final int posLoc = gl.glGetAttribLocation(progID, "vPosition"); final int colorLoc = gl.glGetAttribLocation(progID, "vColor"); gl.glEnableVertexAttribArray(posLoc); gl.glEnableVertexAttribArray(colorLoc); final int stride = 6 * Buffers.SIZEOF_FLOAT; // final int cOff = 3 * Buffers.SIZEOF_FLOAT; gl.glVertexAttribPointer(posLoc, 3, GL.GL_FLOAT, false, stride, vertexColorDataBuffer); vertexColorDataBuffer.position(3); // move to cOff gl.glVertexAttribPointer(colorLoc,3, GL.GL_FLOAT, false, stride, vertexColorDataBuffer); vertexColorDataBuffer.position(0); // rewind cOff gl.glDrawElements(GL.GL_TRIANGLES, 3, GL.GL_UNSIGNED_SHORT, indicesBuffer); gl.glDisableVertexAttribArray(posLoc); gl.glDisableVertexAttribArray(colorLoc); } private void displayVBOOnly(final GL3 gl) { final int posLoc = gl.glGetAttribLocation(progID, "vPosition"); final int colorLoc = gl.glGetAttribLocation(progID, "vColor"); gl.glEnableVertexAttribArray(posLoc); gl.glEnableVertexAttribArray(colorLoc); gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vbo); final int stride = 6 * Buffers.SIZEOF_FLOAT; final int cOff = 3 * Buffers.SIZEOF_FLOAT; gl.glVertexAttribPointer(posLoc, 3, GL.GL_FLOAT, false, stride, 0L); gl.glVertexAttribPointer(colorLoc,3, GL.GL_FLOAT, false, stride, cOff); gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, ibo); gl.glDrawElements(GL.GL_TRIANGLES, 3, GL.GL_UNSIGNED_SHORT, 0L); gl.glDisableVertexAttribArray(posLoc); gl.glDisableVertexAttribArray(colorLoc); gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0); gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, 0); } private void displayVBOVAO(final GL3 gl) { try { gl.glBindVertexArray(vao); gl.glDrawElements(GL.GL_TRIANGLES, 3, GL.GL_UNSIGNED_SHORT, 0L); gl.glBindVertexArray(0); } catch (final GLException ex) { Logger.getLogger(TestBug692GL3VAONEWT.class.getName()).log(Level.SEVERE,null,ex); } } @Override public void display(final GLAutoDrawable drawable) { final GL3bc gl = drawable.getGL().getGL3bc(); final float color = ((float) currentMode.ordinal() + 1) / (Mode.values().length + 2); gl.glClearColor(color, color, color, 0); gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); gl.glUseProgram(progID); final Mode newMode; { currentModeIdx = ( currentModeIdx + 1 ) % allModes.length; newMode = allModes[ currentModeIdx ]; } if (newMode != currentMode) { currentMode = newMode; System.out.println("Display mode: " + currentMode); } currentMode.display(this, gl); gl.glUseProgram(0); } @Override public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int w, final int h) { } } private void testImpl(final GLProfile profile, final GL3VAODemo.Mode[] modes) throws InterruptedException { final GLCapabilities capabilities = new GLCapabilities(profile); final GLWindow glWindow = GLWindow.create(capabilities); glWindow.setSize(512, 512); final Animator anim = new Animator(glWindow); final QuitAdapter quitAdapter = new QuitAdapter(); glWindow.addKeyListener(quitAdapter); glWindow.addWindowListener(quitAdapter); final GL3VAODemo vaoTest = new GL3VAODemo(modes); glWindow.addGLEventListener(vaoTest); glWindow.setVisible(true); anim.start(); final long t0 = System.currentTimeMillis(); long t1 = t0; while(!quitAdapter.shouldQuit() && t1-t0<duration) { Thread.sleep(100); t1 = System.currentTimeMillis(); } anim.stop(); glWindow.destroy(); } @Test public void test01CPUSource() throws GLException, InterruptedException { if( ! GLProfile.isAvailable(GLProfile.GL3bc) ) { System.err.println("GL3bc n/a"); return; } final GL3VAODemo.Mode[] modes = new GL3VAODemo.Mode[] { GL3VAODemo.Mode.CPU_SRC }; testImpl(GLProfile.get(GLProfile.GL3bc), modes); } @Test public void test02VBOOnly() throws GLException, InterruptedException { if( ! GLProfile.isAvailable(GLProfile.GL3bc) ) { System.err.println("GL3bc n/a"); return; } final GL3VAODemo.Mode[] modes = new GL3VAODemo.Mode[] { GL3VAODemo.Mode.VBO_ONLY }; testImpl(GLProfile.get(GLProfile.GL3bc), modes); } @Test public void test03VBOVAO() throws GLException, InterruptedException { if( ! GLProfile.isAvailable(GLProfile.GL3bc) ) { System.err.println("GL3bc n/a"); return; } final GL3VAODemo.Mode[] modes = new GL3VAODemo.Mode[] { GL3VAODemo.Mode.VBO_VAO }; testImpl(GLProfile.get(GLProfile.GL3bc), modes); } @Test public void test12CPUSourceAndVBOOnly() throws GLException, InterruptedException { if( ! GLProfile.isAvailable(GLProfile.GL3bc) ) { System.err.println("GL3bc n/a"); return; } final GL3VAODemo.Mode[] modes = new GL3VAODemo.Mode[] { GL3VAODemo.Mode.CPU_SRC, GL3VAODemo.Mode.VBO_ONLY }; testImpl(GLProfile.get(GLProfile.GL3bc), modes); } @Test public void test13CPUSourceAndVBOVAO() throws GLException, InterruptedException { if( ! GLProfile.isAvailable(GLProfile.GL3bc) ) { System.err.println("GL3bc n/a"); return; } final GL3VAODemo.Mode[] modes = new GL3VAODemo.Mode[] { GL3VAODemo.Mode.CPU_SRC, GL3VAODemo.Mode.VBO_VAO }; testImpl(GLProfile.get(GLProfile.GL3bc), modes); } @Test public void test23VBOOnlyAndVBOVAO() throws GLException, InterruptedException { if( ! GLProfile.isAvailable(GLProfile.GL3bc) ) { System.err.println("GL3bc n/a"); return; } final GL3VAODemo.Mode[] modes = new GL3VAODemo.Mode[] { GL3VAODemo.Mode.VBO_ONLY, GL3VAODemo.Mode.VBO_VAO }; testImpl(GLProfile.get(GLProfile.GL3bc), modes); } @Test public void test88AllModes() throws GLException, InterruptedException { if( ! GLProfile.isAvailable(GLProfile.GL3bc) ) { System.err.println("GL3bc n/a"); return; } final GL3VAODemo.Mode[] modes = new GL3VAODemo.Mode[] { GL3VAODemo.Mode.CPU_SRC, GL3VAODemo.Mode.VBO_ONLY, GL3VAODemo.Mode.VBO_VAO }; testImpl(GLProfile.get(GLProfile.GL3bc), modes); } public static void main(final String args[]) throws IOException { for(int i=0; i<args.length; i++) { if(args[i].equals("-time")) { duration = MiscUtils.atoi(args[++i], (int)duration); } } final String tstname = TestBug692GL3VAONEWT.class.getName(); org.junit.runner.JUnitCore.main(tstname); } }