JOGL v2.6.0-rc-20250706
JOGL, High-Performance Graphics Binding for Java™ (public API).
TestSharedContextVBOES2NEWT5.java
Go to the documentation of this file.
1/**
2 * Copyright 2019 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.util.List;
32
33import com.jogamp.newt.opengl.GLWindow;
34
35import com.jogamp.nativewindow.util.InsetsImmutable;
36import com.jogamp.opengl.GLCapabilities;
37import com.jogamp.opengl.GLContext;
38import com.jogamp.opengl.GLProfile;
39
40import com.jogamp.opengl.util.Animator;
41
42import jogamp.opengl.GLContextShareSet;
43
44import com.jogamp.opengl.test.junit.util.GLTestUtil;
45import com.jogamp.opengl.test.junit.util.MiscUtils;
46import com.jogamp.opengl.test.junit.util.NewtTestUtil;
47import com.jogamp.opengl.test.junit.util.UITestCase;
48import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2;
49
50import org.junit.Assert;
51import org.junit.BeforeClass;
52import org.junit.Test;
53import org.junit.FixMethodOrder;
54import org.junit.runners.MethodSorters;
55
56/**
57 * Analyze Bug 1312: Test potential memory leak in {@link GLContextShareSet}
58 * due to its usage of hard references.
59 * <p>
60 * Test uses the asynchronous one animator per instance and GL buffer mapping path only.
61 * </p>
62 */
63@FixMethodOrder(MethodSorters.NAME_ASCENDING)
65 static GLProfile glp;
66 static GLCapabilities caps;
67 static final int width=128, height=128;
68
69 @BeforeClass
70 public static void initClass() {
73 Assert.assertNotNull(glp);
74 caps = new GLCapabilities(glp);
75 Assert.assertNotNull(caps);
76 } else {
77 setTestSupported(false);
78 }
79 }
80
81 protected GLWindow createGLWindow(final int x, final int y, final GearsES2 gears) throws InterruptedException {
82 final GLWindow glWindow = GLWindow.create(caps);
83 Assert.assertNotNull(glWindow);
84 glWindow.setPosition(x, y);
85 glWindow.setTitle("Shared Gears NEWT Test: "+x+"/"+y+" shared true");
86 glWindow.setSize(width, height);
87 glWindow.addGLEventListener(gears);
88
89 return glWindow;
90 }
91
92 @Test
93 public void test01CleanDtorOrder() throws InterruptedException {
94 asyncEachAnimator(true, 3);
95 }
96
97 // @Test
98 public void test02DirtyDtorOrder() throws InterruptedException {
99 asyncEachAnimator(false, 3);
100 }
101
102 public void asyncEachAnimator(final boolean destroyCleanOrder, final int loops) throws InterruptedException {
103 // master
104 final Animator a1 = new Animator(0 /* w/o AWT */);
105 final GearsES2 g1 = new GearsES2(0);
106 g1.setVerbose(false);
107 g1.setSyncObjects(g1); // this is master, since rendered we must use it as sync
108 g1.setUseMappedBuffers(true);
109 g1.setValidateBuffers(true);
110 final GLWindow f1 = createGLWindow(0, 0, g1);
111 a1.add(f1);
112 a1.start();
113
114 f1.setVisible(true);
115 Assert.assertTrue(NewtTestUtil.waitForRealized(f1, true, null));
116 Assert.assertTrue(NewtTestUtil.waitForVisible(f1, true, null));
117 Assert.assertTrue(GLTestUtil.waitForContextCreated(f1, true, null));
118 Assert.assertTrue("Gears1 not initialized", g1.waitForInit(true));
119 System.err.println("XXX-0-C-M - GLContextShareSet.Map");
120 GLContextShareSet.printMap(System.err);
121 final InsetsImmutable insets = f1.getInsets();
122 final GLContext ctx1 = f1.getContext();
123
124 // slaves
125 final int slaveCount = 10;
126 final int slavesPerRow=4;
127 for(int j=0; j<loops; j++) {
128 final Animator[] sa = new Animator[slaveCount];
129 final GearsES2[] sg = new GearsES2[slaveCount];
130 final GLWindow[] sf = new GLWindow[slaveCount];
131 final GLContext[] sc = new GLContext[slaveCount];
132 for(int i=0; i<slaveCount; i++) {
133 final Animator a2 = new Animator(0 /* w/o AWT */);
134 final GearsES2 g2 = new GearsES2(0);
135 g2.setVerbose(false);
136 g2.setSharedGears(g1); // also uses master g1 as sync, if required
137 final int y = 1 + i/slavesPerRow;
138 final int x = i%slavesPerRow;
139 final GLWindow f2 = createGLWindow(width*x,
140 insets.getTotalHeight()+height*y, g2);
141 f2.setUndecorated(true);
142 f2.setSharedAutoDrawable(f1);
143 a2.add(f2);
144 a2.start();
145 f2.setVisible(true);
146
147 Assert.assertTrue(NewtTestUtil.waitForRealized(f2, true, null));
148 Assert.assertTrue(NewtTestUtil.waitForVisible(f2, true, null));
149 Assert.assertTrue(GLTestUtil.waitForContextCreated(f2, true, null));
150 Assert.assertTrue("Gears2 not initialized", g2.waitForInit(true));
151 sa[i] = a2;
152 sg[i] = g2;
153 sf[i] = f2;
154 sc[i] = f2.getContext();
155 }
156
157 {
158 final List<GLContext> ctx1Shares = ctx1.getCreatedShares();
159 Assert.assertTrue("Gears1 is shared", !g1.usesSharedGears());
160 Assert.assertTrue("Ctx1 is not shared", ctx1.isShared());
161 Assert.assertEquals("Ctx1 has unexpected number of created shares", slaveCount, ctx1Shares.size());
162 Assert.assertEquals("Ctx1 Master Context is different", ctx1, ctx1.getSharedMaster());
163 }
164 for(int i=0; i<slaveCount; i++) {
165 final List<GLContext> ctxSShares = sc[i].getCreatedShares();
166 Assert.assertTrue("Gears2 is not shared", sg[i].usesSharedGears());
167 Assert.assertTrue("CtxS["+i+"] is not shared", sc[i].isShared());
168 Assert.assertEquals("CtxS["+i+"] has unexpected number of created shares", slaveCount, ctxSShares.size());
169 Assert.assertEquals("CtxS["+i+"] Master Context is different", ctx1, sc[i].getSharedMaster());
170 }
171 System.err.println("XXX-"+j+"-C - GLContextShareSet.Map");
172 GLContextShareSet.printMap(System.err);
173
174 try {
175 Thread.sleep(duration);
176 } catch(final Exception e) {
177 e.printStackTrace();
178 }
179
180 if( destroyCleanOrder ) {
181 System.err.println("XXX Destroy in clean order");
182 for(int i=slaveCount-1; 0<=i; i--) {
183 sa[i].stop();
184 sf[i].destroy();
185 Assert.assertTrue(NewtTestUtil.waitForVisible(sf[i], false, null));
186 Assert.assertTrue(NewtTestUtil.waitForRealized(sf[i], false, null));
187 }
188 } else {
189 System.err.println("XXX Destroy in creation order (but Master) - Driver Impl. May trigger driver Bug i.e. not postponing GL ctx destruction after releasing all refs.");
190 for(int i=0; i<slaveCount; i++) {
191 sa[i].stop();
192 sf[i].destroy();
193 Assert.assertTrue(NewtTestUtil.waitForVisible(sf[i], false, null));
194 Assert.assertTrue(NewtTestUtil.waitForRealized(sf[i], false, null));
195 }
196 }
197 System.err.println("XXX-"+j+"-X-SX1 - GLContextShareSet.Map");
198 GLContextShareSet.printMap(System.err);
199 Assert.assertEquals("GLContextShareSet ctx1.createdCount is not 1", 1, GLContextShareSet.getCreatedShareCount(ctx1));
200 Assert.assertEquals("GLContextShareSet ctx1.destroyedCount is not slaveCount", slaveCount, GLContextShareSet.getDestroyedShareCount(ctx1));
201 for(int i=0; i<slaveCount; i++) {
202 sa[i] = null;
203 sg[i] = null;
204 sf[i] = null;
205 sc[i] = null;
206 }
207 {
208 // Ensure nulled objects got destroyed and taken from the GLContextShareSet map.
209 System.gc();
210 try { Thread.sleep(100); } catch (final InterruptedException ie) {}
211 System.gc();
212 try { Thread.sleep(100); } catch (final InterruptedException ie) {}
213 }
214 System.err.println("XXX-"+j+"-X-SX2 - GLContextShareSet.Map");
215 GLContextShareSet.printMap(System.err);
216 Assert.assertEquals("GLContextShareSet ctx1.createdCount is not 1", 1, GLContextShareSet.getCreatedShareCount(ctx1));
217 Assert.assertEquals("GLContextShareSet ctx1.destroyedCount is not 0", 0, GLContextShareSet.getDestroyedShareCount(ctx1));
218 }
219
220 // stop master
221 System.gc();
222 System.err.println("XXX-X-X-M1 - GLContextShareSet.Map");
223 GLContextShareSet.printMap(System.err);
224 Assert.assertEquals("GLContextShareSet ctx1.createdCount is not 1", 1, GLContextShareSet.getCreatedShareCount(ctx1));
225 Assert.assertEquals("GLContextShareSet ctx1.destroyedCount is not 0", 0, GLContextShareSet.getDestroyedShareCount(ctx1));
226 Assert.assertEquals("GLContextShareSet is not 1", 1, GLContextShareSet.getSize());
227 a1.stop();
228 f1.destroy();
229 System.err.println("XXX-X-X-M2 - GLContextShareSet.Map");
230 GLContextShareSet.printMap(System.err);
231 Assert.assertTrue(NewtTestUtil.waitForVisible(f1, false, null));
232 Assert.assertTrue(NewtTestUtil.waitForRealized(f1, false, null));
233 Assert.assertEquals("GLContextShareSet ctx1.createdCount is not 0", 0, GLContextShareSet.getCreatedShareCount(ctx1));
234 Assert.assertEquals("GLContextShareSet ctx1.destroyedCount is not 0", 0, GLContextShareSet.getDestroyedShareCount(ctx1));
235 {
236 final List<GLContext> ctx1Shares = ctx1.getCreatedShares();
237 Assert.assertFalse("Ctx1 is still shared", ctx1.isShared());
238 Assert.assertEquals("Ctx1 still has created shares", 0, ctx1Shares.size());
239 Assert.assertEquals("Ctx1 Master Context is not null", null, ctx1.getSharedMaster());
240 }
241 Assert.assertEquals("GLContextShareSet is not 0", 0, GLContextShareSet.getSize());
242 }
243
244 static long duration = 1000; // ms - ~60 frames
245 static boolean mainRun = false;
246
247 public static void main(final String args[]) {
248 mainRun = true;
249 for(int i=0; i<args.length; i++) {
250 if(args[i].equals("-time")) {
251 i++;
252 try {
253 duration = Integer.parseInt(args[i]);
254 } catch (final Exception ex) { ex.printStackTrace(); }
255 }
256 }
257 /**
258 BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
259 System.err.println("Press enter to continue");
260 System.err.println(stdin.readLine()); */
261 org.junit.runner.JUnitCore.main(TestSharedContextVBOES2NEWT5.class.getName());
262 }
263}
An implementation of GLAutoDrawable and Window interface, using a delegated Window instance,...
Definition: GLWindow.java:121
final void setPosition(final int x, final int y)
Sets the location of the window's client area excluding insets (window decorations) in window units.
Definition: GLWindow.java:525
final void setTitle(final String title)
Definition: GLWindow.java:297
final void setSize(final int width, final int height)
Sets the size of the window's client area in window units, excluding decorations.
Definition: GLWindow.java:625
final void setVisible(final boolean visible)
Calls setVisible(true, visible), i.e.
Definition: GLWindow.java:615
final InsetsImmutable getInsets()
Returns the insets defined as the width and height of the window decoration on the left,...
Definition: GLWindow.java:431
final void setUndecorated(final boolean value)
Definition: GLWindow.java:337
final void destroy()
Destroys all resources associated with this GLAutoDrawable, inclusive the GLContext.
Definition: GLWindow.java:605
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.
Abstraction for an OpenGL rendering context.
Definition: GLContext.java:74
final boolean isShared()
Returns true if this GLContext is shared, otherwise false.
Definition: GLContext.java:261
final GLContext getSharedMaster()
Returns the shared master GLContext of this GLContext if shared, otherwise return null.
Definition: GLContext.java:272
final List< GLContext > getCreatedShares()
Returns a new list of created GLContext shared with this GLContext.
Definition: GLContext.java:277
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 final String GL2ES2
The intersection of the desktop GL3, GL2 and embedded ES2 profile.
Definition: GLProfile.java:594
static GLProfile get(final AbstractGraphicsDevice device, String profile)
Returns a GLProfile object.
Analyze Bug 1312: Test potential memory leak in GLContextShareSet due to its usage of hard references...
GLWindow createGLWindow(final int x, final int y, final GearsES2 gears)
void asyncEachAnimator(final boolean destroyCleanOrder, final int loops)
boolean waitForInit(final boolean initialized)
Definition: GearsES2.java:187
static boolean waitForContextCreated(final GLAutoDrawable autoDrawable, final boolean created, final Runnable waitAction)
Definition: GLTestUtil.java:42
static boolean waitForRealized(final Screen screen, final boolean realized, final Runnable waitAction)
static boolean waitForVisible(final Window win, final boolean visible, final Runnable waitAction)
final synchronized void add(final GLAutoDrawable drawable)
Adds a drawable to this animator's list of rendering drawables.
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
Immutable insets representing rectangular window decoration insets on all four edges in window units.
void addGLEventListener(GLEventListener listener)
Adds the given listener to the end of this drawable queue.
GLContext getContext()
Returns the context associated with this drawable.