From 629e7a6dbba346c2ff16b0b75211eb36dcf41b00 Mon Sep 17 00:00:00 2001 From: codeanticode Date: Thu, 19 Jul 2012 17:37:04 +0000 Subject: [PATCH] Some cleanup in Texture, PGraphicsOpenGL.loadTexture() works in all situations and faster in non-FBO-backed surfaces. --- .../src/processing/opengl/PFontTexture.java | 2 +- .../opengl/src/processing/opengl/PGL.java | 39 +++- .../processing/opengl/PGraphicsOpenGL.java | 115 ++++++++---- .../src/processing/opengl/PShapeOpenGL.java | 9 +- .../opengl/src/processing/opengl/Texture.java | 167 +++++++++++++----- 5 files changed, 233 insertions(+), 99 deletions(-) diff --git a/java/libraries/opengl/src/processing/opengl/PFontTexture.java b/java/libraries/opengl/src/processing/opengl/PFontTexture.java index e8342b76a..f9e350889 100644 --- a/java/libraries/opengl/src/processing/opengl/PFontTexture.java +++ b/java/libraries/opengl/src/processing/opengl/PFontTexture.java @@ -371,7 +371,7 @@ class PFontTexture implements PConstants { void updateTex() { textures[texIndex].bind(); - textures[texIndex].setTexels(pixels, 0, crop[0] - 1, crop[1] + crop[3] - 1, crop[2] + 2, -crop[3] + 2); + textures[texIndex].setNative(pixels, 0, crop[0] - 1, crop[1] + crop[3] - 1, crop[2] + 2, -crop[3] + 2); textures[texIndex].unbind(); } } diff --git a/java/libraries/opengl/src/processing/opengl/PGL.java b/java/libraries/opengl/src/processing/opengl/PGL.java index e89ed2ab3..7d88248e5 100644 --- a/java/libraries/opengl/src/processing/opengl/PGL.java +++ b/java/libraries/opengl/src/processing/opengl/PGL.java @@ -647,10 +647,10 @@ public class PGL { // Create the color texture... gl.glGenTextures(1, glColorTexID, 0); gl.glBindTexture(GL.GL_TEXTURE_2D, glColorTexID[0]); - gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST); - gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST); - gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE); - gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE); + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST); + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST); + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE); + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE); gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA, fboWidth, fboHeight, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, null); gl.glBindTexture(GL.GL_TEXTURE_2D, 0); @@ -762,10 +762,30 @@ public class PGL { } - public boolean usingPrimaryFBO() { + public boolean primaryIsFboBacked() { return glColorFboID[0] != 0; } + + public int getFboTexTarget() { + return GL.GL_TEXTURE_2D; + } + + + public int getFboTexName() { + return glColorTexID[0]; + } + + + public int getFboWidth() { + return fboWidth; + } + + + public int getFboHeight() { + return fboHeight; + } + public void bindPrimaryColorFBO() { if (multisample) { @@ -1039,10 +1059,15 @@ public class PGL { } - public void glTexParameterf(int target, int param, int value) { - gl.glTexParameterf(target, param, value); + public void glTexParameteri(int target, int param, int value) { + gl.glTexParameteri(target, param, value); } + + public void glGetTexParameteriv(int target, int param, int[] values, int offset) { + gl.glGetTexParameteriv(target, param, values, offset); + } + public void glGenerateMipmap(int target) { gl.glGenerateMipmap(target); diff --git a/java/libraries/opengl/src/processing/opengl/PGraphicsOpenGL.java b/java/libraries/opengl/src/processing/opengl/PGraphicsOpenGL.java index 5459d6966..304c04b97 100644 --- a/java/libraries/opengl/src/processing/opengl/PGraphicsOpenGL.java +++ b/java/libraries/opengl/src/processing/opengl/PGraphicsOpenGL.java @@ -367,8 +367,11 @@ public class PGraphicsOpenGL extends PGraphics { protected IntBuffer pixelBuffer; /** Array to store pixels in OpenGL format. */ - protected int[] rgbaPixels; + protected int[] nativePixels; + /** IntBuffer wrapping the native pixels array. */ + protected IntBuffer nativePixelBuffer; + /** Flag to indicate if the user is manipulating the * pixels array through the set()/get() methods */ protected boolean setgetPixels; @@ -1796,7 +1799,7 @@ public class PGraphicsOpenGL extends PGraphics { pgl.glDrawBuffer(PGL.GL_BACK); } offscreenNotCurrent = false; - } else if (pgl.usingPrimaryFBO()) { + } else if (pgl.primaryIsFboBacked()) { if (op == OP_READ) { // We read from the color FBO, but the multisample FBO is currently bound, so: offscreenNotCurrent = true; @@ -4921,14 +4924,7 @@ public class PGraphicsOpenGL extends PGraphics { allocatePixels(); if (!setgetPixels) { - readPixels(); - - if (primarySurface) { - // Copy pixels to the texture associated to the primary surface - // so both are in sync. - loadTextureImpl(POINT, false); - pixelsToTexture(); - } + readPixels(); } if (needEndDraw) { @@ -4970,19 +4966,20 @@ public class PGraphicsOpenGL extends PGraphics { int i0 = y * width + x; int len = w * h; - if (rgbaPixels == null || rgbaPixels.length < len) { - rgbaPixels = new int[len]; + if (nativePixels == null || nativePixels.length < len) { + nativePixels = new int[len]; + nativePixelBuffer = IntBuffer.wrap(nativePixels); } - PApplet.arrayCopy(pixels, i0, rgbaPixels, 0, len); - PGL.javaToNativeARGB(rgbaPixels, w, h); + PApplet.arrayCopy(pixels, i0, nativePixels, 0, len); + PGL.javaToNativeARGB(nativePixels, w, h); // Copying pixel buffer to screen texture... if (primarySurface) { loadTextureImpl(POINT, false); // (first making sure that the screen texture is valid). } pgl.copyToTexture(texture.glTarget, texture.glFormat, texture.glName, - x, y, w, h, IntBuffer.wrap(rgbaPixels)); + x, y, w, h, IntBuffer.wrap(nativePixels)); if (primarySurface || offscreenMultisample) { // ...and drawing the texture to screen... but only @@ -5004,7 +5001,7 @@ public class PGraphicsOpenGL extends PGraphics { public int get(int x, int y) { - loadPixels(); + loadPixels(); setgetPixels = true; return super.get(x, y); } @@ -5041,13 +5038,55 @@ public class PGraphicsOpenGL extends PGraphics { // array, and then the pixels array into the screen texture. public void loadTexture() { if (primarySurface) { + boolean needEndDraw = false; + if (!drawing) { + beginDraw(); + needEndDraw = true; + } + loadTextureImpl(Texture.POINT, false); - loadPixels(); - pixelsToTexture(); + + flush(); // To make sure the color buffer is updated. + + if (pgl.primaryIsFboBacked()) { + // Copy the contents of the FBO used by the primary surface into texture, this copy + // operation is very fast because it is resolved in the GPU. + texture.set(pgl.getFboTexTarget(), pgl.getFboTexName(), pgl.getFboWidth(), pgl.getFboHeight(), width, height); + } else { + // Here we go the slow route: we first copy the contents of the color buffer into a pixels array (but we keep it + // in native format) and then copy this array into the texture. + if (nativePixels == null || nativePixels.length < width * height) { + nativePixels = new int[width * height]; + nativePixelBuffer = IntBuffer.wrap(nativePixels); + } + + beginPixelsOp(OP_READ); + pgl.glReadPixels(0, 0, width, height, PGL.GL_RGBA, PGL.GL_UNSIGNED_BYTE, nativePixelBuffer); + endPixelsOp(); + + texture.setNative(nativePixels, 0, 0, width, height); + } + + if (needEndDraw) { + endDraw(); + } } } + // Just marks the whole texture as updated + public void updateTexture() { + texture.updateTexels(); + } + + + // Marks the specified rectanglular subregion in the texture as + // updated. + public void updateTexture(int x, int y, int w, int h) { + texture.updateTexels(x, y, w, h); + } + + // Draws wherever it is in the screen texture right now to the display. public void updateDisplay() { flush(); @@ -5065,15 +5104,17 @@ public class PGraphicsOpenGL extends PGraphics { } if (texture == null || texture != img.getCache(pgPrimary)) { - Texture.Parameters params; - if (primarySurface) { - params = new Texture.Parameters(ARGB, Texture.POINT, false); - } else { - params = new Texture.Parameters(ARGB, Texture.BILINEAR, false); - } - - texture = addTexture(img, params); - + Texture tex = (Texture)img.getCache(pgPrimary); + Texture.Parameters params = tex != null ? tex.getParameters() : null; + if (tex == null || tex.contextIsOutdated() || !validSurfaceTex(tex)) { + if (primarySurface) { + params = new Texture.Parameters(ARGB, Texture.POINT, false); + } else { + params = new Texture.Parameters(ARGB, Texture.BILINEAR, false); + } + tex = addTexture(img, params); + } + texture = tex; texture.setFlippedY(true); this.setCache(pgPrimary, texture); this.setParams(pgPrimary, params); @@ -5111,18 +5152,18 @@ public class PGraphicsOpenGL extends PGraphics { texture.glWidth, texture.glHeight, x, y, x + w, y + h); } - - - protected void pixelsToTexture() { - texture.set(pixels); - } - - - protected void textureToPixels() { - texture.get(pixels); - } + protected boolean validSurfaceTex(Texture tex) { + Texture.Parameters params = tex.getParameters(); + if (primarySurface) { + return params.sampling == Texture.POINT && !params.mipmaps; + } else { + return params.sampling == Texture.BILINEAR && !params.mipmaps; + } + } + + ////////////////////////////////////////////////////////////// // IMAGE CONVERSION diff --git a/java/libraries/opengl/src/processing/opengl/PShapeOpenGL.java b/java/libraries/opengl/src/processing/opengl/PShapeOpenGL.java index c20cdc067..cd288c48d 100644 --- a/java/libraries/opengl/src/processing/opengl/PShapeOpenGL.java +++ b/java/libraries/opengl/src/processing/opengl/PShapeOpenGL.java @@ -3907,8 +3907,7 @@ public class PShapeOpenGL extends PShape { if (textureImage != null) { tex = g.getTexture(textureImage); if (tex != null) { - pgl.enableTexturing(tex.glTarget); - pgl.glBindTexture(tex.glTarget, tex.glName); + tex.bind(); } } @@ -3928,8 +3927,7 @@ public class PShapeOpenGL extends PShape { // Rendering line or point triangles, which are never lit nor textured. if (!renderingStroke) { if (tex != null) { - pgl.glBindTexture(tex.glTarget, 0); - pgl.disableTexturing(tex.glTarget); + tex.unbind(); tex = null; } @@ -3977,8 +3975,7 @@ public class PShapeOpenGL extends PShape { } if (tex != null) { - pgl.glBindTexture(tex.glTarget, 0); - pgl.disableTexturing(tex.glTarget); + tex.unbind(); } } diff --git a/java/libraries/opengl/src/processing/opengl/Texture.java b/java/libraries/opengl/src/processing/opengl/Texture.java index b4751c1a6..0039f4e66 100644 --- a/java/libraries/opengl/src/processing/opengl/Texture.java +++ b/java/libraries/opengl/src/processing/opengl/Texture.java @@ -89,6 +89,7 @@ public class Texture implements PConstants { protected boolean usingMipmaps; protected float maxTexcoordU; protected float maxTexcoordV; + protected boolean bound; protected boolean flippedX; protected boolean flippedY; @@ -241,15 +242,25 @@ public class Texture implements PConstants { public void set(Texture tex) { - copyTexels(tex, 0, 0, tex.width, tex.height, true); + copyTexture(tex, 0, 0, tex.width, tex.height, true); } public void set(Texture tex, int x, int y, int w, int h) { - copyTexels(tex, x, y, w, h, true); + copyTexture(tex, x, y, w, h, true); } + public void set(int texTarget, int texName, int texWidth, int texHeight, int w, int h) { + copyTexture(texTarget, texName, texWidth, texHeight, 0, 0, w, h, true); + } + + + public void set(int texTarget, int texName, int texWidth, int texHeight, int target, int tex, int x, int y, int w, int h) { + copyTexture(texTarget, texName, texWidth, texHeight, x, y, w, h, true); + } + + public void set(int[] pixels) { set(pixels, 0, 0, width, height, ARGB); } @@ -276,15 +287,14 @@ public class Texture implements PConstants { return; } - pgl.enableTexturing(glTarget); - pgl.glBindTexture(glTarget, glName); + bind(); if (usingMipmaps) { if (PGraphicsOpenGL.autoMipmapGenSupported) { // Automatic mipmap generation. int[] rgbaPixels = new int[w * h]; convertToRGBA(pixels, rgbaPixels, format, w, h); - setTexels(rgbaPixels, x, y, w, h); + setNative(rgbaPixels, x, y, w, h); pgl.glGenerateMipmap(glTarget); rgbaPixels = null; } else { @@ -344,23 +354,53 @@ public class Texture implements PConstants { int[] rgbaPixels = new int[w * h]; convertToRGBA(pixels, rgbaPixels, format, w, h); - setTexels(rgbaPixels, x, y, w, h); + setNative(rgbaPixels, x, y, w, h); rgbaPixels = null; } } else { int[] rgbaPixels = new int[w * h]; convertToRGBA(pixels, rgbaPixels, format, w, h); - setTexels(rgbaPixels, x, y, w, h); + setNative(rgbaPixels, x, y, w, h); rgbaPixels = null; } - pgl.glBindTexture(glTarget, 0); - pgl.disableTexturing(glTarget); + unbind(); updateTexels(x, y, w, h); } + //////////////////////////////////////////////////////////// + + // Native set methods + + + public void setNative(int[] pix, int x, int y, int w, int h) { + setNative(pix, 0, x, y, w, h); + } + + + public void setNative(int[] pix, int level, int x, int y, int w, int h) { + bind(); + pgl.glTexSubImage2D(glTarget, level, x, y, w, h, PGL.GL_RGBA, PGL.GL_UNSIGNED_BYTE, IntBuffer.wrap(pix)); + unbind(); + updateTexels(x, y, w, h); + } + + + public void setNative(IntBuffer buffer, int x, int y, int w, int h) { + setNative(buffer, 0, x, y, w, h); + } + + + public void setNative(IntBuffer buffer, int level, int x, int y, int w, int h) { + bind(); + pgl.glTexSubImage2D(glTarget, level, x, y, w, h, PGL.GL_RGBA, PGL.GL_UNSIGNED_BYTE, buffer); + unbind(); + updateTexels(x, y, w, h); + } + + //////////////////////////////////////////////////////////// // Get methods @@ -425,14 +465,24 @@ public class Texture implements PConstants { public void put(Texture tex) { - copyTexels(tex, 0, 0, tex.width, tex.height, false); + copyTexture(tex, 0, 0, tex.width, tex.height, false); } public void put(Texture tex, int x, int y, int w, int h) { - copyTexels(tex, x, y, w, h, false); + copyTexture(tex, x, y, w, h, false); } + + public void put(int texTarget, int texName, int texWidth, int texHeight, int w, int h) { + copyTexture(texTarget, texName, texWidth, texHeight, 0, 0, w, h, false); + } + + + public void put(int texTarget, int texName, int texWidth, int texHeight, int target, int tex, int x, int y, int w, int h) { + copyTexture(texTarget, texName, texWidth, texHeight, x, y, w, h, false); + } + //////////////////////////////////////////////////////////// @@ -505,18 +555,29 @@ public class Texture implements PConstants { // Bind/unbind - public void bind() { - pgl.enableTexturing(glTarget); - pgl.glBindTexture(glTarget, glName); + public void bind() { + if (!bound) { + pgl.enableTexturing(glTarget); + pgl.glBindTexture(glTarget, glName); + bound = true; + } } public void unbind() { - pgl.enableTexturing(glTarget); - pgl.glBindTexture(glTarget, 0); + if (bound) { + pgl.enableTexturing(glTarget); + pgl.glBindTexture(glTarget, 0); + bound = false; + } } + public boolean bound() { + return bound; + } + + ////////////////////////////////////////////////////////////// // Modified flag @@ -636,7 +697,7 @@ public class Texture implements PConstants { try { data = bufferCache.remove(0); } catch (NoSuchElementException ex) { - PGraphics.showWarning("PTexture: don't have pixel data to copy to texture"); + PGraphics.showWarning("Don't have pixel data to copy to texture"); } if (data != null) { @@ -644,7 +705,7 @@ public class Texture implements PConstants { init(data.w, data.h); } bind(); - setTexels(data.rgbBuf, 0, 0, width, height); + setNative(data.rgbBuf, 0, 0, width, height); unbind(); data.dispose(); @@ -661,7 +722,7 @@ public class Texture implements PConstants { try { data = bufferCache.remove(0); } catch (NoSuchElementException ex) { - PGraphics.showWarning("PTexture: don't have pixel data to copy to texture"); + PGraphics.showWarning("Don't have pixel data to copy to texture"); } if (data != null) { @@ -669,7 +730,7 @@ public class Texture implements PConstants { init(data.w, data.h); } bind(); - setTexels(data.rgbBuf, 0, 0, width, height); + setNative(data.rgbBuf, 0, 0, width, height); unbind(); data.rgbBuf.get(pixels); @@ -688,7 +749,7 @@ public class Texture implements PConstants { try { disposeBufferMethod = bufferSource.getClass().getMethod("disposeBuffer", new Class[] { Object.class }); } catch (Exception e) { - throw new RuntimeException("PTexture: provided source object doesn't have a disposeBuffer method."); + throw new RuntimeException("Provided source object doesn't have a disposeBuffer method."); } } @@ -974,10 +1035,10 @@ public class Texture implements PConstants { glName = pg.createTextureObject(context.code()); pgl.glBindTexture(glTarget, glName); - pgl.glTexParameterf(glTarget, PGL.GL_TEXTURE_MIN_FILTER, glMinFilter); - pgl.glTexParameterf(glTarget, PGL.GL_TEXTURE_MAG_FILTER, glMagFilter); - pgl.glTexParameterf(glTarget, PGL.GL_TEXTURE_WRAP_S, glWrapS); - pgl.glTexParameterf(glTarget, PGL.GL_TEXTURE_WRAP_T, glWrapT); + pgl.glTexParameteri(glTarget, PGL.GL_TEXTURE_MIN_FILTER, glMinFilter); + pgl.glTexParameteri(glTarget, PGL.GL_TEXTURE_MAG_FILTER, glMagFilter); + pgl.glTexParameteri(glTarget, PGL.GL_TEXTURE_WRAP_S, glWrapS); + pgl.glTexParameteri(glTarget, PGL.GL_TEXTURE_WRAP_T, glWrapT); // First, we use glTexImage2D to set the full size of the texture (glW/glH might be diff // from w/h in the case that the GPU doesn't support NPOT textures) @@ -988,6 +1049,7 @@ public class Texture implements PConstants { pgl.glBindTexture(glTarget, 0); pgl.disableTexturing(glTarget); + bound = false; } @@ -1024,9 +1086,9 @@ public class Texture implements PConstants { // Copies source texture tex into this. - protected void copyTexels(Texture tex, int x, int y, int w, int h, boolean scale) { + protected void copyTexture(Texture tex, int x, int y, int w, int h, boolean scale) { if (tex == null) { - throw new RuntimeException("PTexture: source texture is null"); + throw new RuntimeException("Source texture is null"); } if (tempFbo == null) { @@ -1044,39 +1106,48 @@ public class Texture implements PConstants { // Rendering tex into "this", and scaling the source rectangle // to cover the entire destination region. pgl.drawTexture(tex.glTarget, tex.glName, tex.glWidth, tex.glHeight, - x, y, w, h, 0, 0, width, height); + x, y, w, h, 0, 0, width, height); } else { // Rendering tex into "this" but without scaling so the contents // of the source texture fall in the corresponding texels of the // destination. pgl.drawTexture(tex.glTarget, tex.glName, tex.glWidth, tex.glHeight, - x, y, w, h, x, y, w, h); + x, y, w, h, x, y, w, h); } pg.popFramebuffer(); updateTexels(x, y, w, h); } - protected void setTexels(int[] pix, int x, int y, int w, int h) { - setTexels(pix, 0, x, y, w, h); - } - - - protected void setTexels(int[] pix, int level, int x, int y, int w, int h) { - pgl.glTexSubImage2D(glTarget, level, x, y, w, h, PGL.GL_RGBA, PGL.GL_UNSIGNED_BYTE, IntBuffer.wrap(pix)); - updateTexels(x, y, w, h); - } - - - protected void setTexels(IntBuffer buffer, int x, int y, int w, int h) { - setTexels(buffer, 0, x, y, w, h); - } - - - protected void setTexels(IntBuffer buffer, int level, int x, int y, int w, int h) { - pgl.glTexSubImage2D(glTarget, level, x, y, w, h, PGL.GL_RGBA, PGL.GL_UNSIGNED_BYTE, buffer); - updateTexels(x, y, w, h); + // Copies source texture tex into this. + protected void copyTexture(int texTarget, int texName, int texWidth, int texHeight, int x, int y, int w, int h, boolean scale) { + if (tempFbo == null) { + tempFbo = new FrameBuffer(parent, glWidth, glHeight); + } + + // This texture is the color (destination) buffer of the FBO. + tempFbo.setColorBuffer(this); + tempFbo.disableDepthTest(); + + // FBO copy: + pg.pushFramebuffer(); + pg.setFramebuffer(tempFbo); + if (scale) { + // Rendering tex into "this", and scaling the source rectangle + // to cover the entire destination region. + pgl.drawTexture(texTarget, texName, texWidth, texHeight, + x, y, w, h, 0, 0, width, height); + + } else { + // Rendering tex into "this" but without scaling so the contents + // of the source texture fall in the corresponding texels of the + // destination. + pgl.drawTexture(texTarget, texName, texWidth, texHeight, + x, y, w, h, x, y, w, h); + } + pg.popFramebuffer(); + updateTexels(x, y, w, h); }