diff --git a/data/ofxGPUFont/shaders/ES3/font_ub.frag b/data/ofxGPUFont/shaders/ES3/font_ub.frag index 5af667f..4b8edb8 100644 --- a/data/ofxGPUFont/shaders/ES3/font_ub.frag +++ b/data/ofxGPUFont/shaders/ES3/font_ub.frag @@ -13,15 +13,20 @@ 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 +const int CURVES_BUFFER_SIZE = {{CURVES_BUFFER_SIZE}}; + +layout(std140) uniform curves_t { + vec4 data[{{CURVES_BUFFER_SIZE}}]; +} curves; uniform isampler2D glyphs; -uniform sampler2DArray curves; ivec3 curvesTextureSize; int curveBreakPoint; //uniform sampler2D iChannel0; @@ -57,16 +62,21 @@ Glyph loadGlyph(int index) { } Curve loadCurve(int index) { - Curve result; - //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; + int index_a = int(floor((float(index) / 4.0) * 6.0)); + int index_b = index_a + 1; + vec4 a = curves.data[index_a]; + vec4 b = curves.data[index_b]; + Curve curve; + if (index % 2 == 0) { + curve.p0 = vec2(a[0], a[1]); + curve.p1 = vec2(a[2], a[3]); + curve.p2 = vec2(b[0], b[1]); + } else { + curve.p0 = vec2(a[2], a[3]); + curve.p1 = vec2(b[0], b[1]); + curve.p2 = vec2(b[2], b[3]); + } + return curve; } float computeCoverage(float inverseDiameter, vec2 p0, vec2 p1, vec2 p2) { @@ -124,10 +134,6 @@ 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. diff --git a/src/gpufont/font.hpp b/src/gpufont/font.hpp index 6522312..5377948 100644 --- a/src/gpufont/font.hpp +++ b/src/gpufont/font.hpp @@ -7,6 +7,8 @@ #include "ofMain.h" #include "ofUtils.h" +#include "shader_catalog.hpp" + #include #include #ifdef TARGET_OPENGLES @@ -429,14 +431,14 @@ class Font { // If hinting is enabled, worldSize must be an integer and defines the font size in pixels used for hinting. // Otherwise, worldSize can be an arbitrary floating-point value. - Font(FT_Face face, float worldSize = 1.0f, bool hinting = false, GLint bufferTargetType = GL_TEXTURE_2D_ARRAY) : + Font(FT_Face face, float worldSize = 1.0f, bool hinting = false, GLint bufferTargetType = GL_TEXTURE_2D_ARRAY, GLuint shaderProgram = 0) : face(face), hinting(hinting), BUFFER_TARGET_TYPE(bufferTargetType), + program(shaderProgram), worldSize(worldSize){ // 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; @@ -463,22 +465,7 @@ class Font { emSize = face->units_per_EM; } - //int amounters = 0; - //for(int i = 0; i < 1024; i++){ - //testGL(10 + i); - //amounters = i + 1; - //cout << "added " << ofToString(amounters) << " LOL" << endl; - //bool hasError = false; - //GLenum err; - //while((err = glGetError()) != GL_NO_ERROR){ - //hasError = true; - //// Process/log the error. - //} - //if(hasError){ - //cout << "WHOOOPS" << endl; - //break; - //} - //} + ubo_curves_index = glGetUniformBlockIndex(program, "curves_t"); glGenVertexArrays(1, &vao); @@ -492,8 +479,13 @@ class Font { glGenBuffers(1, &glyphBufferName); glGenBuffers(1, &curveBufferName); #else - glGenTextures(1, &glyphBufferName); - glGenTextures(1, &curveBufferName); + if(BUFFER_TARGET_TYPE == GL_TEXTURE_2D_ARRAY){ + glGenTextures(1, &glyphBufferName); + glGenTextures(1, &curveBufferName); + }else{ + glGenTextures(1, &glyphBufferName); + glGenBuffers(1, &curveBufferName); + } #endif glBindVertexArray(vao); @@ -526,7 +518,26 @@ class Font { int widthCurveTextureLayer = 0; int previousCurveBufferSize = 0; int bufferCurveSize = (sizeof(BufferCurve) / (sizeof(glm::float32_t) * 1)); + int shaderCurvesBufferSize = -1; + GLint ubo_curves_index; + void generateCurveBuffers(){ + int shaderBufferByteSize = sizeof(BufferCurve) * shaderCurvesBufferSize; + shaderBufferCurves = bufferCurves; + shaderBufferCurves.resize(shaderCurvesBufferSize); + + //cout << "generate curve buffer" << endl + //<< "\t shaderBufferByteSize: " << ofToString(shaderBufferByteSize) + //<< "\t bufferCurves.size(): " << ofToString(bufferCurves.size()) + //<< "\t shaderBufferCurves.size(): " << ofToString(shaderBufferCurves.size()) + //<< endl; + + ubo_curves_index = glGetUniformBlockIndex(program, "curves_t"); + glBindBufferBase(GL_UNIFORM_BUFFER, 0, curveBufferName); + glBufferData(GL_UNIFORM_BUFFER, shaderBufferByteSize, shaderBufferCurves.data(), GL_DYNAMIC_DRAW); + glUniformBlockBinding(program, ubo_curves_index, 0); + + } void generateCurveTextureLayers(int amountCurves){ //cout << "generateCurveTextureLayers()" << endl; int curveBufferSize = amountCurves * bufferCurveSize; @@ -575,6 +586,11 @@ class Font { } previousCurveBufferSize = curveBufferSize; } + void uploadCurveBuffer(){ + + glBindBufferBase(GL_UNIFORM_BUFFER, 0, curveBufferName); + glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(BufferCurve) * bufferCurves.size(), bufferCurves.data()); + } void uploadCurveTextureLayers(){ glActiveTexture(GL_TEXTURE0 + curveBufferUnit); glBindTexture(GL_TEXTURE_2D_ARRAY, curveBufferName); @@ -673,6 +689,8 @@ class Font { } void prepareGlyphsForText(const std::vector & variationText, FT_Library & library, + shared_ptr shaderCatalog, + shared_ptr shader, bool forceChange = false){ bool changed = false; @@ -731,13 +749,50 @@ class Font { 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. - uploadBuffers(); + if(BUFFER_TARGET_TYPE == GL_UNIFORM_BUFFER){ + int curvesBufferSize = getUboCurvesBufferSize(bufferCurves); + if(curvesBufferSize != shaderCurvesBufferSize){ + shaderCurvesBufferSize = curvesBufferSize; + shaderCatalog->setReplacement("{{CURVES_BUFFER_SIZE}}", std::to_string(shaderCurvesBufferSize)); + //cout << shaderCatalog->getReplacement("{{CURVES_BUFFER_SIZE}}") << " <<<< _ replacement for curves buffer size " << endl; + shaderCatalog->requestUpdate("font_ub", true); + shaderCatalog->update(); + shader = shaderCatalog->get("font_ub"); + program = shader->program; + generateCurveBuffers(); + uploadBuffers(); + }else{ + uploadBuffers(); + } + }else{ + 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. + uploadBuffers(); + } } } + int getUboCurvesBufferSize(const std::vector & bc){ + int actualShaderBufferSize = ceil((bc.size() * 6.0) / 4.0); + int thresholdBase = 2048; + int n_max = gl_max_uniform_block_size / thresholdBase; + for(int i = 0; i < n_max + 1; i++){ + int levelSize = thresholdBase * i; + // prevent flattering around a threshold + // when going a level down + // but immediately + if(actualShaderBufferSize <= levelSize){ + if(shaderCurvesBufferSize == levelSize + thresholdBase){ + return shaderCurvesBufferSize; + } + return levelSize; + } + } + // you want more? well, we don't have it :( + return gl_max_uniform_block_size; + } + private: bool initializedGlyphsBufferTexture = false; @@ -849,35 +904,12 @@ class Font { GL_RG_INTEGER, GL_INT, bufferGlyphs.data()); - - //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(); + if(BUFFER_TARGET_TYPE == GL_TEXTURE_2D_ARRAY){ + uploadCurveTextureLayers(); + }else if(BUFFER_TARGET_TYPE == GL_UNIFORM_BUFFER){ + uploadCurveBuffer(); + } #endif dirtyBuffers = false; } @@ -1183,14 +1215,19 @@ class Font { #else glyphsUniformLocation = glGetUniformLocation(program, "glyphs"); glUniform1i(glyphsUniformLocation, glyphBufferUnit); - curvesUniformLocation = glGetUniformLocation(program, "curves"); - glUniform1i(curvesUniformLocation, curveBufferUnit); glActiveTexture(GL_TEXTURE0 + glyphBufferUnit); glBindTexture(GL_TEXTURE_2D, glyphBufferName); - glActiveTexture(GL_TEXTURE0 + curveBufferUnit); - glBindTexture(GL_TEXTURE_2D_ARRAY, curveBufferName); + if(BUFFER_TARGET_TYPE == GL_TEXTURE_2D_ARRAY){ + curvesUniformLocation = glGetUniformLocation(program, "curves"); + glUniform1i(curvesUniformLocation, curveBufferUnit); + glActiveTexture(GL_TEXTURE0 + curveBufferUnit); + glBindTexture(GL_TEXTURE_2D_ARRAY, curveBufferName); + }else if(BUFFER_TARGET_TYPE == GL_UNIFORM_BUFFER){ + glBindBuffer(GL_UNIFORM_BUFFER, curveBufferName); + glUniformBlockBinding(program, ubo_curves_index, 0); + } glActiveTexture(GL_TEXTURE0); #endif @@ -1428,7 +1465,8 @@ class Font { std::vector bufferGlyphs; std::vector bufferCurves; - std::vector bufferCurveLayerPointers = {0}; + std::vector shaderBufferCurves; // ubo + std::vector bufferCurveLayerPointers = {0}; // t2a std::unordered_map glyphs; static GLint gl_max_texture_size; @@ -1446,6 +1484,7 @@ class Font { static std::shared_ptr loadFont(FT_Library & library, const std::string & filename, GLint bufferTargetType, + GLuint shaderProgram, float worldSize = 1.0f, bool hinting = false){ std::string error; @@ -1457,16 +1496,17 @@ static std::shared_ptr loadFont(FT_Library & library, std::cout << "ofxGPUFont::font.hpp[" << __LINE__ << "] FT loaded " << filename << ": " << error << std::endl; } - return std::make_shared (face, worldSize, hinting, bufferTargetType); + return std::make_shared (face, worldSize, hinting, bufferTargetType, shaderProgram); } static void initializeFont(FT_Library & library, const std::string & filename, shared_ptr & mainFont, + GLuint shaderProgram, GLint bufferTargetType = GL_TEXTURE_2D_ARRAY){ cout << "initializeFont" << endl; - auto font = loadFont(library, filename, bufferTargetType); + auto font = loadFont(library, filename, bufferTargetType, shaderProgram); if(!font){ return; } diff --git a/src/gpufont/shader_catalog.cpp b/src/gpufont/shader_catalog.cpp index 5aedc5c..e72e5da 100644 --- a/src/gpufont/shader_catalog.cpp +++ b/src/gpufont/shader_catalog.cpp @@ -208,7 +208,7 @@ class ShaderCatalog::Impl { if(error != ""){ std::cerr << "[shader] " << error << std::endl; }else{ - std::cerr << "[shader] reloaded " << name << std::endl; + //std::cerr << "[shader] reloaded " << name << std::endl; glDeleteProgram(it->second->program); it->second->program = program; } diff --git a/src/gpufont/shader_catalog.hpp b/src/gpufont/shader_catalog.hpp index 903dbd6..7dd31af 100644 --- a/src/gpufont/shader_catalog.hpp +++ b/src/gpufont/shader_catalog.hpp @@ -13,7 +13,7 @@ namespace ofxGPUFont { class ShaderCatalog { public: struct Entry { - unsigned int program; + GLuint program; Entry() : program(0){ }