Showing the changes to src/charvdev.cc (1 of 5 files). Show all
Unified · Split
| @@ -0,0 +1,316 @@ | |||
| 1 | /* This file is part of Zutty. | ||
| 2 | * Copyright (C) 2020 Tom Szilagyi | ||
| 3 | * | ||
| 4 | * This program is free software: you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License as published by | ||
| 6 | * the Free Software Foundation, either version 3 of the License, or | ||
| 7 | * (at your option) any later version. | ||
| 8 | * | ||
| 9 | * See the file LICENSE for the full license. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include "charvdev.h" | ||
| 13 | |||
| 14 | #include <iostream> | ||
| 15 | |||
| 16 | namespace { | ||
| 17 | |||
| 18 | static const char *computeShaderSource = R"(#version 310 es | ||
| 19 | |||
| 20 | layout (local_size_x = 1, local_size_y = 1) in; | ||
| 21 | layout (rgba32f, binding = 0) writeonly lowp uniform image2D imgOut; | ||
| 22 | layout (binding = 1) uniform lowp sampler2D atlas; | ||
| 23 | layout (binding = 2) uniform lowp sampler2D text; | ||
| 24 | uniform lowp ivec2 glyphPixels; | ||
| 25 | |||
| 26 | void main () { | ||
| 27 | vec3 color = vec3 (0.85, 0.85, 0.85); | ||
| 28 | |||
| 29 | ivec2 charPos = ivec2 (gl_GlobalInvocationID.xy); | ||
| 30 | ivec4 charData = ivec4 (texelFetch (text, charPos, 0) * 256.0); | ||
| 31 | ivec2 charCode = charData.xy; // .zw: attrs, colors | ||
| 32 | ivec2 atlasPos = charCode; // TODO lookup in atlas mapping | ||
| 33 | |||
| 34 | for (int j = 0; j < glyphPixels.x; j++) { | ||
| 35 | for (int k = 0; k < glyphPixels.y; k++) { | ||
| 36 | ivec2 txCoords = atlasPos * glyphPixels + ivec2 (j, k); | ||
| 37 | vec4 pixel = vec4 (color * texelFetch (atlas, txCoords, 0).r, 1.0); | ||
| 38 | ivec2 pxCoords = charPos * glyphPixels + ivec2 (j, k); | ||
| 39 | imageStore (imgOut, pxCoords, pixel); | ||
| 40 | } | ||
| 41 | } | ||
| 42 | } | ||
| 43 | )"; | ||
| 44 | |||
| 45 | static const char *vertexShaderSource = R"(#version 310 es | ||
| 46 | |||
| 47 | layout (location = 0) in vec2 pos; | ||
| 48 | layout (location = 1) in lowp vec2 vertexTexCoord; | ||
| 49 | |||
| 50 | out vec2 texCoord; | ||
| 51 | |||
| 52 | void main () { | ||
| 53 | texCoord = vertexTexCoord; | ||
| 54 | gl_Position = vec4 (pos, 0.0, 1.0); | ||
| 55 | } | ||
| 56 | )"; | ||
| 57 | |||
| 58 | static const char *fragmentShaderSource = R"(#version 310 es | ||
| 59 | |||
| 60 | in highp vec2 texCoord; | ||
| 61 | |||
| 62 | layout (rgba32f, binding = 0) readonly lowp uniform image2D imgOut; | ||
| 63 | |||
| 64 | uniform highp vec2 viewPixels; | ||
| 65 | |||
| 66 | layout (location = 0) out lowp vec4 outColor; | ||
| 67 | |||
| 68 | void main () { | ||
| 69 | outColor = imageLoad (imgOut, ivec2 (texCoord * viewPixels)); | ||
| 70 | } | ||
| 71 | )"; | ||
| 72 | |||
| 73 | |||
| 74 | GLuint | ||
| 75 | createShader (GLuint type, const char* src, const char* name) | ||
| 76 | { | ||
| 77 | GLint stat; | ||
| 78 | GLuint shader = glCreateShader (type); | ||
| 79 | glShaderSource (shader, 1, &src, nullptr); | ||
| 80 | glCompileShader (shader); | ||
| 81 | glGetShaderiv (shader, GL_COMPILE_STATUS, &stat); | ||
| 82 | if (!stat) { | ||
| 83 | char log[1000]; | ||
| 84 | GLsizei len; | ||
| 85 | glGetShaderInfoLog (shader, 1000, &len, log); | ||
| 86 | std::cerr << "Error compiling " << name << " shader:\n" | ||
| 87 | << log << std::endl; | ||
| 88 | exit (1); | ||
| 89 | } | ||
| 90 | return shader; | ||
| 91 | } | ||
| 92 | |||
| 93 | void | ||
| 94 | linkProgram (GLuint program, const char* name) | ||
| 95 | { | ||
| 96 | GLint stat; | ||
| 97 | glLinkProgram (program); | ||
| 98 | glGetProgramiv (program, GL_LINK_STATUS, &stat); | ||
| 99 | if (!stat) { | ||
| 100 | char log[1000]; | ||
| 101 | GLsizei len; | ||
| 102 | glGetProgramInfoLog (program, 1000, &len, log); | ||
| 103 | std::cerr << "Error: linking " << name << " program:\n" | ||
| 104 | << log << std::endl; | ||
| 105 | exit (1); | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | void | ||
| 110 | setupTexture (GLuint target, GLuint& texture) | ||
| 111 | { | ||
| 112 | if (texture) | ||
| 113 | { | ||
| 114 | glDeleteTextures (1, &texture); | ||
| 115 | } | ||
| 116 | glGenTextures (1, &texture); | ||
| 117 | glActiveTexture (target); | ||
| 118 | glBindTexture (GL_TEXTURE_2D, texture); | ||
| 119 | glPixelStorei (GL_UNPACK_ALIGNMENT, 1); | ||
| 120 | glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | ||
| 121 | glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||
| 122 | glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||
| 123 | glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | namespace zutty { | ||
| 128 | |||
| 129 | CharVdev::CharVdev (const std::string& priFontPath, | ||
| 130 | const std::string& altFontPath) | ||
| 131 | : fnt (priFontPath) | ||
| 132 | { | ||
| 133 | createShaders (); | ||
| 134 | |||
| 135 | /* | ||
| 136 | * Setup draw program | ||
| 137 | */ | ||
| 138 | glUseProgram (P_draw); | ||
| 139 | |||
| 140 | glDisable (GL_CULL_FACE); | ||
| 141 | glDisable (GL_DEPTH_TEST); | ||
| 142 | glEnable (GL_BLEND); | ||
| 143 | glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | ||
| 144 | glCheckError (); | ||
| 145 | |||
| 146 | static const GLfloat verts[4][2] = { | ||
| 147 | { -1, 1 }, | ||
| 148 | { 1, 1 }, | ||
| 149 | { -1, -1 }, | ||
| 150 | { 1, -1 } | ||
| 151 | }; | ||
| 152 | static const GLfloat texCoords[4][2] = { | ||
| 153 | { 0, 0 }, | ||
| 154 | { 1, 0 }, | ||
| 155 | { 0, 1 }, | ||
| 156 | { 1, 1 } | ||
| 157 | }; | ||
| 158 | glVertexAttribPointer (A_pos, 2, GL_FLOAT, GL_FALSE, 0, verts); | ||
| 159 | glVertexAttribPointer (A_vertexTexCoord, 2, GL_FLOAT, GL_FALSE, 0, | ||
| 160 | texCoords); | ||
| 161 | glCheckError (); | ||
| 162 | |||
| 163 | /* | ||
| 164 | * Setup compute program | ||
| 165 | */ | ||
| 166 | glUseProgram (P_compute); | ||
| 167 | glUniform2i (compU_glyphPixels, fnt.getPx (), fnt.getPy ()); | ||
| 168 | glCheckError (); | ||
| 169 | |||
| 170 | setupTexture (GL_TEXTURE1, T_atlas); | ||
| 171 | glTexImage2D (GL_TEXTURE_2D, 0, GL_RED, | ||
| 172 | fnt.getPx () * fnt.getNx (), fnt.getPy () * fnt.getNy (), | ||
| 173 | 0, GL_RED, GL_UNSIGNED_BYTE, fnt.getAtlas ()); | ||
| 174 | glCheckError (); | ||
| 175 | } | ||
| 176 | |||
| 177 | CharVdev::~CharVdev () | ||
| 178 | { | ||
| 179 | } | ||
| 180 | |||
| 181 | void | ||
| 182 | CharVdev::resize (uint16_t pxWidth_, uint16_t pxHeight_) | ||
| 183 | { | ||
| 184 | if (pxWidth == pxWidth_ && pxHeight == pxHeight_) | ||
| 185 | return; | ||
| 186 | |||
| 187 | pxWidth = pxWidth_; | ||
| 188 | pxHeight = pxHeight_; | ||
| 189 | nCols = pxWidth / fnt.getPx (); | ||
| 190 | nRows = pxHeight / fnt.getPy (); | ||
| 191 | |||
| 192 | text_data = std::unique_ptr <uint8_t []> (new uint8_t [4 * nCols * nRows]); | ||
| 193 | memset (text_data.get (), 0, 4 * nCols * nRows); | ||
| 194 | for (uint16_t j = 0; j < nRows; ++j) | ||
| 195 | { | ||
| 196 | for (uint16_t k = 0; k < nCols; ++k) | ||
| 197 | { | ||
| 198 | text_data[4 * (j * nCols + k)] = k > 255 ? 0 : k; | ||
| 199 | text_data[4 * (j * nCols + k) + 1] = k > 255 ? 0 : j; | ||
| 200 | text_data[4 * (j * nCols + k) + 2] = 0; // color and | ||
| 201 | text_data[4 * (j * nCols + k) + 3] = 0; // attributes | ||
| 202 | } | ||
| 203 | } | ||
| 204 | |||
| 205 | std::cout << "resize to " << pxWidth << " x " << pxHeight | ||
| 206 | << " pixels, " << nCols << " x " << nRows << " chars" | ||
| 207 | << std::endl; | ||
| 208 | |||
| 209 | GLint viewWidth = nCols * fnt.getPx (); | ||
| 210 | GLint viewHeight = nRows * fnt.getPy (); | ||
| 211 | glViewport (0, pxHeight - viewHeight, viewWidth, viewHeight); | ||
| 212 | |||
| 213 | glUseProgram (P_draw); | ||
| 214 | |||
| 215 | glUniform2f (drawU_viewPixels, (GLfloat)viewWidth, (GLfloat)viewHeight); | ||
| 216 | |||
| 217 | glUseProgram (P_compute); | ||
| 218 | |||
| 219 | setupTexture (GL_TEXTURE0, T_output); | ||
| 220 | glTexStorage2D (GL_TEXTURE_2D, 1, GL_RGBA32F, viewWidth, viewHeight); | ||
| 221 | glBindImageTexture (0, T_output, 0, GL_FALSE, 0, GL_WRITE_ONLY, | ||
| 222 | GL_RGBA32F); | ||
| 223 | glCheckError (); | ||
| 224 | |||
| 225 | setupTexture (GL_TEXTURE2, T_text); | ||
| 226 | glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, nCols, nRows, 0, GL_RGBA, | ||
| 227 | GL_UNSIGNED_BYTE, text_data.get ()); | ||
| 228 | glCheckError (); | ||
| 229 | } | ||
| 230 | |||
| 231 | void | ||
| 232 | CharVdev::draw () | ||
| 233 | { | ||
| 234 | { | ||
| 235 | uint16_t cRow = nRows - 1; | ||
| 236 | uint16_t cCol = nCols - 1; | ||
| 237 | uint32_t cnt = ++draw_count; | ||
| 238 | while (cnt) | ||
| 239 | { | ||
| 240 | uint32_t digit = (cnt % 10) + 18; | ||
| 241 | text_data[4 * (cRow * nCols + cCol)] = digit; | ||
| 242 | text_data[4 * (cRow * nCols + cCol) + 1] = 0; | ||
| 243 | cnt /= 10; | ||
| 244 | --cCol; | ||
| 245 | } | ||
| 246 | } | ||
| 247 | |||
| 248 | glUseProgram (P_compute); | ||
| 249 | glActiveTexture (GL_TEXTURE0); | ||
| 250 | glBindTexture (GL_TEXTURE_2D, T_output); | ||
| 251 | glActiveTexture (GL_TEXTURE1); | ||
| 252 | glBindTexture (GL_TEXTURE_2D, T_atlas); | ||
| 253 | glActiveTexture (GL_TEXTURE2); | ||
| 254 | glBindTexture (GL_TEXTURE_2D, T_text); | ||
| 255 | glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, nCols, nRows, GL_RGBA, | ||
| 256 | GL_UNSIGNED_BYTE, text_data.get ()); | ||
| 257 | glCheckError (); | ||
| 258 | |||
| 259 | glDispatchCompute (nCols, nRows, 1); | ||
| 260 | glMemoryBarrier (GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); | ||
| 261 | glCheckError (); | ||
| 262 | |||
| 263 | glUseProgram (P_draw); | ||
| 264 | glClear (GL_COLOR_BUFFER_BIT); | ||
| 265 | |||
| 266 | glActiveTexture (GL_TEXTURE0); | ||
| 267 | glBindTexture (GL_TEXTURE_2D, T_output); | ||
| 268 | |||
| 269 | glEnableVertexAttribArray (A_pos); | ||
| 270 | glEnableVertexAttribArray (A_vertexTexCoord); | ||
| 271 | glDrawArrays (GL_TRIANGLE_STRIP, 0, 4); | ||
| 272 | } | ||
| 273 | |||
| 274 | // private methods | ||
| 275 | |||
| 276 | void | ||
| 277 | CharVdev::createShaders () | ||
| 278 | { | ||
| 279 | GLuint S_compute, S_fragment, S_vertex; | ||
| 280 | |||
| 281 | S_compute = createShader (GL_COMPUTE_SHADER, computeShaderSource, | ||
| 282 | "compute"); | ||
| 283 | S_fragment = createShader (GL_FRAGMENT_SHADER, fragmentShaderSource, | ||
| 284 | "fragment"); | ||
| 285 | S_vertex = createShader (GL_VERTEX_SHADER, vertexShaderSource, | ||
| 286 | "vertex"); | ||
| 287 | |||
| 288 | P_compute = glCreateProgram (); | ||
| 289 | glAttachShader (P_compute, S_compute); | ||
| 290 | linkProgram (P_compute, "compute"); | ||
| 291 | glUseProgram (P_compute); | ||
| 292 | |||
| 293 | compU_glyphPixels = glGetUniformLocation (P_compute, "glyphPixels"); | ||
| 294 | |||
| 295 | std::cout << "compute program:" | ||
| 296 | << "\n uniform glyphPixels at " << compU_glyphPixels | ||
| 297 | << std::endl; | ||
| 298 | |||
| 299 | P_draw = glCreateProgram (); | ||
| 300 | glAttachShader (P_draw, S_fragment); | ||
| 301 | glAttachShader (P_draw, S_vertex); | ||
| 302 | linkProgram (P_draw, "draw"); | ||
| 303 | glUseProgram (P_draw); | ||
| 304 | |||
| 305 | A_pos = glGetAttribLocation (P_draw, "pos"); | ||
| 306 | A_vertexTexCoord = glGetAttribLocation (P_draw, "vertexTexCoord"); | ||
| 307 | drawU_viewPixels = glGetUniformLocation (P_draw, "viewPixels"); | ||
| 308 | |||
| 309 | std::cout << "draw program:" | ||
| 310 | << "\n attrib pos at " << A_pos | ||
| 311 | << "\n attrib vertexTexCoord at " << A_vertexTexCoord | ||
| 312 | << "\n uniform viewPixels at " << drawU_viewPixels | ||
| 313 | << std::endl; | ||
| 314 | } | ||
| 315 | |||
| 316 | } // namespace zutty | ||