mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-07-15 05:51:26 -07:00
523 lines
16 KiB
C
523 lines
16 KiB
C
// Copyright (c) 2024 Epic Games Tools
|
|
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
|
|
|
////////////////////////////////
|
|
//~ rjf: OS Portion Includes
|
|
|
|
#if OS_WINDOWS
|
|
# include "render/opengl/win32/render_opengl_win32.c"
|
|
#elif OS_LINUX
|
|
# include "render/opengl/linux/render_opengl_linux.c"
|
|
#else
|
|
# error OS portion of OpenGL rendering backend not defined.
|
|
#endif
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Attribute Tables
|
|
|
|
global read_only R_OGL_Attribute r_ogl_rect_input_attributes[] =
|
|
{
|
|
{0, str8_lit_comp("c2v_dst_rect"), GL_FLOAT, 4},
|
|
{1, str8_lit_comp("c2v_src_rect"), GL_FLOAT, 4},
|
|
{2, str8_lit_comp("c2v_colors_0"), GL_FLOAT, 4},
|
|
{3, str8_lit_comp("c2v_colors_1"), GL_FLOAT, 4},
|
|
{4, str8_lit_comp("c2v_colors_2"), GL_FLOAT, 4},
|
|
{5, str8_lit_comp("c2v_colors_3"), GL_FLOAT, 4},
|
|
{6, str8_lit_comp("c2v_corner_radii"), GL_FLOAT, 4},
|
|
{7, str8_lit_comp("c2v_style"), GL_FLOAT, 4},
|
|
};
|
|
|
|
global read_only R_OGL_Attribute r_ogl_single_color_output_attributes[] =
|
|
{
|
|
{0, str8_lit_comp("final_color")},
|
|
};
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Generated Code
|
|
|
|
#include "render/opengl/generated/render_opengl.meta.c"
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Helpers
|
|
|
|
internal R_Handle
|
|
r_ogl_handle_from_tex2d(R_OGL_Tex2D *t)
|
|
{
|
|
R_Handle h = {(U64)t};
|
|
return h;
|
|
}
|
|
|
|
internal R_OGL_Tex2D *
|
|
r_ogl_tex2d_from_handle(R_Handle h)
|
|
{
|
|
R_OGL_Tex2D *t = (R_OGL_Tex2D *)h.u64[0];
|
|
return t;
|
|
}
|
|
|
|
internal R_OGL_FormatInfo
|
|
r_ogl_format_info_from_tex2dformat(R_Tex2DFormat fmt)
|
|
{
|
|
R_OGL_FormatInfo result;
|
|
result.internal_format = GL_RGBA;
|
|
result.format = GL_RGBA;
|
|
result.base_type = GL_UNSIGNED_BYTE;
|
|
// TODO(rjf)
|
|
return result;
|
|
}
|
|
|
|
internal GLuint
|
|
r_ogl_instance_buffer_from_size(U64 size)
|
|
{
|
|
GLuint buffer = r_ogl_state->scratch_buffer_64kb;
|
|
if(size > KB(64))
|
|
{
|
|
// rjf: build buffer
|
|
U64 flushed_buffer_size = size;
|
|
flushed_buffer_size += MB(1)-1;
|
|
flushed_buffer_size -= flushed_buffer_size%MB(1);
|
|
glGenBuffers(1, &buffer);
|
|
glBindBuffer(GL_ARRAY_BUFFER, buffer);
|
|
glBufferData(GL_ARRAY_BUFFER, flushed_buffer_size, 0, GL_DYNAMIC_DRAW);
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
// rjf: push buffer to flush list
|
|
R_OGL_FlushBuffer *n = push_array(r_ogl_state->buffer_flush_arena, R_OGL_FlushBuffer, 1);
|
|
n->id = buffer;
|
|
SLLQueuePush(r_ogl_state->first_buffer_to_flush, r_ogl_state->last_buffer_to_flush, n);
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
internal void
|
|
r_ogl_debug_message_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam)
|
|
{
|
|
raddbg_log("[OpenGL] %.*s\n", (int)length, message);
|
|
fprintf(stderr, "[OpenGL] %.*s\n", (int)length, message);
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ rjf: Backend Hooks
|
|
|
|
//- rjf: top-level layer initialization
|
|
|
|
r_hook void
|
|
r_init(CmdLine *cmdln)
|
|
{
|
|
//- rjf: do os-specific portion of work
|
|
r_ogl_os_init(cmdln);
|
|
|
|
//- rjf: top-level initialization
|
|
Arena *arena = arena_alloc();
|
|
r_ogl_state = push_array(arena, R_OGL_State, 1);
|
|
r_ogl_state->arena = arena;
|
|
|
|
//- rjf: load gl procedures
|
|
#define X(name, r, p) name = (name##_FunctionType *)r_ogl_os_load_procedure(#name);
|
|
R_OGL_ProcedureXList
|
|
#undef X
|
|
|
|
//- rjf: build all shaders
|
|
for EachEnumVal(R_OGL_ShaderKind, k)
|
|
{
|
|
// rjf: compile
|
|
struct {GLenum type; String8 *src; GLuint out; String8 errors;} stages[] =
|
|
{
|
|
{GL_VERTEX_SHADER, r_ogl_shader_kind_vshad_src_table[k]},
|
|
{GL_FRAGMENT_SHADER, r_ogl_shader_kind_pshad_src_table[k]},
|
|
};
|
|
for EachElement(idx, stages)
|
|
{
|
|
stages[idx].out = glCreateShader(stages[idx].type);
|
|
GLint src_size = stages[idx].src->size;
|
|
glShaderSource(stages[idx].out, 1, (char **)&stages[idx].src->str, &src_size);
|
|
glCompileShader(stages[idx].out);
|
|
GLint info_log_length = 0;
|
|
GLint status = 0;
|
|
glGetShaderiv(stages[idx].out, GL_COMPILE_STATUS, &status);
|
|
glGetShaderiv(stages[idx].out, GL_INFO_LOG_LENGTH, &info_log_length);
|
|
if(info_log_length != 0)
|
|
{
|
|
stages[idx].errors.str = push_array(r_ogl_state->arena, U8, info_log_length+1);
|
|
stages[idx].errors.size = info_log_length;
|
|
glGetShaderInfoLog(stages[idx].out, info_log_length, 0, (char *)stages[idx].errors.str);
|
|
}
|
|
raddbg_pin(text(stages[idx].errors.str));
|
|
}
|
|
|
|
// rjf: attach compilations to program
|
|
GLuint program = glCreateProgram();
|
|
for EachElement(idx, stages)
|
|
{
|
|
glAttachShader(program, stages[idx].out);
|
|
}
|
|
|
|
// rjf: bind inputs
|
|
R_OGL_AttributeArray inputs = r_ogl_shader_kind_input_attributes_table[k];
|
|
for EachIndex(idx, inputs.count)
|
|
{
|
|
glBindAttribLocation(program, inputs.v[idx].index, (char *)inputs.v[idx].name.str);
|
|
}
|
|
|
|
// rjf: bind outputs
|
|
R_OGL_AttributeArray outputs = r_ogl_shader_kind_output_attributes_table[k];
|
|
for EachIndex(idx, outputs.count)
|
|
{
|
|
glBindFragDataLocation(program, outputs.v[idx].index, (char *)outputs.v[idx].name.str);
|
|
}
|
|
|
|
// rjf: link / validate / store
|
|
glLinkProgram(program);
|
|
glValidateProgram(program);
|
|
r_ogl_state->shaders[k] = program;
|
|
}
|
|
|
|
//- rjf: set up built-in resources
|
|
glGenVertexArrays(1, &r_ogl_state->all_purpose_vao);
|
|
glGenBuffers(1, &r_ogl_state->scratch_buffer_64kb);
|
|
glBindBuffer(GL_ARRAY_BUFFER, r_ogl_state->scratch_buffer_64kb);
|
|
glBufferData(GL_ARRAY_BUFFER, KB(64), 0, GL_DYNAMIC_DRAW);
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
glGenTextures(1, &r_ogl_state->white_texture);
|
|
glBindTexture(GL_TEXTURE_2D, r_ogl_state->white_texture);
|
|
U32 white_pixel = 0xffffffff;
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &white_pixel);
|
|
glEnable(GL_FRAMEBUFFER_SRGB);
|
|
|
|
//- rjf: set up buffer flush state
|
|
r_ogl_state->buffer_flush_arena = arena_alloc();
|
|
|
|
//- rjf: set up debug callback
|
|
B32 debug_mode = cmd_line_has_flag(cmdln, str8_lit("opengl_debug"));
|
|
#if BUILD_DEBUG
|
|
debug_mode = 1;
|
|
#endif
|
|
if(debug_mode)
|
|
{
|
|
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
|
glDebugMessageCallback(r_ogl_debug_message_callback, 0);
|
|
}
|
|
}
|
|
|
|
//- rjf: window setup/teardown
|
|
|
|
r_hook R_Handle
|
|
r_window_equip(OS_Handle window)
|
|
{
|
|
R_Handle result = r_ogl_os_window_equip(window);
|
|
return result;
|
|
}
|
|
|
|
r_hook void
|
|
r_window_unequip(OS_Handle window, R_Handle window_equip)
|
|
{
|
|
r_ogl_os_window_unequip(window, window_equip);
|
|
}
|
|
|
|
//- rjf: textures
|
|
|
|
r_hook R_Handle
|
|
r_tex2d_alloc(R_ResourceKind kind, Vec2S32 size, R_Tex2DFormat format, void *data)
|
|
{
|
|
//- rjf: allocate texture record
|
|
R_OGL_Tex2D *tex2d = r_ogl_state->free_tex2d;
|
|
if(tex2d)
|
|
{
|
|
SLLStackPop(r_ogl_state->free_tex2d);
|
|
}
|
|
else
|
|
{
|
|
tex2d = push_array(r_ogl_state->arena, R_OGL_Tex2D, 1);
|
|
}
|
|
|
|
//- rjf: map kind/format -> gl counterparts
|
|
R_OGL_FormatInfo gl_fmt_info = r_ogl_format_info_from_tex2dformat(format);
|
|
|
|
//- rjf: allocate GL texture
|
|
{
|
|
glGenTextures(1, &tex2d->id);
|
|
glBindTexture(GL_TEXTURE_2D, tex2d->id);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, gl_fmt_info.internal_format, size.x, size.y, 0, gl_fmt_info.format, gl_fmt_info.base_type, data);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
}
|
|
|
|
//- rjf: fill
|
|
tex2d->resource_kind = kind;
|
|
tex2d->fmt = format;
|
|
tex2d->size = size;
|
|
|
|
//- rjf: bundle & return
|
|
R_Handle result = r_ogl_handle_from_tex2d(tex2d);
|
|
return result;
|
|
}
|
|
|
|
r_hook void
|
|
r_tex2d_release(R_Handle texture)
|
|
{
|
|
R_OGL_Tex2D *t = r_ogl_tex2d_from_handle(texture);
|
|
if(t != 0)
|
|
{
|
|
glDeleteTextures(1, &t->id);
|
|
SLLStackPush(r_ogl_state->free_tex2d, t);
|
|
}
|
|
}
|
|
|
|
r_hook R_ResourceKind
|
|
r_kind_from_tex2d(R_Handle texture)
|
|
{
|
|
R_ResourceKind result = R_ResourceKind_Static;
|
|
R_OGL_Tex2D *t = r_ogl_tex2d_from_handle(texture);
|
|
if(t)
|
|
{
|
|
result = t->resource_kind;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
r_hook Vec2S32
|
|
r_size_from_tex2d(R_Handle texture)
|
|
{
|
|
Vec2S32 result = {0, 0};
|
|
R_OGL_Tex2D *t = r_ogl_tex2d_from_handle(texture);
|
|
if(t)
|
|
{
|
|
result = t->size;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
r_hook R_Tex2DFormat
|
|
r_format_from_tex2d(R_Handle texture)
|
|
{
|
|
R_Tex2DFormat result = R_Tex2DFormat_RGBA8;
|
|
R_OGL_Tex2D *t = r_ogl_tex2d_from_handle(texture);
|
|
if(t)
|
|
{
|
|
result = t->fmt;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
r_hook void
|
|
r_fill_tex2d_region(R_Handle texture, Rng2S32 subrect, void *data)
|
|
{
|
|
R_OGL_Tex2D *t = r_ogl_tex2d_from_handle(texture);
|
|
if(t)
|
|
{
|
|
R_OGL_FormatInfo fmt_info = r_ogl_format_info_from_tex2dformat(t->fmt);
|
|
glBindTexture(GL_TEXTURE_2D, t->id);
|
|
Vec2S32 rect_size = dim_2s32(subrect);
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, subrect.x0, subrect.y0, rect_size.x, rect_size.y, fmt_info.format, fmt_info.base_type, data);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
}
|
|
}
|
|
|
|
//- rjf: buffers
|
|
|
|
r_hook R_Handle
|
|
r_buffer_alloc(R_ResourceKind kind, U64 size, void *data)
|
|
{
|
|
R_Handle result = {0};
|
|
return result;
|
|
}
|
|
|
|
r_hook void
|
|
r_buffer_release(R_Handle buffer)
|
|
{
|
|
// TODO(rjf)
|
|
}
|
|
|
|
//- rjf: frame markers
|
|
|
|
r_hook void
|
|
r_begin_frame(void)
|
|
{
|
|
// TODO(rjf)
|
|
}
|
|
|
|
r_hook void
|
|
r_end_frame(void)
|
|
{
|
|
// TODO(rjf)
|
|
}
|
|
|
|
r_hook void
|
|
r_window_begin_frame(OS_Handle os, R_Handle r)
|
|
{
|
|
r_ogl_os_select_window(os, r);
|
|
|
|
//- rjf: unpack window viewport info
|
|
Rng2F32 client_rect = os_client_rect_from_window(os);
|
|
Vec2F32 client_rect_dim = dim_2f32(client_rect);
|
|
|
|
//- rjf: clear and reset state
|
|
glClearColor(0, 0, 0, 0);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
glViewport(0, 0, (S32)client_rect_dim.x, (S32)client_rect_dim.y);
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
}
|
|
|
|
r_hook void
|
|
r_window_end_frame(OS_Handle os, R_Handle r)
|
|
{
|
|
for(R_OGL_FlushBuffer *flush_buffer = r_ogl_state->first_buffer_to_flush; flush_buffer != 0; flush_buffer = flush_buffer->next)
|
|
{
|
|
glDeleteBuffers(1, &flush_buffer->id);
|
|
}
|
|
arena_clear(r_ogl_state->buffer_flush_arena);
|
|
r_ogl_state->first_buffer_to_flush = r_ogl_state->last_buffer_to_flush = 0;
|
|
r_ogl_os_window_swap(os, r);
|
|
}
|
|
|
|
//- rjf: render pass submission
|
|
|
|
r_hook void
|
|
r_window_submit(OS_Handle window, R_Handle window_equip, R_PassList *passes)
|
|
{
|
|
Rng2F32 viewport_rect = os_client_rect_from_window(window);
|
|
Vec2F32 viewport_dim = dim_2f32(viewport_rect);
|
|
for(R_PassNode *pass_n = passes->first; pass_n != 0; pass_n = pass_n->next)
|
|
{
|
|
R_Pass *pass = &pass_n->v;
|
|
switch(pass->kind)
|
|
{
|
|
default:{}break;
|
|
|
|
////////////////////////
|
|
//- rjf: ui rendering pass
|
|
//
|
|
case R_PassKind_UI:
|
|
{
|
|
//- rjf: unpack params
|
|
R_PassParams_UI *params = pass->params_ui;
|
|
R_BatchGroup2DList *rect_batch_groups = ¶ms->rects;
|
|
|
|
//- rjf: draw each batch group
|
|
GLuint shader = r_ogl_state->shaders[R_OGL_ShaderKind_Rect];
|
|
glBindVertexArrayScope(r_ogl_state->all_purpose_vao) glUseProgramScope(shader)
|
|
{
|
|
for(R_BatchGroup2DNode *group_n = rect_batch_groups->first; group_n != 0; group_n = group_n->next)
|
|
{
|
|
R_BatchList *batches = &group_n->batches;
|
|
R_BatchGroup2DParams *group_params = &group_n->params;
|
|
|
|
//- rjf: unpack texture
|
|
R_Tex2DFormat texture_fmt = R_Tex2DFormat_RGBA8;
|
|
GLuint texture_id = r_ogl_state->white_texture;
|
|
{
|
|
R_OGL_Tex2D *tex = r_ogl_tex2d_from_handle(group_params->tex);
|
|
if(tex != 0)
|
|
{
|
|
texture_id = tex->id;
|
|
texture_fmt = tex->fmt;
|
|
}
|
|
}
|
|
|
|
//- rjf: get & fill buffer
|
|
GLuint buffer = r_ogl_instance_buffer_from_size(batches->byte_count);
|
|
{
|
|
glBindBuffer(GL_ARRAY_BUFFER, buffer);
|
|
U64 off = 0;
|
|
for(R_BatchNode *batch_n = batches->first; batch_n != 0; batch_n = batch_n->next)
|
|
{
|
|
glBufferSubData(GL_ARRAY_BUFFER, off, batch_n->v.byte_count, batch_n->v.v);
|
|
off += batch_n->v.byte_count;
|
|
}
|
|
}
|
|
|
|
//- rjf: bind input attributes
|
|
{
|
|
R_OGL_AttributeArray inputs = r_ogl_shader_kind_input_attributes_table[R_OGL_ShaderKind_Rect];
|
|
U64 off = 0;
|
|
for EachIndex(idx, inputs.count)
|
|
{
|
|
glEnableVertexAttribArray(inputs.v[idx].index);
|
|
glVertexAttribDivisor(inputs.v[idx].index, 1);
|
|
glVertexAttribPointer(inputs.v[idx].index, inputs.v[idx].count, inputs.v[idx].type, GL_FALSE, sizeof(R_Rect2DInst), (void *)(off));
|
|
// TODO(rjf): this is not correct if type != GL_FLOAT
|
|
off += inputs.v[idx].count*sizeof(F32);
|
|
}
|
|
}
|
|
|
|
//- rjf: bind texture
|
|
{
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, texture_id);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
|
switch(group_params->tex_sample_kind)
|
|
{
|
|
default:
|
|
case R_Tex2DSampleKind_Nearest:
|
|
{
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
}break;
|
|
case R_Tex2DSampleKind_Linear:
|
|
{
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
}break;
|
|
}
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
|
|
glUniform1i(glGetUniformLocation(shader, "u_tex_color"), 0);
|
|
}
|
|
|
|
//- rjf: upload misc. uniforms
|
|
{
|
|
Mat4x4F32 texture_sample_channel_map = r_sample_channel_map_from_tex2dformat(texture_fmt);
|
|
glUniformMatrix4fv(glGetUniformLocation(shader, "u_texture_sample_channel_map"), 1, 0, &texture_sample_channel_map.v[0][0]);
|
|
glUniform2f(glGetUniformLocation(shader, "u_viewport_size_px"), viewport_dim.x, viewport_dim.y);
|
|
glUniform1f(glGetUniformLocation(shader, "u_opacity"), 1.f - group_params->transparency);
|
|
}
|
|
|
|
//- rjf: set up scissor
|
|
if(group_params->clip.x0 != 0 ||
|
|
group_params->clip.x1 != 0 ||
|
|
group_params->clip.y0 != 0 ||
|
|
group_params->clip.y1 != 0)
|
|
{
|
|
Rng2F32 clip = group_params->clip;
|
|
glScissor(clip.x0, viewport_dim.y - clip.y1, (clip.x1-clip.x0) + 1, (clip.y1-clip.y0)+1);
|
|
glEnable(GL_SCISSOR_TEST);
|
|
}
|
|
|
|
//- rjf: draw
|
|
{
|
|
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, batches->byte_count / batches->bytes_per_inst);
|
|
}
|
|
|
|
//- rjf: unset scissor
|
|
glDisable(GL_SCISSOR_TEST);
|
|
}
|
|
}
|
|
}break;
|
|
|
|
////////////////////////
|
|
//- rjf: blur rendering pass
|
|
//
|
|
case R_PassKind_Blur:
|
|
{
|
|
R_PassParams_Blur *params = pass->params_blur;
|
|
GLuint shader = r_ogl_state->shaders[R_OGL_ShaderKind_Blur];
|
|
// TODO(rjf): glBindVertexArrayScope(r_ogl_state->all_purpose_vao) glUseProgramScope(shader)
|
|
{
|
|
// TODO(rjf)
|
|
}
|
|
}break;
|
|
|
|
|
|
////////////////////////
|
|
//- rjf: 3d geometry rendering pass
|
|
//
|
|
case R_PassKind_Geo3D:
|
|
{
|
|
//- rjf: unpack params
|
|
R_PassParams_Geo3D *params = pass->params_geo3d;
|
|
R_BatchGroup3DMap *mesh_group_map = ¶ms->mesh_batches;
|
|
// TODO(rjf)
|
|
}break;
|
|
}
|
|
}
|
|
}
|