/**
* Copyright 2012 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.util.concurrent.atomic.AtomicInteger;
import com.jogamp.common.util.InterruptSource;
import com.jogamp.nativewindow.NativeSurface;
import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.GLCapabilities;
import com.jogamp.opengl.GLEventListener;
import com.jogamp.opengl.GLProfile;
import org.junit.Test;
import org.junit.FixMethodOrder;
import org.junit.runners.MethodSorters;
import com.jogamp.newt.opengl.GLWindow;
import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2;
import com.jogamp.opengl.test.junit.util.UITestCase;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class TestGLContextSurfaceLockNEWT extends UITestCase {
static final int demoSize = 64;
public abstract class MyRunnable implements Runnable {
final Object postSync;
final int id;
boolean done = false;
public MyRunnable(final Object postSync, final int id) {
this.postSync = postSync;
this.id = id;
}
public boolean done() { return done; }
}
public class RudeAnimator extends MyRunnable {
private final GLAutoDrawable glad;
private final int frameCount;
public RudeAnimator(final GLAutoDrawable glad, final int frameCount, final Object postSync, final int id) {
super(postSync, id);
this.glad = glad;
this.frameCount = frameCount;
}
public void run() {
System.err.println("Animatr "+id+", count "+frameCount+": PRE: "+Thread.currentThread().getName());
for(int c=0; c<frameCount; c++) {
glad.display();
}
System.err.println("Animatr "+id+": DONE/SYNC: "+Thread.currentThread().getName());
synchronized (postSync) {
done = true;
System.err.println("Animatr "+id+": END: "+Thread.currentThread().getName());
postSync.notifyAll();
}
}
}
/**
* Emulates a resize behavior with immediate display() call
* while the surface is still locked.
*/
public class RudeResizer extends MyRunnable {
private final GLWindow win;
private final int actionCount;
public RudeResizer(final GLWindow win, final int actionCount, final Object postSync, final int id) {
super(postSync, id);
this.win = win;
this.actionCount = actionCount;
}
public void run() {
System.err.println("Resizer "+id+", count "+actionCount+": PRE: "+Thread.currentThread().getName());
for(int c=0; c<actionCount; c++) {
win.runOnEDTIfAvail(true, new Runnable() {
public void run() {
// Normal resize, may trigger immediate display within lock
win.setSize(win.getSurfaceWidth()+1, win.getSurfaceHeight()+1);
// Force display within surface lock.
// This procedure emulates the sensitive behavior
// for all platforms directly.
final int res = win.lockSurface();
if(res > NativeSurface.LOCK_SURFACE_NOT_READY) {
try {
win.display();
} finally {
win.unlockSurface();
}
}
}});
}
System.err.println("Resizer "+id+": DONE/SYNC: "+Thread.currentThread().getName());
synchronized (postSync) {
done = true;
System.err.println("Resizer "+id+": END: "+Thread.currentThread().getName());
postSync.notifyAll();
}
}
}
protected static boolean done(final MyRunnable[] tasks) {
for(int i=tasks.length-1; i>=0; i--) {
if(!tasks[i].done()) {
return false;
}
}
return true;
}
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 class MyEventCounter implements GLEventListener {
AtomicInteger reshapeCount = new AtomicInteger(0);
AtomicInteger displayCount = new AtomicInteger(0);
@Override
public void init(final GLAutoDrawable drawable) {
}
@Override
public void dispose(final GLAutoDrawable drawable) {
System.err.println("*** reshapes: "+reshapeCount+", displays "+displayCount);
}
@Override
public void display(final GLAutoDrawable drawable) {
displayCount.incrementAndGet();
}
@Override
public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) {
reshapeCount.incrementAndGet();
}
public void reset() {
reshapeCount.set(0);
displayCount.set(0);
}
}
protected void runJOGLTasks(final int animThreadCount, final int frameCount, final int reszThreadCount, final int resizeCount) throws InterruptedException {
final GLWindow glWindow = GLWindow.create(new GLCapabilities(GLProfile.getDefault()));
final MyEventCounter myEventCounter = new MyEventCounter();
glWindow.addGLEventListener(new GearsES2(0));
glWindow.addGLEventListener(myEventCounter);
glWindow.setSize(demoSize, demoSize);
glWindow.setVisible(true);
final String currentThreadName = Thread.currentThread().getName();
final Object sync = new Object();
final MyRunnable[] animTasks = new MyRunnable[animThreadCount];
final MyRunnable[] resizeTasks = new MyRunnable[animThreadCount];
final InterruptSource.Thread[] animThreads = new InterruptSource.Thread[reszThreadCount];
final InterruptSource.Thread[] resizeThreads = new InterruptSource.Thread[reszThreadCount];
System.err.println("animThreadCount "+animThreadCount+", frameCount "+frameCount);
System.err.println("reszThreadCount "+reszThreadCount+", resizeCount "+resizeCount);
System.err.println("tasks "+(animTasks.length+resizeTasks.length)+", threads "+(animThreads.length+resizeThreads.length));
for(int i=0; i<animThreadCount; i++) {
System.err.println("create anim task/thread "+i);
animTasks[i] = new RudeAnimator(glWindow, frameCount, sync, i);
animThreads[i] = new InterruptSource.Thread(null, animTasks[i], currentThreadName+"-anim"+i);
}
for(int i=0; i<reszThreadCount; i++) {
System.err.println("create resz task/thread "+i);
resizeTasks[i] = new RudeResizer(glWindow, resizeCount, sync, i);
resizeThreads[i] = new InterruptSource.Thread(null, resizeTasks[i], currentThreadName+"-resz"+i);
}
myEventCounter.reset();
int j=0, k=0;
for(int i=0; i<reszThreadCount+animThreadCount; i++) {
if(0==i%2) {
System.err.println("start resize thread "+j);
resizeThreads[j++].start();
} else {
System.err.println("start anim thread "+k);
animThreads[k++].start();
}
}
synchronized (sync) {
while(!done(resizeTasks) || !done(animTasks)) {
try {
sync.wait();
} catch (final InterruptedException e) {
throw new RuntimeException(e);
}
}
}
int i=0;
while(i<30 && !isDead(animThreads) && !isDead(resizeThreads)) {
Thread.sleep(100);
i++;
}
glWindow.destroy();
}
@Test
public void test01_1A1RThreads_100Resizes() throws InterruptedException {
runJOGLTasks(1, 200, 1, 100);
}
@Test
public void test01_3A3RThreads_50Resizes() throws InterruptedException {
runJOGLTasks(3, 100, 3, 50);
}
public static void main(final String args[]) throws IOException {
for(int i=0; i<args.length; i++) {
if(args[i].equals("-time")) {
i++;
try {
// duration = Integer.parseInt(args[i]);
} catch (final Exception ex) { ex.printStackTrace(); }
}
}
final String tstname = TestGLContextSurfaceLockNEWT.class.getName();
org.junit.runner.JUnitCore.main(tstname);
}
}