vtgl

terminal emulator implemented in OpenGL
git clone anongit@rnpnr.xyz:vtgl.git
Log | Files | Refs | Feed | LICENSE

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, &current, 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 }