/** * Copyright 2013 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. * * --------------------- * * Based on Brian Paul's tile rendering library, found * at <a href = "http://www.mesa3d.org/brianp/TR.html">http://www.mesa3d.org/brianp/TR.html</a>. * * Copyright (C) 1997-2005 Brian Paul. * Licensed under BSD-compatible terms with permission of the author. * See LICENSE.txt for license information. */ package com.jogamp.opengl.util; import com.jogamp.nativewindow.util.Dimension; import com.jogamp.nativewindow.util.DimensionImmutable; import com.jogamp.opengl.GL; import com.jogamp.opengl.GL2ES3; import com.jogamp.opengl.GLAutoDrawable; import com.jogamp.opengl.GLCapabilitiesImmutable; import com.jogamp.opengl.GLDrawable; import com.jogamp.opengl.GLEventListener; import com.jogamp.opengl.GLException; import jogamp.opengl.Debug; /** * A fairly direct port of Brian Paul's tile rendering library, found * at <a href = "http://www.mesa3d.org/brianp/TR.html"> * http://www.mesa3d.org/brianp/TR.html </a> . I've java-fied it, but * the functionality is the same. * <p> * Original code Copyright (C) 1997-2005 Brian Paul. Licensed under * BSD-compatible terms with permission of the author. See LICENSE.txt * for license information. * </p> * <p> * Enhanced for {@link GL} and {@link GL2ES3}, abstracted to suit {@link TileRenderer} and {@link RandomTileRenderer}. * </p> * <a name="pmvmatrix"><h5>PMV Matrix Considerations</h5></a> * <p> * The PMV matrix needs to be reshaped in user code * after calling {@link #beginTile(GL)}, See {@link #beginTile(GL)}. * </p> * <p> * If {@link #attachAutoDrawable(GLAutoDrawable) attaching to} an {@link GLAutoDrawable}, * the {@link TileRendererListener#reshapeTile(TileRendererBase, int, int, int, int, int, int)} method * is being called after {@link #beginTile(GL)} for each rendered tile. * It's implementation shall reshape the PMV matrix according to {@link #beginTile(GL)}. * </p> * <a name="glprequirement"><h5>GL Profile Requirement</h5></a> * <p> * Note that {@link #setImageBuffer(GLPixelBuffer) image buffer} can only be used * in conjunction w/ a {@link GL} instance ≥ {@link GL2ES3} passed to {@link #beginTile(GL)} and {@link #endTile(GL)}.<br> * This is due to setting up the {@link GL2ES3#GL_PACK_ROW_LENGTH pack row length} * for an {@link #setImageSize(int, int) image width} != tile-width, which usually is the case.<br> * Hence a {@link GLException} is thrown in both methods, * if using an {@link #setImageBuffer(GLPixelBuffer) image buffer} * and passing a {@link GL} instance < {@link GL2ES3}. * </p> * <p> * Further more, reading back of MSAA buffers is only supported since {@link GL2ES3} * since it requires to set the {@link GL2ES3#glReadBuffer(int) read-buffer}. * </p> * * @author ryanm, sgothel */ public abstract class TileRendererBase { /** * The width of the final image. See {@link #getParam(int)}. */ public static final int TR_IMAGE_WIDTH = 1; /** * The height of the final image. See {@link #getParam(int)}. */ public static final int TR_IMAGE_HEIGHT = 2; /** * The x-pos of the current tile. See {@link #getParam(int)}. */ public static final int TR_CURRENT_TILE_X_POS = 3; /** * The y-pos of the current tile. See {@link #getParam(int)}. */ public static final int TR_CURRENT_TILE_Y_POS = 4; /** * The width of the current tile. See {@link #getParam(int)}. */ public static final int TR_CURRENT_TILE_WIDTH = 5; /** * The height of the current tile. See {@link #getParam(int)}. */ public static final int TR_CURRENT_TILE_HEIGHT = 6; /* pp */ static final boolean DEBUG = Debug.debug("TileRenderer"); /** * Listener for tile renderer events, intended to extend {@link GLEventListener} implementations, * enabling tile rendering via {@link TileRendererBase#attachAutoDrawable(GLAutoDrawable)}. */ public static interface TileRendererListener { /** * The owning {@link GLAutoDrawable} is {@link TileRendererBase#attachAutoDrawable(GLAutoDrawable) attached} * to the given {@link TileRendererBase} instance. * <p> * The {@link GLContext} of the {@link TileRendererBase}'s {@link TileRendererBase#getAttachedDrawable() attached} {@link GLAutoDrawable} * <i>is not</i> current. * </p> * @param tr the associated {@link TileRendererBase} * @see TileRendererBase#getAttachedDrawable() */ public void addTileRendererNotify(TileRendererBase tr); /** * The owning {@link GLAutoDrawable} is {@link TileRendererBase#detachAutoDrawable() detached} * from the given {@link TileRendererBase} instance. * <p> * The {@link GLContext} of the {@link TileRendererBase}'s {@link TileRendererBase#getAttachedDrawable() attached} {@link GLAutoDrawable} * <i>is not</i> current. * </p> * @param tr the disassociated {@link TileRendererBase} * @see TileRendererBase#getAttachedDrawable() */ public void removeTileRendererNotify(TileRendererBase tr); /** * Called by the {@link TileRendererBase} during tile-rendering via an * {@link TileRendererBase#getAttachedDrawable() attached} {@link GLAutoDrawable}'s * {@link GLAutoDrawable#display()} call for each tile before {@link #display(GLAutoDrawable)}. * <p> * The <a href="TileRendererBase#pmvmatrix">PMV Matrix</a> shall be reshaped * according to the given * <ul> * <li>current tile-position</li> * <li>current tile-size</li> * <li>final image-size</li> * </ul> * The GL viewport is already set to origin 0/0 and the current tile-size.<br> * See details in {@link TileRendererBase#beginTile(GL)}.<br> * </p> * <p> * The {@link GLContext} of the {@link TileRendererBase}'s {@link TileRendererBase#getAttachedDrawable() attached} {@link GLAutoDrawable} * <i>is</i> current. * </p> * @param tr the issuing {@link TileRendererBase} * @param tileX the {@link TileRendererBase#TR_CURRENT_TILE_X_POS current tile's x-pos} * @param tileY the {@link TileRendererBase#TR_CURRENT_TILE_Y_POS current tile's y-pos} * @param tileWidth the {@link TileRendererBase#TR_CURRENT_TILE_WIDTH current tile's width} * @param tileHeight the {@link TileRendererBase#TR_CURRENT_TILE_HEIGHT current tile's height} * @param imageWidth the {@link TileRendererBase#TR_IMAGE_WIDTH final image width} * @param imageHeight the {@link TileRendererBase#TR_IMAGE_HEIGHT final image height} * @see TileRendererBase#getAttachedDrawable() */ public void reshapeTile(TileRendererBase tr, int tileX, int tileY, int tileWidth, int tileHeight, int imageWidth, int imageHeight); /** * Called by the {@link TileRendererBase} during tile-rendering * after {@link TileRendererBase#beginTile(GL)} and before {@link #reshapeTile(TileRendererBase, int, int, int, int, int, int) reshapeTile(..)}. * <p> * If {@link TileRendererBase} is of type {@link TileRenderer}, * method is called for the first tile of all tiles.<br> * Otherwise, i.e. {@link RandomTileRenderer}, method is called for each particular tile. * </p> * <p> * The {@link GLContext} of the {@link TileRenderer}'s {@link TileRenderer#getAttachedDrawable() attached} {@link GLAutoDrawable} * <i>is</i> current. * </p> * @param tr the issuing {@link TileRendererBase} */ public void startTileRendering(TileRendererBase tr); /** * Called by the {@link TileRenderer} during tile-rendering * after {@link TileRendererBase#endTile(GL)} and {@link GLAutoDrawable#swapBuffers()}. * <p> * If {@link TileRendererBase} is of type {@link TileRenderer}, * method is called for the last tile of all tiles.<br> * Otherwise, i.e. {@link RandomTileRenderer}, method is called for each particular tile. * </p> * <p> * The {@link GLContext} of the {@link TileRenderer}'s {@link TileRenderer#getAttachedDrawable() attached} {@link GLAutoDrawable} * <i>is</i> current. * </p> * @param tr the issuing {@link TileRendererBase} */ public void endTileRendering(TileRendererBase tr); } protected final Dimension imageSize = new Dimension(0, 0); protected final GLPixelStorageModes psm = new GLPixelStorageModes(); protected GLPixelBuffer imageBuffer; protected GLPixelBuffer tileBuffer; protected boolean beginCalled = false; protected int currentTileXPos; protected int currentTileYPos; protected int currentTileWidth; protected int currentTileHeight; protected GLAutoDrawable glad; protected boolean gladRequiresPreSwap; protected boolean gladAutoSwapBufferMode = true; protected GLEventListener[] listeners; protected boolean[] listenersInit; protected GLEventListener glEventListenerPre = null; protected GLEventListener glEventListenerPost = null; private final String hashStr(final Object o) { final int h = null != o ? o.hashCode() : 0; return "0x"+Integer.toHexString(h); } protected StringBuilder tileDetails(final StringBuilder sb) { return sb.append("cur "+currentTileXPos+"/"+currentTileYPos+" "+currentTileWidth+"x"+currentTileHeight+", buffer "+hashStr(tileBuffer)); } public StringBuilder toString(final StringBuilder sb) { final int gladListenerCount = null != listeners ? listeners.length : 0; sb.append("tile["); tileDetails(sb); sb.append("], image[size "+imageSize+", buffer "+hashStr(imageBuffer)+"], glad["+ gladListenerCount+" listener, pre "+(null!=glEventListenerPre)+", post "+(null!=glEventListenerPost)+", preSwap "+gladRequiresPreSwap+"]"); sb.append(", isSetup "+isSetup()); return sb; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); return getClass().getSimpleName()+ "["+toString(sb).toString()+"]"; } protected TileRendererBase() { } /** * Gets the parameters of this TileRenderer object * * @param pname The parameter name that is to be retrieved * @return the value of the parameter * @throws IllegalArgumentException if <code>pname</code> is not handled */ public abstract int getParam(int pname) throws IllegalArgumentException; /** * Specify a buffer the tiles to be copied to. This is not * necessary for the creation of the final image, but useful if you * want to inspect each tile in turn. * * @param buffer The buffer itself. Must be large enough to contain a random tile */ public final void setTileBuffer(final GLPixelBuffer buffer) { tileBuffer = buffer; if( DEBUG ) { System.err.println("TileRenderer: tile-buffer "+tileBuffer); } } /** @see #setTileBuffer(GLPixelBuffer) */ public final GLPixelBuffer getTileBuffer() { return tileBuffer; } /** * Sets the desired size of the final image * * @param width The width of the final image * @param height The height of the final image */ public void setImageSize(final int width, final int height) { imageSize.set(width, height); } /** @see #setImageSize(int, int) */ public final DimensionImmutable getImageSize() { return imageSize; } /** * Sets the buffer in which to store the final image * * @param buffer the buffer itself, must be large enough to hold the final image */ public final void setImageBuffer(final GLPixelBuffer buffer) { imageBuffer = buffer; if( DEBUG ) { System.err.println("TileRenderer: image-buffer "+imageBuffer); } } /** @see #setImageBuffer(GLPixelBuffer) */ public final GLPixelBuffer getImageBuffer() { return imageBuffer; } /* pp */ final void validateGL(final GL gl) throws GLException { if( imageBuffer != null && !gl.isGL2ES3()) { throw new GLException("Using image-buffer w/ inssufficient GL context: "+gl.getContext().getGLVersion()+", "+gl.getGLProfile()); } } /** * Returns true if this instance is setup properly, i.e. {@link #setImageSize(int, int)} .., * and ready for {@link #beginTile(GL)}. * Otherwise returns false. */ public abstract boolean isSetup(); /** * Returns true if <i>end of tiling</i> has been reached, otherwise false. * <p> * <i>end of tiling</i> criteria is implementation specific and may never be reached. * </p> * <p> * User needs to {@link #reset()} tiling after reaching <i>end of tiling</i> * before calling {@link #beginTile(GL)} again. * </p> */ public abstract boolean eot(); /** * Method resets implementation's internal state to <i>start of tiling</i> * as required for {@link #beginTile(GL)} if {@link #eot() end of tiling} has been reached. * <p> * Implementation is a <i>nop</i> where {@link #eot() end of tiling} is never reached. * </p> */ public abstract void reset(); /** * Begins rendering a tile. * <p> * This method modifies the viewport, see below. * User shall reset the viewport when finishing all tile rendering, * i.e. after very last call of {@link #endTile(GL)}! * </p> * <p> * The <a href="TileRendererBase.html#pmvmatrix">PMV Matrix</a> * must be reshaped after this call using: * <ul> * <li>Current Viewport * <ul> * <li>x 0</li> * <li>y 0</li> * <li>{@link #TR_CURRENT_TILE_WIDTH current tile's width}</li> * <li>{@link #TR_CURRENT_TILE_HEIGHT current tile's height}</li> * </ul></li> * <li>{@link #TR_CURRENT_TILE_X_POS current tile's x-pos}</li> * <li>{@link #TR_CURRENT_TILE_Y_POS current tile's y-pos}</li> * <li>{@link #TR_IMAGE_WIDTH final image width}</li> * <li>{@link #TR_IMAGE_HEIGHT final image height}</li> * </ul> * </p> * <p> * Use shall render the scene afterwards, concluded with a call to * this renderer {@link #endTile(GL)}. * </p> * <p> * User has to comply with the <a href="TileRendererBase.html#glprequirement">GL profile requirement</a>. * </p> * <p> * If {@link #eot() end of tiling} has been reached, * user needs to {@link #reset()} tiling before calling this method. * </p> * * @param gl The gl context * @throws IllegalStateException if {@link #setImageSize(int, int) image-size} is undefined, * an {@link #isSetup() implementation related setup} has not be performed * or {@ link #eot()} has been reached. See implementing classes. * @throws GLException if {@link #setImageBuffer(GLPixelBuffer) image buffer} is used but <code>gl</code> instance is < {@link GL2ES3} * @see #isSetup() * @see #eot() * @see #reset() */ public abstract void beginTile(GL gl) throws IllegalStateException, GLException; /** * Must be called after rendering the scene, * see {@link #beginTile(GL)}. * <p> * Please consider {@link #reqPreSwapBuffers(GLCapabilitiesImmutable)} to determine * whether you need to perform {@link GLDrawable#swapBuffers() swap-buffers} before or after * calling this method! * </p> * <p> * User has to comply with the <a href="TileRendererBase.html#glprequirement">GL profile requirement</a>. * </p> * * @param gl the gl context * @throws IllegalStateException if beginTile(gl) has not been called * @throws GLException if {@link #setImageBuffer(GLPixelBuffer) image buffer} is used but <code>gl</code> instance is < {@link GL2ES3} */ public abstract void endTile( GL gl ) throws IllegalStateException, GLException; /** * Determines whether the chosen {@link GLCapabilitiesImmutable} * requires a <i>pre-{@link GLDrawable#swapBuffers() swap-buffers}</i> * before accessing the results, i.e. before {@link #endTile(GL)}. * <p> * See {@link GLDrawableUtil#swapBuffersBeforeRead(GLCapabilitiesImmutable)}. * </p> */ public final boolean reqPreSwapBuffers(final GLCapabilitiesImmutable chosenCaps) { return GLDrawableUtil.swapBuffersBeforeRead(chosenCaps); } /** * Attaches the given {@link GLAutoDrawable} to this tile renderer. * <p> * The {@link GLAutoDrawable}'s original {@link GLEventListener} are moved to this tile renderer. * </p> * <p> * {@link GLEventListeners} not implementing {@link TileRendererListener} are ignored while tile rendering. * </p> * <p> * The {@link GLAutoDrawable}'s {@link GLAutoDrawable#getAutoSwapBufferMode() auto-swap mode} is cached * and set to <code>false</code>, since {@link GLAutoDrawable#swapBuffers() swapBuffers()} maybe issued before {@link #endTile(GL)}, * see {@link #reqPreSwapBuffers(GLCapabilitiesImmutable)}. * </p> * <p> * This tile renderer's internal {@link GLEventListener} is then added to the attached {@link GLAutoDrawable} * to handle the tile rendering, replacing the original {@link GLEventListener}.<br> * It's {@link GLEventListener#display(GLAutoDrawable) display} implementations issues: * <ul> * <li>Optional {@link #setGLEventListener(GLEventListener, GLEventListener) pre-glel}.{@link GLEventListener#display(GLAutoDrawable) display(..)}</li> * <li>{@link #beginTile(GL)}</li> * <li>for all original {@link TileRendererListener}: * <ul> * <li>{@link TileRendererListener#reshapeTile(TileRendererBase, int, int, int, int, int, int) reshapeTile(tileX, tileY, tileWidth, tileHeight, imageWidth, imageHeight)}</li> * <li>{@link GLEventListener#display(GLAutoDrawable) display(autoDrawable)}</li> * </ul></li> * <li>if ( {@link #reqPreSwapBuffers(GLCapabilitiesImmutable) pre-swap} ) { {@link GLAutoDrawable#swapBuffers() swapBuffers()} }</li> * <li>{@link #endTile(GL)}</li> * <li>if ( !{@link #reqPreSwapBuffers(GLCapabilitiesImmutable) pre-swap} ) { {@link GLAutoDrawable#swapBuffers() swapBuffers()} }</li> * <li>Optional {@link #setGLEventListener(GLEventListener, GLEventListener) post-glel}.{@link GLEventListener#display(GLAutoDrawable) display(..)}</li> * </ul> * </p> * <p> * Consider using {@link #setGLEventListener(GLEventListener, GLEventListener)} to add pre- and post * hooks to be performed on this renderer {@link GLEventListener}.<br> * The pre-hook is able to allocate memory and setup parameters, since it's called before {@link #beginTile(GL)}.<br> * The post-hook is able to use the rendering result and can even shutdown tile-rendering, * since it's called after {@link #endTile(GL)}. * </p> * <p> * Call {@link #detachAutoDrawable()} to remove the attached {@link GLAutoDrawable} from this tile renderer * and to restore it's original {@link GLEventListener}. * </p> * @param glad the {@link GLAutoDrawable} to attach. * @throws IllegalStateException if an {@link GLAutoDrawable} is already attached * @see #getAttachedDrawable() * @see #detachAutoDrawable() */ public final void attachAutoDrawable(final GLAutoDrawable glad) throws IllegalStateException { if( null != this.glad ) { throw new IllegalStateException("GLAutoDrawable already attached"); } this.glad = glad; final int aSz = glad.getGLEventListenerCount(); listeners = new GLEventListener[aSz]; listenersInit = new boolean[aSz]; for(int i=0; i<aSz; i++) { final GLEventListener l = glad.getGLEventListener(0); listenersInit[i] = glad.getGLEventListenerInitState(l); listeners[i] = glad.removeGLEventListener( l ); final boolean trn; if( listeners[i] instanceof TileRendererListener ) { trn = true; ((TileRendererListener)listeners[i]).addTileRendererNotify(this); } else { trn = false; } if( DEBUG ) { System.err.println("TileRenderer.attach["+i+"]: isInit "+listenersInit[i]+", isTRN "+trn+", "+listeners[i].getClass().getName()); } } glad.addGLEventListener(tiledGLEL); gladAutoSwapBufferMode = glad.getAutoSwapBufferMode(); gladRequiresPreSwap = this.reqPreSwapBuffers(glad.getChosenGLCapabilities()); glad.setAutoSwapBufferMode(false); if( DEBUG ) { System.err.println("TileRenderer: attached: "+glad); System.err.println("TileRenderer: preSwap "+gladRequiresPreSwap+", "+glad.getChosenGLCapabilities()+", cached "+listeners.length+" listener"); } } /** * Returns a previously {@link #attachAutoDrawable(GLAutoDrawable) attached} {@link GLAutoDrawable}, * <code>null</code> if none is attached. * <p> * If called from {@link TileRendererListener#addTileRendererNotify(TileRendererBase)} * or {@link TileRendererListener#removeTileRendererNotify(TileRendererBase)}, method returns the * just attached or soon to be detached {@link GLAutoDrawable}. * </p> * @see #attachAutoDrawable(GLAutoDrawable) * @see #detachAutoDrawable() */ public final GLAutoDrawable getAttachedDrawable() { return glad; } /** * Detaches the given {@link GLAutoDrawable} from this tile renderer. * @see #attachAutoDrawable(GLAutoDrawable) * @see #getAttachedDrawable() */ public final void detachAutoDrawable() { if( null != glad ) { glad.removeGLEventListener(tiledGLEL); final int aSz = listenersInit.length; for(int i=0; i<aSz; i++) { final GLEventListener l = listeners[i]; if( l instanceof TileRendererListener ) { ((TileRendererListener)l).removeTileRendererNotify(this); } glad.addGLEventListener(l); glad.setGLEventListenerInitState(l, listenersInit[i]); } glad.setAutoSwapBufferMode(gladAutoSwapBufferMode); if( DEBUG ) { System.err.println("TileRenderer: detached: "+glad); System.err.println("TileRenderer: "+glad.getChosenGLCapabilities()); } listeners = null; listenersInit = null; glad = null; } } /** * Set {@link GLEventListener} for pre- and post operations when used w/ * {@link #attachAutoDrawable(GLAutoDrawable)} * for each {@link GLEventListener} callback. * @param preTile the pre operations * @param postTile the post operations */ public final void setGLEventListener(final GLEventListener preTile, final GLEventListener postTile) { glEventListenerPre = preTile; glEventListenerPost = postTile; } /** * Rendering one tile, by simply calling {@link GLAutoDrawable#display()}. * * @throws IllegalStateException if no {@link GLAutoDrawable} is {@link #attachAutoDrawable(GLAutoDrawable) attached} * or imageSize is not set */ public final void display() throws IllegalStateException { if( null == glad ) { throw new IllegalStateException("No GLAutoDrawable attached"); } glad.display(); } private final GLEventListener tiledGLEL = new GLEventListener() { final TileRenderer tileRenderer = TileRendererBase.this instanceof TileRenderer ? (TileRenderer) TileRendererBase.this : null; @Override public void init(final GLAutoDrawable drawable) { if( null != glEventListenerPre ) { glEventListenerPre.init(drawable); } final int aSz = listenersInit.length; for(int i=0; i<aSz; i++) { final GLEventListener l = listeners[i]; if( !listenersInit[i] && l instanceof TileRendererListener ) { l.init(drawable); listenersInit[i] = true; } } if( null != glEventListenerPost ) { glEventListenerPost.init(drawable); } } @Override public void dispose(final GLAutoDrawable drawable) { if( null != glEventListenerPre ) { glEventListenerPre.dispose(drawable); } final int aSz = listenersInit.length; for(int i=0; i<aSz; i++) { // dispose all GLEventListener, last chance! listeners[i].dispose(drawable); } if( null != glEventListenerPost ) { glEventListenerPost.dispose(drawable); } } @Override public void display(final GLAutoDrawable drawable) { if( null != glEventListenerPre ) { glEventListenerPre.reshape(drawable, 0, 0, currentTileWidth, currentTileHeight); glEventListenerPre.display(drawable); } if( !isSetup() ) { if( DEBUG ) { System.err.println("TileRenderer.glel.display: !setup: "+TileRendererBase.this); } return; } if( eot() ) { if( DEBUG ) { System.err.println("TileRenderer.glel.display: EOT: "+TileRendererBase.this); } return; } final GL gl = drawable.getGL(); beginTile(gl); final int aSz = listenersInit.length; for(int i=0; i<aSz; i++) { final GLEventListener l = listeners[i]; if( l instanceof TileRendererListener ) { final TileRendererListener tl = (TileRendererListener)l; if( null == tileRenderer || 0 == tileRenderer.getCurrentTile() ) { tl.startTileRendering(TileRendererBase.this); } tl.reshapeTile(TileRendererBase.this, currentTileXPos, currentTileYPos, currentTileWidth, currentTileHeight, imageSize.getWidth(), imageSize.getHeight()); l.display(drawable); } } if( gladRequiresPreSwap ) { glad.swapBuffers(); endTile(gl); } else { endTile(gl); glad.swapBuffers(); } if( null == tileRenderer || tileRenderer.eot() ) { for(int i=0; i<aSz; i++) { final GLEventListener l = listeners[i]; if( l instanceof TileRendererListener ) { ((TileRendererListener)l).endTileRendering(TileRendererBase.this); } } } if( null != glEventListenerPost ) { glEventListenerPost.reshape(drawable, 0, 0, currentTileWidth, currentTileHeight); glEventListenerPost.display(drawable); } } @Override public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, final int height) { } }; }