mirror of
https://github.com/processing/processing4.git
synced 2026-01-30 03:41:15 +01:00
Implemented GL resource reallocation/refreshing mechanism
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user