Implemented GL resource reallocation/refreshing mechanism

This commit is contained in:
codeanticode
2011-07-02 00:04:16 +00:00
parent 72c55a1c86
commit 72f18a3201
5 changed files with 165 additions and 88 deletions

View File

@@ -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);
}
}
}

View File

@@ -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();

View File

@@ -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<Object> glObjects = new HashSet<Object>();
static protected Set<Integer> glTextureObjects = new HashSet<Integer>();
static protected Set<Integer> glVertexBuffers = new HashSet<Integer>();
static protected Set<Integer> glFrameBuffers = new HashSet<Integer>();
static protected Set<Integer> glRenderBuffers = new HashSet<Integer>();
static protected Set<Integer> glRenderBuffers = new HashSet<Integer>();
// ........................................................
@@ -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() {

View File

@@ -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;

View File

@@ -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;