From 17d6c5b0f9280cc0684834cdb0dc20a981a24c41 Mon Sep 17 00:00:00 2001 From: Jakub Valtar Date: Tue, 16 Sep 2014 22:09:20 +0200 Subject: [PATCH] New hint for depth reading (see #2771) Use hint(ENABLE_DEPTH_READING) to enable depth reading while using multisampling. --- core/src/processing/core/PConstants.java | 5 +- core/src/processing/core/PGraphics.java | 11 ++ core/src/processing/opengl/PGL.java | 141 ++++++++++++------ .../processing/opengl/PGraphicsOpenGL.java | 26 +++- 4 files changed, 131 insertions(+), 52 deletions(-) diff --git a/core/src/processing/core/PConstants.java b/core/src/processing/core/PConstants.java index 162f936b8e..c95206e3a6 100644 --- a/core/src/processing/core/PConstants.java +++ b/core/src/processing/core/PConstants.java @@ -493,7 +493,10 @@ public interface PConstants { static final int ENABLE_RETINA_PIXELS = 10; static final int DISABLE_RETINA_PIXELS = -10; - static final int HINT_COUNT = 11; + static final int ENABLE_DEPTH_READING = 11; + static final int DISABLE_DEPTH_READING = -11; + + static final int HINT_COUNT = 12; // error messages diff --git a/core/src/processing/core/PGraphics.java b/core/src/processing/core/PGraphics.java index b2552442a9..5b17e97c8b 100644 --- a/core/src/processing/core/PGraphics.java +++ b/core/src/processing/core/PGraphics.java @@ -1080,6 +1080,17 @@ protected void reapplySettings() { * hint(DISABLE_OPENGL_ERROR_REPORT) - Speeds up the P3D renderer setting * by not checking for errors while running. Undo with hint(ENABLE_OPENGL_ERROR_REPORT). *

+ * hint(ENABLE_DEPTH_READING) - Depth and stencil buffers in P2D/P3D will be + * downsampled to make PGL#getDepthValue, PGL#getStencilValue and PGL#readPixels + * work with multisampling. Enabling this introduces some overhead, so if you + * experience bad performance, disable multisampling with noSmooth() instead. + * Same as with smooth(), this hint is not intended to be enabled and disabled + * repeatedely, so call this once in setup() or after creating your PGraphics2D/3D. + * You can restore the default with hint(DISABLE_DEPTH_READING) if you don't plan + * to read depth from this PGraphics anymore. + * + * by not checking for errors while running. Undo with hint(ENABLE_OPENGL_ERROR_REPORT). + *

* As of release 0149, unhint() has been removed in favor of adding * additional ENABLE/DISABLE constants to reset the default behavior. This * prevents the double negatives, and also reinforces which hints can be diff --git a/core/src/processing/opengl/PGL.java b/core/src/processing/opengl/PGL.java index c0946b903f..95489a783f 100644 --- a/core/src/processing/opengl/PGL.java +++ b/core/src/processing/opengl/PGL.java @@ -24,6 +24,7 @@ package processing.opengl; import processing.core.PApplet; +import processing.core.PConstants; import processing.core.PGraphics; import java.io.IOException; @@ -140,13 +141,19 @@ public abstract class PGL { protected boolean firstFrame = true; protected int reqNumSamples; protected int numSamples; + protected IntBuffer glColorFbo; - protected IntBuffer glMultiFbo; - protected IntBuffer glColorBuf; protected IntBuffer glColorTex; protected IntBuffer glDepthStencil; protected IntBuffer glDepth; protected IntBuffer glStencil; + + protected IntBuffer glMultiFbo; + protected IntBuffer glMultiColor; + protected IntBuffer glMultiDepthStencil; + protected IntBuffer glMultiDepth; + protected IntBuffer glMultiStencil; + protected int fboWidth, fboHeight; protected int backTex, frontTex; @@ -281,6 +288,11 @@ public abstract class PGL { "if using any of the built-in OpenGL renderers. If you are using a contributed " + "library, contact the library's developers."; + protected static final String DEPTH_READING_NOT_ENABLED_ERROR = + "Reading depth and stencil values from this multisampled buffer is not enabled. " + + "You can enable it by calling hint(ENABLE_DEPTH_READING) once. " + + "If your sketch becomes too slow, disable multisampling with noSmooth() instead."; + // ........................................................ // Constants @@ -327,13 +339,18 @@ public PGL(PGraphicsOpenGL pg) { this.pg = pg; if (glColorTex == null) { glColorTex = allocateIntBuffer(2); + glColorFbo = allocateIntBuffer(1); - glMultiFbo = allocateIntBuffer(1); - glColorBuf = allocateIntBuffer(1); glDepthStencil = allocateIntBuffer(1); glDepth = allocateIntBuffer(1); glStencil = allocateIntBuffer(1); + glMultiFbo = allocateIntBuffer(1); + glMultiColor = allocateIntBuffer(1); + glMultiDepthStencil = allocateIntBuffer(1); + glMultiDepth = allocateIntBuffer(1); + glMultiStencil = allocateIntBuffer(1); + fboLayerCreated = false; fboLayerInUse = false; firstFrame = false; @@ -372,12 +389,17 @@ public void setPrimary(boolean primary) { protected void deleteSurface() { if (threadIsCurrent() && fboLayerCreated) { deleteTextures(2, glColorTex); + deleteFramebuffers(1, glColorFbo); - deleteFramebuffers(1, glMultiFbo); - deleteRenderbuffers(1, glColorBuf); deleteRenderbuffers(1, glDepthStencil); deleteRenderbuffers(1, glDepth); deleteRenderbuffers(1, glStencil); + + deleteFramebuffers(1, glMultiFbo); + deleteRenderbuffers(1, glMultiColor); + deleteRenderbuffers(1, glMultiDepthStencil); + deleteRenderbuffers(1, glMultiDepth); + deleteRenderbuffers(1, glMultiStencil); } fboLayerCreated = false; @@ -513,9 +535,13 @@ protected void syncBackTexture() { if (1 < numSamples) { bindFramebufferImpl(READ_FRAMEBUFFER, glMultiFbo.get(0)); bindFramebufferImpl(DRAW_FRAMEBUFFER, glColorFbo.get(0)); + int mask = COLOR_BUFFER_BIT; + if (pg.getHint(PConstants.ENABLE_DEPTH_READING)) { + mask |= DEPTH_BUFFER_BIT | STENCIL_BUFFER_BIT; + } blitFramebuffer(0, 0, fboWidth, fboHeight, 0, 0, fboWidth, fboHeight, - COLOR_BUFFER_BIT, NEAREST); + mask, NEAREST); } } @@ -676,26 +702,58 @@ private void createFBOLayer() { framebufferTexture2D(FRAMEBUFFER, COLOR_ATTACHMENT0, TEXTURE_2D, glColorTex.get(backTex), 0); + if (!multisample || pg.getHint(PConstants.ENABLE_DEPTH_READING)) { + // If not multisampled, this is the only depth and stencil buffer. + // If multisampled and depth reading enabled, these are going to + // hold downsampled depth and stencil buffers. + createDepthAndStencilBuffer(false, depthBits, stencilBits, packed); + } + if (multisample) { // Creating multisampled FBO genFramebuffers(1, glMultiFbo); bindFramebufferImpl(FRAMEBUFFER, glMultiFbo.get(0)); // color render buffer... - genRenderbuffers(1, glColorBuf); - bindRenderbuffer(RENDERBUFFER, glColorBuf.get(0)); + genRenderbuffers(1, glMultiColor); + bindRenderbuffer(RENDERBUFFER, glMultiColor.get(0)); renderbufferStorageMultisample(RENDERBUFFER, numSamples, RGBA8, fboWidth, fboHeight); framebufferRenderbuffer(FRAMEBUFFER, COLOR_ATTACHMENT0, - RENDERBUFFER, glColorBuf.get(0)); + RENDERBUFFER, glMultiColor.get(0)); + + // Creating multisampled depth and stencil buffers + createDepthAndStencilBuffer(true, depthBits, stencilBits, packed); } + validateFramebuffer(); + + // Clear all buffers. + clearDepth(1); + clearStencil(0); + int argb = pg.backgroundColor; + float a = ((argb >> 24) & 0xff) / 255.0f; + float r = ((argb >> 16) & 0xff) / 255.0f; + float g = ((argb >> 8) & 0xff) / 255.0f; + float b = ((argb) & 0xff) / 255.0f; + clearColor(r, g, b, a); + clear(DEPTH_BUFFER_BIT | STENCIL_BUFFER_BIT | COLOR_BUFFER_BIT); + + bindFramebufferImpl(FRAMEBUFFER, 0); + + fboLayerCreated = true; + } + + private void createDepthAndStencilBuffer(boolean multisampled, int depthBits, + int stencilBits, boolean packed) { // Creating depth and stencil buffers if (packed && depthBits == 24 && stencilBits == 8) { // packed depth+stencil buffer - genRenderbuffers(1, glDepthStencil); - bindRenderbuffer(RENDERBUFFER, glDepthStencil.get(0)); - if (multisample) { + IntBuffer depthStencilBuf = + multisampled ? glMultiDepthStencil : glDepthStencil; + genRenderbuffers(1, depthStencilBuf); + bindRenderbuffer(RENDERBUFFER, depthStencilBuf.get(0)); + if (multisampled) { renderbufferStorageMultisample(RENDERBUFFER, numSamples, DEPTH24_STENCIL8, fboWidth, fboHeight); } else { @@ -703,9 +761,9 @@ private void createFBOLayer() { fboWidth, fboHeight); } framebufferRenderbuffer(FRAMEBUFFER, DEPTH_ATTACHMENT, RENDERBUFFER, - glDepthStencil.get(0)); + depthStencilBuf.get(0)); framebufferRenderbuffer(FRAMEBUFFER, STENCIL_ATTACHMENT, RENDERBUFFER, - glDepthStencil.get(0)); + depthStencilBuf.get(0)); } else { // separate depth and stencil buffers if (0 < depthBits) { @@ -718,9 +776,10 @@ private void createFBOLayer() { depthComponent = DEPTH_COMPONENT16; } - genRenderbuffers(1, glDepth); - bindRenderbuffer(RENDERBUFFER, glDepth.get(0)); - if (multisample) { + IntBuffer depthBuf = multisampled ? glMultiDepth : glDepth; + genRenderbuffers(1, depthBuf); + bindRenderbuffer(RENDERBUFFER, depthBuf.get(0)); + if (multisampled) { renderbufferStorageMultisample(RENDERBUFFER, numSamples, depthComponent, fboWidth, fboHeight); } else { @@ -728,7 +787,7 @@ private void createFBOLayer() { fboWidth, fboHeight); } framebufferRenderbuffer(FRAMEBUFFER, DEPTH_ATTACHMENT, - RENDERBUFFER, glDepth.get(0)); + RENDERBUFFER, depthBuf.get(0)); } if (0 < stencilBits) { @@ -741,9 +800,10 @@ private void createFBOLayer() { stencilIndex = STENCIL_INDEX1; } - genRenderbuffers(1, glStencil); - bindRenderbuffer(RENDERBUFFER, glStencil.get(0)); - if (multisample) { + IntBuffer stencilBuf = multisampled ? glMultiStencil : glStencil; + genRenderbuffers(1, stencilBuf); + bindRenderbuffer(RENDERBUFFER, stencilBuf.get(0)); + if (multisampled) { renderbufferStorageMultisample(RENDERBUFFER, numSamples, stencilIndex, fboWidth, fboHeight); } else { @@ -751,26 +811,9 @@ private void createFBOLayer() { fboWidth, fboHeight); } framebufferRenderbuffer(FRAMEBUFFER, STENCIL_ATTACHMENT, - RENDERBUFFER, glStencil.get(0)); + RENDERBUFFER, stencilBuf.get(0)); } } - - validateFramebuffer(); - - // Clear all buffers. - clearDepth(1); - clearStencil(0); - int argb = pg.backgroundColor; - float a = ((argb >> 24) & 0xff) / 255.0f; - float r = ((argb >> 16) & 0xff) / 255.0f; - float g = ((argb >> 8) & 0xff) / 255.0f; - float b = ((argb) & 0xff) / 255.0f; - clearColor(r, g, b, a); - clear(DEPTH_BUFFER_BIT | STENCIL_BUFFER_BIT | COLOR_BUFFER_BIT); - - bindFramebufferImpl(FRAMEBUFFER, 0); - - fboLayerCreated = true; } @@ -1168,7 +1211,7 @@ protected void drawTextureRect(int id, int texW, int texH, int scrW, int scrH, } - protected int getColorValue(int scrX, int scrY) { + public int getColorValue(int scrX, int scrY) { if (colorBuffer == null) { colorBuffer = IntBuffer.allocate(1); } @@ -1179,7 +1222,7 @@ protected int getColorValue(int scrX, int scrY) { } - protected float getDepthValue(int scrX, int scrY) { + public float getDepthValue(int scrX, int scrY) { if (depthBuffer == null) { depthBuffer = FloatBuffer.allocate(1); } @@ -1190,10 +1233,11 @@ protected float getDepthValue(int scrX, int scrY) { } - protected byte getStencilValue(int scrX, int scrY) { + public byte getStencilValue(int scrX, int scrY) { if (stencilBuffer == null) { stencilBuffer = ByteBuffer.allocate(1); } + stencilBuffer.rewind(); readPixels(scrX, pg.height - scrY - 1, 1, 1, STENCIL_INDEX, UNSIGNED_BYTE, stencilBuffer); return stencilBuffer.get(0); @@ -2559,8 +2603,15 @@ protected interface FontOutline { // to glReadPixels() should be done in readPixelsImpl(). public void readPixels(int x, int y, int width, int height, int format, int type, Buffer buffer){ - boolean pgCall = format != STENCIL_INDEX && - format != DEPTH_COMPONENT && format != DEPTH_STENCIL; + boolean multisampled = (primaryPGL && 1 < numSamples) || (!primaryPGL && 1 < pg.quality); + + if (!pg.getHint(PConstants.ENABLE_DEPTH_READING) && multisampled && + (format == STENCIL_INDEX || format == DEPTH_COMPONENT || format == DEPTH_STENCIL)) { + PGraphics.showWarning(DEPTH_READING_NOT_ENABLED_ERROR); + return; + } + boolean pgCall = multisampled || + (format != STENCIL_INDEX && format != DEPTH_COMPONENT && format != DEPTH_STENCIL); if (pgCall) pg.beginReadPixels(); readPixelsImpl(x, y, width, height, format, type, buffer); if (pgCall) pg.endReadPixels(); diff --git a/core/src/processing/opengl/PGraphicsOpenGL.java b/core/src/processing/opengl/PGraphicsOpenGL.java index 0770f2d604..bf1fd14cd5 100644 --- a/core/src/processing/opengl/PGraphicsOpenGL.java +++ b/core/src/processing/opengl/PGraphicsOpenGL.java @@ -1888,7 +1888,11 @@ protected void beginPixelsOp(int op) { if (op == OP_READ) { if (offscreenMultisample) { // Making sure the offscreen FBO is up-to-date - multisampleFramebuffer.copyColor(offscreenFramebuffer); + int mask = PGL.COLOR_BUFFER_BIT; + if (hints[ENABLE_DEPTH_READING]) { + mask |= PGL.DEPTH_BUFFER_BIT | PGL.STENCIL_BUFFER_BIT; + } + multisampleFramebuffer.copy(offscreenFramebuffer, mask); } // We always read the screen pixels from the color FBO. pixfb = offscreenFramebuffer; @@ -2115,6 +2119,10 @@ public void hint(int which) { // We flush the geometry using the previous line setting. flush(); } + } else if (which == ENABLE_DEPTH_READING) { + restartPGL(); + } else if (which == DISABLE_DEPTH_READING) { + restartPGL(); } } @@ -6382,11 +6390,17 @@ protected void initOffscreen() { offscreenMultisample = true; // The offscreen framebuffer where the multisampled image is finally drawn - // to doesn't need depth and stencil buffers since they are part of the - // multisampled framebuffer. - offscreenFramebuffer = - new FrameBuffer(this, texture.glWidth, texture.glHeight, 1, 1, 0, 0, - false, false); + // to. If depth reading is disabled it doesn't need depth and stencil buffers + // since they are part of the multisampled framebuffer. + if (hints[ENABLE_DEPTH_READING]) { + offscreenFramebuffer = + new FrameBuffer(this, texture.glWidth, texture.glHeight, 1, 1, + depthBits, stencilBits, packed, false); + } else { + offscreenFramebuffer = + new FrameBuffer(this, texture.glWidth, texture.glHeight, 1, 1, + 0, 0, false, false); + } } else { quality = 0;