From 72f18a32017fec4ddf3383eaa090fec63a0be7c3 Mon Sep 17 00:00:00 2001 From: codeanticode Date: Sat, 2 Jul 2011 00:04:16 +0000 Subject: [PATCH] Implemented GL resource reallocation/refreshing mechanism --- .../src/processing/opengl/PFontTexture.java | 52 ++++++-- .../src/processing/opengl/PFramebuffer.java | 27 +++- .../processing/opengl/PGraphicsOpenGL.java | 123 ++++++++---------- .../src/processing/opengl/PShape3D.java | 29 ++++- .../src/processing/opengl/PTexture.java | 22 +++- 5 files changed, 165 insertions(+), 88 deletions(-) diff --git a/java/libraries/opengl/src/processing/opengl/PFontTexture.java b/java/libraries/opengl/src/processing/opengl/PFontTexture.java index 1849b55f7..0f50ddc28 100644 --- a/java/libraries/opengl/src/processing/opengl/PFontTexture.java +++ b/java/libraries/opengl/src/processing/opengl/PFontTexture.java @@ -30,12 +30,24 @@ import processing.core.PFont; /** * All the infrastructure needed for optimized font rendering - * in OpenGL. + * in OpenGL. Basically, this special class is needed because + * fonts in Processing are handled by a separate PImage for each + * gyph. For performance reasons, all these glyphs should be + * stored in a single OpenGL texture (otherwise, rendering a + * string of text would involve binding and unbinding several + * textures. + * PFontTexture manages the correspondence between individual + * glyphs and the large OpenGL texture containing them. Also, + * in the case that the font size is very large, one single + * OpenGL texture might not be enough to store all the glyphs, + * so PFontTexture also takes care of spreading a single font + * over several textures. * By Andres Colubri * */ class PFontTexture implements PConstants { protected PApplet parent; + protected PGraphicsOpenGL ogl; protected PFont font; protected int maxTexWidth; @@ -51,7 +63,14 @@ class PFontTexture implements PConstants { public PFontTexture(PApplet parent, PFont font, int maxw, int maxh) { this.parent = parent; - this.font = font; + this.font = font; + ogl = (PGraphicsOpenGL)parent.g; + + // Although PFontTexture is not strictly a GL object, since it doesn't directly + // contains any native OpenGL resources, it does contains a list of textures + // that don't depend on any PImage, so it is registered so in the case of a refresh + // event the textures are re-initialized with the correct data. + ogl.registerGLObject(this); initTexture(maxw, maxh); } @@ -61,9 +80,21 @@ class PFontTexture implements PConstants { for (int i = 0; i < textures.length; i++) { textures[i].delete(); } + ogl.unregisterGLObject(this); } + public void refresh() { + // loop over current glyphs. + for (int i = 0; i < font.getGlyphCount(); i++) { + TextureInfo tinfo = glyphTexinfos[i]; + textures[tinfo.texIndex].bind(); + tinfo.updateTex(); + textures[tinfo.texIndex].unbind(); + } + } + + protected void initTexture(int w, int h) { maxTexWidth = w; maxTexHeight = h; @@ -157,8 +188,7 @@ class PFontTexture implements PConstants { TextureInfo tinfo = glyphTexinfos[i]; if (tinfo != null && tinfo.texIndex == currentTex) { tinfo.updateUV(); - } - + } } } @@ -258,9 +288,8 @@ class PFontTexture implements PConstants { setTexture(lastTex); } - textures[currentTex].setTexels(offsetX, offsetY, w, h, rgba); - - TextureInfo tinfo = new TextureInfo(currentTex, offsetX, offsetY, w, h); + //textures[currentTex].setTexels(offsetX, offsetY, w, h, rgba); + TextureInfo tinfo = new TextureInfo(currentTex, offsetX, offsetY, w, h, rgba); offsetX += w; if (idx == glyphTexinfos.length) { @@ -281,8 +310,9 @@ class PFontTexture implements PConstants { public int[] crop; public float u0, u1; public float v0, v1; + public int[] pixels; - public TextureInfo(int tidx, int cropX, int cropY, int cropW, int cropH) { + public TextureInfo(int tidx, int cropX, int cropY, int cropW, int cropH, int[] pix) { texIndex = tidx; crop = new int[4]; // The region of the texture corresponding to the glyph is surrounded by a @@ -292,7 +322,9 @@ class PFontTexture implements PConstants { crop[1] = cropY + 1 + cropH - 2; crop[2] = cropW - 2; crop[3] = -cropH + 2; + pixels = pix; updateUV(); + updateTex(); } void updateUV() { @@ -303,5 +335,9 @@ class PFontTexture implements PConstants { v0 = (float)(crop[1] + crop[3]) / (float)height; v1 = v0 - (float)crop[3] / (float)height; } + + void updateTex() { + textures[texIndex].setTexels(offsetX, crop[0] - 1, crop[1] + crop[3] - 1, crop[2] + 2, -crop[3] + 2, pixels); + } } } \ No newline at end of file diff --git a/java/libraries/opengl/src/processing/opengl/PFramebuffer.java b/java/libraries/opengl/src/processing/opengl/PFramebuffer.java index 221e39123..2c200fc5c 100644 --- a/java/libraries/opengl/src/processing/opengl/PFramebuffer.java +++ b/java/libraries/opengl/src/processing/opengl/PFramebuffer.java @@ -60,8 +60,9 @@ public class PFramebuffer implements PConstants { protected int numColorBuffers; protected int[] colorBufferAttchPoints; - protected int[] glColorBufferTargets; - protected int[] glColorBufferIDs; + protected int[] glColorBufferTargets; // should remove this? + protected int[] glColorBufferIDs; // should remove this? + protected PTexture[] colorBufferTex; protected boolean screenFb; protected boolean noDepth; @@ -83,6 +84,7 @@ public class PFramebuffer implements PConstants { boolean screen) { this.parent = parent; ogl = (PGraphicsOpenGL)parent.g; + ogl.registerGLObject(this); glFboID = 0; glDepthBufferID = 0; @@ -109,8 +111,16 @@ public class PFramebuffer implements PConstants { public void delete() { release(); + for (int i = 0; i < numColorBuffers; i++) { + colorBufferTex[i] = null; + } + ogl.unregisterGLObject(this); } + public void refresh() { + setColorBuffers(colorBufferTex.clone(), colorBufferTex.length); + } + public void clear() { ogl.pushFramebuffer(); ogl.setFramebuffer(this); @@ -254,6 +264,7 @@ public class PFramebuffer implements PConstants { } for (int i = 0; i < numColorBuffers; i++) { + this.colorBufferTex[i] = textures[i]; glColorBufferTargets[i] = textures[i].glTarget; glColorBufferIDs[i] = textures[i].glID; getGl().glFramebufferTexture2D(GL.GL_FRAMEBUFFER, colorBufferAttchPoints[i], @@ -282,7 +293,7 @@ public class PFramebuffer implements PConstants { protected void allocate(int w, int h, int samples, int colorBuffers, int depthBits, int stencilBits, boolean combinedDepthStencil, boolean screen) { - release(); // Just in the case this object is being re-initialized. + release(); // Just in the case this object is being re-allocated. width = w; height = h; @@ -299,9 +310,12 @@ public class PFramebuffer implements PConstants { colorBufferAttchPoints = new int[numColorBuffers]; glColorBufferTargets = new int[numColorBuffers]; glColorBufferIDs = new int[numColorBuffers]; + colorBufferTex = new PTexture[numColorBuffers]; for (int i = 0; i < numColorBuffers; i++) { colorBufferAttchPoints[i] = GL.GL_COLOR_ATTACHMENT0 + i; - } + glColorBufferIDs[i] = 0; + colorBufferTex[i] = null; + } if (depthBits < 1 && stencilBits < 1) { this.depthBits = 0; @@ -349,6 +363,11 @@ public class PFramebuffer implements PConstants { } + protected void reallocate() { + allocate(width, height, nsamples, numColorBuffers, + depthBits, stencilBits, combinedDepthStencil, + screenFb); + } protected void release() { deleteFbo(); diff --git a/java/libraries/opengl/src/processing/opengl/PGraphicsOpenGL.java b/java/libraries/opengl/src/processing/opengl/PGraphicsOpenGL.java index bf5bc80e6..43fdb5075 100644 --- a/java/libraries/opengl/src/processing/opengl/PGraphicsOpenGL.java +++ b/java/libraries/opengl/src/processing/opengl/PGraphicsOpenGL.java @@ -160,10 +160,11 @@ public class PGraphicsOpenGL extends PGraphics { static protected final int GL_FRAME_BUFFER = 2; static protected final int GL_RENDER_BUFFER = 3; + static protected Set glObjects = new HashSet(); static protected Set glTextureObjects = new HashSet(); static protected Set glVertexBuffers = new HashSet(); static protected Set glFrameBuffers = new HashSet(); - static protected Set glRenderBuffers = new HashSet(); + static protected Set glRenderBuffers = new HashSet(); // ........................................................ @@ -675,6 +676,11 @@ public class PGraphicsOpenGL extends PGraphics { // Allocation of the main renderer, which mainly involves initializing OpenGL. if (context == null) { initPrimary(); + // If there are registered GL objects (i.e.: PTexture, PShape3D, etc), it means + // that the context has been recreated, so we need to re-allocate them in + // order to be able to keep using them. This step doesn't refresh their data, this + // is, they are empty after re-allocation. + reallocateGLObjects(); } else { reapplySettings(); } @@ -712,48 +718,45 @@ public class PGraphicsOpenGL extends PGraphics { ////////////////////////////////////////////////////////////// // RESOURCE HANDLING - - /* - protected int createGLResource(int type, Object obj) { - pTextureObjects.add(obj); - - glTextureObjects.add(new Texture(id, obj)); - } - public void recreateGLResources() { - for each cached object, run its init method (which in turn will delete and - recreate the cached opengl resources). - } - */ - - public void recreateGLResources() { - - + protected void reallocateGLObjects() { + if (!glObjects.isEmpty()) { + Object[] globjs = glObjects.toArray(); + for (int i = 0; i < globjs.length; i++) { + if (globjs[i] instanceof PTexture) { + ((PTexture)globjs[i]).reallocate(); + } else if (globjs[i] instanceof PShape3D) { + ((PShape3D)globjs[i]).reallocate(); + } else if (globjs[i] instanceof PFramebuffer) { + ((PFramebuffer)globjs[i]).reallocate(); + } else if (globjs[i] instanceof PFontTexture) { + // No need to do reallocation for a PFontTexture, since its + // textures will reallocate themselves. + } + } + } } protected void registerGLObject(Object obj) { - + glObjects.add(obj); } protected void unregisterGLObject(Object obj) { - + glTextureObjects.remove(obj); } - protected int createGLResource(int type) { int id = 0; if (type == GL_TEXTURE_OBJECT) { int[] temp = new int[1]; gl.glGenTextures(1, temp, 0); id = temp[0]; - glTextureObjects.add(id); - //pTextureObjects.add((PTexture)obj); + glTextureObjects.add(id); } else if (type == GL_VERTEX_BUFFER) { int[] temp = new int[1]; gl.glGenBuffers(1, temp, 0); id = temp[0]; glVertexBuffers.add(id); - //pTextureObjects.add((PTexture)obj); } else if (type == GL_FRAME_BUFFER) { int[] temp = new int[1]; gl.glGenFramebuffers(1, temp, 0); @@ -1123,6 +1126,28 @@ public class PGraphicsOpenGL extends PGraphics { } + public void reallocateGL() { + reallocateGLObjects(); + } + + + public void refreshGL() { + if (!glObjects.isEmpty()) { + Object[] globjs = glObjects.toArray(); + for (int i = 0; i < globjs.length; i++) { + if (globjs[i] instanceof PTexture) { + ((PTexture)globjs[i]).refresh(); + } else if (globjs[i] instanceof PShape3D) { + ((PShape3D)globjs[i]).refresh(); + } else if (globjs[i] instanceof PFramebuffer) { + ((PFramebuffer)globjs[i]).refresh(); + } else if (globjs[i] instanceof PFontTexture) { + ((PFontTexture)globjs[i]).refresh(); + } + } + } + } + protected void saveGLState() { saveGLMatrices(); } @@ -1310,11 +1335,11 @@ public class PGraphicsOpenGL extends PGraphics { } else if (which == DISABLE_OPENGL_2X_SMOOTH) { if (opengl2X) { if (primarySurface) { - releaseContext(); - // TODO: we need to recreate resources here...? + releaseContext(); context.destroy(); context = null; allocate(); + refreshGL(); throw new PApplet.RendererChangeException(); } else { initOffscreen(); @@ -1328,10 +1353,10 @@ public class PGraphicsOpenGL extends PGraphics { if (!opengl4X) { if (primarySurface) { releaseContext(); - // TODO: we need to recreate resources here...? context.destroy(); context = null; allocate(); + refreshGL(); throw new PApplet.RendererChangeException(); } else { initOffscreen(); @@ -6652,9 +6677,10 @@ return width * (1 + ox) / 2.0f; img.setParams(ogl, params); } PTexture tex = new PTexture(img.parent, img.width, img.height, params); - img.loadPixels(); + img.loadPixels(); tex.set(img.pixels); img.setCache(ogl, tex); + tex.setImage(img); // The parent image so the texture can regenerate itself upon re-allocation. return tex; } @@ -7045,49 +7071,6 @@ return width * (1 + ox) / 2.0f; offscreenFramebuffer.setColorBuffer(texture); offscreenFramebuffer.clear(); - - - /* - if (offscreenMultisample) { - // We have multisampling. The fbo with the depth and stencil buffers is the - // multisampled fbo, not the regular one. This is because the actual drawing - // occurs on the multisampled surface, which is blit into the color buffer - // of the regular fbo at endDraw(). - if (offscreenDepthBits == 24 && offscreenStencilBits == 8) { - // A single 24-8 depth-stencil buffer is created here. - offscreenFramebufferMultisample.addDepthStencilBuffer(); - } else { - // Separate depth and stencil buffers with custom number of bits are - // created here. But there is no guarantee that this will lead to a valid - // offscreen surface. - offscreenFramebufferMultisample.addDepthBuffer(offscreenDepthBits); - if (0 < offscreenStencilBits) { - offscreenFramebufferMultisample.addStencilBuffer(offscreenStencilBits); - } - } - offscreenFramebufferMultisample.clear(); - - offscreenFramebuffer.setColorBuffer(texture); - } else { - // No multisampling. - offscreenFramebuffer.setColorBuffer(texture); - - if (offscreenDepthBits == 24 && offscreenStencilBits == 8) { - // A single 24-8 depth-stencil buffer is created here. - offscreenFramebuffer.addDepthStencilBuffer(); - } else { - // Separate depth and stencil buffers with custom number of bits are - // created here. But there is no guarantee that this will lead to a valid - // offscreen surface. - offscreenFramebuffer.addDepthBuffer(offscreenDepthBits); - if (0 < offscreenStencilBits) { - offscreenFramebuffer.addStencilBuffer(offscreenStencilBits); - } - } - } - */ - - } protected void getGLObjects() { diff --git a/java/libraries/opengl/src/processing/opengl/PShape3D.java b/java/libraries/opengl/src/processing/opengl/PShape3D.java index b8b87e52c..f9ada1f40 100644 --- a/java/libraries/opengl/src/processing/opengl/PShape3D.java +++ b/java/libraries/opengl/src/processing/opengl/PShape3D.java @@ -176,6 +176,8 @@ public class PShape3D extends PShape { this(); this.papplet = parent; ogl = (PGraphicsOpenGL)parent.g; + ogl.registerGLObject(this); + this.family = PShape.GROUP; this.name = "root"; this.root = this; @@ -188,6 +190,7 @@ public class PShape3D extends PShape { public PShape3D(PApplet parent, String filename, Parameters params) { this.papplet = parent; ogl = (PGraphicsOpenGL)parent.g; + ogl.registerGLObject(this); this.family = PShape.GROUP; this.name = "root"; @@ -206,6 +209,7 @@ public class PShape3D extends PShape { public PShape3D(PApplet parent, int size, Parameters params) { this.papplet = parent; ogl = (PGraphicsOpenGL)parent.g; + ogl.registerGLObject(this); this.family = PShape.GROUP; this.name = "root"; @@ -222,9 +226,30 @@ public class PShape3D extends PShape { public void delete() { + if (root != this) return; // Can be done only from the root shape. + release(); + ogl.unregisterGLObject(this); + } + + public void refresh() { if (root != this) return; // Can be done only from the root shape. - release(); + // Loading/updating each piece of data so the arrays on the CPU-side + // are copied to the VBOs on the GPU. + + loadVertices(); + updateVertices(); + + loadColors(); + updateColors(); + + loadNormals(); + updateNormals(); + + for (int i = 0; i < numTexBuffers; i++) { + loadTexcoords(); + updateTexcoords(); + } } //////////////////////////////////////////////////////////// @@ -2177,7 +2202,7 @@ public class PShape3D extends PShape { protected void allocate(int numVert) { - release(); // Just in the case this object is being re-initialized. + release(); // Just in the case this object is being re-allocated. vertexCount = numVert; numTexBuffers = 1; diff --git a/java/libraries/opengl/src/processing/opengl/PTexture.java b/java/libraries/opengl/src/processing/opengl/PTexture.java index a81df98e2..40a83df6d 100644 --- a/java/libraries/opengl/src/processing/opengl/PTexture.java +++ b/java/libraries/opengl/src/processing/opengl/PTexture.java @@ -37,8 +37,9 @@ import processing.core.PImage; public class PTexture implements PConstants { public int width, height; - protected PApplet parent; - protected PGraphicsOpenGL ogl; + protected PApplet parent; // The Processing applet + protected PGraphicsOpenGL ogl; // The main renderer + protected PImage img; // The parent image // These are public but use at your own risk! public int glID; @@ -92,6 +93,7 @@ public class PTexture implements PConstants { this.height = height; ogl = (PGraphicsOpenGL)parent.g; + ogl.registerGLObject(this); glID = 0; @@ -120,7 +122,8 @@ public class PTexture implements PConstants { this.parent = parent; ogl = (PGraphicsOpenGL)parent.g; - + ogl.registerGLObject(this); + glID = 0; PImage img = parent.loadImage(filename); @@ -131,6 +134,14 @@ public class PTexture implements PConstants { public void delete() { release(); + img = null; + ogl.unregisterGLObject(this); + } + + public void refresh() { + if (img != null && img.pixels != null) { + set(img.pixels); + } } //////////////////////////////////////////////////////////// @@ -691,7 +702,7 @@ public class PTexture implements PConstants { * @param h int */ protected void allocate(int w, int h) { - release(); // Just in the case this object is being re-initialized. + release(); // Just in the case this object is being re-allocated. if (PGraphicsOpenGL.npotTexSupported) { glWidth = w; @@ -929,6 +940,9 @@ public class PTexture implements PConstants { // Utilities + protected void setImage(PImage img) { + this.img = img; + } protected GL getGl() { return ogl.gl;