JOGL v2.6.0-rc-20250706
JOGL, High-Performance Graphics Binding for Java™ (public API).
TestSharedExternalContextAWT.java
Go to the documentation of this file.
1package com.jogamp.opengl.test.junit.jogl.acore;
2
3import java.awt.EventQueue;
4import java.awt.event.ActionEvent;
5import java.awt.event.ActionListener;
6import java.lang.reflect.InvocationTargetException;
7import java.util.concurrent.CountDownLatch;
8import java.util.concurrent.Executors;
9import java.util.concurrent.ScheduledExecutorService;
10import java.util.concurrent.TimeUnit;
11
12import javax.swing.Timer;
13
14import org.junit.Test;
15
16import com.jogamp.common.os.Platform;
17import com.jogamp.common.util.InterruptSource;
18import com.jogamp.common.util.locks.LockFactory;
19import com.jogamp.common.util.locks.RecursiveLock;
20import com.jogamp.opengl.*;
21import com.jogamp.opengl.test.junit.util.DumpGLInfo;
22
23/**
24 * Bug 1160.
25 * Test for context sharing with an external context. Creates an external GL context, then
26 * sets up an offscreen drawable which shares with it. The test contains two cases: one
27 * which creates and repaints the offscreen drawable on the EDT, and one which does so on
28 * a dedicated thread. On Windows+NVidia, the former fails.
29 */
31
32 static final int LATCH_COUNT = 5;
33
34 private void doTest(final boolean aUseEDT) throws Exception {
35 final CountDownLatch testLatch = new CountDownLatch(1);
36 final CountDownLatch masterLatch = new CountDownLatch(1);
37 final CountDownLatch slaveLatch = new CountDownLatch(LATCH_COUNT);
38 final MyGLEventListener listener = new MyGLEventListener(aUseEDT, slaveLatch);
39
40 /**
41 * For the purpose of this test, this offscreen drawable will be used to create
42 * an external GL context. In the actual application, the external context
43 * represents a GL context which lives outside the JVM.
44 */
45 final Runnable runnable = new Runnable() {
46 public void run() {
47 System.err.println("Master Thread Start: "+Thread.currentThread().getName());
48 final GLProfile glProfile = GLProfile.getDefault();
49 final GLCapabilities caps = new GLCapabilities(glProfile);
51 GLProfile.getDefaultDevice(), caps, null, 512, 512
52 );
53 // The listener will set up the context sharing in its init() method.
54 buffer.addGLEventListener(new DumpGLInfo(Platform.getNewline()+Platform.getNewline()+"Master GLContext", false, false, false));
55 buffer.addGLEventListener(listener);
56 buffer.display();
57 masterLatch.countDown();
58
59 // wait until test has finished
60 try {
61 testLatch.await();
62 } catch (final InterruptedException e) {
63 e.printStackTrace();
64 }
65 System.err.println("Master Thread End: "+Thread.currentThread().getName());
66 }
67 };
68
69 // Kick off thread creating the actual external context
70 // which is suppose to lie outside of the JVM.
71 // The thread is kept alive, since this detail
72 // may be required for the OpenGL driver implementation.
73 final Thread thread = new InterruptSource.Thread(null, runnable);
74 thread.setDaemon(true);
75 thread.start();
76 masterLatch.await(3, TimeUnit.SECONDS);
77
78 // Wait for slave to finish.
79 slaveLatch.await(3, TimeUnit.SECONDS);
80
81 // signal master test has finished
82 testLatch.countDown();
83
84 // If exceptions occurred, fail.
85 final Exception e = listener.fException;
86 if (e != null) {
87 throw e;
88 }
89 }
90
91 @Test
92 public void test01OnEDT() throws Exception {
93 doTest(true);
94 }
95
96 @Test
97 public void test02OnExecutorThread() throws Exception {
98 doTest(false);
99 }
100
101 /**
102 * Listener that creates an external drawable and an offscreen drawable, with context
103 * sharing between the two.
104 */
105 private static class MyGLEventListener implements GLEventListener {
106 GLOffscreenAutoDrawable fOffscreenDrawable;
107 final boolean fUseEDT;
108 final CountDownLatch fLatch;
109 final RecursiveLock masterLock = LockFactory.createRecursiveLock();
110
111 private Exception fException = null;
112
113 public MyGLEventListener(final boolean aUseEDT, final CountDownLatch aLatch) {
114 fUseEDT = aUseEDT;
115 fLatch = aLatch;
116 }
117
118 @Override
119 public void init(final GLAutoDrawable drawable) {
120 // FIXME: We actually need to hook into GLContext make-current lock
121 masterLock.lock();
122 try {
123 final GL2 gl = drawable.getGL().getGL2();
124 gl.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
126
127 System.err.println(); System.err.println();
128 System.err.println("Master (orig) Ct: "+drawable.getContext());
129 // Create the external context on the caller thread.
131 System.err.println(); System.err.println();
132 System.err.println("External Context: "+master);
133
134 // This runnable creates an offscreen drawable which shares with the external context.
135 final Runnable initializer = new Runnable() {
136 public void run() {
137 // FIXME: We actually need to hook into GLContext make-current lock
138 // masterLock.lock();
139 try {
140 fOffscreenDrawable = GLDrawableFactory.getFactory(false).createOffscreenAutoDrawable(
143 null, // new DefaultGLCapabilitiesChooser(),
144 512, 512
145 );
146 fOffscreenDrawable.setSharedContext(master);
147 fOffscreenDrawable.addGLEventListener(new DumpGLInfo(Platform.getNewline()+Platform.getNewline()+"Slave GLContext", false, false, false));
148
149 try {
150 System.err.println(); System.err.println();
151 System.err.println("Current: "+GLContext.getCurrent());
152 fOffscreenDrawable.display();
153 } catch (final GLException e) {
154 fException = e;
155 throw e;
156 }
157 } finally {
158 // masterLock.unlock();
159 }
160 }
161 };
162
163 /**
164 * Depending on the test case, invoke the initialization on the EDT or on an
165 * executor thread. The test also displays the offscreen drawable a few times
166 * before finishing.
167 */
168 if (fUseEDT) {
169 // Initialize using invokeLater().
170 try {
171 // We cannot use EventQueue.invokeAndWait(..) since it will
172 // block this will block the current thread, holding the context!
173 // The whole issue w/ an external shared context is make-current
174 // synchronization. JOGL attempts to lock the surface/drawable
175 // of the master context to avoid concurrent usage.
176 // The semantic constraints of a shared context are not well defined,
177 // i.e. some driver may allow creating a shared context w/ a master context
178 // to be in use - others don't.
179 // Hence it is up to the user to sync the external master context in this case,
180 // see 'masterLock' of in this code!
181 // EventQueue.invokeAndWait(initializer);
182 EventQueue.invokeLater(initializer);
183 } catch (final Exception e) {
184 fException = e;
185 }
186
187 // Display using a Swing timer, i.e. also on the EDT.
188 final Timer t = new Timer(200, new ActionListener() {
189 int i = 0;
190
191 @Override
192 public void actionPerformed(final ActionEvent e) {
193 if (++i > LATCH_COUNT) {
194 return;
195 }
196
197 System.err.println("Update on EDT");
198 fOffscreenDrawable.display();
199 fLatch.countDown();
200 }
201 });
202 t.start();
203 } else {
204 // Initialize and display using a single-threaded executor.
205 final ScheduledExecutorService exe = Executors.newSingleThreadScheduledExecutor();
206 exe.submit(initializer);
207 exe.scheduleAtFixedRate(new Runnable() {
208 int i = 0;
209
210 @Override
211 public void run() {
212 if (++i > LATCH_COUNT) {
213 return;
214 }
215
216 System.err.println("Update on Executor thread");
217 fOffscreenDrawable.display();
218 fLatch.countDown();
219 }
220 }, 0, 200, TimeUnit.MILLISECONDS);
221 }
222 } finally {
223 masterLock.unlock();
224 }
225 }
226
227 @Override
228 public void dispose(final GLAutoDrawable drawable) {
229 // FIXME: We actually need to hook into GLContext make-current lock
230 masterLock.lock();
231 try {
232 } finally {
233 masterLock.unlock();
234 }
235 }
236
237 @Override
238 public void display(final GLAutoDrawable drawable) {
239 // FIXME: We actually need to hook into GLContext make-current lock
240 masterLock.lock();
241 try {
242 } finally {
243 masterLock.unlock();
244 }
245 }
246
247 @Override
248 public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) {
249 // FIXME: We actually need to hook into GLContext make-current lock
250 masterLock.lock();
251 try {
252 } finally {
253 masterLock.unlock();
254 }
255 }
256 }
257
258 public static void main(final String[] pArgs)
259 {
260 org.junit.runner.JUnitCore.main(TestSharedExternalContextAWT.class.getName());
261 }
262}
Specifies a set of OpenGL capabilities.
Abstraction for an OpenGL rendering context.
Definition: GLContext.java:74
static GLContext getCurrent()
Returns this thread current context.
Definition: GLContext.java:515
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...
abstract GLContext createExternalGLContext()
static GLDrawableFactory getFactory(final GLProfile glProfile)
Returns the sole GLDrawableFactory instance.
A generic exception for OpenGL errors used throughout the binding as a substitute for RuntimeExceptio...
Specifies the the OpenGL profile.
Definition: GLProfile.java:77
static GLProfile getDefault(final AbstractGraphicsDevice device)
Returns a default GLProfile object, reflecting the best for the running platform.
Definition: GLProfile.java:739
static AbstractGraphicsDevice getDefaultDevice()
A higher-level abstraction than GLDrawable which supplies an event based mechanism (GLEventListener) ...
GL getGL()
Returns the GL pipeline object this GLAutoDrawable uses.
void addGLEventListener(GLEventListener listener)
Adds the given listener to the end of this drawable queue.
GLContext getContext()
Returns the context associated with this drawable.
GL2 getGL2()
Casts this object to the GL2 interface.
Declares events which client code can use to manage OpenGL rendering into a GLAutoDrawable.
Platform-independent GLAutoDrawable specialization, exposing offscreen functionality.
void setSharedContext(GLContext sharedContext)
Specifies an OpenGL context, which shall be shared by this GLAutoDrawable's GLContext.
static final int GL_COLOR_BUFFER_BIT
GL_ES_VERSION_2_0, GL_VERSION_1_1, GL_VERSION_1_0, GL_VERSION_ES_1_0 Define "GL_COLOR_BUFFER_BIT" wit...
Definition: GL.java:390
void glClearColor(float red, float green, float blue, float alpha)
Entry point to C language function: void {@native glClearColor}(GLfloat red, GLfloat green,...
void glClear(int mask)
Entry point to C language function: void {@native glClear}(GLbitfield mask) Part of GL_ES_VERSION_...