use a texture array instead of texture
This commit is contained in:
parent
351a9ac2ec
commit
7feb9630e6
2 changed files with 238 additions and 88 deletions
|
@ -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.
|
||||
|
|
|
@ -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/glext.h>
|
||||
#ifdef TARGET_OPENGLES
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#include <GLES/gl.h>
|
||||
#ifdef TARGET_OPENGLES
|
||||
//#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,6 +795,7 @@ class Font {
|
|||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
void uploadBuffers(){
|
||||
if(dirtyBuffers){
|
||||
//cout << "bufferGlyphs.size(): " << bufferGlyphs.size() << endl;
|
||||
//cout << "bufferCurves.size(): " << bufferCurves.size() << endl;
|
||||
#ifndef TARGET_OPENGLES
|
||||
|
@ -684,9 +810,7 @@ class Font {
|
|||
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();
|
||||
int bufferGlyphsSize = bufferGlyphs.size() * sizeof(BufferGlyph);
|
||||
if(!isPowerOfTwo(bufferGlyphsSize)){
|
||||
bufferGlyphsSize = calculateUpperPowerOfTwo(bufferGlyphsSize);
|
||||
}
|
||||
|
@ -713,35 +837,37 @@ class Font {
|
|||
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());
|
||||
//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;
|
||||
|
|
Loading…
Reference in a new issue