vtgl_static.c (8655B)
1 /* See LICENSE for copyright details */ 2 #include "vtgl.h" 3 4 #define static_path_join(a, b) (a OS_PATH_SEPERATOR b) 5 6 #ifdef _DEBUG 7 /* NOTE(rnp): spins until render thread finishes its work */ 8 function void 9 vtgl_wait_complete_work(TerminalMemory *memory) 10 { 11 RenderThreadContext *ctx = &((Term *)memory->memory)->render_thread; 12 while (ctx->running); 13 } 14 #endif 15 16 function OS_FILE_WATCH_CALLBACK_FN(queue_shader_reload) 17 { 18 ShaderReloadContext *src = user_ctx; 19 WorkQueueWork *work = work_queue_push(src->work_queue); 20 if (work) { 21 work->kind = WQK_RELOAD_SHADER; 22 work->shader_reload_context = src; 23 work_queue_push_commit(src->work_queue); 24 } 25 } 26 27 function void 28 gl_debug_logger(u32 src, u32 type, u32 id, u32 lvl, i32 len, const char *msg, const void *data) 29 { 30 (void)src; (void)type; (void)id; 31 32 Stream *err = (Stream *)data; 33 stream_push_s8(err, s8("[GL Error ")); 34 switch (lvl) { 35 case GL_DEBUG_SEVERITY_HIGH: stream_push_s8(err, s8("HIGH]: ")); break; 36 case GL_DEBUG_SEVERITY_MEDIUM: stream_push_s8(err, s8("MEDIUM]: ")); break; 37 case GL_DEBUG_SEVERITY_LOW: stream_push_s8(err, s8("LOW]: ")); break; 38 case GL_DEBUG_SEVERITY_NOTIFICATION: stream_push_s8(err, s8("NOTIFICATION]: ")); break; 39 default: stream_push_s8(err, s8("INVALID]: ")); break; 40 } 41 stream_push_s8(err, (s8){.len = len, .data = (u8 *)msg}); 42 stream_push_byte(err, '\n'); 43 os_write_err_msg(stream_to_s8(err)); 44 stream_reset(err, 0); 45 } 46 47 function u32 48 gen_2D_texture(iv2 size, u32 format, u32 filter, u32 *rgba) 49 { 50 /* TODO: logging */ 51 u32 result; 52 glGenTextures(1, &result); 53 glBindTexture(GL_TEXTURE_2D, result); 54 glTexImage2D(GL_TEXTURE_2D, 0, format, size.w, size.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba); 55 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); 56 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); 57 return result; 58 } 59 60 function b32 61 try_wait_sync(i32 *sync, i32 timeout_ms, os_wait_on_value_fn *os_wait_on_value) 62 { 63 b32 result = 0; 64 for (;;) { 65 i32 current = atomic_load(sync); 66 if (current && atomic_cas(sync, ¤t, 0)) { 67 result = 1; 68 break; 69 } 70 if (!timeout_ms || !os_wait_on_value(sync, 0, timeout_ms)) 71 break; 72 } 73 return result; 74 } 75 76 77 function void 78 vtgl_render_thread_initialize(OS *os, RenderThreadContext *ctx) 79 { 80 os->gl_make_context_current(ctx->window); 81 82 ctx->error_stream = stream_alloc(&ctx->arena, KB(1)); 83 84 Arena a = ctx->arena; 85 86 glDebugMessageCallback(gl_debug_logger, &ctx->error_stream); 87 glEnable(GL_DEBUG_OUTPUT); 88 /* NOTE: shut up useless shader compilation statistics */ 89 glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, 90 GL_DEBUG_SEVERITY_NOTIFICATION, 91 0, 0, GL_FALSE); 92 93 glGenVertexArrays(1, &ctx->gl.vao); 94 glBindVertexArray(ctx->gl.vao); 95 96 glGenBuffers(ARRAY_COUNT(ctx->gl.vbos), ctx->gl.vbos); 97 98 RenderPushBuffer *rpb = 0; 99 /* NOTE: vertex position buffer */ 100 glBindBuffer(GL_ARRAY_BUFFER, ctx->gl.vbos[0]); 101 glBufferData(GL_ARRAY_BUFFER, sizeof(rpb->positions), 0, GL_DYNAMIC_DRAW); 102 glEnableVertexAttribArray(0); 103 glVertexAttribPointer(0, 2, GL_FLOAT, 0, 0, 0); 104 105 /* NOTE: vertex texture coordinate buffer */ 106 glBindBuffer(GL_ARRAY_BUFFER, ctx->gl.vbos[1]); 107 glBufferData(GL_ARRAY_BUFFER, sizeof(rpb->texture_coordinates), 0, GL_DYNAMIC_DRAW); 108 glEnableVertexAttribArray(1); 109 glVertexAttribPointer(1, 2, GL_FLOAT, 0, 0, 0); 110 111 /* NOTE: vertex colour buffer */ 112 glBindBuffer(GL_ARRAY_BUFFER, ctx->gl.vbos[2]); 113 glBufferData(GL_ARRAY_BUFFER, sizeof(rpb->colours), 0, GL_DYNAMIC_DRAW); 114 glEnableVertexAttribArray(2); 115 glVertexAttribPointer(2, 4, GL_FLOAT, 0, 0, 0); 116 117 /* NOTE: fill in element index buffer */ 118 i32 *element_indices = alloc(&a, i32, 6 * ARRAY_COUNT(rpb->positions)); 119 for (i32 i = 0, j = 0; i < 6 * ARRAY_COUNT(rpb->positions); i += 6, j++) { 120 element_indices[i + 0] = 4 * j; 121 element_indices[i + 1] = 4 * j + 1; 122 element_indices[i + 2] = 4 * j + 2; 123 element_indices[i + 3] = 4 * j; 124 element_indices[i + 4] = 4 * j + 2; 125 element_indices[i + 5] = 4 * j + 3; 126 } 127 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ctx->gl.vbos[4]); 128 glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * ARRAY_COUNT(rpb->positions) * sizeof(i32), 129 element_indices, GL_STATIC_DRAW); 130 131 ctx->gl.glyph_bitmap_tex = gen_2D_texture(ctx->gl.glyph_bitmap_dim, GL_RGBA, GL_NEAREST, 0); 132 /* NOTE: set pixel 0,0 to white (tile 0,0 is reserved). We can use this texture for 133 * drawing glyphs from the font cache or for drawing plain rectangles */ 134 u32 white = 0xFFFFFFFF; 135 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &white); 136 LABEL_GL_OBJECT(GL_TEXTURE, ctx->gl.glyph_bitmap_tex, s8("Glyph_Bitmap")); 137 138 glEnable(GL_BLEND); 139 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 140 141 glDisable(GL_DEPTH_TEST); 142 glDisable(GL_CULL_FACE); 143 144 /* NOTE: Generate an intermediate framebuffer for rendering to. This 145 * allows for additional post processing via a second shader stage */ 146 glGenFramebuffers(1, &ctx->gl.fb); 147 glBindFramebuffer(GL_FRAMEBUFFER, ctx->gl.fb); 148 149 ctx->gl.fb_tex_unit = 1; 150 glActiveTexture(GL_TEXTURE0 + ctx->gl.fb_tex_unit); 151 iv2 ws = ctx->monitor_size; 152 ctx->gl.fb_tex = gen_2D_texture(ws, GL_RGBA, GL_NEAREST, 0); 153 glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, ctx->gl.fb_tex, 0); 154 LABEL_GL_OBJECT(GL_TEXTURE, ctx->gl.fb_tex, s8("Framebuffer_Texture")); 155 156 glGenBuffers(1, &ctx->gl.render_shader_ubo); 157 glBindBuffer(GL_UNIFORM_BUFFER, ctx->gl.render_shader_ubo); 158 glBufferData(GL_UNIFORM_BUFFER, sizeof(ShaderParameters), 0, GL_DYNAMIC_DRAW); 159 glBindBufferBase(GL_UNIFORM_BUFFER, 0, ctx->gl.render_shader_ubo); 160 LABEL_GL_OBJECT(GL_BUFFER, ctx->gl.render_shader_ubo, s8("ShaderParameters")); 161 162 glActiveTexture(GL_TEXTURE0); 163 } 164 165 function OS_THREAD_ENTRY_POINT_FN(vtgl_render_thread_entry) 166 { 167 TerminalMemory *memory = (TerminalMemory *)context; 168 RenderThreadContext *ctx = &((Term *)memory->memory)->render_thread; 169 vtgl_render_thread_initialize(os, ctx); 170 for (;;) { 171 ctx->running = 0; 172 try_wait_sync(sync, -1, os->wait_on_value); 173 ctx->running = 1; 174 vtgl_render_frame(os, memory, ctx->input); 175 os->gl_swap_buffers(ctx->window); 176 } 177 return 0; 178 } 179 180 function iv2 181 vtgl_initialize(OS *os, TerminalMemory *memory, TerminalInput *input, void *window, iptr child, 182 iv2 requested_cells, iv2 monitor_size) 183 { 184 Term *t = (Term *)memory->memory; 185 Arena a = {.beg = (u8 *)(t + 1), .end = memory->memory + memory->memory_size}; 186 187 t->os = os; 188 189 t->cursor.state = CURSOR_NORMAL; 190 t->cursor.style.fg = g_colours.data[g_colours.fgidx]; 191 t->cursor.style.bg = g_colours.data[g_colours.bgidx]; 192 193 t->views[0].log = os->allocate_ring_buffer(BACKLOG_SIZE); 194 t->views[0].lines = line_buffer_alloc(&a, t->views[0].log.data, t->cursor.style, BACKLOG_LINES); 195 t->views[1].log = os->allocate_ring_buffer(ALT_BACKLOG_SIZE); 196 t->views[1].lines = line_buffer_alloc(&a, t->views[1].log.data, t->cursor.style, ALT_BACKLOG_LINES); 197 198 t->views[0].fb.backing_store = memory_block_from_arena(&a, MB(2)); 199 t->views[1].fb.backing_store = memory_block_from_arena(&a, MB(2)); 200 201 selection_clear(&t->selection); 202 203 RenderThreadContext *rtc = &t->render_thread; 204 rtc->arena = sub_arena(&a, MB(8)); 205 rtc->input = input; 206 rtc->window = (iptr)window; 207 rtc->monitor_size = monitor_size; 208 rtc->gl.glyph_bitmap_dim = monitor_size; 209 210 init_fonts(&rtc->fa, &a, rtc->gl.glyph_bitmap_dim); 211 212 v2 cs = fa_cell_size(&rtc->fa); 213 iv2 requested_size = { 214 .x = cs.x * requested_cells.x + 2 * g_term_margin.x, 215 .y = cs.y * requested_cells.y + 2 * g_term_margin.y, 216 }; 217 if (requested_cells.x < 0 || requested_cells.y < 0) 218 requested_size = (iv2){.x = -1, .y = -1}; 219 220 #define X(en, sn) do { \ 221 ShaderReloadContext *src = rtc->shader_reload_contexts + SID_ ##en; \ 222 src->work_queue = &rtc->work_queue; \ 223 src->label = s8(#en); \ 224 src->path = s8(static_path_join(SHADER_PATH_PREFIX, sn ".glsl")); \ 225 src->shader = SID_ ## en; \ 226 os->add_file_watch(src->path.data, queue_shader_reload, src); \ 227 } while (0); 228 FRAGMENT_SHADERS 229 #undef X 230 231 WorkQueueWork *work = work_queue_push(&rtc->work_queue); 232 if (work) { 233 work->kind = WQK_RELOAD_ALL_SHADERS; 234 work_queue_push_commit(&rtc->work_queue); 235 } 236 237 os->gl_make_context_current(0); 238 rtc->sync = os->spawn_thread(os, vtgl_render_thread_entry, "[render]", (iptr)memory); 239 240 t->size = (iv2){.x = 1, .y = 1}; 241 t->state |= TS_NEEDS_RESIZE; 242 243 t->error_stream = stream_alloc(&a, KB(256)); 244 t->saved_title = stream_alloc(&a, KB(16)); 245 t->arena_for_frame = a; 246 t->child = child; 247 248 return requested_size; 249 }