diff --git a/src/java/org/lwjgl/opengl/AWTGLCanvas.java b/src/java/org/lwjgl/opengl/AWTGLCanvas.java
index 4e38941c..96ae00f5 100644
--- a/src/java/org/lwjgl/opengl/AWTGLCanvas.java
+++ b/src/java/org/lwjgl/opengl/AWTGLCanvas.java
@@ -109,6 +109,16 @@ public class AWTGLCanvas extends Canvas implements Drawable, ComponentListener,
return context;
}
+ public Context createSharedContext() throws LWJGLException {
+ synchronized ( GlobalLock.lock ) {
+ synchronized ( SYNC_LOCK ) {
+ if ( context == null ) throw new IllegalStateException("Canvas not yet displayable");
+
+ return new Context(peer_info, context.getContextAttribs(), context);
+ }
+ }
+ }
+
/** Constructor using the default PixelFormat. */
public AWTGLCanvas() throws LWJGLException {
this(new PixelFormat());
@@ -175,7 +185,7 @@ public class AWTGLCanvas extends Canvas implements Drawable, ComponentListener,
*/
public void removeNotify() {
synchronized ( SYNC_LOCK ) {
- destroyContext();
+ destroy();
super.removeNotify();
}
}
@@ -225,7 +235,7 @@ public class AWTGLCanvas extends Canvas implements Drawable, ComponentListener,
}
/** Destroy the OpenGL context. This happens when the component becomes undisplayable */
- private void destroyContext() {
+ public final void destroy() {
synchronized ( SYNC_LOCK ) {
try {
if ( context != null ) {
diff --git a/src/java/org/lwjgl/opengl/Context.java b/src/java/org/lwjgl/opengl/Context.java
index cdd2f7ef..fa15faa1 100644
--- a/src/java/org/lwjgl/opengl/Context.java
+++ b/src/java/org/lwjgl/opengl/Context.java
@@ -61,7 +61,7 @@ final class Context {
private final ByteBuffer handle;
private final PeerInfo peer_info;
- private final IntBuffer attribList;
+ private final ContextAttribs contextAttribs;
private final boolean forwardCompatible;
/** Whether the context has been destroyed */
@@ -94,6 +94,10 @@ final class Context {
return peer_info;
}
+ ContextAttribs getContextAttribs() {
+ return contextAttribs;
+ }
+
static Context getCurrentContext() {
return (Context)current_context_local.get();
}
@@ -109,6 +113,9 @@ final class Context {
GLContext.loadOpenGLLibrary();
try {
this.peer_info = peer_info;
+ this.contextAttribs = attribs;
+
+ IntBuffer attribList;
if ( attribs != null ) {
attribList = attribs.getAttribList();
forwardCompatible = attribs.isForwardCompatible();
diff --git a/src/java/org/lwjgl/opengl/Display.java b/src/java/org/lwjgl/opengl/Display.java
index b344ef15..40483458 100644
--- a/src/java/org/lwjgl/opengl/Display.java
+++ b/src/java/org/lwjgl/opengl/Display.java
@@ -143,6 +143,26 @@ public final class Display {
return isCreated() ? context : null;
}
}
+
+ public Context createSharedContext() throws LWJGLException {
+ synchronized ( GlobalLock.lock ) {
+ if ( !isCreated() ) throw new IllegalStateException("Display must be created.");
+
+ return new Context(peer_info, context.getContextAttribs(), context);
+ }
+ }
+
+ public void makeCurrent() throws LWJGLException {
+ Display.makeCurrent();
+ }
+
+ public void releaseContext() throws LWJGLException {
+ Display.releaseContext();
+ }
+
+ public void destroy() {
+ Display.destroy();
+ }
};
}
diff --git a/src/java/org/lwjgl/opengl/Drawable.java b/src/java/org/lwjgl/opengl/Drawable.java
index 8e542f19..82b0556b 100644
--- a/src/java/org/lwjgl/opengl/Drawable.java
+++ b/src/java/org/lwjgl/opengl/Drawable.java
@@ -31,6 +31,8 @@
*/
package org.lwjgl.opengl;
+import org.lwjgl.LWJGLException;
+
/**
* The Drawable interface describes an OpenGL drawable with an associated
* Context.
@@ -39,5 +41,38 @@ package org.lwjgl.opengl;
*/
public interface Drawable {
+
+ /**
+ * [INTERNAL USE ONLY] Returns the Drawable's Context.
+ *
+ * @return the Drawable's Context
+ */
Context getContext();
+
+ /**
+ * [INTERNAL USE ONLY] Creates a new Context that is shared with the Drawable's Context.
+ *
+ * @return a Context shared with the Drawable's Context.
+ */
+ Context createSharedContext() throws LWJGLException;
+
+ /**
+ * Makes the Drawable's context current in the current thread.
+ *
+ * @throws LWJGLException
+ */
+ void makeCurrent() throws LWJGLException;
+
+ /**
+ * If the Drawable's context is current in the current thread, no context will be current after a call to this method.
+ *
+ * @throws LWJGLException
+ */
+ void releaseContext() throws LWJGLException;
+
+ /**
+ * Destroys the Drawable.
+ */
+ void destroy();
+
}
diff --git a/src/java/org/lwjgl/opengl/Pbuffer.java b/src/java/org/lwjgl/opengl/Pbuffer.java
index e0a40561..d0cb1b99 100644
--- a/src/java/org/lwjgl/opengl/Pbuffer.java
+++ b/src/java/org/lwjgl/opengl/Pbuffer.java
@@ -255,6 +255,12 @@ public final class Pbuffer implements Drawable {
return context;
}
+ public Context createSharedContext() throws LWJGLException {
+ synchronized ( GlobalLock.lock ) {
+ return new Context(peer_info, context.getContextAttribs(), context);
+ }
+ }
+
private void checkDestroyed() {
if (destroyed)
throw new IllegalStateException("Pbuffer is destroyed");
@@ -281,6 +287,12 @@ public final class Pbuffer implements Drawable {
context.makeCurrent();
}
+ public void releaseContext() throws LWJGLException {
+ checkDestroyed();
+ if ( context.isCurrent() )
+ Context.releaseCurrentContext();
+ }
+
/**
* Gets the Pbuffer capabilities.
*
diff --git a/src/java/org/lwjgl/opengl/SharedDrawable.java b/src/java/org/lwjgl/opengl/SharedDrawable.java
new file mode 100644
index 00000000..68a9ec66
--- /dev/null
+++ b/src/java/org/lwjgl/opengl/SharedDrawable.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2002-2008 LWJGL Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * * Neither the name of 'LWJGL' nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 THE COPYRIGHT OWNER 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.
+ */
+package org.lwjgl.opengl;
+
+import org.lwjgl.LWJGLException;
+import org.lwjgl.LWJGLUtil;
+
+/**
+ * @author Spasi
+ * @since 20 Απρ 2010
+ */
+
+/**
+ * A Drawable implementation that shares its context with another Drawable. This is useful
+ * for background loading of resources. See org.lwjgl.test.opengl.multithread.BackgroundLoad
+ * for an example.
+ *
+ * @author Spasi
+ */
+public final class SharedDrawable implements Drawable {
+
+ private Context context;
+
+ private boolean destroyed;
+
+ public SharedDrawable(final Drawable drawable) throws LWJGLException {
+ this.context = drawable.createSharedContext();
+ }
+
+ public Context getContext() {
+ return context;
+ }
+
+ public Context createSharedContext() {
+ throw new UnsupportedOperationException();
+ }
+
+ public void makeCurrent() throws LWJGLException {
+ checkDestroyed();
+ context.makeCurrent();
+
+ }
+
+ public void releaseContext() throws LWJGLException {
+ checkDestroyed();
+ if ( context.isCurrent() )
+ Context.releaseCurrentContext();
+ }
+
+ public void destroy() {
+ if ( destroyed )
+ return;
+
+ try {
+ context.forceDestroy();
+ destroyed = true;
+ } catch (LWJGLException e) {
+ LWJGLUtil.log("Exception occurred while destroying SharedDrawable: " + e);
+ }
+ }
+
+ private void checkDestroyed() {
+ if ( destroyed )
+ throw new IllegalStateException("SharedDrawable is destroyed");
+ }
+
+}
\ No newline at end of file
diff --git a/src/java/org/lwjgl/test/opengl/awt/DemoBox.java b/src/java/org/lwjgl/test/opengl/awt/DemoBox.java
index b0250ac4..9604b4b3 100644
--- a/src/java/org/lwjgl/test/opengl/awt/DemoBox.java
+++ b/src/java/org/lwjgl/test/opengl/awt/DemoBox.java
@@ -150,7 +150,7 @@ public class DemoBox extends Frame {
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
- demoCanvas.destroy();
+ demoCanvas.destroyCanvas();
dispose();
System.exit(0);
}
@@ -269,7 +269,7 @@ public class DemoBox extends Frame {
}
}
- public void destroy() {
+ public void destroyCanvas() {
setActiveDemo(null);
renderThread = null;
}
diff --git a/src/java/org/lwjgl/test/opengl/multithread/BackgroundLoadTest.java b/src/java/org/lwjgl/test/opengl/multithread/BackgroundLoadTest.java
new file mode 100644
index 00000000..d24a29b2
--- /dev/null
+++ b/src/java/org/lwjgl/test/opengl/multithread/BackgroundLoadTest.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 2002-2008 LWJGL Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * * Neither the name of 'LWJGL' nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 THE COPYRIGHT OWNER 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.
+ */
+package org.lwjgl.test.opengl.multithread;
+
+import org.lwjgl.BufferUtils;
+import org.lwjgl.LWJGLException;
+import org.lwjgl.input.Keyboard;
+import org.lwjgl.input.Mouse;
+import org.lwjgl.opengl.*;
+import org.lwjgl.util.glu.GLU;
+import org.lwjgl.util.glu.Sphere;
+
+import java.nio.FloatBuffer;
+
+/**
+ * A test of loading textures in a background thread. This can be achieved in 2 ways:
+ *
+ * a) A dummy Pbuffer is created and its context shares the rendering context.
+ * b) A SharedDrawable is used.
+ *
+ * When the test starts, there's no texture created and rendering is done with texturing disabled.
+ * 2 seconds later a "dummy" texture is created in the background thread and texturing is enabled. This dummy texture
+ * can by anything the developer wants to have as a placeholder while textures are being loaded.
+ * Finally, 5 seconds later the "true" texture is loaded and displayed. This texture will change every 5 seconds after
+ * that, until the test is terminated (ESC key).
+ *
+ * @author Spasi
+ */
+public final class BackgroundLoadTest {
+
+ private static boolean run = true;
+
+ private static BackgroundLoader backgroundLoader;
+
+ private static Sphere sphere;
+
+ private BackgroundLoadTest() {
+ }
+
+ public static void main(String[] args) {
+ initialize(args);
+
+ Util.checkGLError();
+
+ try {
+ backgroundLoader.start();
+ } catch (LWJGLException e) {
+ kill("Failed to start background thread.", e);
+ }
+
+ Util.checkGLError();
+
+ while ( run ) {
+ if ( !Display.isVisible() )
+ Thread.yield();
+ else {
+ handleIO();
+
+ GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
+
+ renderObject();
+
+ Util.checkGLError();
+
+ // Restore camera position.
+ GL11.glPopMatrix();
+ GL11.glPushMatrix();
+ }
+
+ Display.update();
+
+ if ( Display.isCloseRequested() )
+ break;
+ }
+
+ cleanup();
+ System.exit(0);
+ }
+
+ private static void initialize(String[] args) {
+ if ( args.length != 1 )
+ argsError();
+
+ DisplayMode displayMode = null;
+
+ try {
+ DisplayMode[] modes = Display.getAvailableDisplayModes();
+
+ displayMode = chooseMode(modes, 1024, 768);
+ if ( displayMode == null )
+ displayMode = chooseMode(modes, 800, 600);
+ if ( displayMode == null )
+ displayMode = chooseMode(modes, 640, 480);
+ if ( displayMode == null )
+ kill("Failed to set an appropriate display mode.");
+
+ System.out.println("Setting display mode to: " + displayMode);
+ Display.setDisplayMode(displayMode);
+ Display.setTitle("Background Loading Test");
+ Display.create(new PixelFormat(8, 24, 0));
+ } catch (LWJGLException e) {
+ kill(e.getMessage());
+ }
+
+ GL11.glViewport(0, 0, displayMode.getWidth(), displayMode.getHeight());
+
+ GL11.glMatrixMode(GL11.GL_PROJECTION);
+ GL11.glLoadIdentity();
+ GLU.gluPerspective(45, displayMode.getWidth() / (float)displayMode.getHeight(), 1.0f, 10.0f);
+
+ GL11.glMatrixMode(GL11.GL_MODELVIEW);
+ GL11.glLoadIdentity();
+
+ // Setup camera position.
+ GL11.glTranslatef(0.0f, 0.0f, -4.0f);
+ GL11.glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
+ GL11.glPushMatrix();
+
+ GL11.glClearDepth(1.0f);
+ GL11.glDepthFunc(GL11.GL_LEQUAL);
+
+ GL11.glHint(GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_NICEST);
+
+ GL11.glFrontFace(GL11.GL_CCW);
+ GL11.glPolygonMode(GL11.GL_FRONT, GL11.GL_FILL);
+
+ GL11.glCullFace(GL11.GL_BACK);
+ GL11.glEnable(GL11.GL_CULL_FACE);
+
+ GL11.glAlphaFunc(GL11.GL_GREATER, 0.0f);
+ GL11.glEnable(GL11.GL_ALPHA_TEST);
+
+ GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
+ GL11.glDisable(GL11.GL_BLEND);
+
+ GL11.glShadeModel(GL11.GL_SMOOTH);
+
+ final FloatBuffer vectorBuffer = BufferUtils.createFloatBuffer(4);
+
+ vectorBuffer.clear();
+ vectorBuffer.put(0, 1.0f).put(1, 1.0f).put(2, 1.0f).put(3, 1.0f);
+ GL11.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, vectorBuffer);
+
+ vectorBuffer.put(0, 1.0f).put(1, 1.0f).put(2, 1.0f).put(3, 1.0f);
+ GL11.glLight(GL11.GL_LIGHT0, GL11.GL_AMBIENT, vectorBuffer);
+
+ vectorBuffer.put(0, 1.0f).put(1, 1.0f).put(2, 0.5f).put(3, 1.0f);
+ GL11.glLight(GL11.GL_LIGHT0, GL11.GL_SPECULAR, vectorBuffer);
+
+ vectorBuffer.put(0, -1.0f / 3.0f).put(1, 1.0f / 3.0f).put(2, 1.0f / 3.0f).put(3, 0.0f); // Infinite
+ GL11.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, vectorBuffer);
+
+ vectorBuffer.put(0, 0.2f).put(1, 0.2f).put(2, 0.2f).put(3, 1.0f);
+ GL11.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, vectorBuffer);
+
+ GL11.glEnable(GL11.GL_LIGHT0);
+ GL11.glEnable(GL11.GL_LIGHTING);
+
+ sphere = new Sphere();
+
+ if ( "PB".equalsIgnoreCase(args[0]) ) {
+ backgroundLoader = new BackgroundLoader() {
+ Drawable getDrawable() throws LWJGLException {
+ return new Pbuffer(2, 2, new PixelFormat(8, 24, 0), Display.getDrawable());
+ }
+ };
+ } else if ( "SD".equalsIgnoreCase(args[0]) ) {
+ backgroundLoader = new BackgroundLoader() {
+ Drawable getDrawable() throws LWJGLException {
+ return new SharedDrawable(Display.getDrawable());
+ }
+ };
+ } else {
+ argsError();
+ }
+ }
+
+ private static void handleIO() {
+ if ( Keyboard.getNumKeyboardEvents() != 0 ) {
+ while ( Keyboard.next() ) {
+ if ( Keyboard.getEventKeyState() )
+ continue;
+
+ switch ( Keyboard.getEventKey() ) {
+ case Keyboard.KEY_ESCAPE:
+ run = false;
+ break;
+ }
+ }
+ }
+
+ while ( Mouse.next() ) ;
+ }
+
+ static void renderObject() {
+ GL11.glColor3f(1.0f, 1.0f, 1.0f);
+
+ int texID = backgroundLoader.getTexID();
+ if ( texID == 0 ) {
+ sphere.setTextureFlag(false);
+ GL11.glDisable(GL11.GL_TEXTURE_2D);
+ } else {
+ sphere.setTextureFlag(true);
+ GL11.glEnable(GL11.GL_TEXTURE_2D);
+ GL11.glBindTexture(GL11.GL_TEXTURE_2D, texID);
+ }
+
+ sphere.draw(1.0f, 32, 32);
+
+ if ( texID != 0 ) { // Unbind so we can update from the background thread.
+ GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
+ GL11.glDisable(GL11.GL_TEXTURE_2D);
+ }
+ }
+
+ private static DisplayMode chooseMode(DisplayMode[] modes, int width, int height) {
+ DisplayMode bestMode = null;
+
+ for ( int i = 0; i < modes.length; i++ ) {
+ DisplayMode mode = modes[i];
+ if ( mode.getWidth() == width && mode.getHeight() == height && mode.getFrequency() <= 85 ) {
+ if ( bestMode == null || (mode.getBitsPerPixel() >= bestMode.getBitsPerPixel() && mode.getFrequency() > bestMode.getFrequency()) )
+ bestMode = mode;
+ }
+ }
+
+ return bestMode;
+ }
+
+ private static void cleanup() {
+ backgroundLoader.cleanup();
+
+ Thread.yield(); // Let background thread finish.
+
+ if ( Display.isCreated() )
+ Display.destroy();
+ }
+
+ private static void argsError() {
+ System.out.println("\nInvalid program arguments.");
+ System.out.println("\nUsage: BackgroundLoadTest , where argument can be one of the following:\n");
+ System.out.println("PB\t- Use a Pbuffer context for the background thread.");
+ System.out.println("SD\t- Use a SharedDrawable context for the background thread.");
+
+ cleanup();
+ System.exit(-1);
+ }
+
+ static void kill(String reason) {
+ System.out.println("The BackgroundLoadTest program was terminated because an error occured.\n");
+ System.out.println("Reason: " + (reason == null ? "Unknown" : reason));
+
+ cleanup();
+ System.exit(-1);
+ }
+
+ static void kill(String reason, Throwable t) {
+ System.out.println("The BackgroundLoadTest program was terminated because an exception occured.\n");
+ System.out.println("Reason: " + (reason == null ? "Unknown" : reason));
+
+ System.out.println("Exception message: " + t.getMessage());
+
+ cleanup();
+ System.exit(-1);
+ }
+
+}
\ No newline at end of file
diff --git a/src/java/org/lwjgl/test/opengl/multithread/BackgroundLoader.java b/src/java/org/lwjgl/test/opengl/multithread/BackgroundLoader.java
new file mode 100644
index 00000000..ad76387f
--- /dev/null
+++ b/src/java/org/lwjgl/test/opengl/multithread/BackgroundLoader.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2002-2008 LWJGL Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * * Neither the name of 'LWJGL' nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 THE COPYRIGHT OWNER 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.
+ */
+/*
+ * Created by LWJGL.
+ * User: spasi
+ * Date: 2004-03-30
+ * Time: 8:41:42 pm
+ */
+package org.lwjgl.test.opengl.multithread;
+
+import org.lwjgl.BufferUtils;
+import org.lwjgl.LWJGLException;
+import org.lwjgl.opengl.Drawable;
+import org.lwjgl.opengl.GL11;
+import org.lwjgl.util.Color;
+import org.lwjgl.util.ReadableColor;
+
+import java.nio.ByteBuffer;
+
+abstract class BackgroundLoader {
+
+ private static final int WIDTH = 32;
+ private static final int HEIGHT = 32;
+
+ private static final Object lock = new Object();
+
+ private Drawable drawable;
+
+ private boolean running;
+
+ private ByteBuffer texture;
+ private int texID;
+
+ protected BackgroundLoader() {
+ running = true;
+ texture = BufferUtils.createByteBuffer(WIDTH * HEIGHT * 3);
+ }
+
+ abstract Drawable getDrawable() throws LWJGLException;
+
+ void cleanup() {
+ running = false;
+ }
+
+ void start() throws LWJGLException {
+ new Thread(new Runnable() {
+ public void run() {
+ System.out.println("-- Background Thread started --");
+
+ System.out.println("** Sleeping, no texture created yet **");
+
+ long start = System.currentTimeMillis();
+
+ try {
+ Thread.sleep(2000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ try {
+ drawable = getDrawable();
+ drawable.makeCurrent();
+ } catch (LWJGLException e) {
+ throw new RuntimeException(e);
+ }
+
+ System.out.println("** Drawable created **");
+
+ synchronized ( lock ) {
+ // Create a "dummy" texture while we wait for texture IO
+ createCheckerTexture(Color.RED, Color.WHITE, 2);
+
+ texID = GL11.glGenTextures();
+ GL11.glBindTexture(GL11.GL_TEXTURE_2D, texID);
+ GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGB, WIDTH, HEIGHT, 0, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, texture);
+
+ GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
+ GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
+
+ GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
+ }
+
+ System.out.println("** Dummy texture created **");
+
+ long lastTextureCreated = System.currentTimeMillis(); // Delay first texture creation
+ int count = 0;
+ while ( running ) {
+ long time = System.currentTimeMillis();
+ if ( time - lastTextureCreated < 5000 ) { // Update the texture every 5 seconds
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ continue;
+ }
+
+ // Create the "true" texture
+ if ( count % 2 == 0 )
+ createGradientTexture(Color.RED, Color.BLUE);
+ else
+ createGradientTexture(Color.GREEN, Color.YELLOW);
+
+ GL11.glBindTexture(GL11.GL_TEXTURE_2D, texID);
+ GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGB, WIDTH, HEIGHT, 0, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, texture);
+
+ GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
+ GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
+
+ GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
+
+ System.out.println("** Created new gradient texture **");
+
+ lastTextureCreated = System.currentTimeMillis();
+ count++;
+ }
+
+ drawable.destroy();
+
+ System.out.println("-- Background Thread finished --");
+ }
+ }).start();
+ }
+
+ int getTexID() {
+ synchronized ( lock ) {
+ return texID;
+ }
+ }
+
+ private void createCheckerTexture(final ReadableColor a, final ReadableColor b, final int size) {
+ int i = 0;
+ for ( int y = 0; y < HEIGHT; y++ ) {
+ for ( int x = 0; x < WIDTH; x++ ) {
+ ReadableColor c = (x / size) % 2 == 0 ? ((y / size) % 2 == 0 ? a : b) : ((y / size) % 2 == 0 ? b : a);
+ texture.put(i + 0, c.getRedByte());
+ texture.put(i + 1, c.getGreenByte());
+ texture.put(i + 2, c.getBlueByte());
+ i += 3;
+ }
+ }
+ }
+
+ private void createGradientTexture(final ReadableColor a, final ReadableColor b) {
+ float l = 0.0f;
+ int i = 0;
+ for ( int y = 0; y < HEIGHT; y++ ) {
+ for ( int x = 0; x < WIDTH; x++ ) {
+ texture.put(i + 0, lerp(a.getRed(), b.getRed(), l));
+ texture.put(i + 1, lerp(a.getGreen(), b.getGreen(), l));
+ texture.put(i + 2, lerp(a.getBlue(), b.getBlue(), l));
+ i += 3;
+ }
+ l += (1.0f / (HEIGHT - 1));
+ }
+ }
+
+ private static byte lerp(final int a, final int b, final float l) {
+ return (byte)Math.round(((1.0f - l) * a + l * b));
+ }
+
+}
\ No newline at end of file