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 | |