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