use a texture array instead of texture

This commit is contained in:
jrkb 2023-04-29 18:39:00 +02:00
parent 351a9ac2ec
commit 7feb9630e6
2 changed files with 238 additions and 88 deletions

View file

@ -2,6 +2,7 @@
precision highp float; precision highp float;
precision highp isampler2D; precision highp isampler2D;
precision highp sampler2D; precision highp sampler2D;
precision highp sampler2DArray;
// Based on: http://wdobbie.com/post/gpu-text-rendering-with-vector-textures/ // Based on: http://wdobbie.com/post/gpu-text-rendering-with-vector-textures/
@ -12,10 +13,18 @@ struct Glyph {
struct Curve { struct Curve {
vec2 p0, p1, p2; vec2 p0, p1, p2;
}; };
// CURVE_SIZE
// is the amount of vec2's in the curve
// we can hardcode this, as we can simply
// count the floats in the struct
// 6 floats => 3 vec2's
#define CURVE_SIZE 3
uniform isampler2D glyphs; uniform isampler2D glyphs;
uniform sampler2D curves; uniform sampler2DArray curves;
uniform sampler2D iChannel0; ivec3 curvesTextureSize;
int curveBreakPoint;
//uniform sampler2D iChannel0;
//uniform vec4 color; //uniform vec4 color;
// Controls for debugging and exploring: // Controls for debugging and exploring:
@ -49,9 +58,14 @@ Glyph loadGlyph(int index) {
Curve loadCurve(int index) { Curve loadCurve(int index) {
Curve result; Curve result;
result.p0 = texelFetch(curves, ivec2(3*index+0, 0), 0).xy; //int dw = curveBreakPoint; // width including dead space
result.p1 = texelFetch(curves, ivec2(3*index+1, 0), 0).xy; int dw = curvesTextureSize[0];
result.p2 = texelFetch(curves, ivec2(3*index+2, 0), 0).xy; int w = dw - (dw % CURVE_SIZE); // effectual width
int ix = (index * CURVE_SIZE) % w;
int iz = ((index * CURVE_SIZE) + 2) / w;
result.p0 = texelFetch(curves, ivec3(ix+0, 0, iz), 0).xy;
result.p1 = texelFetch(curves, ivec3(ix+1, 0, iz), 0).xy;
result.p2 = texelFetch(curves, ivec3(ix+2, 0, iz), 0).xy;
return result; return result;
} }
@ -109,13 +123,23 @@ vec2 rotate(vec2 v) {
} }
void main() { void main() {
curvesTextureSize = textureSize(curves, 0);
curveBreakPoint = (curvesTextureSize[0] * 2) / 3;
//int w = curvesTextureSize[0]; // 16384 / 2 = 8192
float alpha = 0.0; float alpha = 0.0;
// Inverse of the diameter of a pixel in uv units for anti-aliasing. // Inverse of the diameter of a pixel in uv units for anti-aliasing.
vec2 inverseDiameter = 1.0 / (antiAliasingWindowSize * fwidth(uv)); vec2 inverseDiameter = 1.0 / (antiAliasingWindowSize * fwidth(uv));
vec4 c = color;
Glyph glyph = loadGlyph(bufferIndex); Glyph glyph = loadGlyph(bufferIndex);
for (int i = 0; i < glyph.count; i++) { for (int i = 0; i < glyph.count; i++) {
//if (glyph.start + i > curveBreakPoint) {
//c = vec4(1.0,0.0,0.0,1.0);
//}
Curve curve = loadCurve(glyph.start + i); Curve curve = loadCurve(glyph.start + i);
vec2 p0 = curve.p0 - uv; vec2 p0 = curve.p0 - uv;
@ -133,7 +157,7 @@ void main() {
} }
alpha = clamp(alpha, 0.0, 1.0); alpha = clamp(alpha, 0.0, 1.0);
result = vec4(color.rgb, color.a * alpha); result = vec4(c.rgb, c.a * alpha);
if (enableControlPointsVisualization) { if (enableControlPointsVisualization) {
// Visualize control points. // Visualize control points.

View file

@ -3,13 +3,18 @@
// This file was extracted to improve the organization of the code, // This file was extracted to improve the organization of the code,
// but it is still compiled in the "main.cpp" translation unit, // but it is still compiled in the "main.cpp" translation unit,
// because both files have mostly the same dependencies (OpenGL, GLM, FreeType). // because both files have mostly the same dependencies (OpenGL, GLM, FreeType).
#include "fwd.hpp"
#include "ofMain.h" #include "ofMain.h"
#include "ofUtils.h" #include "ofUtils.h"
#include <GL/gl.h>
#include <GL/glext.h> #include <GL/glext.h>
#include <GLES/gl.h>
#ifdef TARGET_OPENGLES #ifdef TARGET_OPENGLES
#include <GL/gl.h> //#include <GL/gl.h>
#include <GLES/gl.h> //#include <GLES/gl.h>
//#include <GLES2/gl2ext.h>
//#include <GLES3/gl3.h>
#endif #endif
#include <ft2build.h> #include <ft2build.h>
#include FT_FREETYPE_H #include FT_FREETYPE_H
@ -190,7 +195,7 @@ class Font {
}; };
struct BufferCurve { struct BufferCurve {
float x0, y0, x1, y1, x2, y2; glm::float32_t x0, y0, x1, y1, x2, y2;
}; };
// keep this, just because I wrote it // keep this, just because I wrote it
@ -424,6 +429,12 @@ class Font {
gpuTextureOffset(gpuTextureOffset){ gpuTextureOffset(gpuTextureOffset){
// TODO: modularize init, so we can initialize with settings and text // TODO: modularize init, so we can initialize with settings and text
if(gl_max_texture_size == -1){
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &gl_max_texture_size);
cout << "GL_MAX_TEXTURE_SIZE: " << ofToString(gl_max_texture_size) << endl;
}
if(hinting){ if(hinting){
loadFlags = FT_LOAD_NO_BITMAP; loadFlags = FT_LOAD_NO_BITMAP;
kerningMode = FT_KERNING_DEFAULT; kerningMode = FT_KERNING_DEFAULT;
@ -486,7 +497,7 @@ class Font {
glVertexAttribPointer(3, 4, GL_FLOAT, false, sizeof(BufferVertex), (void *)offsetof(BufferVertex, r)); glVertexAttribPointer(3, 4, GL_FLOAT, false, sizeof(BufferVertex), (void *)offsetof(BufferVertex, r));
glBindVertexArray(0); glBindVertexArray(0);
uploadBuffers(); //uploadBuffers();
#ifndef TARGET_OPENGLES #ifndef TARGET_OPENGLES
glBindTexture(GL_TEXTURE_BUFFER, glyphTextureName); glBindTexture(GL_TEXTURE_BUFFER, glyphTextureName);
@ -499,6 +510,82 @@ class Font {
#else #else
#endif #endif
} }
int amountCurveTextureLayers = 1;
int widthCurveTextureLayer = 0;
int previousCurveBufferSize = 0;
int bufferCurveSize = (sizeof(BufferCurve) / (sizeof(glm::float32_t) * 1));
void generateCurveTextureLayers(int amountCurves){
//cout << "generateCurveTextureLayers()" << endl;
int curveBufferSize = amountCurves * bufferCurveSize;
curveBufferSize = calculateUpperPowerOfTwo(curveBufferSize);
int overshoot = curveBufferSize % gl_max_texture_size == 0 ? 0 : 1;
int neededLayers = (curveBufferSize / gl_max_texture_size) + overshoot;
// LOL
//cout << "\tlayers needed: " << ofToString(neededLayers) << endl
//<< "\tcurveBufferSize p2: " << ofToString(curveBufferSize) << endl
//<< "\tcurveBufferSize or: " << ofToString(bufferCurves.size()) << endl
//<< "\tsize of BufferCurve: " << ofToString(bufferCurveSize) << endl
//<< "\ttotal glyphs: " << ofToString(glyphs.size()) << endl
//;
if(neededLayers != amountCurveTextureLayers
|| curveBufferSize > previousCurveBufferSize){
glDeleteTextures(1, &curveBufferName);
glGenTextures(1, &curveBufferName);
//glActiveTexture(GL_TEXTURE0 + curveBufferUnit);
glBindTexture(GL_TEXTURE_2D_ARRAY, curveBufferName);
if(neededLayers == 1){
widthCurveTextureLayer = curveBufferSize;
glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RG32F, curveBufferSize, 1, neededLayers);
}else{
if(neededLayers > 4){
neededLayers = 4;
// TODO: implement multiple arrays
// LOL
//cout << "more layers than 4 needed" << endl;
}else{
}
widthCurveTextureLayer = gl_max_texture_size;
glTexStorage3D(GL_TEXTURE_2D_ARRAY,
1,
GL_RG32F,
gl_max_texture_size,
1,
neededLayers);
}
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
//glActiveTexture(GL_TEXTURE0);
amountCurveTextureLayers = neededLayers;
}
previousCurveBufferSize = curveBufferSize;
}
void uploadCurveTextureLayers(){
glActiveTexture(GL_TEXTURE0 + curveBufferUnit);
glBindTexture(GL_TEXTURE_2D_ARRAY, curveBufferName);
for(int i = 0; i < amountCurveTextureLayers; i++){
int width = widthCurveTextureLayer;
int height = 1;
int layerPointer = i == 0 ? 0 :
5461;
//(i * gl_max_texture_size) - (gl_max_texture_size % bufferCurveSize);
glTexSubImage3D(GL_TEXTURE_2D_ARRAY,
0,
0,
0,
i,
width,
height,
1,
GL_RG,
GL_FLOAT,
&bufferCurves[layerPointer]);
}
glActiveTexture(GL_TEXTURE0);
}
~Font(){ ~Font(){
glDeleteVertexArrays(1, &vao); glDeleteVertexArrays(1, &vao);
@ -565,7 +652,8 @@ class Font {
it++; it++;
} }
uploadBuffers(); dirtyBuffers = true;
//uploadBuffers();
} }
void prepareGlyphsForText(const std::vector <GlyphIdentity> & variationText, void prepareGlyphsForText(const std::vector <GlyphIdentity> & variationText,
FT_Library & library, FT_Library & library,
@ -579,8 +667,10 @@ class Font {
} }
int i = 0; int i = 0;
string text = "";
for(const auto & glyphIdentity : variationText){ for(const auto & glyphIdentity : variationText){
const uint32_t & charcode = glyphIdentity.charcode; const uint32_t & charcode = glyphIdentity.charcode;
text += charcode;
if(charcode == '\0'){ // do we need this? if(charcode == '\0'){ // do we need this?
break; break;
} }
@ -608,13 +698,24 @@ class Font {
continue; continue;
} }
// LOL
//cout << (char)glyphIdentity.charcode
//<< " " << ofToString(glyphIdentity.coords[0]) << " "
//<< " (" << ofToString(bufferCurves.size()) << ")" << endl;
buildGlyph(glyphIdentity, buildGlyph(glyphIdentity,
glyphIndex); glyphIndex);
changed = true; changed = true;
i++; i++;
} }
if(changed){ if(changed){
dirtyBuffers = true;
glyphBufferUnit = 1;
curveBufferUnit = 2;
generateCurveTextureLayers(bufferCurves.size());
// Reupload the full buffer contents. To make this even more // Reupload the full buffer contents. To make this even more
// dynamic, the buffers could be overallocated and only the added // dynamic, the buffers could be overallocated and only the added
// data could be uploaded. // data could be uploaded.
@ -625,6 +726,7 @@ class Font {
private: private:
bool initializedGlyphsBufferTexture = false; bool initializedGlyphsBufferTexture = false;
bool initializedCurvesBufferTexture = false; bool initializedCurvesBufferTexture = false;
bool dirtyBuffers = false;
int calcTextureOffset(){ int calcTextureOffset(){
return gpuTextureOffset * 2; return gpuTextureOffset * 2;
} }
@ -637,11 +739,34 @@ class Font {
//cout << "GL_MAX_ARRAY_TEXTURE_LAYERS: " << ofToString(size) << endl; //cout << "GL_MAX_ARRAY_TEXTURE_LAYERS: " << ofToString(size) << endl;
//size = 0; //size = 0;
GLsizei width = 16384; GLsizei width = gl_max_texture_size;
GLsizei height = 1; GLsizei height = 1;
GLsizei layerCount = 4; GLsizei layerCount = 4;
GLsizei mipLevelCount = 1; GLsizei mipLevelCount = 1;
//GLsizei bufSize = 42;
//GLint * params[bufSize];
//for(int i = 0; i < bufSize; i++){
//params[i] = 0;
//}
//glGetInternalformativ(GL_TEXTURE_2D_ARRAY,
//GL_RG32F,
//GL_MAX_DEPTH,
//bufSize / 2,
//params[0]);
//glGetInternalformativ(GL_TEXTURE_2D_ARRAY,
//GL_RG32F,
//GL_MAX_LAYERS,
//bufSize / 2,
//params[21]);
//stringstream s;
//s << "and here:" << endl;
//for(int i = 0; i < bufSize; i++){
//s << ofToString(i) << ": " << ofToString(params[i]) << endl;
//}
//cout << s.str();
// Read you texels here. In the current example, we have 2*2*2 = 8 texels, with each texel being 4 GLubytes. // Read you texels here. In the current example, we have 2*2*2 = 8 texels, with each texel being 4 GLubytes.
GLubyte texels[layerCount][width * height]; GLubyte texels[layerCount][width * height];
for(int l = 0; l < layerCount; l++){ for(int l = 0; l < layerCount; l++){
@ -652,7 +777,7 @@ class Font {
glGenTextures(1, &texture); glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D_ARRAY, texture); glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
// Allocate the storage. // Allocate the storage.
glTexStorage3D(GL_TEXTURE_2D_ARRAY, mipLevelCount, GL_RGBA8, width, height, layerCount); glTexStorage3D(GL_TEXTURE_2D_ARRAY, mipLevelCount, GL_RG32F, width, height, layerCount);
// Upload pixel data. // Upload pixel data.
// The first 0 refers to the mipmap level (level 0, since there's only 1) // The first 0 refers to the mipmap level (level 0, since there's only 1)
// The following 2 zeroes refers to the x and y offsets in case you only want to specify a subrectangle. // The following 2 zeroes refers to the x and y offsets in case you only want to specify a subrectangle.
@ -660,7 +785,7 @@ class Font {
// Altogether you can specify a 3D box subset of the overall texture, but only one mip level at a time. // Altogether you can specify a 3D box subset of the overall texture, but only one mip level at a time.
for(int i = 0; i < (int)layerCount; i++){ for(int i = 0; i < (int)layerCount; i++){
//cout << "OFFSET " << ofToString(i) << endl; //cout << "OFFSET " << ofToString(i) << endl;
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, width, height, layerCount, GL_RGBA, GL_UNSIGNED_BYTE, texels[i]); glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, width, height, layerCount, GL_RG, GL_FLOAT, texels[i]);
} }
// Always set reasonable texture parameters // Always set reasonable texture parameters
@ -670,6 +795,7 @@ class Font {
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
} }
void uploadBuffers(){ void uploadBuffers(){
if(dirtyBuffers){
//cout << "bufferGlyphs.size(): " << bufferGlyphs.size() << endl; //cout << "bufferGlyphs.size(): " << bufferGlyphs.size() << endl;
//cout << "bufferCurves.size(): " << bufferCurves.size() << endl; //cout << "bufferCurves.size(): " << bufferCurves.size() << endl;
#ifndef TARGET_OPENGLES #ifndef TARGET_OPENGLES
@ -684,9 +810,7 @@ class Font {
int size; int size;
//glGetIntegerv(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &size); //glGetIntegerv(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &size);
//cout << "GL_MAX_SHADER_STORAGE_BLOCK_SIZE: " << ofToString(size) << endl; //cout << "GL_MAX_SHADER_STORAGE_BLOCK_SIZE: " << ofToString(size) << endl;
glyphBufferUnit = 1; int bufferGlyphsSize = bufferGlyphs.size() * sizeof(BufferGlyph);
curveBufferUnit = 2;
int bufferGlyphsSize = bufferGlyphs.size();
if(!isPowerOfTwo(bufferGlyphsSize)){ if(!isPowerOfTwo(bufferGlyphsSize)){
bufferGlyphsSize = calculateUpperPowerOfTwo(bufferGlyphsSize); bufferGlyphsSize = calculateUpperPowerOfTwo(bufferGlyphsSize);
} }
@ -713,35 +837,37 @@ class Font {
GL_INT, GL_INT,
bufferGlyphs.data()); bufferGlyphs.data());
int bufferCurvesSize = bufferCurves.size(); //int bufferCurvesSize = bufferCurves.size() * bufferCurveSize;
if(!isPowerOfTwo(bufferCurvesSize)){ //if(!isPowerOfTwo(bufferCurvesSize)){
bufferCurvesSize = calculateUpperPowerOfTwo(bufferCurvesSize); //bufferCurvesSize = calculateUpperPowerOfTwo(bufferCurvesSize);
} //}
cout << "bufferCurvesSize: " << ofToString(bufferCurvesSize) << endl; //glActiveTexture(GL_TEXTURE0 + curveBufferUnit);
glActiveTexture(GL_TEXTURE0 + curveBufferUnit); //glBindTexture(GL_TEXTURE_2D, curveBufferName);
glBindTexture(GL_TEXTURE_2D, curveBufferName); //if(!initializedCurvesBufferTexture){
if(!initializedCurvesBufferTexture){ //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, //GL_NEAREST);
GL_NEAREST); //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, //GL_NEAREST);
GL_NEAREST); //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); //initializedCurvesBufferTexture = true;
initializedCurvesBufferTexture = true; //}
} //glTexImage2D(GL_TEXTURE_2D,
glTexImage2D(GL_TEXTURE_2D, //0,
0, //GL_RG32F,
GL_RG32F, //bufferCurvesSize,
bufferCurvesSize * sizeof(glm::highp_f32), //1,
1, //0,
0, //GL_RG,
GL_RG, //GL_FLOAT,
GL_FLOAT, //bufferCurves.data());
bufferCurves.data());
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
uploadCurveTextureLayers();
#endif #endif
dirtyBuffers = false;
}
} }
void buildGlyph(const GlyphIdentity & glyphIdentity, void buildGlyph(const GlyphIdentity & glyphIdentity,
@ -976,8 +1102,6 @@ class Font {
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
#else #else
// Important! The GLint for the location needs to stick around
// we cannot reuse a location as in Desktop for some reason
glyphsUniformLocation = glGetUniformLocation(program, "glyphs"); glyphsUniformLocation = glGetUniformLocation(program, "glyphs");
glUniform1i(glyphsUniformLocation, glyphBufferUnit); glUniform1i(glyphsUniformLocation, glyphBufferUnit);
curvesUniformLocation = glGetUniformLocation(program, "curves"); curvesUniformLocation = glGetUniformLocation(program, "curves");
@ -987,7 +1111,7 @@ class Font {
glBindTexture(GL_TEXTURE_2D, glyphBufferName); glBindTexture(GL_TEXTURE_2D, glyphBufferName);
glActiveTexture(GL_TEXTURE0 + curveBufferUnit); glActiveTexture(GL_TEXTURE0 + curveBufferUnit);
glBindTexture(GL_TEXTURE_2D, curveBufferName); glBindTexture(GL_TEXTURE_2D_ARRAY, curveBufferName);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
#endif #endif
@ -1219,9 +1343,11 @@ class Font {
std::vector <BufferGlyph> bufferGlyphs; std::vector <BufferGlyph> bufferGlyphs;
std::vector <BufferCurve> bufferCurves; std::vector <BufferCurve> bufferCurves;
//std::unordered_map <uint32_t, Glyph> glyphs; std::vector <int> bufferCurveLayerPointers = {0};
std::unordered_map <GlyphIdentity, Glyph> glyphs; std::unordered_map <GlyphIdentity, Glyph> glyphs;
static GLint gl_max_texture_size;
public: public:
// ID of the shader program to use. // ID of the shader program to use.
GLuint program = 0; GLuint program = 0;