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

View file

@ -3,13 +3,18 @@
// This file was extracted to improve the organization of the code,
// but it is still compiled in the "main.cpp" translation unit,
// because both files have mostly the same dependencies (OpenGL, GLM, FreeType).
#include "fwd.hpp"
#include "ofMain.h"
#include "ofUtils.h"
#include <GL/gl.h>
#include <GL/glext.h>
#include <GLES/gl.h>
#ifdef TARGET_OPENGLES
#include <GL/gl.h>
#include <GLES/gl.h>
//#include <GL/gl.h>
//#include <GLES/gl.h>
//#include <GLES2/gl2ext.h>
//#include <GLES3/gl3.h>
#endif
#include <ft2build.h>
#include FT_FREETYPE_H
@ -190,7 +195,7 @@ class Font {
};
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
@ -424,6 +429,12 @@ class Font {
gpuTextureOffset(gpuTextureOffset){
// 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){
loadFlags = FT_LOAD_NO_BITMAP;
kerningMode = FT_KERNING_DEFAULT;
@ -486,7 +497,7 @@ class Font {
glVertexAttribPointer(3, 4, GL_FLOAT, false, sizeof(BufferVertex), (void *)offsetof(BufferVertex, r));
glBindVertexArray(0);
uploadBuffers();
//uploadBuffers();
#ifndef TARGET_OPENGLES
glBindTexture(GL_TEXTURE_BUFFER, glyphTextureName);
@ -499,6 +510,82 @@ class Font {
#else
#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(){
glDeleteVertexArrays(1, &vao);
@ -565,7 +652,8 @@ class Font {
it++;
}
uploadBuffers();
dirtyBuffers = true;
//uploadBuffers();
}
void prepareGlyphsForText(const std::vector <GlyphIdentity> & variationText,
FT_Library & library,
@ -579,8 +667,10 @@ class Font {
}
int i = 0;
string text = "";
for(const auto & glyphIdentity : variationText){
const uint32_t & charcode = glyphIdentity.charcode;
text += charcode;
if(charcode == '\0'){ // do we need this?
break;
}
@ -608,13 +698,24 @@ class Font {
continue;
}
// LOL
//cout << (char)glyphIdentity.charcode
//<< " " << ofToString(glyphIdentity.coords[0]) << " "
//<< " (" << ofToString(bufferCurves.size()) << ")" << endl;
buildGlyph(glyphIdentity,
glyphIndex);
changed = true;
i++;
}
if(changed){
dirtyBuffers = true;
glyphBufferUnit = 1;
curveBufferUnit = 2;
generateCurveTextureLayers(bufferCurves.size());
// Reupload the full buffer contents. To make this even more
// dynamic, the buffers could be overallocated and only the added
// data could be uploaded.
@ -625,6 +726,7 @@ class Font {
private:
bool initializedGlyphsBufferTexture = false;
bool initializedCurvesBufferTexture = false;
bool dirtyBuffers = false;
int calcTextureOffset(){
return gpuTextureOffset * 2;
}
@ -637,11 +739,34 @@ class Font {
//cout << "GL_MAX_ARRAY_TEXTURE_LAYERS: " << ofToString(size) << endl;
//size = 0;
GLsizei width = 16384;
GLsizei width = gl_max_texture_size;
GLsizei height = 1;
GLsizei layerCount = 4;
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.
GLubyte texels[layerCount][width * height];
for(int l = 0; l < layerCount; l++){
@ -652,7 +777,7 @@ class Font {
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
// 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.
// 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.
@ -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.
for(int i = 0; i < (int)layerCount; i++){
//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
@ -670,78 +795,79 @@ class Font {
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
void uploadBuffers(){
//cout << "bufferGlyphs.size(): " << bufferGlyphs.size() << endl;
//cout << "bufferCurves.size(): " << bufferCurves.size() << endl;
#ifndef TARGET_OPENGLES
glBindBuffer(GL_TEXTURE_BUFFER, glyphBufferName);
glBufferData(GL_TEXTURE_BUFFER, sizeof(BufferGlyph) * bufferGlyphs.size(), bufferGlyphs.data(), GL_STATIC_DRAW);
glBindBuffer(GL_TEXTURE_BUFFER, 0);
if(dirtyBuffers){
//cout << "bufferGlyphs.size(): " << bufferGlyphs.size() << endl;
//cout << "bufferCurves.size(): " << bufferCurves.size() << endl;
#ifndef TARGET_OPENGLES
glBindBuffer(GL_TEXTURE_BUFFER, glyphBufferName);
glBufferData(GL_TEXTURE_BUFFER, sizeof(BufferGlyph) * bufferGlyphs.size(), bufferGlyphs.data(), GL_STATIC_DRAW);
glBindBuffer(GL_TEXTURE_BUFFER, 0);
glBindBuffer(GL_TEXTURE_BUFFER, curveBufferName);
glBufferData(GL_TEXTURE_BUFFER, sizeof(BufferCurve) * bufferCurves.size(), bufferCurves.data(), GL_STATIC_DRAW);
glBindBuffer(GL_TEXTURE_BUFFER, 0);
#else
int size;
//glGetIntegerv(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &size);
//cout << "GL_MAX_SHADER_STORAGE_BLOCK_SIZE: " << ofToString(size) << endl;
glyphBufferUnit = 1;
curveBufferUnit = 2;
int bufferGlyphsSize = bufferGlyphs.size();
if(!isPowerOfTwo(bufferGlyphsSize)){
bufferGlyphsSize = calculateUpperPowerOfTwo(bufferGlyphsSize);
}
glActiveTexture(GL_TEXTURE0 + glyphBufferUnit);
glBindTexture(GL_TEXTURE_2D, glyphBufferName);
if(!initializedGlyphsBufferTexture){
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_MAG_FILTER,
GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
initializedGlyphsBufferTexture = true;
}
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RG32I,
bufferGlyphsSize,
1,
0,
GL_RG_INTEGER,
GL_INT,
bufferGlyphs.data());
glBindBuffer(GL_TEXTURE_BUFFER, curveBufferName);
glBufferData(GL_TEXTURE_BUFFER, sizeof(BufferCurve) * bufferCurves.size(), bufferCurves.data(), GL_STATIC_DRAW);
glBindBuffer(GL_TEXTURE_BUFFER, 0);
#else
int size;
//glGetIntegerv(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &size);
//cout << "GL_MAX_SHADER_STORAGE_BLOCK_SIZE: " << ofToString(size) << endl;
int bufferGlyphsSize = bufferGlyphs.size() * sizeof(BufferGlyph);
if(!isPowerOfTwo(bufferGlyphsSize)){
bufferGlyphsSize = calculateUpperPowerOfTwo(bufferGlyphsSize);
}
glActiveTexture(GL_TEXTURE0 + glyphBufferUnit);
glBindTexture(GL_TEXTURE_2D, glyphBufferName);
if(!initializedGlyphsBufferTexture){
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_MAG_FILTER,
GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
initializedGlyphsBufferTexture = true;
}
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RG32I,
bufferGlyphsSize,
1,
0,
GL_RG_INTEGER,
GL_INT,
bufferGlyphs.data());
int bufferCurvesSize = bufferCurves.size();
if(!isPowerOfTwo(bufferCurvesSize)){
bufferCurvesSize = calculateUpperPowerOfTwo(bufferCurvesSize);
}
cout << "bufferCurvesSize: " << ofToString(bufferCurvesSize) << endl;
glActiveTexture(GL_TEXTURE0 + curveBufferUnit);
glBindTexture(GL_TEXTURE_2D, curveBufferName);
if(!initializedCurvesBufferTexture){
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_MAG_FILTER,
GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
initializedCurvesBufferTexture = true;
}
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RG32F,
bufferCurvesSize * sizeof(glm::highp_f32),
1,
0,
GL_RG,
GL_FLOAT,
bufferCurves.data());
glActiveTexture(GL_TEXTURE0);
#endif
//int bufferCurvesSize = bufferCurves.size() * bufferCurveSize;
//if(!isPowerOfTwo(bufferCurvesSize)){
//bufferCurvesSize = calculateUpperPowerOfTwo(bufferCurvesSize);
//}
//glActiveTexture(GL_TEXTURE0 + curveBufferUnit);
//glBindTexture(GL_TEXTURE_2D, curveBufferName);
//if(!initializedCurvesBufferTexture){
//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_MAG_FILTER,
//GL_NEAREST);
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
//GL_NEAREST);
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
//initializedCurvesBufferTexture = true;
//}
//glTexImage2D(GL_TEXTURE_2D,
//0,
//GL_RG32F,
//bufferCurvesSize,
//1,
//0,
//GL_RG,
//GL_FLOAT,
//bufferCurves.data());
glActiveTexture(GL_TEXTURE0);
uploadCurveTextureLayers();
#endif
dirtyBuffers = false;
}
}
void buildGlyph(const GlyphIdentity & glyphIdentity,
@ -976,8 +1102,6 @@ class Font {
glActiveTexture(GL_TEXTURE0);
#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");
glUniform1i(glyphsUniformLocation, glyphBufferUnit);
curvesUniformLocation = glGetUniformLocation(program, "curves");
@ -987,7 +1111,7 @@ class Font {
glBindTexture(GL_TEXTURE_2D, glyphBufferName);
glActiveTexture(GL_TEXTURE0 + curveBufferUnit);
glBindTexture(GL_TEXTURE_2D, curveBufferName);
glBindTexture(GL_TEXTURE_2D_ARRAY, curveBufferName);
glActiveTexture(GL_TEXTURE0);
#endif
@ -1219,9 +1343,11 @@ class Font {
std::vector <BufferGlyph> bufferGlyphs;
std::vector <BufferCurve> bufferCurves;
//std::unordered_map <uint32_t, Glyph> glyphs;
std::vector <int> bufferCurveLayerPointers = {0};
std::unordered_map <GlyphIdentity, Glyph> glyphs;
static GLint gl_max_texture_size;
public:
// ID of the shader program to use.
GLuint program = 0;