JOGL v2.6.0-rc-20250706
JOGL, High-Performance Graphics Binding for Java™ (public API).
TestSharedContextNewtAWTBug523.java
Go to the documentation of this file.
1/**
2 * Copyright 2011-2023 JogAmp Community. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification, are
5 * permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this list of
8 * conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 * of conditions and the following disclaimer in the documentation and/or other materials
12 * provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 *
24 * The views and conclusions contained in the software and documentation are those of the
25 * authors and should not be interpreted as representing official policies, either expressed
26 * or implied, of JogAmp Community.
27 */
28
29package com.jogamp.opengl.test.junit.jogl.acore;
30
31import java.awt.Component;
32import java.awt.Dimension;
33import java.awt.event.WindowAdapter;
34import java.awt.event.WindowEvent;
35import java.io.IOException;
36import java.lang.reflect.InvocationTargetException;
37import java.nio.FloatBuffer;
38import java.nio.IntBuffer;
39import java.util.HashSet;
40import java.util.Set;
41import java.util.concurrent.Semaphore;
42import java.util.concurrent.TimeUnit;
43import java.util.concurrent.atomic.AtomicInteger;
44
45import com.jogamp.opengl.GL;
46import com.jogamp.opengl.GL2;
47import com.jogamp.opengl.GLAutoDrawable;
48import com.jogamp.opengl.GLCapabilities;
49import com.jogamp.opengl.GLContext;
50import com.jogamp.opengl.GLDrawableFactory;
51import com.jogamp.opengl.GLEventListener;
52import com.jogamp.opengl.GLOffscreenAutoDrawable;
53import com.jogamp.opengl.GLProfile;
54import com.jogamp.opengl.awt.GLCanvas;
55import com.jogamp.opengl.fixedfunc.GLMatrixFunc;
56import com.jogamp.opengl.fixedfunc.GLPointerFunc;
57import com.jogamp.opengl.glu.GLU;
58import javax.swing.Box;
59import javax.swing.BoxLayout;
60import javax.swing.JFrame;
61import javax.swing.JLabel;
62import javax.swing.JPanel;
63import javax.swing.JSlider;
64import javax.swing.SwingConstants;
65import javax.swing.SwingUtilities;
66import javax.swing.event.ChangeEvent;
67import javax.swing.event.ChangeListener;
68
69import org.junit.Assert;
70import org.junit.BeforeClass;
71import org.junit.Test;
72import org.junit.FixMethodOrder;
73import org.junit.runners.MethodSorters;
74
75import com.jogamp.common.ExceptionUtils;
76import com.jogamp.common.nio.Buffers;
77import com.jogamp.newt.awt.NewtCanvasAWT;
78import com.jogamp.newt.opengl.GLWindow;
79import com.jogamp.opengl.test.junit.util.AWTRobotUtil;
80import com.jogamp.opengl.test.junit.util.TestUtil;
81import com.jogamp.opengl.test.junit.util.TestUtil.WindowClosingListener;
82import com.jogamp.opengl.test.junit.util.UITestCase;
83import com.jogamp.opengl.util.Animator;
84
85
86/**
87 * TestSharedContextNewtAWTBug523
88 *
89 * Opens a single JFrame with two OpenGL widgets and sliders to adjust the view orientation.
90 *
91 * Each OpenGL widget renders a red triangle and a blue triangle.
92 * The red triangle is rendered using glBegin / glVertex / glEnd.
93 * The blue triangle is rendered using vertex buffer objects.
94 *
95 * VAO's are not used to allow testing on OSX GL2 context!
96 *
97 * If static useNewt is true, then those OpenGL widgets are GLWindow objects in a NewtCanvasAWT.
98 * If static useNewt is false, then those OpenGL widgets are GLCanvas objects.
99 *
100 * If shareContext is true, then the two OpenGL windows are initialized with a shared context,
101 * so that they share the vertex buffer and array objects and display lists.
102 * If shareContext is false, then the two OpenGL windows each have their own context, and the blue
103 * triangle fails to render in one of the windows.
104 *
105 * The four test cases run through the four combinations of useNewt and shareContext.
106 *
107 * Similar test cases are {@link TestSharedContextListNEWT}, {@link TestSharedContextListAWT},
108 * {@link TestSharedContextVBOES2NEWT1} and {@link TestSharedContextVBOES1NEWT}.
109 *
110 */
111@FixMethodOrder(MethodSorters.NAME_ASCENDING)
113
114 static long durationPerTest = 1000;
115
116 // counters for instances of event listener TwoTriangles
117 // private static int instanceCounter;
118 private static int initializationCounter;
119
120 // This semaphore is released once each time a GLEventListener destroy method is called.
121 // The main thread waits twice on this semaphore to ensure both canvases have finished cleaning up.
122 private static Semaphore disposalCompleteSemaphore = new Semaphore(0);
123
124 // Buffer objects can be shared across shared OpenGL context.
125 // If we run with sharedContext, then the tests will use these shared buffer objects,
126 // otherwise each event listener allocates its own buffer objects.
127 private static AtomicInteger sharedVertexBufferObjects = new AtomicInteger(0);
128 private static AtomicInteger sharedIndexBufferObjects = new AtomicInteger(0);
129
130 @BeforeClass
131 public static void initClass() {
133 setTestSupported(false);
134 }
135 }
136
137 static private GLOffscreenAutoDrawable initShared(final GLCapabilities caps) {
138 final GLOffscreenAutoDrawable sharedDrawable = GLDrawableFactory.getFactory(caps.getGLProfile()).createOffscreenAutoDrawable(null, caps, null, 64, 64);
139 Assert.assertNotNull(sharedDrawable);
140 // init and render one frame, which will setup the Gears display lists
141 sharedDrawable.display();
142 final GLContext ctx = sharedDrawable.getContext();
143 Assert.assertNotNull("Shared drawable's ctx is null", ctx);
144 Assert.assertTrue("Shared drawable's ctx is not created", ctx.isCreated());
145 return sharedDrawable;
146 }
147
148 static private void releaseShared(final GLOffscreenAutoDrawable sharedDrawable) {
149 if(null != sharedDrawable) {
150 sharedDrawable.destroy();
151 }
152 }
153
154 // inner class that implements the event listener
155 static class TwoTriangles implements GLEventListener {
156
157 boolean useShared;
158 int canvasWidth;
159 int canvasHeight;
160 private static final float boundsRadius = 2f;
161 private float viewDistance;
162 private static float viewDistanceFactor = 1.0f;
163 private float xAxisRotation;
164 private float yAxisRotation;
165 private static final float viewFovDegrees = 15f;
166
167 // Buffer objects can be shared across canvas instances, if those canvases are initialized with the same GLContext.
168 // If we run with sharedBufferObjects true, then the tests will use these shared buffer objects.
169 // If we run with sharedBufferObjects false, then each event listener allocates its own buffer objects.
170 private final int [] privateVertexBufferObjects = {0};
171 private final int [] privateIndexBufferObjects = {0};
172
173 public static int createVertexBuffer(final GL2 gl2) {
174 final FloatBuffer vertexBuffer = Buffers.newDirectFloatBuffer(18);
175 vertexBuffer.put(new float[]{
176 1.0f, -0.5f, 0f, // vertex 1
177 0f, 0f, 1f, // normal 1
178 1.5f, -0.5f, 0f, // vertex 2
179 0f, 0f, 1f, // normal 2
180 1.0f, 0.5f, 0f, // vertex 3
181 0f, 0f, 1f // normal 3
182 });
183 vertexBuffer.position(0);
184
185 final int[] vbo = { 0 };
186 gl2.glGenBuffers(1, vbo, 0);
187 gl2.glBindBuffer(GL.GL_ARRAY_BUFFER, vbo[0]);
188 gl2.glBufferData(GL.GL_ARRAY_BUFFER, vertexBuffer.capacity() * Buffers.SIZEOF_FLOAT, vertexBuffer, GL.GL_STATIC_DRAW);
189 gl2.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
190 return vbo[0];
191 }
192 public static int createVertexIndexBuffer(final GL2 gl2) {
193 final IntBuffer indexBuffer = Buffers.newDirectIntBuffer(3);
194 indexBuffer.put(new int[]{0, 1, 2});
195 indexBuffer.position(0);
196
197 final int[] vbo = { 0 };
198 gl2.glGenBuffers(1, vbo, 0);
199 gl2.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, vbo[0]);
200 gl2.glBufferData(GL.GL_ELEMENT_ARRAY_BUFFER, indexBuffer.capacity() * Buffers.SIZEOF_INT, indexBuffer, GL.GL_STATIC_DRAW);
201 gl2.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, 0);
202 return vbo[0];
203 }
204
205 TwoTriangles (final int canvasWidth, final int canvasHeight, final boolean useShared) {
206 // instanceNum = instanceCounter++;
207 this.canvasWidth = canvasWidth;
208 this.canvasHeight = canvasHeight;
209 this.useShared = useShared;
210 }
211
212 public void setXAxisRotation(final float xRot) {
213 xAxisRotation = xRot;
214 }
215
216 public void setYAxisRotation(final float yRot) {
217 yAxisRotation = yRot;
218 }
219
220 public void setViewDistanceFactor(final float factor) {
221 viewDistanceFactor = factor;
222 }
223
224
225 @Override
226 public void init(final GLAutoDrawable drawable) {
227 final GL2 gl2 = drawable.getGL().getGL2();
228
229 System.err.println("INIT GL IS: " + gl2.getClass().getName());
230
231 // Disable VSync
232 gl2.setSwapInterval(0);
233
234 // Setup the drawing area and shading mode
235 gl2.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
236
237 // the first instance of TwoTriangles initializes the shared buffer objects;
238 // synchronizing to deal with potential liveness issues if the data is initialized from one thread and used on another
239 synchronized (this) {
240 // use either the shared or private vertex buffers, as
241 int [] vertexBufferObjects;
242 int [] indexBufferObjects;
243 //
244 if (useShared) {
245 System.err.println("Using shared VBOs on slave 0x"+Integer.toHexString(hashCode()));
246 privateVertexBufferObjects[0] = sharedVertexBufferObjects.get();
247 privateIndexBufferObjects[0] = sharedIndexBufferObjects.get();
248 } else {
249 System.err.println("Using local VBOs on slave 0x"+Integer.toHexString(hashCode()));
250 }
251 vertexBufferObjects = privateVertexBufferObjects;
252 indexBufferObjects = privateIndexBufferObjects;
253
254 // if buffer sharing is enabled, then the first of the two event listeners to be
255 // initialized will allocate the buffers, and the other will re-use the allocated one
256 if (vertexBufferObjects[0] == 0) {
257 System.err.println("Creating vertex VBO on slave 0x"+Integer.toHexString(hashCode()));
258 vertexBufferObjects[0] = createVertexBuffer(gl2);
259 if (useShared) {
260 sharedVertexBufferObjects.set(vertexBufferObjects[0]);
261 }
262 }
263
264 // A check in the case that buffer sharing is enabled but context sharing is not enabled -- in that
265 // case, the buffer objects are not shareable, and the blue triangle cannot be rendereds.
266 // Furthermore, although the calls to bind and draw elements do not cause an error from glGetError
267 // when this check is removed, true blue triangle is not rendered anyways, and more importantly,
268 // I found that with my system glDrawElements causes a runtime exception 50% of the time. Executing the binds
269 // to unshareable buffers sets up glDrawElements for unpredictable crashes -- sometimes it does, sometimes not.
270 if (gl2.glIsBuffer(vertexBufferObjects[0])) {
271 gl2.glBindBuffer(GL.GL_ARRAY_BUFFER, vertexBufferObjects[0]);
272 //
273 gl2.glEnableClientState(GLPointerFunc.GL_VERTEX_ARRAY);
274 gl2.glVertexPointer(3, GL.GL_FLOAT, 6 * Buffers.SIZEOF_FLOAT, 0);
275 //
276 gl2.glEnableClientState(GLPointerFunc.GL_NORMAL_ARRAY);
277 gl2.glNormalPointer(GL.GL_FLOAT, 6 * Buffers.SIZEOF_FLOAT, 3 * Buffers.SIZEOF_FLOAT);
278 } else {
279 System.err.println("Vertex VBO is not a buffer on slave 0x"+Integer.toHexString(hashCode()));
280 }
281
282 if (indexBufferObjects[0] == 0) {
283 System.err.println("Creating index VBO on slave 0x"+Integer.toHexString(hashCode()));
284 indexBufferObjects[0] = createVertexIndexBuffer(gl2);
285 if (useShared) {
286 sharedIndexBufferObjects.set(indexBufferObjects[0]);
287 }
288 }
289
290 // again, a check in the case that buffer sharing is enabled but context sharing is not enabled
291 if (gl2.glIsBuffer(indexBufferObjects[0])) {
292 gl2.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, indexBufferObjects[0]);
293 } else {
294 System.err.println("Index VBO is not a buffer on slave 0x"+Integer.toHexString(hashCode()));
295 }
296
297 gl2.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, 0);
298 gl2.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
299 gl2.glDisableClientState(GLPointerFunc.GL_VERTEX_ARRAY);
300 gl2.glDisableClientState(GLPointerFunc.GL_NORMAL_ARRAY);
301
302 initializationCounter++;
303 } // synchronized (this)
304 }
305
306 @Override
307 public void dispose(final GLAutoDrawable drawable) {
308
309 synchronized (this) {
310 initializationCounter--;
311
312 final GL2 gl2 = drawable.getGL().getGL2();
313
314 // release shared resources
315 if (initializationCounter == 0 || !useShared) {
316 // use either the shared or private vertex buffers, as
317 int [] vertexBufferObjects;
318 int [] indexBufferObjects;
319 if (useShared) {
320 privateVertexBufferObjects[0] = sharedVertexBufferObjects.get();
321 privateIndexBufferObjects[0] = sharedIndexBufferObjects.get();
322 sharedVertexBufferObjects.set(0);
323 sharedIndexBufferObjects.set(0);
324 }
325 vertexBufferObjects = privateVertexBufferObjects;
326 indexBufferObjects = privateIndexBufferObjects;
327
328 gl2.glDeleteBuffers(1, vertexBufferObjects, 0);
329 logAnyErrorCodes(this, gl2, "dispose.2");
330 gl2.glDeleteBuffers(1, indexBufferObjects, 0);
331 logAnyErrorCodes(this, gl2, "dispose.3");
332 vertexBufferObjects[0] = 0;
333 indexBufferObjects[0] = 0;
334 }
335
336 // release the main thread once for each disposal
337 disposalCompleteSemaphore.release();
338 } // synchronized (this)
339 }
340
341 @Override
342 public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) {
343 System.err.println("reshape: "+canvasWidth+"x"+canvasHeight+" -> "+width+"x"+height+", [drawable pixel "+drawable.getSurfaceWidth()+"x"+drawable.getSurfaceHeight()+"]");
344 canvasWidth = width;
345 canvasHeight = height;
346 final GL2 gl2 = drawable.getGL().getGL2();
347 viewDistance = setupViewFrustum(gl2, canvasWidth, canvasHeight, boundsRadius, 1.0f, viewFovDegrees);
348 }
349
350 @Override
351 public void display(final GLAutoDrawable drawable) {
352
353 // wait until all instances are initialized before attempting to draw using the
354 // vertex array object, because the buffers are allocated in init and when the
355 // buffers are shared, we need to ensure that they are allocated before trying to use them
356 synchronized (this) {
357 if (initializationCounter != 2) {
358 return;
359 }
360 }
361
362 final GL2 gl2 = drawable.getGL().getGL2();
363 final GLU glu = new GLU();
364
365 logAnyErrorCodes(this, gl2, "display.0");
366
367 // Clear the drawing area
368 gl2.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
369
370 gl2.glViewport(0, 0, canvasWidth, canvasHeight);
371 gl2.glMatrixMode(GLMatrixFunc.GL_PROJECTION);
372 gl2.glLoadIdentity();
373 glu.gluPerspective(viewFovDegrees, (float)canvasWidth/(float)canvasHeight,
374 viewDistance*viewDistanceFactor-boundsRadius, viewDistance*viewDistanceFactor+boundsRadius);
375
376 // Reset the current matrix to the "identity"
377 gl2.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
378 gl2.glLoadIdentity();
379
380 // draw the scene
381 gl2.glPushAttrib(GL2.GL_ALL_ATTRIB_BITS);
382 gl2.glPushMatrix();
383
384 glu.gluLookAt(0, 0, 0 + viewDistance*viewDistanceFactor, 0, 0, 0, 0, 1, 0);
385 gl2.glRotatef(xAxisRotation, 1, 0, 0);
386 gl2.glRotatef(yAxisRotation, 0, 1, 0);
387
388 gl2.glDisable(GL.GL_CULL_FACE);
389 gl2.glEnable(GL.GL_DEPTH_TEST);
390
391 logAnyErrorCodes(this, gl2, "display.1");
392
393 // draw the triangles
394 drawTwoTriangles(gl2);
395
396 gl2.glPopMatrix();
397 gl2.glPopAttrib();
398
399 // Flush all drawing operations to the graphics card
400 gl2.glFlush();
401
402 logAnyErrorCodes(this, gl2, "display.X");
403 }
404
405 public void drawTwoTriangles(final GL2 gl2) {
406
407 // draw a red triangle the old fashioned way
408 gl2.glColor3f(1f, 0f, 0f);
409 gl2.glBegin(GL.GL_TRIANGLES);
410 gl2.glVertex3d(-1.5, -0.5, 0);
411 gl2.glNormal3d(0, 0, 1);
412 gl2.glVertex3d(-0.5, -0.5, 0);
413 gl2.glNormal3d(0, 0, 1);
414 gl2.glVertex3d(-0.75, 0.5, 0);
415 gl2.glNormal3d(0, 0, 1);
416 gl2.glEnd();
417
418 logAnyErrorCodes(this, gl2, "drawTwoTriangles.1");
419
420 // draw the blue triangle using a vertex array object, sharing the vertex and index buffer objects across
421 // contexts; if context sharing is not initialized, then one window will simply have to live without a blue triangle
422 //
423 // synchronizing to deal with potential liveness issues if the data is initialized from one
424 // thread and used on another
425 boolean vboBound = false;
426 // use either the shared or private vertex buffers, as
427 int [] vertexBufferObjects;
428 int [] indexBufferObjects;
429 synchronized (this) {
430 if (useShared) {
431 privateVertexBufferObjects[0] = sharedVertexBufferObjects.get();
432 privateIndexBufferObjects[0] = sharedIndexBufferObjects.get();
433 }
434 vertexBufferObjects = privateVertexBufferObjects;
435 indexBufferObjects = privateIndexBufferObjects;
436 } // synchronized (this)
437
438 // A check in the case that buffer sharing is enabled but context sharing is not enabled -- in that
439 // case, the buffer objects are not shareable, and the blue triangle cannot be rendereds.
440 // Furthermore, although the calls to bind and draw elements do not cause an error from glGetError
441 // when this check is removed, true blue triangle is not rendered anyways, and more importantly,
442 // I found that with my system glDrawElements causes a runtime exception 50% of the time. Executing the binds
443 // to unshareable buffers sets up glDrawElements for unpredictable crashes -- sometimes it does, sometimes not.
444 final boolean isVBO1 = gl2.glIsBuffer(indexBufferObjects[0]);
445 final boolean isVBO2 = gl2.glIsBuffer(vertexBufferObjects[0]);
446 final boolean useVBO = isVBO1 && isVBO2;
447 if ( useVBO ) {
448 gl2.glBindBuffer(GL.GL_ARRAY_BUFFER, vertexBufferObjects[0]);
449 gl2.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, indexBufferObjects[0]);
450
451 gl2.glEnableClientState(GLPointerFunc.GL_VERTEX_ARRAY);
452 // gl2.glVertexPointer(3, GL2.GL_FLOAT, 6 * GLBuffers.SIZEOF_FLOAT, 0);
453 gl2.glEnableClientState(GLPointerFunc.GL_NORMAL_ARRAY);
454 // gl2.glNormalPointer(GL2.GL_FLOAT, 6 * GLBuffers.SIZEOF_FLOAT, 3 * GLBuffers.SIZEOF_FLOAT);
455 vboBound = true;
456 }
457 // System.err.println("XXX VBO bound "+vboBound+"[ vbo1 "+isVBO1+", vbo2 "+isVBO2+"]");
458
459 logAnyErrorCodes(this, gl2, "drawTwoTriangles.2");
460
461 if (vboBound) {
462 gl2.glColor3f(0f, 0f, 1f);
463 gl2.glDrawElements(GL.GL_TRIANGLES, 3, GL.GL_UNSIGNED_INT, 0);
464 gl2.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
465 gl2.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, 0);
466 gl2.glDisableClientState(GLPointerFunc.GL_VERTEX_ARRAY);
467 gl2.glDisableClientState(GLPointerFunc.GL_NORMAL_ARRAY);
468 }
469
470 logAnyErrorCodes(this, gl2, "drawTwoTriangles.3");
471 }
472
473 public void displayChanged(final GLAutoDrawable drawable, final boolean modeChanged, final boolean deviceChanged) {
474 }
475
476 } // inner class TwoTriangles
477
478 private static final Set<String> errorSet = new HashSet<String>();
479
480 public static void logAnyErrorCodes(final Object obj, final GL gl, final String prefix) {
481 final int glError = gl.glGetError();
482 if(glError != GL.GL_NO_ERROR) {
483 final String errStr = "GL-Error: "+prefix + " on obj 0x"+Integer.toHexString(obj.hashCode())+", OpenGL error: 0x" + Integer.toHexString(glError);
484 if( errorSet.add(errStr) ) {
485 System.err.println(errStr);
486 ExceptionUtils.dumpStack(System.err);
487 }
488 }
489 final int status = gl.glCheckFramebufferStatus(GL.GL_FRAMEBUFFER);
490 if (status != GL.GL_FRAMEBUFFER_COMPLETE) {
491 final String errStr = "GL-Error: "+prefix + " on obj 0x"+Integer.toHexString(obj.hashCode())+", glCheckFramebufferStatus: 0x" + Integer.toHexString(status);
492 if( errorSet.add(errStr) ) {
493 System.err.println(errStr);
494 ExceptionUtils.dumpStack(System.err);
495 }
496 }
497 }
498
499 /**
500 * Sets the OpenGL projection matrix and front and back clipping planes for
501 * a viewport and returns the distance the camera should be placed from
502 * the center of the scene's bounding sphere such that the geometry is
503 * centered in the view frustum.
504 *
505 * @param gl2 current OpenGL context
506 * @param width width of GLDrawable
507 * @param height height of GLDrawable
508 * @param boundsRadius radius of a minimal bounding sphere of objects to be
509 * rendered in the viewport
510 * @param zoomFactor affects how far away the camera is placed from the scene; changing the
511 * zoom from 1.0 to 0.5 would make the scene appear half the size
512 * @param viewFovDegrees the desired field of vision for the viewport,
513 * higher is more fish-eye
514 * @return the distance the camera should be from the center of the scenes
515 * bounding sphere
516 */
517 public static float setupViewFrustum(final GL2 gl2, final int width, final int height, final float boundsRadius, final float zoomFactor, final float viewFovDegrees) {
518 assert boundsRadius > 0.0f;
519 assert zoomFactor > 0.0f;
520 assert viewFovDegrees > 0.0f;
521
522 final GLU glu = new GLU();
523
524 final float aspectRatio = (float) width / (float) height;
525 final float boundRadiusAdjusted = boundsRadius / zoomFactor;
526 final float lowestFov = aspectRatio > 1.0f ? viewFovDegrees : aspectRatio * viewFovDegrees;
527 final float viewDist = (float) (boundRadiusAdjusted / Math.sin( (lowestFov / 2.0) * (Math.PI / 180.0) ));
528
530 gl2.glLoadIdentity();
531 glu.gluPerspective(viewFovDegrees, aspectRatio, 0.1*viewDist, viewDist + boundRadiusAdjusted);
532
533 return viewDist;
534 }
535
536 @Test
537 public void test01UseAWTNotShared() throws InterruptedException, InvocationTargetException {
538 testContextSharingCreateVisibleDestroy(false, false);
539 }
540
541 @Test
542 public void test02UseAWTSharedContext() throws InterruptedException, InvocationTargetException {
543 testContextSharingCreateVisibleDestroy(false, true);
544 }
545
546 @Test
547 public void test10UseNEWTNotShared() throws InterruptedException, InvocationTargetException {
548 testContextSharingCreateVisibleDestroy(true, false);
549 }
550
551 @Test
552 public void test11UseNEWTSharedContext() throws InterruptedException, InvocationTargetException {
553 testContextSharingCreateVisibleDestroy(true, true);
554 }
555
556 /**
557 * Assemble the user interface and start the animator.
558 * It waits until the window is closed an then attempts orderly shutdown and resource deallocation.
559 */
560 public void testContextSharingCreateVisibleDestroy(final boolean useNewt, final boolean shareContext) throws InterruptedException, InvocationTargetException {
561 final JFrame frame = new JFrame("JSlider with "+(shareContext?"Shared":"NonShared")+" "+(useNewt?"NEWT":"AWT")+"-OpenGL Widget");
562 final TestUtil.WindowClosingListener awtClosingListener = AWTRobotUtil.addClosingListener(frame);
563
564 //
565 // GLDrawableFactory factory = GLDrawableFactory.getFactory(GLProfile.get(GLProfile.GL2));
566 // GLContext sharedContext = factory.getOrCreateSharedContext(factory.getDefaultDevice());
567 //
568 final GLCapabilities glCapabilities = new GLCapabilities(GLProfile.get(GLProfile.GL2));
569 glCapabilities.setSampleBuffers(true);
570 glCapabilities.setNumSamples(4);
571
572 final GLOffscreenAutoDrawable sharedDrawable;
573 if(shareContext) {
574 sharedDrawable = initShared(glCapabilities);
575 } else {
576 sharedDrawable = null;
577 }
578
579 final TwoTriangles eventListener1 = new TwoTriangles(480, 480, shareContext);
580 final TwoTriangles eventListener2 = new TwoTriangles(480, 480, shareContext);
581
582 final Component openGLComponent1;
583 final Component openGLComponent2;
584 final GLAutoDrawable openGLAutoDrawable1;
585 final GLAutoDrawable openGLAutoDrawable2;
586
587 if (useNewt) {
588 final GLWindow glWindow1 = GLWindow.create(glCapabilities);
589 if(shareContext) {
590 glWindow1.setSharedAutoDrawable(sharedDrawable);
591 }
592 final NewtCanvasAWT newtCanvasAWT1 = new NewtCanvasAWT(glWindow1);
593 newtCanvasAWT1.setPreferredSize(new Dimension(eventListener1.canvasWidth, eventListener1.canvasHeight));
594 glWindow1.addGLEventListener(eventListener1);
595 //
596 final GLWindow glWindow2 = GLWindow.create(glCapabilities);
597 if(shareContext) {
598 glWindow2.setSharedAutoDrawable(sharedDrawable);
599 }
600 final NewtCanvasAWT newtCanvasAWT2 = new NewtCanvasAWT(glWindow2);
601 newtCanvasAWT2.setPreferredSize(new Dimension(eventListener2.canvasWidth, eventListener2.canvasHeight));
602 glWindow2.addGLEventListener(eventListener2);
603
604 openGLComponent1 = newtCanvasAWT1;
605 openGLComponent2 = newtCanvasAWT2;
606 openGLAutoDrawable1 = glWindow1;
607 openGLAutoDrawable2 = glWindow2;
608 } else {
609 // Implementation using two GLCanvas instances; for GLCanvas context sharing to work, you must pass it in
610 // through the constructor; if you set it after it has no effect -- it does no harm if you initialized the ctor
611 // with the shared context, but if you didn't, it also doesn't trigger sharing
612 final GLCanvas canvas1;
613 final GLCanvas canvas2;
614
615 if (shareContext) {
616 canvas1 = new GLCanvas(glCapabilities);
617 canvas1.setSharedAutoDrawable(sharedDrawable);
618 canvas2 = new GLCanvas(glCapabilities);
619 canvas2.setSharedAutoDrawable(sharedDrawable);
620 } else {
621 canvas1 = new GLCanvas(glCapabilities);
622 canvas2 = new GLCanvas(glCapabilities);
623 }
624
625 canvas1.setSize(eventListener1.canvasWidth, eventListener1.canvasHeight);
626 canvas1.addGLEventListener(eventListener1);
627 //
628 canvas2.setSize(eventListener2.canvasWidth, eventListener2.canvasHeight);
629 canvas2.addGLEventListener(eventListener2);
630
631 openGLComponent1 = canvas1;
632 openGLComponent2 = canvas2;
633 openGLAutoDrawable1 = canvas1;
634 openGLAutoDrawable2 = canvas2;
635 }
636
637 // Create slider for x rotation.
638 // The vertically oriented slider rotates around the x axis
639 final JSlider xAxisRotationSlider = new JSlider(SwingConstants.VERTICAL, -180, 180, 1);
640 xAxisRotationSlider.setPaintTicks(false);
641 xAxisRotationSlider.setPaintLabels(false);
642 xAxisRotationSlider.setSnapToTicks(false);
643 xAxisRotationSlider.addChangeListener(new ChangeListener() {
644
645 @Override
646 public void stateChanged(final ChangeEvent e) {
647 eventListener1.setXAxisRotation(xAxisRotationSlider.getValue());
648 eventListener2.setXAxisRotation(xAxisRotationSlider.getValue());
649 }
650 });
651 final JLabel xAxisRotationLabel = new JLabel("X-Axis Rotation");
652
653 // Create slider for y rotation.
654 // The horizontally oriented slider rotates around the y axis
655 final JSlider yAxisRotationSlider = new JSlider(SwingConstants.HORIZONTAL, -180, 180, 1);
656 yAxisRotationSlider.setPaintTicks(false);
657 yAxisRotationSlider.setPaintLabels(false);
658 yAxisRotationSlider.setSnapToTicks(false);
659 yAxisRotationSlider.addChangeListener(new ChangeListener() {
660
661 @Override
662 public void stateChanged(final ChangeEvent e) {
663 eventListener1.setYAxisRotation(yAxisRotationSlider.getValue());
664 eventListener2.setYAxisRotation(yAxisRotationSlider.getValue());
665 }
666 });
667 final JLabel yAxisRotationLabel = new JLabel("Y-Axis Rotation");
668
669 // Create slider for view distance factor.
670 // We want a range of 0.0 to 10.0 with 0.1 increments (so we can scale down using 0.0 to 1.0).
671 // So, set JSlider to 0 to 100 and divide by 10.0 in stateChanged
672 final JSlider viewDistanceFactorSlider = new JSlider(SwingConstants.HORIZONTAL, 0, 100, 10);
673 viewDistanceFactorSlider.setPaintTicks(false);
674 viewDistanceFactorSlider.setPaintLabels(false);
675 viewDistanceFactorSlider.setSnapToTicks(false);
676 viewDistanceFactorSlider.addChangeListener(new ChangeListener() {
677
678 @Override
679 public void stateChanged(final ChangeEvent e) {
680 eventListener1.setViewDistanceFactor(viewDistanceFactorSlider.getValue() / 10.0f);
681 eventListener2.setViewDistanceFactor(viewDistanceFactorSlider.getValue() / 10.0f);
682 }
683 });
684 final JLabel viewDistanceFactorLabel = new JLabel("View Distance Factor");
685
686 // group the view distance and label into a vertical panel
687 final JPanel viewDistancePanel = new JPanel();
688 viewDistancePanel.setLayout(new BoxLayout(viewDistancePanel, BoxLayout.PAGE_AXIS));
689 viewDistancePanel.add(Box.createVerticalGlue());
690 viewDistancePanel.add(viewDistanceFactorSlider);
691 viewDistancePanel.add(viewDistanceFactorLabel);
692 viewDistancePanel.add(Box.createVerticalGlue());
693
694 // group both OpenGL canvases / windows into a horizontal panel
695 final JPanel openGLPanel = new JPanel();
696 openGLPanel.setLayout(new BoxLayout(openGLPanel, BoxLayout.LINE_AXIS));
697 openGLPanel.add(openGLComponent1);
698 openGLPanel.add(Box.createHorizontalStrut(5));
699 openGLPanel.add(openGLComponent2);
700
701 // group the open GL panel and the y-axis rotation slider into a vertical panel.
702 final JPanel canvasAndYAxisPanel = new JPanel();
703 canvasAndYAxisPanel.setLayout(new BoxLayout(canvasAndYAxisPanel, BoxLayout.PAGE_AXIS));
704 canvasAndYAxisPanel.add(openGLPanel);
705 canvasAndYAxisPanel.add(Box.createVerticalGlue());
706 canvasAndYAxisPanel.add(yAxisRotationSlider);
707 canvasAndYAxisPanel.add(yAxisRotationLabel);
708
709 // group the X-axis rotation slider and label into a horizontal panel.
710 final JPanel xAxisPanel = new JPanel();
711 xAxisPanel.setLayout(new BoxLayout(xAxisPanel, BoxLayout.LINE_AXIS));
712 xAxisPanel.add(xAxisRotationSlider);
713 xAxisPanel.add(xAxisRotationLabel);
714
715 final JPanel mainPanel = (JPanel) frame.getContentPane();
716 mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.LINE_AXIS));
717 mainPanel.add(viewDistancePanel);
718 mainPanel.add(Box.createHorizontalGlue());
719 mainPanel.add(canvasAndYAxisPanel);
720 mainPanel.add(Box.createHorizontalGlue());
721 mainPanel.add(xAxisPanel);
722
723 final Animator animator = new Animator(Thread.currentThread().getThreadGroup());
724 animator.setUpdateFPSFrames(1, null);
725 animator.add(openGLAutoDrawable1);
726 animator.add(openGLAutoDrawable2);
727
728 final Semaphore windowOpenSemaphore = new Semaphore(0);
729 final Semaphore closingSemaphore = new Semaphore(0);
730
731 // signal the main thread when the frame is closed
732 frame.addWindowListener(new WindowAdapter() {
733
734 @Override
735 public void windowClosing(final WindowEvent e) {
736 closingSemaphore.release();
737 }
738 });
739
740 // make the window visible using the EDT
741 SwingUtilities.invokeLater( new Runnable() {
742 @Override
743 public void run() {
744 frame.pack();
745 frame.setVisible(true);
746 windowOpenSemaphore.release();
747 }
748 });
749
750 // wait for the window to be visible and start the animation
751 try {
752 final boolean windowOpened = windowOpenSemaphore.tryAcquire(5000, TimeUnit.MILLISECONDS);
753 Assert.assertEquals(true, windowOpened);
754 } catch (final InterruptedException e) {
755 System.err.println("Closing wait interrupted: " + e.getMessage());
756 }
757 animator.start();
758
759 // sleep for test duration, then request the window to close, wait for the window to close,s and stop the animation
760 try {
761 while(animator.isAnimating() && animator.getTotalFPSDuration() < durationPerTest) {
762 Thread.sleep(100);
763 }
764 AWTRobotUtil.closeWindow(frame, true, awtClosingListener, null);
765 final boolean windowClosed = closingSemaphore.tryAcquire(5000, TimeUnit.MILLISECONDS);
766 Assert.assertEquals(true, windowClosed);
767 } catch (final InterruptedException e) {
768 System.err.println("Closing wait interrupted: " + e.getMessage());
769 }
770 animator.stop();
771
772 // ask the EDT to dispose of the frame;
773 // if using newt, explicitly dispose of the canvases because otherwise it seems our destroy methods are not called
774 SwingUtilities.invokeLater( new Runnable() {
775 @Override
776 public void run() {
777 frame.setVisible(false);
778 frame.dispose();
779 if (useNewt) {
780 ((NewtCanvasAWT)openGLComponent1).destroy();
781 ((NewtCanvasAWT)openGLComponent2).destroy();
782 }
783 closingSemaphore.release();
784 }
785 });
786
787 // wait for orderly destruction; it seems that if we share a GLContext across newt windows, bad things happen;
788 // I must be doing something wrong but I haven't figured it out yet
789 try {
790 final boolean windowsDisposed = closingSemaphore.tryAcquire(5000, TimeUnit.MILLISECONDS);
791 Assert.assertEquals(true, windowsDisposed);
792 } catch (final InterruptedException e) {
793 System.err.println("Closing wait interrupted: " + e.getMessage());
794 }
795
796 // ensure that the two OpenGL canvas' destroy methods completed successfully and released resources before we move on
797 int disposalSuccesses = 0;
798 try {
799 boolean acquired;
800 acquired = disposalCompleteSemaphore.tryAcquire(5000, TimeUnit.MILLISECONDS);
801 if (acquired){
802 disposalSuccesses++;
803 }
804 acquired = disposalCompleteSemaphore.tryAcquire(5000, TimeUnit.MILLISECONDS);
805 if (acquired){
806 disposalSuccesses++;
807 }
808 } catch (final InterruptedException e) {
809 System.err.println("Clean exit interrupted: " + e.getMessage());
810 }
811
812 Assert.assertEquals(true, disposalSuccesses == 2);
813
814 releaseShared(sharedDrawable);
815 }
816
817 static int atoi(final String a) {
818 int i=0;
819 try {
820 i = Integer.parseInt(a);
821 } catch (final Exception ex) { ex.printStackTrace(); }
822 return i;
823 }
824
825 public static void main(final String[] args) throws IOException {
826 for(int i=0; i<args.length; i++) {
827 if(args[i].equals("-time")) {
828 if (++i < args.length) {
829 durationPerTest = atoi(args[i]);
830 }
831 }
832 }
833 org.junit.runner.JUnitCore.main(TestSharedContextNewtAWTBug523.class.getName());
834 }
835
836}
837
AWT Canvas containing a NEWT Window using native parenting.
NEWT Window events are provided for notification purposes ONLY.
An implementation of GLAutoDrawable and Window interface, using a delegated Window instance,...
Definition: GLWindow.java:121
static GLWindow create(final GLCapabilitiesImmutable caps)
Creates a new GLWindow attaching a new Window referencing a new default Screen and default Display wi...
Definition: GLWindow.java:169
Specifies a set of OpenGL capabilities.
void setNumSamples(final int numSamples)
If sample buffers are enabled, indicates the number of buffers to be allocated.
final GLProfile getGLProfile()
Returns the GL profile you desire or used by the drawable.
void setSampleBuffers(final boolean enable)
Defaults to false.
Abstraction for an OpenGL rendering context.
Definition: GLContext.java:74
final boolean isCreated()
Indicates whether the underlying native OpenGL context has been created.
Definition: GLContext.java:604
abstract GLOffscreenAutoDrawable createOffscreenAutoDrawable(AbstractGraphicsDevice device, GLCapabilitiesImmutable caps, GLCapabilitiesChooser chooser, int width, int height)
Creates a realized GLOffscreenAutoDrawable incl it's offscreen NativeSurface with the given capabilit...
static GLDrawableFactory getFactory(final GLProfile glProfile)
Returns the sole GLDrawableFactory instance.
Specifies the the OpenGL profile.
Definition: GLProfile.java:77
static boolean isAvailable(final AbstractGraphicsDevice device, final String profile)
Returns the availability of a profile on a device.
Definition: GLProfile.java:305
static GLProfile get(final AbstractGraphicsDevice device, String profile)
Returns a GLProfile object.
static final String GL2
The desktop OpenGL profile 1.x up to 3.0.
Definition: GLProfile.java:579
A heavyweight AWT component which provides OpenGL rendering support.
Definition: GLCanvas.java:170
final void setSharedAutoDrawable(final GLAutoDrawable sharedAutoDrawable)
Specifies an GLAutoDrawable, which OpenGL context shall be shared by this GLAutoDrawable's GLContext.
Definition: GLCanvas.java:288
void addGLEventListener(final GLEventListener listener)
Adds the given listener to the end of this drawable queue.
Definition: GLCanvas.java:1065
Provides access to the OpenGL Utility Library (GLU).
Definition: GLU.java:43
void gluPerspective(float fovy, float aspect, float zNear, float zFar)
Definition: GLU.java:1362
static void logAnyErrorCodes(final Object obj, final GL gl, final String prefix)
void testContextSharingCreateVisibleDestroy(final boolean useNewt, final boolean shareContext)
Assemble the user interface and start the animator.
static float setupViewFrustum(final GL2 gl2, final int width, final int height, final float boundsRadius, final float zoomFactor, final float viewFovDegrees)
Sets the OpenGL projection matrix and front and back clipping planes for a viewport and returns the d...
static TestUtil.WindowClosingListener addClosingListener(final java.awt.Window win)
static boolean closeWindow(final java.awt.Window win, final boolean willClose, final TestUtil.WindowClosingListener closingListener, final Runnable waitAction)
Programmatically issue windowClosing on AWT or NEWT.
final synchronized void add(final GLAutoDrawable drawable)
Adds a drawable to this animator's list of rendering drawables.
final void setUpdateFPSFrames(final int frames, final PrintStream out)
final synchronized boolean start()
Starts this animator, if not running.
Definition: Animator.java:344
final synchronized boolean stop()
Stops this animator.
Definition: Animator.java:368
A higher-level abstraction than GLDrawable which supplies an event based mechanism (GLEventListener) ...
void addGLEventListener(GLEventListener listener)
Adds the given listener to the end of this drawable queue.
void destroy()
Destroys all resources associated with this GLAutoDrawable, inclusive the GLContext.
GLContext getContext()
Returns the context associated with this drawable.
Declares events which client code can use to manage OpenGL rendering into a GLAutoDrawable.
Platform-independent GLAutoDrawable specialization, exposing offscreen functionality.
int glCheckFramebufferStatus(int target)
Entry point to C language function: GLenum {@native glCheckFramebufferStatus}(GLenum target) Part ...
static final int GL_NO_ERROR
GL_ES_VERSION_2_0, GL_VERSION_1_1, GL_VERSION_1_0, GL_VERSION_ES_1_0 Define "GL_NO_ERROR" with expres...
Definition: GL.java:481
int glGetError()
Entry point to C language function: GLenum {@native glGetError}() Part of GL_ES_VERSION_2_0,...
static final int GL_FRAMEBUFFER_COMPLETE
GL_ES_VERSION_2_0, GL_ARB_framebuffer_object, GL_VERSION_3_0, GL_EXT_framebuffer_object,...
Definition: GL.java:605
static final int GL_FRAMEBUFFER
GL_ES_VERSION_2_0, GL_ARB_framebuffer_object, GL_VERSION_3_0, GL_OES_framebuffer_object,...
Definition: GL.java:630
void setSize(int width, int height)
Requests a new width and height for this AWTGLAutoDrawable.
Subset of OpenGL fixed function pipeline's matrix operations.
static final int GL_PROJECTION
Matrix mode projection.
void glLoadIdentity()
Load the current matrix with the identity matrix.
void glMatrixMode(int mode)
Sets the current matrix mode.