/** * Copyright 2010 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 com.jogamp.common.util.InterruptSource; import com.jogamp.common.util.InterruptedRuntimeException; import com.jogamp.nativewindow.Capabilities; import com.jogamp.nativewindow.util.InsetsImmutable; import com.jogamp.opengl.GLCapabilities; import com.jogamp.opengl.GLProfile; import org.junit.Assert; import org.junit.BeforeClass; import com.jogamp.newt.Display; import com.jogamp.newt.NewtFactory; import com.jogamp.newt.Screen; import com.jogamp.newt.Window; import com.jogamp.newt.opengl.GLWindow; import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; import com.jogamp.opengl.test.junit.util.UITestCase; import com.jogamp.opengl.test.junit.util.ValidateLockListener; import com.jogamp.opengl.util.Animator; /** * Concurrent and lock-free initialization and rendering using exclusive NEWT Display EDT instances, or * concurrent locked initialization and lock-free rendering using a shared NEWT Display EDT instances. * <p> * Rendering is always lock-free and independent of the EDT. * </p> * <p> * Each test is decorated w/ {@link GLProfile#shutdown()} to ensure that * implicit {@link GLProfile#initSingleton()} is also being tested. * </p> */ public abstract class InitConcurrentBaseNEWT extends UITestCase { static final int demoWinSize = 128; static long duration = 300; // ms static InsetsImmutable insets = null; static int num_x, num_y; @BeforeClass public static void initClass() { final Window dummyWindow = NewtFactory.createWindow(new Capabilities()); dummyWindow.setSize(demoWinSize, demoWinSize); dummyWindow.setVisible(true); Assert.assertEquals(true, dummyWindow.isVisible()); Assert.assertEquals(true, dummyWindow.isNativeValid()); insets = dummyWindow.getInsets(); final int scrnHeight = dummyWindow.getScreen().getHeight(); final int scrnWidth = dummyWindow.getScreen().getWidth(); final int[] demoScreenSize = dummyWindow.convertToPixelUnits(new int[] { demoWinSize, demoWinSize }); final int[] insetsScreenSize = dummyWindow.convertToPixelUnits(new int[] { insets.getTotalWidth(), insets.getTotalHeight() }); num_x = scrnWidth / ( demoScreenSize[0] + insetsScreenSize[0] ) - 2; num_y = scrnHeight / ( demoScreenSize[1] + insetsScreenSize[1] ) - 2; dummyWindow.destroy(); } public static class JOGLTask implements Runnable { private final int id; private final Object postSync; private final boolean reuse; private boolean done = false; public JOGLTask(final Object postSync, final int id, final boolean reuse) { this.postSync = postSync; this.id = id; this.reuse = reuse; } public void run() { final int x = ( id % num_x ) * ( demoWinSize + insets.getTotalHeight() ); final int y = ( (id / num_x) % num_y ) * ( demoWinSize + insets.getTotalHeight() ); System.err.println("JOGLTask "+id+": START: "+x+"/"+y+", reuse "+reuse+" - "+Thread.currentThread().getName()); final Display display = NewtFactory.createDisplay(null, reuse); final Screen screen = NewtFactory.createScreen(display, 0); final GLWindow glWindow = GLWindow.create(screen, new GLCapabilities(GLProfile.getDefault())); Assert.assertNotNull(glWindow); glWindow.setTitle("Task "+id); glWindow.setPosition(x + insets.getLeftWidth(), y + insets.getTopHeight() ); glWindow.addGLEventListener(new ValidateLockListener()); glWindow.addGLEventListener(new GearsES2(0)); final Animator animator = new Animator(glWindow); glWindow.setSize(demoWinSize, demoWinSize); glWindow.setVisible(true); animator.setUpdateFPSFrames(60, null); System.err.println("JOGLTask "+id+": INITIALIZED: "+", "+display+" - "+Thread.currentThread().getName()); animator.start(); Assert.assertEquals(true, animator.isAnimating()); Assert.assertEquals(true, glWindow.isVisible()); Assert.assertEquals(true, glWindow.isNativeValid()); Assert.assertEquals(true, glWindow.isRealized()); System.err.println("JOGLTask "+id+": RUNNING: "+Thread.currentThread().getName()); while(animator.isAnimating() && animator.getTotalFPSDuration()<duration) { try { Thread.sleep(100); } catch (final InterruptedException e) { e.printStackTrace(); } } animator.stop(); glWindow.destroy(); System.err.println("JOGLTask "+id+": DONE/SYNC: "+Thread.currentThread().getName()); synchronized (postSync) { done = true; System.err.println("JOGLTask "+id+": END: "+Thread.currentThread().getName()); postSync.notifyAll(); } } public boolean done() { return done; } } protected static boolean done(final JOGLTask[] tasks) { for(int i=tasks.length-1; i>=0; i--) { if(!tasks[i].done()) { return false; } } return true; } protected static String doneDump(final JOGLTask[] tasks) { final StringBuilder sb = new StringBuilder(); sb.append("["); for(int i=0; i<tasks.length; i++) { if(i>0) { sb.append(", "); } sb.append(i).append(": ").append(tasks[i].done()); } sb.append("]"); return sb.toString(); } protected static boolean isDead(final Thread[] threads) { for(int i=threads.length-1; i>=0; i--) { if(threads[i].isAlive()) { return false; } } return true; } protected static String isAliveDump(final Thread[] threads) { final StringBuilder sb = new StringBuilder(); sb.append("["); for(int i=0; i<threads.length; i++) { if(i>0) { sb.append(", "); } sb.append(i).append(": ").append(threads[i].isAlive()); } sb.append("]"); return sb.toString(); } protected void runJOGLTasks(final int num, final boolean reuse) throws InterruptedException { GLProfile.shutdown(); System.err.println("InitConcurrentBaseNEWT "+num+" threads, reuse display: "+reuse); final String currentThreadName = Thread.currentThread().getName(); final Object syncDone = new Object(); final JOGLTask[] tasks = new JOGLTask[num]; final InterruptSource.Thread[] threads = new InterruptSource.Thread[num]; int i; for(i=0; i<num; i++) { tasks[i] = new JOGLTask(syncDone, i, reuse); threads[i] = new InterruptSource.Thread(null, tasks[i], currentThreadName+"-jt"+i); } final long t0 = System.currentTimeMillis(); for(i=0; i<num; i++) { threads[i].start(); } i=0; synchronized (syncDone) { while(!done(tasks)) { try { syncDone.wait(500); } catch (final InterruptedException e) { throw new InterruptedRuntimeException(e); } System.err.println(i+": "+doneDump(tasks)); i++; } } final long t1 = System.currentTimeMillis(); System.err.println("total: "+(t1-t0)/1000.0+"s"); Assert.assertTrue("Tasks are incomplete. Complete: "+doneDump(tasks), done(tasks)); i=0; while(i<30 && !isDead(threads)) { Thread.sleep(100); i++; } Assert.assertTrue("Threads are still alive after 3s. Alive: "+isAliveDump(threads), isDead(threads)); GLProfile.shutdown(); } }