/** * Copyright 2010 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 jogamp.graph.curve.opengl; import java.nio.FloatBuffer; import com.jogamp.opengl.GL2ES2; import com.jogamp.opengl.GL; import com.jogamp.opengl.GLUniformData; import jogamp.graph.curve.opengl.shader.AttributeNames; import jogamp.graph.curve.opengl.shader.UniformNames; import jogamp.opengl.Debug; import com.jogamp.common.util.PropertyAccess; import com.jogamp.graph.curve.Region; import com.jogamp.graph.curve.opengl.GLRegion; import com.jogamp.graph.curve.opengl.RegionRenderer; import com.jogamp.graph.curve.opengl.RenderState; import com.jogamp.opengl.FBObject; import com.jogamp.opengl.FBObject.Attachment; import com.jogamp.opengl.FBObject.TextureAttachment; import com.jogamp.opengl.math.FloatUtil; import com.jogamp.opengl.math.geom.AABBox; import com.jogamp.opengl.util.GLArrayDataServer; import com.jogamp.opengl.util.glsl.ShaderProgram; import com.jogamp.opengl.util.texture.Texture; import com.jogamp.opengl.util.texture.TextureCoords; import com.jogamp.opengl.util.texture.TextureSequence; public class VBORegion2PVBAAES2 extends GLRegion { private static final boolean DEBUG_FBO_1 = false; private static final boolean DEBUG_FBO_2 = false; /** * Boundary triggering FBO resize if * <pre> * fbo[Width|Height] - targetFbo[Width|Height] > RESIZE_BOUNDARY. * </pre> * <p> * Increasing the FBO will add RESIZE_BOUNDARY/2. * </p> * <p> * Reducing FBO resize to gain performance. * </p> * <p> * Defaults to disabled since: * - not working properly * - FBO texture rendered > than desired size * - FBO resize itself should be fast enough ?! * </p> */ private static final int RESIZE_BOUNDARY; static { Debug.initSingleton(); final String key = "jogl.debug.graph.curve.vbaa.resizeLowerBoundary"; RESIZE_BOUNDARY = Math.max(0, PropertyAccess.getIntProperty(key, true, 0)); if( RESIZE_BOUNDARY > 0 ) { System.err.println("key: "+RESIZE_BOUNDARY); } } private final RenderState.ProgramLocal rsLocal; // Pass-1: private GLArrayDataServer gca_VerticesAttr; private GLArrayDataServer gca_CurveParamsAttr; private GLArrayDataServer gca_ColorsAttr; private GLArrayDataServer indicesBuffer; private final GLUniformData gcu_ColorTexUnit; private final float[] colorTexBBox; // x0, y0, x1, y1 private final GLUniformData gcu_ColorTexBBox; private ShaderProgram spPass1 = null; // Pass-2: private GLArrayDataServer gca_FboVerticesAttr; private GLArrayDataServer gca_FboTexCoordsAttr; private GLArrayDataServer indicesFbo; private final GLUniformData gcu_FboTexUnit; private final GLUniformData gcu_FboTexSize; private final float[] pmvMatrix02 = new float[2*16]; // P + Mv private final GLUniformData gcu_PMVMatrix02; private ShaderProgram spPass2 = null; private FBObject fbo; private TextureAttachment texA; private int fboWidth = 0; private int fboHeight = 0; private boolean fboDirty = true; final int[] maxTexSize = new int[] { -1 } ; /** * <p> * Since multiple {@link Region}s may share one * {@link ShaderProgram}, the uniform data must always be updated. * </p> * * @param gl * @param renderer * @param renderModes * @param pass1 * @param quality * @param sampleCount */ public void useShaderProgram(final GL2ES2 gl, final RegionRenderer renderer, final int renderModes, final boolean pass1, final int quality, final int sampleCount) { final RenderState rs = renderer.getRenderState(); final boolean updateLocGlobal = renderer.useShaderProgram(gl, renderModes, pass1, quality, sampleCount, colorTexSeq); final ShaderProgram sp = renderer.getRenderState().getShaderProgram(); final boolean updateLocLocal; if( pass1 ) { updateLocLocal = !sp.equals(spPass1); spPass1 = sp; if( DEBUG ) { System.err.println("XXX changedSP.p1 updateLocation loc "+updateLocLocal+" / glob "+updateLocGlobal); } if( updateLocLocal ) { rs.updateAttributeLoc(gl, true, gca_VerticesAttr, true); rs.updateAttributeLoc(gl, true, gca_CurveParamsAttr, true); if( null != gca_ColorsAttr ) { rs.updateAttributeLoc(gl, true, gca_ColorsAttr, true); } } rsLocal.update(gl, rs, updateLocLocal, renderModes, true, true); rs.updateUniformLoc(gl, updateLocLocal, gcu_PMVMatrix02, true); if( null != gcu_ColorTexUnit ) { rs.updateUniformLoc(gl, updateLocLocal, gcu_ColorTexUnit, true); rs.updateUniformLoc(gl, updateLocLocal, gcu_ColorTexBBox, true); } } else { updateLocLocal = !sp.equals(spPass2); spPass2 = sp; if( DEBUG ) { System.err.println("XXX changedSP.p2 updateLocation loc "+updateLocLocal+" / glob "+updateLocGlobal); } if( updateLocLocal ) { rs.updateAttributeLoc(gl, true, gca_FboVerticesAttr, true); rs.updateAttributeLoc(gl, true, gca_FboTexCoordsAttr, true); } rsLocal.update(gl, rs, updateLocLocal, renderModes, false, true); rs.updateUniformDataLoc(gl, updateLocLocal, false /* updateData */, gcu_FboTexUnit, true); // FIXME always update if changing tex-unit rs.updateUniformLoc(gl, updateLocLocal, gcu_FboTexSize, sampleCount > 1); // maybe optimized away for sampleCount <= 1 } } public VBORegion2PVBAAES2(final int renderModes, final TextureSequence colorTexSeq, final int pass2TexUnit) { super(renderModes, colorTexSeq); rsLocal = new RenderState.ProgramLocal(); final int initialElementCount = 256; // Pass 1: indicesBuffer = GLArrayDataServer.createData(3, GL.GL_SHORT, initialElementCount, GL.GL_STATIC_DRAW, GL.GL_ELEMENT_ARRAY_BUFFER); gca_VerticesAttr = GLArrayDataServer.createGLSL(AttributeNames.VERTEX_ATTR_NAME, 3, GL.GL_FLOAT, false, initialElementCount, GL.GL_STATIC_DRAW); gca_CurveParamsAttr = GLArrayDataServer.createGLSL(AttributeNames.CURVEPARAMS_ATTR_NAME, 3, GL.GL_FLOAT, false, initialElementCount, GL.GL_STATIC_DRAW); if( hasColorChannel() ) { gca_ColorsAttr = GLArrayDataServer.createGLSL(AttributeNames.COLOR_ATTR_NAME, 4, GL.GL_FLOAT, false, initialElementCount, GL.GL_STATIC_DRAW); } else { gca_ColorsAttr = null; } if( hasColorTexture() ) { gcu_ColorTexUnit = new GLUniformData(UniformNames.gcu_ColorTexUnit, colorTexSeq.getTextureUnit()); colorTexBBox = new float[4]; gcu_ColorTexBBox = new GLUniformData(UniformNames.gcu_ColorTexBBox, 4, FloatBuffer.wrap(colorTexBBox)); } else { gcu_ColorTexUnit = null; colorTexBBox = null; gcu_ColorTexBBox = null; } FloatUtil.makeIdentity(pmvMatrix02, 0); FloatUtil.makeIdentity(pmvMatrix02, 16); gcu_PMVMatrix02 = new GLUniformData(UniformNames.gcu_PMVMatrix02, 4, 4, FloatBuffer.wrap(pmvMatrix02)); // Pass 2: gcu_FboTexUnit = new GLUniformData(UniformNames.gcu_FboTexUnit, pass2TexUnit); gcu_FboTexSize = new GLUniformData(UniformNames.gcu_FboTexSize, 2, FloatBuffer.wrap(new float[2])); indicesFbo = GLArrayDataServer.createData(3, GL.GL_SHORT, 2, GL.GL_STATIC_DRAW, GL.GL_ELEMENT_ARRAY_BUFFER); indicesFbo.puts((short) 0); indicesFbo.puts((short) 1); indicesFbo.puts((short) 3); indicesFbo.puts((short) 1); indicesFbo.puts((short) 2); indicesFbo.puts((short) 3); indicesFbo.seal(true); gca_FboTexCoordsAttr = GLArrayDataServer.createGLSL(AttributeNames.FBO_TEXCOORDS_ATTR_NAME, 2, GL.GL_FLOAT, false, 4, GL.GL_STATIC_DRAW); gca_FboTexCoordsAttr.putf(0); gca_FboTexCoordsAttr.putf(0); gca_FboTexCoordsAttr.putf(0); gca_FboTexCoordsAttr.putf(1); gca_FboTexCoordsAttr.putf(1); gca_FboTexCoordsAttr.putf(1); gca_FboTexCoordsAttr.putf(1); gca_FboTexCoordsAttr.putf(0); gca_FboTexCoordsAttr.seal(true); gca_FboVerticesAttr = GLArrayDataServer.createGLSL(AttributeNames.FBO_VERTEX_ATTR_NAME, 3, GL.GL_FLOAT, false, 4, GL.GL_STATIC_DRAW); } @Override protected final void clearImpl(final GL2ES2 gl) { if(DEBUG_INSTANCE) { System.err.println("VBORegion2PES2 Clear: " + this); // Thread.dumpStack(); } if( null != indicesBuffer ) { indicesBuffer.seal(gl, false); indicesBuffer.rewind(); } if( null != gca_VerticesAttr ) { gca_VerticesAttr.seal(gl, false); gca_VerticesAttr.rewind(); } if( null != gca_CurveParamsAttr ) { gca_CurveParamsAttr.seal(gl, false); gca_CurveParamsAttr.rewind(); } if( null != gca_ColorsAttr ) { gca_ColorsAttr.seal(gl, false); gca_ColorsAttr.rewind(); } fboDirty = true; } @Override protected final void pushVertex(final float[] coords, final float[] texParams, final float[] rgba) { gca_VerticesAttr.putf(coords[0]); gca_VerticesAttr.putf(coords[1]); gca_VerticesAttr.putf(coords[2]); gca_CurveParamsAttr.putf(texParams[0]); gca_CurveParamsAttr.putf(texParams[1]); gca_CurveParamsAttr.putf(texParams[2]); if( null != gca_ColorsAttr ) { if( null != rgba ) { gca_ColorsAttr.putf(rgba[0]); gca_ColorsAttr.putf(rgba[1]); gca_ColorsAttr.putf(rgba[2]); gca_ColorsAttr.putf(rgba[3]); } else { throw new IllegalArgumentException("Null color given for COLOR_CHANNEL rendering mode"); } } } @Override protected final void pushIndex(final int idx) { indicesBuffer.puts((short)idx); } @Override protected void updateImpl(final GL2ES2 gl) { // seal buffers indicesBuffer.seal(gl, true); indicesBuffer.enableBuffer(gl, false); gca_CurveParamsAttr.seal(gl, true); gca_CurveParamsAttr.enableBuffer(gl, false); gca_VerticesAttr.seal(gl, true); gca_VerticesAttr.enableBuffer(gl, false); if( null != gca_ColorsAttr ) { gca_ColorsAttr.seal(gl, true); gca_ColorsAttr.enableBuffer(gl, false); } if( null != gcu_ColorTexUnit && colorTexSeq.isTextureAvailable() ) { final TextureSequence.TextureFrame frame = colorTexSeq.getLastTexture(); final Texture tex = frame.getTexture(); final TextureCoords tc = tex.getImageTexCoords(); final float tcSx = 1f / ( tc.right() - tc.left() ); colorTexBBox[0] = box.getMinX() * tcSx; colorTexBBox[2] = box.getMaxX() * tcSx; if( tex.getMustFlipVertically() ) { final float tcSy = 1f / ( tc.bottom() - tc.top() ); colorTexBBox[1] = box.getMaxY() * tcSy; colorTexBBox[3] = box.getMinY() * tcSy; } else { final float tcSy = 1f / ( tc.top() - tc.bottom() ); colorTexBBox[1] = box.getMinY() * tcSy; colorTexBBox[3] = box.getMaxY() * tcSy; } } gca_FboVerticesAttr.seal(gl, false); { final FloatBuffer fb = (FloatBuffer)gca_FboVerticesAttr.getBuffer(); fb.put( 2, box.getMinZ()); fb.put( 5, box.getMinZ()); fb.put( 8, box.getMinZ()); fb.put(11, box.getMinZ()); } // Pending gca_FboVerticesAttr-seal and fboPMVMatrix-setup, follow fboDirty // push data 2 GPU .. indicesFbo.seal(gl, true); indicesFbo.enableBuffer(gl, false); fboDirty = true; // the buffers were disabled, since due to real/fbo switching and other vbo usage } private final AABBox drawWinBox = new AABBox(); private final int[] drawView = new int[] { 0, 0, 0, 0 }; private final float[] drawVec4Tmp0 = new float[4]; private final float[] drawVec4Tmp1 = new float[4]; private final float[] drawVec4Tmp2 = new float[4]; private final float[] drawMat4PMv = new float[16]; private static final int border = 2; // surrounding border, i.e. width += 2*border, height +=2*border @Override protected void drawImpl(final GL2ES2 gl, final RegionRenderer renderer, final int[/*1*/] sampleCount) { if( 0 >= indicesBuffer.getElementCount() ) { if(DEBUG_INSTANCE) { System.err.printf("VBORegion2PVBAAES2.drawImpl: Empty%n"); } return; // empty! } if( Float.isInfinite(box.getWidth()) || Float.isInfinite(box.getHeight()) ) { if(DEBUG_INSTANCE) { System.err.printf("VBORegion2PVBAAES2.drawImpl: Inf %s%n", box); } return; // inf } final int vpWidth = renderer.getWidth(); final int vpHeight = renderer.getHeight(); if(vpWidth <=0 || vpHeight <= 0 || null==sampleCount || sampleCount[0] <= 0){ renderRegion(gl); } else { if(0 > maxTexSize[0]) { gl.glGetIntegerv(GL.GL_MAX_TEXTURE_SIZE, maxTexSize, 0); } final RenderState rs = renderer.getRenderState(); final float winWidth, winHeight; final float ratioObjWinWidth, ratioObjWinHeight; final float diffObjWidth, diffObjHeight; final float diffObjBorderWidth, diffObjBorderHeight; int targetFboWidth, targetFboHeight; { final float diffWinWidth, diffWinHeight; final int targetWinWidth, targetWinHeight; // Calculate perspective pixel width/height for FBO, // considering the sampleCount. drawView[2] = vpWidth; drawView[3] = vpHeight; renderer.getMatrix().multPMvMatrixf(drawMat4PMv, 0); box.mapToWindow(drawWinBox, drawMat4PMv, drawView, true /* useCenterZ */, drawVec4Tmp0, drawVec4Tmp1, drawVec4Tmp2); winWidth = drawWinBox.getWidth(); winHeight = drawWinBox.getHeight(); targetWinWidth = (int)Math.ceil(winWidth); targetWinHeight = (int)Math.ceil(winHeight); diffWinWidth = targetWinWidth-winWidth; diffWinHeight = targetWinHeight-winHeight; ratioObjWinWidth = box.getWidth() / winWidth; ratioObjWinHeight= box.getHeight() / winHeight; diffObjWidth = diffWinWidth * ratioObjWinWidth; diffObjHeight = diffWinHeight * ratioObjWinHeight; diffObjBorderWidth = border * ratioObjWinWidth; diffObjBorderHeight = border * ratioObjWinHeight; targetFboWidth = (targetWinWidth+2*border)*sampleCount[0]; targetFboHeight = (targetWinHeight+2*border)*sampleCount[0]; if( DEBUG_FBO_2 ) { final float ratioWinWidth, ratioWinHeight; ratioWinWidth = winWidth/targetWinWidth; ratioWinHeight = winHeight/targetWinHeight; final float renderFboWidth, renderFboHeight; renderFboWidth = (winWidth+2*border)*sampleCount[0]; renderFboHeight = (winHeight+2*border)*sampleCount[0]; final float ratioFboWidth, ratioFboHeight; ratioFboWidth = renderFboWidth/targetFboWidth; ratioFboHeight = renderFboHeight/targetFboHeight; final float diffFboWidth, diffFboHeight; diffFboWidth = targetFboWidth-renderFboWidth; diffFboHeight = targetFboHeight-renderFboHeight; System.err.printf("XXX.MinMax obj %s%n", box.toString()); System.err.printf("XXX.MinMax obj d[%.3f, %.3f], r[%f, %f], b[%f, %f]%n", diffObjWidth, diffObjHeight, ratioObjWinWidth, ratioObjWinWidth, diffObjBorderWidth, diffObjBorderHeight); System.err.printf("XXX.MinMax win %s%n", drawWinBox.toString()); System.err.printf("XXX.MinMax view[%d, %d] -> win[%.3f, %.3f], i[%d x %d], d[%.3f, %.3f], r[%f, %f]: FBO f[%.3f, %.3f], i[%d x %d], d[%.3f, %.3f], r[%f, %f], samples %d%n", drawView[2], drawView[3], winWidth, winHeight, targetWinWidth, targetWinHeight, diffWinWidth, diffWinHeight, ratioWinWidth, ratioWinHeight, renderFboWidth, renderFboHeight, targetFboWidth, targetFboHeight, diffFboWidth, diffFboHeight, ratioFboWidth, ratioFboHeight, sampleCount[0]); } } if( 0 >= targetFboWidth || 0 >= targetFboHeight ) { // Nothing .. return; } final int deltaFboWidth = Math.abs(targetFboWidth-fboWidth); final int deltaFboHeight = Math.abs(targetFboHeight-fboHeight); final boolean hasDelta = 0!=deltaFboWidth || 0!=deltaFboHeight; if( DEBUG_FBO_2 ) { System.err.printf("XXX.maxDelta: hasDelta %b: %d / %d, %.3f, %.3f%n", hasDelta, deltaFboWidth, deltaFboHeight, (float)deltaFboWidth/fboWidth, (float)deltaFboHeight/fboHeight); System.err.printf("XXX.Scale %d * [%f x %f]: %d x %d%n", sampleCount[0], winWidth, winHeight, targetFboWidth, targetFboHeight); } if( hasDelta || fboDirty || isShapeDirty() || null == fbo ) { final int maxLength = Math.max(targetFboWidth, targetFboHeight); if( maxLength > maxTexSize[0] ) { if( targetFboWidth > targetFboHeight ) { sampleCount[0] = (int)Math.floor(maxTexSize[0] / (winWidth+2*border)); } else { sampleCount[0] = (int)Math.floor(maxTexSize[0] / (winHeight+2*border)); } final float renderFboWidth, renderFboHeight; renderFboWidth = (winWidth+2*border)*sampleCount[0]; renderFboHeight = (winWidth+2*border)*sampleCount[0]; targetFboWidth = (int)Math.ceil(renderFboWidth); targetFboHeight = (int)Math.ceil(renderFboHeight); if( DEBUG_FBO_1 ) { System.err.printf("XXX.Rescale (MAX): win[%.3f, %.3f]: FBO f[%.3f, %.3f], i[%d x %d], msaa %d%n", winWidth, winHeight, renderFboWidth, renderFboHeight, targetFboWidth, targetFboHeight, sampleCount[0]); } if( sampleCount[0] <= 0 ) { // Last way out! renderRegion(gl); return; } } final int newFboWidth, newFboHeight, resizeCase; if( 0 >= RESIZE_BOUNDARY ) { // Resize w/o optimization newFboWidth = targetFboWidth; newFboHeight = targetFboHeight; resizeCase = 0; } else { if( 0 >= fboWidth || 0 >= fboHeight || null == fbo ) { // Case: New FBO newFboWidth = targetFboWidth; newFboHeight = targetFboHeight; resizeCase = 1; } else if( targetFboWidth > fboWidth || targetFboHeight > fboHeight ) { // Case: Inscrease FBO Size, add boundary/2 if avail newFboWidth = ( targetFboWidth + RESIZE_BOUNDARY/2 < maxTexSize[0] ) ? targetFboWidth + RESIZE_BOUNDARY/2 : targetFboWidth; newFboHeight = ( targetFboHeight+ RESIZE_BOUNDARY/2 < maxTexSize[0] ) ? targetFboHeight + RESIZE_BOUNDARY/2 : targetFboHeight; resizeCase = 2; } else if( targetFboWidth < fboWidth && targetFboHeight < fboHeight && fboWidth - targetFboWidth < RESIZE_BOUNDARY && fboHeight - targetFboHeight < RESIZE_BOUNDARY ) { // Case: Decreased FBO Size Request within boundary newFboWidth = fboWidth; newFboHeight = fboHeight; resizeCase = 3; } else { // Case: Decreased-Size-Beyond-Boundary or No-Resize newFboWidth = targetFboWidth; newFboHeight = targetFboHeight; resizeCase = 4; } } final int dResizeWidth = newFboWidth - targetFboWidth; final int dResizeHeight = newFboHeight - targetFboHeight; final float diffObjResizeWidth = dResizeWidth*ratioObjWinWidth; final float diffObjResizeHeight = dResizeHeight*ratioObjWinHeight; if( DEBUG_FBO_1 ) { System.err.printf("XXX.resizeFBO: case %d, has %dx%d > target %dx%d, resize: i[%d x %d], f[%.3f x %.3f] -> %dx%d%n", resizeCase, fboWidth, fboHeight, targetFboWidth, targetFboHeight, dResizeWidth, dResizeHeight, diffObjResizeWidth, diffObjResizeHeight, newFboWidth, newFboHeight); } final float minX = box.getMinX()-diffObjBorderWidth; final float minY = box.getMinY()-diffObjBorderHeight; final float maxX = box.getMaxX()+diffObjBorderWidth+diffObjWidth+diffObjResizeWidth; final float maxY = box.getMaxY()+diffObjBorderHeight+diffObjHeight+diffObjResizeHeight; gca_FboVerticesAttr.seal(false); { final FloatBuffer fb = (FloatBuffer)gca_FboVerticesAttr.getBuffer(); fb.put(0, minX); fb.put( 1, minY); fb.put(3, minX); fb.put( 4, maxY); fb.put(6, maxX); fb.put( 7, maxY); fb.put(9, maxX); fb.put(10, minY); fb.position(12); } gca_FboVerticesAttr.seal(true); FloatUtil.makeOrtho(pmvMatrix02, 0, true, minX, maxX, minY, maxY, -1, 1); useShaderProgram(gl, renderer, getRenderModes(), true, getQuality(), sampleCount[0]); renderRegion2FBO(gl, rs, targetFboWidth, targetFboHeight, newFboWidth, newFboHeight, vpWidth, vpHeight, sampleCount[0]); } else if( isStateDirty() ) { useShaderProgram(gl, renderer, getRenderModes(), true, getQuality(), sampleCount[0]); renderRegion2FBO(gl, rs, targetFboWidth, targetFboHeight, fboWidth, fboHeight, vpWidth, vpHeight, sampleCount[0]); } useShaderProgram(gl, renderer, getRenderModes(), false, getQuality(), sampleCount[0]); renderFBO(gl, rs, targetFboWidth, targetFboHeight, vpWidth, vpHeight, sampleCount[0]); } } private void renderFBO(final GL2ES2 gl, final RenderState rs, final int targetFboWidth, final int targetFboHeight, final int vpWidth, final int vpHeight, final int sampleCount) { gl.glViewport(0, 0, vpWidth, vpHeight); if( rs.isHintMaskSet(RenderState.BITHINT_BLENDING_ENABLED) ) { // RGB is already multiplied w/ alpha via renderRegion2FBO(..) gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA); } gl.glUniform(gcu_FboTexSize); gl.glActiveTexture(GL.GL_TEXTURE0 + gcu_FboTexUnit.intValue()); fbo.use(gl, texA); gca_FboVerticesAttr.enableBuffer(gl, true); gca_FboTexCoordsAttr.enableBuffer(gl, true); indicesFbo.bindBuffer(gl, true); // keeps VBO binding gl.glDrawElements(GL.GL_TRIANGLES, indicesFbo.getElementCount() * indicesFbo.getComponentCount(), GL.GL_UNSIGNED_SHORT, 0); indicesFbo.bindBuffer(gl, false); gca_FboTexCoordsAttr.enableBuffer(gl, false); gca_FboVerticesAttr.enableBuffer(gl, false); fbo.unuse(gl); // setback: gl.glActiveTexture(currentActiveTextureEngine[0]); } private void renderRegion2FBO(final GL2ES2 gl, final RenderState rs, final int targetFboWidth, final int targetFboHeight, final int newFboWidth, final int newFboHeight, final int vpWidth, final int vpHeight, final int sampleCount) { if( 0 >= targetFboWidth || 0 >= targetFboHeight ) { throw new IllegalArgumentException("fboSize must be greater than 0: "+targetFboWidth+"x"+targetFboHeight); } final boolean blendingEnabled = rs.isHintMaskSet(RenderState.BITHINT_BLENDING_ENABLED); if(null == fbo) { fboWidth = newFboWidth; fboHeight = newFboHeight; final FloatBuffer fboTexSize = (FloatBuffer) gcu_FboTexSize.getBuffer(); { fboTexSize.put(0, fboWidth); fboTexSize.put(1, fboHeight); } fbo = new FBObject(); fbo.init(gl, fboWidth, fboHeight, 0); // Shall not use bilinear (GL_LINEAR), due to own VBAA. Result is smooth w/o it now! // FIXME: FXAA requires bilinear filtering! // texA = fbo.attachTexture2D(gl, 0, true, GL.GL_LINEAR, GL.GL_LINEAR, GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE); texA = fbo.attachTexture2D(gl, 0, true, GL.GL_NEAREST, GL.GL_NEAREST, GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE); if( !blendingEnabled ) { // no depth-buffer w/ blending fbo.attachRenderbuffer(gl, Attachment.Type.DEPTH, FBObject.DEFAULT_BITS); } if( DEBUG_FBO_1 ) { System.err.printf("XXX.createFBO: %dx%d%n%s%n", fboWidth, fboHeight, fbo.toString()); } } else if( newFboWidth != fboWidth || newFboHeight != fboHeight ) { fbo.reset(gl, newFboWidth, newFboHeight, 0); fbo.bind(gl); if( DEBUG_FBO_1 ) { System.err.printf("XXX.resetFBO: %dx%d -> %dx%d, target %dx%d%n", fboWidth, fboHeight, newFboWidth, newFboHeight, targetFboWidth, targetFboHeight); } fboWidth = newFboWidth; fboHeight = newFboHeight; final FloatBuffer fboTexSize = (FloatBuffer) gcu_FboTexSize.getBuffer(); { fboTexSize.put(0, fboWidth); fboTexSize.put(1, fboHeight); } } else { fbo.bind(gl); } //render texture gl.glViewport(0, 0, fboWidth, fboHeight); if( blendingEnabled ) { gl.glClearColor(0f, 0f, 0f, 0.0f); gl.glClear(GL.GL_COLOR_BUFFER_BIT); // no depth-buffer w/ blending // For already pre-multiplied alpha values, use: // gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA); // Multiply RGB w/ Alpha, preserve alpha for renderFBO(..) gl.glBlendFuncSeparate(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA, GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA); } else { gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); } renderRegion(gl); fbo.unbind(gl); fboDirty = false; } private void renderRegion(final GL2ES2 gl) { gl.glUniform(gcu_PMVMatrix02); gca_VerticesAttr.enableBuffer(gl, true); gca_CurveParamsAttr.enableBuffer(gl, true); if( null != gca_ColorsAttr ) { gca_ColorsAttr.enableBuffer(gl, true); } indicesBuffer.bindBuffer(gl, true); // keeps VBO binding if( null != gcu_ColorTexUnit && colorTexSeq.isTextureAvailable() ) { final TextureSequence.TextureFrame frame = colorTexSeq.getNextTexture(gl); gl.glActiveTexture(GL.GL_TEXTURE0 + colorTexSeq.getTextureUnit()); final Texture tex = frame.getTexture(); tex.bind(gl); tex.enable(gl); // nop on core gcu_ColorTexUnit.setData(colorTexSeq.getTextureUnit()); gl.glUniform(gcu_ColorTexUnit); // Always update, since program maybe used by multiple regions gl.glUniform(gcu_ColorTexBBox); // Always update, since program maybe used by multiple regions gl.glDrawElements(GL.GL_TRIANGLES, indicesBuffer.getElementCount() * indicesBuffer.getComponentCount(), GL.GL_UNSIGNED_SHORT, 0); tex.disable(gl); // nop on core } else { gl.glDrawElements(GL.GL_TRIANGLES, indicesBuffer.getElementCount() * indicesBuffer.getComponentCount(), GL.GL_UNSIGNED_SHORT, 0); } indicesBuffer.bindBuffer(gl, false); if( null != gca_ColorsAttr ) { gca_ColorsAttr.enableBuffer(gl, false); } gca_CurveParamsAttr.enableBuffer(gl, false); gca_VerticesAttr.enableBuffer(gl, false); } @Override protected void destroyImpl(final GL2ES2 gl) { if(DEBUG_INSTANCE) { System.err.println("VBORegion2PES2 Destroy: " + this); // Thread.dumpStack(); } if(null != fbo) { fbo.destroy(gl); fbo = null; texA = null; } if(null != gca_VerticesAttr) { gca_VerticesAttr.destroy(gl); gca_VerticesAttr = null; } if(null != gca_CurveParamsAttr) { gca_CurveParamsAttr.destroy(gl); gca_CurveParamsAttr = null; } if(null != gca_ColorsAttr) { gca_ColorsAttr.destroy(gl); gca_ColorsAttr = null; } if(null != indicesBuffer) { indicesBuffer.destroy(gl); indicesBuffer = null; } if(null != gca_FboVerticesAttr) { gca_FboVerticesAttr.destroy(gl); gca_FboVerticesAttr = null; } if(null != gca_FboTexCoordsAttr) { gca_FboTexCoordsAttr.destroy(gl); gca_FboTexCoordsAttr = null; } if(null != indicesFbo) { indicesFbo.destroy(gl); indicesFbo = null; } spPass1 = null; spPass2 = null; } }