/** * 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 http://www.mesa3d.org/brianp/TR.html. * * 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 javax.media.nativewindow.util.Dimension; import javax.media.nativewindow.util.DimensionImmutable; import javax.media.opengl.GL; import javax.media.opengl.GL2ES3; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLCapabilitiesImmutable; import javax.media.opengl.GLDrawable; import javax.media.opengl.GLEventListener; import javax.media.opengl.GLException; import jogamp.opengl.Debug; /** * A fairly direct port of Brian Paul's tile rendering library, found * at * http://www.mesa3d.org/brianp/TR.html . I've java-fied it, but * the functionality is the same. *
* Original code Copyright (C) 1997-2005 Brian Paul. Licensed under * BSD-compatible terms with permission of the author. See LICENSE.txt * for license information. *
** Enhanced for {@link GL} and {@link GL2ES3}, abstracted to suit {@link TileRenderer} and {@link RandomTileRenderer}. *
** The PMV matrix needs to be reshaped in user code * after calling {@link #beginTile(GL)}, See {@link #beginTile(GL)}. *
** 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)}. *
*
* 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)}.
* 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.
* 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}.
*
* 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}. *
* * @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. ** The {@link GLContext} of the {@link TileRendererBase}'s {@link TileRendererBase#getAttachedDrawable() attached} {@link GLAutoDrawable} * is not current. *
* @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. ** The {@link GLContext} of the {@link TileRendererBase}'s {@link TileRendererBase#getAttachedDrawable() attached} {@link GLAutoDrawable} * is not current. *
* @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)}. ** The PMV Matrix shall be reshaped * according to the given *
* The {@link GLContext} of the {@link TileRendererBase}'s {@link TileRendererBase#getAttachedDrawable() attached} {@link GLAutoDrawable} * is current. *
* @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(..)}. *
* If {@link TileRendererBase} is of type {@link TileRenderer},
* method is called for the first tile of all tiles.
* Otherwise, i.e. {@link RandomTileRenderer}, method is called for each particular tile.
*
* The {@link GLContext} of the {@link TileRenderer}'s {@link TileRenderer#getAttachedDrawable() attached} {@link GLAutoDrawable} * is current. *
* @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()}. *
* If {@link TileRendererBase} is of type {@link TileRenderer},
* method is called for the last tile of all tiles.
* Otherwise, i.e. {@link RandomTileRenderer}, method is called for each particular tile.
*
* The {@link GLContext} of the {@link TileRenderer}'s {@link TileRenderer#getAttachedDrawable() attached} {@link GLAutoDrawable} * is current. *
* @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 ifpname
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 end of tiling has been reached, otherwise false.
* * end of tiling criteria is implementation specific and may never be reached. *
** User needs to {@link #reset()} tiling after reaching end of tiling * before calling {@link #beginTile(GL)} again. *
*/ public abstract boolean eot(); /** * Method resets implementation's internal state to start of tiling * as required for {@link #beginTile(GL)} if {@link #eot() end of tiling} has been reached. ** Implementation is a nop where {@link #eot() end of tiling} is never reached. *
*/ public abstract void reset(); /** * Begins rendering a tile. ** 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)}! *
** The PMV Matrix * must be reshaped after this call using: *
* Use shall render the scene afterwards, concluded with a call to * this renderer {@link #endTile(GL)}. *
** User has to comply with the GL profile requirement. *
** If {@link #eot() end of tiling} has been reached, * user needs to {@link #reset()} tiling before calling this method. *
* * @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 butgl
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)}.
* * Please consider {@link #reqPreSwapBuffers(GLCapabilitiesImmutable)} to determine * whether you need to perform {@link GLDrawable#swapBuffers() swap-buffers} before or after * calling this method! *
** User has to comply with the GL profile requirement. *
* * @param gl the gl context * @throws IllegalStateException if beginTile(gl) has not been called * @throws GLException if {@link #setImageBuffer(GLPixelBuffer) image buffer} is used butgl
instance is < {@link GL2ES3}
*/
public abstract void endTile( GL gl ) throws IllegalStateException, GLException;
/**
* Determines whether the chosen {@link GLCapabilitiesImmutable}
* requires a pre-{@link GLDrawable#swapBuffers() swap-buffers}
* before accessing the results, i.e. before {@link #endTile(GL)}.
* * See {@link GLDrawableUtil#swapBuffersBeforeRead(GLCapabilitiesImmutable)}. *
*/ public final boolean reqPreSwapBuffers(final GLCapabilitiesImmutable chosenCaps) { return GLDrawableUtil.swapBuffersBeforeRead(chosenCaps); } /** * Attaches the given {@link GLAutoDrawable} to this tile renderer. ** The {@link GLAutoDrawable}'s original {@link GLEventListener} are moved to this tile renderer. *
** {@link GLEventListeners} not implementing {@link TileRendererListener} are ignored while tile rendering. *
*
* The {@link GLAutoDrawable}'s {@link GLAutoDrawable#getAutoSwapBufferMode() auto-swap mode} is cached
* and set to false
, since {@link GLAutoDrawable#swapBuffers() swapBuffers()} maybe issued before {@link #endTile(GL)},
* see {@link #reqPreSwapBuffers(GLCapabilitiesImmutable)}.
*
* 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}.
* It's {@link GLEventListener#display(GLAutoDrawable) display} implementations issues:
*
* Consider using {@link #setGLEventListener(GLEventListener, GLEventListener)} to add pre- and post
* hooks to be performed on this renderer {@link GLEventListener}.
* The pre-hook is able to allocate memory and setup parameters, since it's called before {@link #beginTile(GL)}.
* The post-hook is able to use the rendering result and can even shutdown tile-rendering,
* since it's called after {@link #endTile(GL)}.
*
* Call {@link #detachAutoDrawable()} to remove the attached {@link GLAutoDrawable} from this tile renderer * and to restore it's original {@link GLEventListener}. *
* @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* If called from {@link TileRendererListener#addTileRendererNotify(TileRendererBase)} * or {@link TileRendererListener#removeTileRendererNotify(TileRendererBase)}, method returns the * just attached or soon to be detached {@link GLAutoDrawable}. *
* @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