Optimize text rendering for long strings
This commit is contained in:
parent
678414f014
commit
3b78739efd
3 changed files with 71 additions and 56 deletions
85
main.cpp
85
main.cpp
|
|
@ -76,7 +76,7 @@ struct debug_clock_t
|
||||||
#define TIME_BLOCK(name) debug_clock_t _name_##timer(#name, __COUNTER__);
|
#define TIME_BLOCK(name) debug_clock_t _name_##timer(#name, __COUNTER__);
|
||||||
|
|
||||||
struct charglyph_t {
|
struct charglyph_t {
|
||||||
uint32_t tex; // handle to glyph texture
|
int32_t tex; // index of character in tex array
|
||||||
glm::ivec2 size; // 2d dimensions
|
glm::ivec2 size; // 2d dimensions
|
||||||
glm::ivec2 bearing; // offset from baseline to left/top of glyph
|
glm::ivec2 bearing; // offset from baseline to left/top of glyph
|
||||||
uint32_t advance; // offset to next glyph
|
uint32_t advance; // offset to next glyph
|
||||||
|
|
@ -109,10 +109,16 @@ struct selection_t {
|
||||||
int32_t nselected;
|
int32_t nselected;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const int32_t ARRAY_LIMIT = 400;
|
||||||
|
|
||||||
charglyph_t glyphmap[128];
|
charglyph_t glyphmap[128];
|
||||||
uint32_t text_program;
|
uint32_t text_program;
|
||||||
uint32_t textvao, textvbo;
|
uint32_t textvao, textvbo;
|
||||||
uint32_t hex_program;
|
uint32_t hex_program;
|
||||||
|
uint32_t texture_array;
|
||||||
|
glm::mat4 transforms[ARRAY_LIMIT];
|
||||||
|
int32_t letter_map[ARRAY_LIMIT];
|
||||||
|
|
||||||
|
|
||||||
// error processing
|
// error processing
|
||||||
enum err_type {
|
enum err_type {
|
||||||
|
|
@ -238,16 +244,24 @@ void render_text(const char *text, float x, float y, float scale, glm::vec3 colo
|
||||||
{
|
{
|
||||||
// TODO: Group draw calls together instead of doing 1 draw call/character. Use an atlas with GL_TEXTURE_ARRAY
|
// TODO: Group draw calls together instead of doing 1 draw call/character. Use an atlas with GL_TEXTURE_ARRAY
|
||||||
// Take tips from https://www.youtube.com/watch?v=S0PyZKX4lyI
|
// Take tips from https://www.youtube.com/watch?v=S0PyZKX4lyI
|
||||||
|
scale *= 48.0f/256;
|
||||||
|
|
||||||
// activate corresponding render state
|
// activate corresponding render state
|
||||||
glUseProgram(text_program);
|
glUseProgram(text_program);
|
||||||
glUniform3f(glGetUniformLocation(text_program, "text_color"), color.x, color.y, color.z);
|
glUniform3f(glGetUniformLocation(text_program, "text_color"), color.x, color.y, color.z);
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_2D_ARRAY, texture_array);
|
||||||
glBindVertexArray(textvao);
|
glBindVertexArray(textvao);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, textvbo);
|
||||||
|
|
||||||
|
int idx = 0;
|
||||||
// iterate through 128 ASCI characters
|
// iterate through 128 ASCI characters
|
||||||
for (const char *p = text; *p != '\0'; ++p)
|
for (const char *p = text; *p != '\0'; ++p)
|
||||||
{
|
{
|
||||||
|
// Avoid drawing more than 400 chars at a time
|
||||||
|
if (idx == ARRAY_LIMIT - 1)
|
||||||
|
break;
|
||||||
|
|
||||||
char c = *p;
|
char c = *p;
|
||||||
charglyph_t charglyph = glyphmap[(size_t)c];
|
charglyph_t charglyph = glyphmap[(size_t)c];
|
||||||
|
|
||||||
|
|
@ -258,31 +272,19 @@ void render_text(const char *text, float x, float y, float scale, glm::vec3 colo
|
||||||
}
|
}
|
||||||
|
|
||||||
float xpos = x + charglyph.bearing.x * scale;
|
float xpos = x + charglyph.bearing.x * scale;
|
||||||
float ypos = y - (charglyph.size.y - charglyph.bearing.y) * scale;
|
float ypos = y - (256 - charglyph.bearing.y) * scale;
|
||||||
|
|
||||||
glm::mat4 transform = glm::translate(glm::mat4(1.0f), glm::vec3(xpos, ypos, 0)) * glm::scale(glm::mat4(1.0f), glm::vec3(charglyph.size.x * scale, charglyph.size.y * scale, 0));
|
transforms[idx] = glm::translate(glm::mat4(1.0f), glm::vec3(xpos, ypos, 0)) * glm::scale(glm::mat4(1.0f), glm::vec3(256 * scale, 256 * scale, 0));
|
||||||
glUniformMatrix4fv(glGetUniformLocation(text_program, "transform"), 1, GL_FALSE, &transform[0][0]);
|
letter_map[idx] = charglyph.tex;
|
||||||
|
|
||||||
// update VBO for each character
|
|
||||||
// float vertices[4][4] = {
|
|
||||||
// { xpos, ypos + h, 0.0f, 0.0f },
|
|
||||||
// { xpos, ypos, 0.0f, 1.0f },
|
|
||||||
// { xpos + w, ypos, 1.0f, 1.0f },
|
|
||||||
// { xpos + w, ypos + h, 1.0f, 0.0f }
|
|
||||||
// };
|
|
||||||
// render glyph texture over quad
|
|
||||||
glBindTexture(GL_TEXTURE_2D, charglyph.tex);
|
|
||||||
// update content of VBO memory
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, textvbo);
|
|
||||||
//glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
|
|
||||||
|
|
||||||
// render quad
|
|
||||||
glEnable(GL_CULL_FACE);
|
|
||||||
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, 1);
|
|
||||||
glDisable(GL_CULL_FACE);
|
|
||||||
// now advance cursors for next glyph (note that advance is number of 1/64 pixels)
|
// now advance cursors for next glyph (note that advance is number of 1/64 pixels)
|
||||||
x += (charglyph.advance >> 6) * scale; // bitshift by 6 to get value in pixels (2^6 = 64)
|
x += (charglyph.advance >> 6) * scale; // bitshift by 6 to get value in pixels (2^6 = 64)
|
||||||
|
++idx;
|
||||||
}
|
}
|
||||||
|
glUniformMatrix4fv(glGetUniformLocation(text_program, "transforms"), idx, GL_FALSE, &transforms[0][0][0]);
|
||||||
|
glUniform1iv(glGetUniformLocation(text_program, "letter_map"), idx, &letter_map[0]);
|
||||||
|
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, idx);
|
||||||
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
glBindVertexArray(0);
|
glBindVertexArray(0);
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
@ -413,10 +415,15 @@ int main([[maybe_unused]]int argc, [[maybe_unused]]char **argv)
|
||||||
MAIN_ERROR(ERR_FONT_INIT);
|
MAIN_ERROR(ERR_FONT_INIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
FT_Set_Pixel_Sizes(face, 0, 48);
|
FT_Set_Pixel_Sizes(face, 256, 256);
|
||||||
|
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // disable byte-alignment restriction
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // disable byte-alignment restriction
|
||||||
|
|
||||||
|
glGenTextures(1, &texture_array);
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_2D_ARRAY, texture_array);
|
||||||
|
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_R8, 256, 256, 128, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
|
||||||
|
|
||||||
for (unsigned char c = 0; c < 128; c++)
|
for (unsigned char c = 0; c < 128; c++)
|
||||||
{
|
{
|
||||||
// load character glyph
|
// load character glyph
|
||||||
|
|
@ -424,29 +431,21 @@ int main([[maybe_unused]]int argc, [[maybe_unused]]char **argv)
|
||||||
{
|
{
|
||||||
MAIN_ERROR(ERR_CHAR_LOAD);
|
MAIN_ERROR(ERR_CHAR_LOAD);
|
||||||
}
|
}
|
||||||
// generate texture
|
|
||||||
uint32_t texture;
|
glTexSubImage3D(GL_TEXTURE_2D_ARRAY,0, 0, 0, int(c),
|
||||||
glGenTextures(1, &texture);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, texture);
|
|
||||||
glTexImage2D(
|
|
||||||
GL_TEXTURE_2D,
|
|
||||||
0,
|
|
||||||
GL_RED,
|
|
||||||
face->glyph->bitmap.width,
|
face->glyph->bitmap.width,
|
||||||
face->glyph->bitmap.rows,
|
face->glyph->bitmap.rows,
|
||||||
0,
|
1, GL_RED, GL_UNSIGNED_BYTE,
|
||||||
GL_RED,
|
face->glyph->bitmap.buffer);
|
||||||
GL_UNSIGNED_BYTE,
|
|
||||||
face->glyph->bitmap.buffer
|
|
||||||
);
|
|
||||||
// set texture options
|
// set texture options
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
// now store character for later use
|
// now store character for later use
|
||||||
charglyph_t charglyph = {
|
charglyph_t charglyph = {
|
||||||
texture,
|
int(c),
|
||||||
glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows),
|
glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows),
|
||||||
glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top),
|
glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top),
|
||||||
(uint32_t)face->glyph->advance.x
|
(uint32_t)face->glyph->advance.x
|
||||||
|
|
@ -460,6 +459,12 @@ int main([[maybe_unused]]int argc, [[maybe_unused]]char **argv)
|
||||||
// text initialization
|
// text initialization
|
||||||
text_program = make_gl_program("shaders/text.vert", "shaders/text.frag");
|
text_program = make_gl_program("shaders/text.vert", "shaders/text.frag");
|
||||||
|
|
||||||
|
for (int i = 0; i < ARRAY_LIMIT; ++i)
|
||||||
|
{
|
||||||
|
letter_map[i] = 0;
|
||||||
|
transforms[i] = glm::mat4(1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
static GLfloat vertex_data[] = {
|
static GLfloat vertex_data[] = {
|
||||||
0.0f, 1.0f,
|
0.0f, 1.0f,
|
||||||
0.0f, 0.0f,
|
0.0f, 0.0f,
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,18 @@
|
||||||
#version 330 core
|
#version 330 core
|
||||||
in vec2 texuv;
|
|
||||||
out vec4 color;
|
out vec4 color;
|
||||||
|
|
||||||
uniform sampler2D text;
|
in VS_OUT {
|
||||||
|
vec2 texuv;
|
||||||
|
flat int index;
|
||||||
|
} fs_in;
|
||||||
|
|
||||||
|
uniform sampler2DArray text;
|
||||||
|
uniform int letter_map[400];
|
||||||
uniform vec3 text_color;
|
uniform vec3 text_color;
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, texuv).r);
|
vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, vec3(fs_in.texuv.xy, letter_map[fs_in.index])).r);
|
||||||
color = vec4(text_color, 1.0) * sampled;
|
color = vec4(text_color, 1.0) * sampled;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
#version 330 core
|
#version 330 core
|
||||||
layout (location = 0) in vec2 vertex; // <vec2 pos>
|
layout (location = 0) in vec2 vertex; // <vec2 pos>
|
||||||
out vec2 texuv;
|
|
||||||
|
|
||||||
uniform mat4 transform;
|
out VS_OUT {
|
||||||
|
vec2 texuv;
|
||||||
|
flat int index;
|
||||||
|
} vs_out;
|
||||||
|
|
||||||
|
uniform mat4 transforms[400];
|
||||||
uniform mat4 projection;
|
uniform mat4 projection;
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
gl_Position = projection * transform * vec4(vertex.xy, 0.0, 1.0);
|
gl_Position = projection * transforms[gl_InstanceID] * vec4(vertex.xy, 0.0, 1.0);
|
||||||
texuv = vertex.xy;
|
vs_out.index = gl_InstanceID;
|
||||||
texuv.y=1.0f-texuv.y;
|
vs_out.texuv = vec2(vertex.x, 1.0f - vertex.y);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue