opengl render backend

This commit is contained in:
Ryan Fleury
2025-05-09 19:48:41 -07:00
parent 1b7a57914e
commit 48b8c41713
19 changed files with 1598 additions and 38 deletions
+1
View File
@@ -40,6 +40,7 @@ if "%~1"=="release" if "%~2"=="" echo [default mode, assuming `raddbg` build] &&
set auto_compile_flags=
if "%telemetry%"=="1" set auto_compile_flags=%auto_compile_flags% -DPROFILE_TELEMETRY=1 && echo [telemetry profiling enabled]
if "%asan%"=="1" set auto_compile_flags=%auto_compile_flags% -fsanitize=address && echo [asan enabled]
if "%opengl%"=="1" set auto_compile_flags=%auto_compile_flags% -DR_BACKEND=R_BACKEND_OPENGL && echo [opengl render backend]
:: --- Compile/Link Line Definitions ------------------------------------------
set cl_common= /I..\src\ /I..\local\ /nologo /FC /Z7
+1 -1
View File
@@ -46,7 +46,7 @@ load_paths =
commands =
{
//- rjf: [raddbg]
.f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
.f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg telemetry opengl", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
//- rjf: [textperf]
// .f1 = { .win = "raddbg_stable --ipc kill_all && build no_meta telemetry textperf && raddbg_stable --ipc bring_to_front && raddbg_stable --ipc run", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
+15
View File
@@ -386,6 +386,21 @@ derotate_4x4f32(Mat4x4F32 mat)
return mat;
}
internal Mat4x4F32
transpose_4x4f32(Mat4x4F32 mat)
{
Mat4x4F32 result =
{
{
mat.v[0][0], mat.v[1][0], mat.v[2][0], mat.v[3][0],
mat.v[0][1], mat.v[1][1], mat.v[2][1], mat.v[3][1],
mat.v[0][2], mat.v[1][2], mat.v[2][2], mat.v[3][2],
mat.v[0][3], mat.v[1][3], mat.v[2][3], mat.v[3][3],
}
};
return result;
}
////////////////////////////////
//~ rjf: Range Ops
+1
View File
@@ -545,6 +545,7 @@ internal Mat4x4F32 mul_4x4f32(Mat4x4F32 a, Mat4x4F32 b);
internal Mat4x4F32 scale_4x4f32(Mat4x4F32 m, F32 scale);
internal Mat4x4F32 inverse_4x4f32(Mat4x4F32 m);
internal Mat4x4F32 derotate_4x4f32(Mat4x4F32 mat);
internal Mat4x4F32 transpose_4x4f32(Mat4x4F32 mat);
////////////////////////////////
//~ rjf: Range Ops
@@ -279,8 +279,7 @@ str8_lit_comp(
" corner_radii_px.w,\n"
" corner_radii_px.z,\n"
" };\n"
" float2 cornercoords__pct = float2(\n"
" (c2v.vertex_id >> 1) ? 1.f : 0.f,\n"
" float2 cornercoords__pct = float2((c2v.vertex_id >> 1) ? 1.f : 0.f,\n"
" (c2v.vertex_id & 1) ? 0.f : 1.f);\n"
" \n"
" float2 vertex_position__pct = vertex_positions__scrn[c2v.vertex_id] / viewport_size;\n"
+17 -32
View File
@@ -669,11 +669,11 @@ r_tex2d_release(R_Handle handle)
ProfBeginFunction();
OS_MutexScopeW(r_d3d11_state->device_rw_mutex)
{
R_D3D11_Tex2D *texture = r_d3d11_tex2d_from_handle(handle);
if(texture != &r_d3d11_tex2d_nil)
{
SLLStackPush(r_d3d11_state->first_to_free_tex2d, texture);
}
R_D3D11_Tex2D *texture = r_d3d11_tex2d_from_handle(handle);
if(texture != &r_d3d11_tex2d_nil)
{
SLLStackPush(r_d3d11_state->first_to_free_tex2d, texture);
}
}
ProfEnd();
}
@@ -705,19 +705,19 @@ r_fill_tex2d_region(R_Handle handle, Rng2S32 subrect, void *data)
ProfBeginFunction();
OS_MutexScopeW(r_d3d11_state->device_rw_mutex)
{
R_D3D11_Tex2D *texture = r_d3d11_tex2d_from_handle(handle);
if(texture != &r_d3d11_tex2d_nil)
{
R_D3D11_Tex2D *texture = r_d3d11_tex2d_from_handle(handle);
if(texture != &r_d3d11_tex2d_nil)
{
Assert(texture->kind == R_ResourceKind_Dynamic && "only dynamic texture can update region");
U64 bytes_per_pixel = r_tex2d_format_bytes_per_pixel_table[texture->format];
Vec2S32 dim = v2s32(subrect.x1 - subrect.x0, subrect.y1 - subrect.y0);
Vec2S32 dim = v2s32(subrect.x1 - subrect.x0, subrect.y1 - subrect.y0);
D3D11_BOX dst_box =
{
(UINT)subrect.x0, (UINT)subrect.y0, 0,
(UINT)subrect.x1, (UINT)subrect.y1, 1,
};
r_d3d11_state->device_ctx->lpVtbl->UpdateSubresource(r_d3d11_state->device_ctx, (ID3D11Resource *)texture->texture, 0, &dst_box, data, dim.x*bytes_per_pixel, 0);
}
r_d3d11_state->device_ctx->lpVtbl->UpdateSubresource(r_d3d11_state->device_ctx, (ID3D11Resource *)texture->texture, 0, &dst_box, data, dim.x*bytes_per_pixel, 0);
}
}
ProfEnd();
}
@@ -823,10 +823,10 @@ r_end_frame(void)
tex = next)
{
next = tex->next;
tex->view->lpVtbl->Release(tex->view);
tex->texture->lpVtbl->Release(tex->texture);
tex->view = 0;
tex->texture = 0;
tex->view->lpVtbl->Release(tex->view);
tex->texture->lpVtbl->Release(tex->texture);
tex->view = 0;
tex->texture = 0;
tex->generation += 1;
SLLStackPush(r_d3d11_state->first_free_tex2d, tex);
}
@@ -1119,29 +1119,14 @@ r_window_submit(OS_Handle window, R_Handle window_equip, R_PassList *passes)
R_D3D11_Tex2D *texture = r_d3d11_tex2d_from_handle(texture_handle);
// rjf: get texture sample map matrix, based on format
Vec4F32 texture_sample_channel_map[] =
{
{1, 0, 0, 0},
{0, 1, 0, 0},
{0, 0, 1, 0},
{0, 0, 0, 1},
};
switch(texture->format)
{
default: break;
case R_Tex2DFormat_R8:
{
MemoryZeroArray(texture_sample_channel_map);
texture_sample_channel_map[0] = v4f32(1, 1, 1, 1);
}break;
}
Mat4x4F32 texture_sample_channel_map = r_sample_channel_map_from_tex2dformat(texture->format);
// rjf: upload uniforms
R_D3D11_Uniforms_Rect uniforms = {0};
{
uniforms.viewport_size = v2f32(resolution.x, resolution.y);
uniforms.opacity = 1-group_params->transparency;
MemoryCopyArray(uniforms.texture_sample_channel_map, texture_sample_channel_map);
uniforms.texture_sample_channel_map = texture_sample_channel_map;
uniforms.texture_t2d_size = v2f32(texture->size.x, texture->size.y);
uniforms.xform[0] = v4f32(group_params->xform.v[0][0], group_params->xform.v[1][0], group_params->xform.v[2][0], 0);
uniforms.xform[1] = v4f32(group_params->xform.v[0][1], group_params->xform.v[1][1], group_params->xform.v[2][1], 0);
+1 -1
View File
@@ -29,7 +29,7 @@ struct R_D3D11_Uniforms_Rect
Vec2F32 viewport_size;
F32 opacity;
F32 _padding0_;
Vec4F32 texture_sample_channel_map[4];
Mat4x4F32 texture_sample_channel_map;
Vec2F32 texture_t2d_size;
Vec2F32 translate;
Vec4F32 xform[3];
+1 -2
View File
@@ -277,8 +277,7 @@ vs_main(CPU2Vertex c2v)
corner_radii_px.w,
corner_radii_px.z,
};
float2 cornercoords__pct = float2(
(c2v.vertex_id >> 1) ? 1.f : 0.f,
float2 cornercoords__pct = float2((c2v.vertex_id >> 1) ? 1.f : 0.f,
(c2v.vertex_id & 1) ? 0.f : 1.f);
float2 vertex_position__pct = vertex_positions__scrn[c2v.vertex_id] / viewport_size;
@@ -0,0 +1,38 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
//- GENERATED CODE
C_LINKAGE_BEGIN
String8 r_ogl_shader_kind_name_table[2] =
{
str8_lit_comp("rect"),
str8_lit_comp("blur"),
};
String8 * r_ogl_shader_kind_vshad_src_table[2] =
{
&r_ogl_rect_vshad_src,
&r_ogl_blur_vshad_src,
};
String8 * r_ogl_shader_kind_pshad_src_table[2] =
{
&r_ogl_rect_pshad_src,
&r_ogl_blur_pshad_src,
};
R_OGL_AttributeArray r_ogl_shader_kind_input_attributes_table[2] =
{
{ r_ogl_rect_input_attributes, ArrayCount(r_ogl_rect_input_attributes) },
{ 0, },
};
R_OGL_AttributeArray r_ogl_shader_kind_output_attributes_table[2] =
{
{ r_ogl_single_color_output_attributes, ArrayCount(r_ogl_single_color_output_attributes) },
{ r_ogl_single_color_output_attributes, ArrayCount(r_ogl_single_color_output_attributes) },
};
C_LINKAGE_END
@@ -0,0 +1,276 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
//- GENERATED CODE
#ifndef RENDER_OPENGL_META_H
#define RENDER_OPENGL_META_H
typedef enum R_OGL_ShaderKind
{
R_OGL_ShaderKind_Rect,
R_OGL_ShaderKind_Blur,
R_OGL_ShaderKind_COUNT,
} R_OGL_ShaderKind;
C_LINKAGE_BEGIN
extern String8 r_ogl_shader_kind_name_table[2];
extern String8 * r_ogl_shader_kind_vshad_src_table[2];
extern String8 * r_ogl_shader_kind_pshad_src_table[2];
extern R_OGL_AttributeArray r_ogl_shader_kind_input_attributes_table[2];
extern R_OGL_AttributeArray r_ogl_shader_kind_output_attributes_table[2];
read_only global String8 r_ogl_rect_vshad_src =
str8_lit_comp(
""
"\n"
"#version 330 core\n"
"\n"
"in vec4 c2v_dst_rect;\n"
"in vec4 c2v_src_rect;\n"
"in vec4 c2v_colors_0;\n"
"in vec4 c2v_colors_1;\n"
"in vec4 c2v_colors_2;\n"
"in vec4 c2v_colors_3;\n"
"in vec4 c2v_corner_radii;\n"
"in vec4 c2v_style; // x: border_thickness_px, y: softness_px, z: omit_texture, w: unused\n"
"\n"
"out vec2 v2p_sdf_sample_pos;\n"
"out vec2 v2p_texcoord_pct;\n"
"out vec2 v2p_rect_half_size_px;\n"
"out vec4 v2p_tint;\n"
"out float v2p_corner_radius;\n"
"out float v2p_border_thickness;\n"
"out float v2p_softness;\n"
"out float v2p_omit_texture;\n"
"\n"
"uniform sampler2D u_tex_color;\n"
"uniform vec2 u_viewport_size_px;\n"
"\n"
"void main(void)\n"
"{\n"
" // rjf: constants\n"
" vec2 vertices[] = vec2[](vec2(-1, -1), vec2(-1, +1), vec2(+1, -1), vec2(+1, +1));\n"
" \n"
" // rjf: find dst position\n"
" vec2 dst_half_size = (c2v_dst_rect.zw - c2v_dst_rect.xy) / 2;\n"
" vec2 dst_center = (c2v_dst_rect.zw + c2v_dst_rect.xy) / 2;\n"
" vec2 dst_position = vertices[gl_VertexID] * dst_half_size + dst_center;\n"
" \n"
" // rjf: find src position\n"
" vec2 src_half_size = (c2v_src_rect.zw - c2v_src_rect.xy) / 2;\n"
" vec2 src_center = (c2v_src_rect.zw + c2v_src_rect.xy) / 2;\n"
" vec2 src_position = vertices[gl_VertexID] * src_half_size + src_center;\n"
" \n"
" // rjf: find color\n"
" vec4 colors[] = vec4[](c2v_colors_0, c2v_colors_1, c2v_colors_2, c2v_colors_3);\n"
" vec4 color = colors[gl_VertexID];\n"
" \n"
" // rjf: find corner radius\n"
" float corner_radii[] = float[](c2v_corner_radii.x, c2v_corner_radii.y, c2v_corner_radii.z, c2v_corner_radii.w);\n"
" float corner_radius = corner_radii[gl_VertexID];\n"
" \n"
" // rjf: fill outputs\n"
" vec2 dst_verts_pct = vec2(((gl_VertexID >> 1) != 1) ? 1.f : 0.f,\n"
" ((gl_VertexID & 1) != 0) ? 0.f : 1.f);\n"
" ivec2 u_tex_color_size_i = textureSize(u_tex_color, 0);\n"
" vec2 u_tex_color_size = vec2(float(u_tex_color_size_i.x), float(u_tex_color_size_i.y));\n"
" {\n"
" gl_Position = vec4(2 * dst_position.x / u_viewport_size_px.x - 1,\n"
" 2 * (1 - dst_position.y / u_viewport_size_px.y) - 1,\n"
" 0.0, 1.0);\n"
" v2p_sdf_sample_pos = (2.f * dst_verts_pct - 1.f) * dst_half_size;\n"
" v2p_texcoord_pct = src_position / u_tex_color_size;\n"
" v2p_rect_half_size_px = dst_half_size;\n"
" v2p_tint = color;\n"
" v2p_corner_radius = corner_radius;\n"
" v2p_border_thickness = c2v_style.x;\n"
" v2p_softness = c2v_style.y;\n"
" v2p_omit_texture = c2v_style.z;\n"
" }\n"
"}\n"
""
);
read_only global String8 r_ogl_rect_pshad_src =
str8_lit_comp(
""
"\n"
"#version 330 core\n"
"\n"
"in vec2 v2p_sdf_sample_pos;\n"
"in vec2 v2p_texcoord_pct;\n"
"in vec2 v2p_rect_half_size_px;\n"
"in vec4 v2p_tint;\n"
"in float v2p_corner_radius;\n"
"in float v2p_border_thickness;\n"
"in float v2p_softness;\n"
"in float v2p_omit_texture;\n"
"\n"
"out vec4 final_color;\n"
"\n"
"uniform float u_opacity;\n"
"uniform sampler2D u_tex_color;\n"
"uniform mat4 u_texture_sample_channel_map;\n"
"\n"
"float rect_sdf(vec2 sample_pos, vec2 rect_half_size, float r)\n"
"{\n"
" return length(max(abs(sample_pos) - rect_half_size + r, 0.0)) - r;\n"
"}\n"
"\n"
"float linear_from_srgb_f32(float x)\n"
"{\n"
" return x < 0.0404482362771082 ? x / 12.92 : pow((x + 0.055) / 1.055, 2.4);\n"
"}\n"
"\n"
"vec4 linear_from_srgba(vec4 v)\n"
"{\n"
" vec4 result = vec4(linear_from_srgb_f32(v.x),\n"
" linear_from_srgb_f32(v.y),\n"
" linear_from_srgb_f32(v.z),\n"
" v.w);\n"
" return result;\n"
"}\n"
"\n"
"void main(void)\n"
"{\n"
" // rjf: sample texture\n"
" vec4 albedo_sample = vec4(1, 1, 1, 1);\n"
" if(v2p_omit_texture < 1)\n"
" {\n"
" albedo_sample = u_texture_sample_channel_map * texture(u_tex_color, v2p_texcoord_pct);\n"
" albedo_sample = linear_from_srgba(albedo_sample);\n"
" }\n"
" \n"
" // rjf: sample for borders\n"
" float border_sdf_t = 1;\n"
" if(v2p_border_thickness > 0)\n"
" {\n"
" float border_sdf_s = rect_sdf(v2p_sdf_sample_pos,\n"
" v2p_rect_half_size_px - vec2(v2p_softness*2.f, v2p_softness*2.f) - v2p_border_thickness,\n"
" max(v2p_corner_radius-v2p_border_thickness, 0));\n"
" border_sdf_t = smoothstep(0, 2*v2p_softness, border_sdf_s);\n"
" }\n"
" if(border_sdf_t < 0.001f)\n"
" {\n"
" discard;\n"
" }\n"
" \n"
" // rjf: sample for corners\n"
" float corner_sdf_t = 1;\n"
" if(v2p_corner_radius > 0 || v2p_softness > 0.75f)\n"
" {\n"
" float corner_sdf_s = rect_sdf(v2p_sdf_sample_pos,\n"
" v2p_rect_half_size_px - vec2(v2p_softness*2.f, v2p_softness*2.f),\n"
" v2p_corner_radius);\n"
" corner_sdf_t = 1-smoothstep(0, 2*v2p_softness, corner_sdf_s);\n"
" }\n"
" \n"
" // rjf: form+return final color\n"
" final_color = albedo_sample;\n"
" final_color *= v2p_tint;\n"
" final_color.a *= u_opacity;\n"
" final_color.a *= corner_sdf_t;\n"
" final_color.a *= border_sdf_t;\n"
"}\n"
""
);
read_only global String8 r_ogl_blur_vshad_src =
str8_lit_comp(
""
"\n"
"#version 330 core\n"
"\n"
"uniform vec4 rect;\n"
"uniform vec4 corner_radii_px;\n"
"uniform vec2 viewport_size;\n"
"uniform uint blur_count;\n"
"\n"
"out vec2 texcoord;\n"
"out vec2 sdf_sample_pos;\n"
"out vec2 rect_half_size;\n"
"out float corner_radius;\n"
"\n"
"void main(void)\n"
"{\n"
" vec2 vertex_positions_scrn[] = vec2[](rect.xw,\n"
" rect.xy,\n"
" rect.zw,\n"
" rect.zy);\n"
" float corner_radii_px[] = float[](corner_radii_px.y,\n"
" corner_radii_px.x,\n"
" corner_radii_px.w,\n"
" corner_radii_px.z);\n"
" vec2 cornercoords_pct = vec2((gl_VertexID >> 1) != 0 ? 1.f : 0.f,\n"
" (gl_VertexID & 1) != 0 ? 0.f : 1.f);\n"
" \n"
" vec2 vertex_position_pct = vertex_positions_scrn[gl_VertexID] / viewport_size;\n"
" vec2 vertex_position_scr = 2.f * vertex_position_pct - 1.f;\n"
" \n"
" vec2 rect_half_size = vec2((rect.z-rect.x)/2, (rect.w-rect.y)/2);\n"
" \n"
" gl_Position = vec4(vertex_position_scr.x, -vertex_position_scr.y, 0.f, 1.f);\n"
" texcoord = vertex_position_pct;\n"
" sdf_sample_pos = (2.f * cornercoords_pct - 1.f) * rect_half_size;\n"
" rect_half_size = rect_half_size - 2.f;\n"
" corner_radius = corner_radii_px[gl_VertexID];\n"
"}\n"
""
);
read_only global String8 r_ogl_blur_pshad_src =
str8_lit_comp(
""
"\n"
"#version 330 core\n"
"\n"
"uniform sampler2D tex;\n"
"uniform vec4 kernel[32];\n"
"uniform int blur_count;\n"
"uniform vec2 direction;\n"
"\n"
"in vec2 texcoord;\n"
"in vec2 sdf_sample_pos;\n"
"in vec2 rect_half_size;\n"
"in float corner_radius;\n"
"\n"
"out vec4 final_color;\n"
"\n"
"float rect_sdf(vec2 sample_pos, vec2 rect_half_size, float r)\n"
"{\n"
" return length(max(abs(sample_pos) - rect_half_size + r, 0.0)) - r;\n"
"}\n"
"\n"
"void main(void)\n"
"{\n"
" // rjf: blend weighted texture samples into color\n"
" vec3 color = kernel[0].x * texture(tex, texcoord).rgb;\n"
" \n"
" for(int i = 1; i < blur_count; i += 1)\n"
" {\n"
" float weight = kernel[i].x;\n"
" float offset = kernel[i].y;\n"
" color += weight * texture(tex, texcoord - offset * direction).rgb;\n"
" color += weight * texture(tex, texcoord + offset * direction).rgb;\n"
" }\n"
" \n"
" // rjf: sample for corners\n"
" float corner_sdf_s = rect_sdf(sdf_sample_pos, rect_half_size, corner_radius);\n"
" float corner_sdf_t = 1-smoothstep(0, 2, corner_sdf_s);\n"
" \n"
" // rjf: weight output color by sdf\n"
" // this is doing alpha testing, leave blurring only where mostly opaque pixels are\n"
" if(corner_sdf_t < 0.9f)\n"
" {\n"
" discard;\n"
" }\n"
" \n"
" final_color = vec4(color, 1.f);\n"
"}\n"
""
);
C_LINKAGE_END
#endif // RENDER_OPENGL_META_H
+480
View File
@@ -0,0 +1,480 @@
// 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"
#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 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);
}
////////////////////////////////
//~ 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, &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, 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, 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, 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_2mb);
glBindBuffer(GL_ARRAY_BUFFER, r_ogl_state->scratch_buffer_2mb);
glBufferData(GL_ARRAY_BUFFER, MB(2), 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 debug callback
glEnable(GL_DEBUG_OUTPUT);
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)
{
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 = &params->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_state->scratch_buffer_2mb; // TODO(rjf): 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];
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 = &params->mesh_batches;
// TODO(rjf)
}break;
}
}
}
+217
View File
@@ -0,0 +1,217 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef RENDER_OPENGL_H
#define RENDER_OPENGL_H
////////////////////////////////
//~ rjf: OS Backend Includes
#if OS_WINDOWS
# include "render/opengl/win32/render_opengl_win32.h"
#else
# error OS portion of OpenGL rendering backend not defined.
#endif
////////////////////////////////
//~ rjf: Shader Metadata Types
typedef struct R_OGL_Attribute R_OGL_Attribute;
struct R_OGL_Attribute
{
U64 index;
String8 name;
GLenum type;
U64 count;
};
typedef struct R_OGL_AttributeArray R_OGL_AttributeArray;
struct R_OGL_AttributeArray
{
R_OGL_Attribute *v;
U64 count;
};
////////////////////////////////
//~ rjf: Generated Code
#include "render/opengl/generated/render_opengl.meta.h"
////////////////////////////////
//~ rjf: Defines
typedef char GLchar;
typedef ptrdiff_t GLsizeiptr;
typedef ptrdiff_t GLintptr;
#define GL_FRAMEBUFFER_SRGB 0x8DB9
#define GL_TEXTURE_MAX_LEVEL 0x813D
#define GL_R8 0x8229
#define GL_ARRAY_BUFFER 0x8892
#define GL_STREAM_DRAW 0x88E0
#define GL_STREAM_READ 0x88E1
#define GL_STREAM_COPY 0x88E2
#define GL_STATIC_DRAW 0x88E4
#define GL_STATIC_READ 0x88E5
#define GL_STATIC_COPY 0x88E6
#define GL_DYNAMIC_DRAW 0x88E8
#define GL_DYNAMIC_READ 0x88E9
#define GL_DYNAMIC_COPY 0x88EA
#define GL_FRAGMENT_SHADER 0x8B30
#define GL_VERTEX_SHADER 0x8B31
#define GL_TESS_EVALUATION_SHADER 0x8E87
#define GL_TESS_CONTROL_SHADER 0x8E88
#define GL_INFO_LOG_LENGTH 0x8B84
#define GL_TEXTURE_2D_ARRAY 0x8C1A
#define GL_COMPILE_STATUS 0x8B81
#define GL_TEXTURE0 0x84C0
#define GL_TEXTURE1 0x84C1
#define GL_TEXTURE2 0x84C2
#define GL_TEXTURE3 0x84C3
#define GL_TEXTURE4 0x84C4
#define GL_TEXTURE5 0x84C5
#define GL_TEXTURE6 0x84C6
#define GL_TEXTURE7 0x84C7
#define GL_TEXTURE8 0x84C8
#define GL_TEXTURE9 0x84C9
#define GL_TEXTURE10 0x84CA
#define GL_TEXTURE11 0x84CB
#define GL_TEXTURE12 0x84CC
#define GL_TEXTURE13 0x84CD
#define GL_TEXTURE14 0x84CE
#define GL_TEXTURE15 0x84CF
#define GL_TEXTURE16 0x84D0
#define GL_TEXTURE17 0x84D1
#define GL_TEXTURE18 0x84D2
#define GL_TEXTURE19 0x84D3
#define GL_TEXTURE20 0x84D4
#define GL_TEXTURE21 0x84D5
#define GL_TEXTURE22 0x84D6
#define GL_TEXTURE23 0x84D7
#define GL_TEXTURE24 0x84D8
#define GL_TEXTURE25 0x84D9
#define GL_TEXTURE26 0x84DA
#define GL_TEXTURE27 0x84DB
#define GL_TEXTURE28 0x84DC
#define GL_TEXTURE29 0x84DD
#define GL_TEXTURE30 0x84DE
#define GL_TEXTURE31 0x84DF
#define GL_DEBUG_OUTPUT 0x92E0
////////////////////////////////
//~ rjf: OpenGL Procedure List
#define R_OGL_ProcedureXList \
X(glGenBuffers, void, (GLsizei n, GLuint *buffers))\
X(glBindBuffer, void, (GLenum target, GLuint buffer))\
X(glGenVertexArrays, void, (GLsizei n, GLuint *arrays))\
X(glBindVertexArray, void, (GLuint array))\
X(glCreateProgram, GLuint, (void))\
X(glCreateShader, GLuint, (GLenum type))\
X(glShaderSource, void, (GLuint shader, GLsizei count, char **string, GLint *length))\
X(glCompileShader, void, (GLuint shader))\
X(glGetShaderiv, void, (GLuint shader, GLenum pname, GLint *params))\
X(glGetShaderInfoLog, void, (GLuint shader, GLsizei bufSize, GLsizei *length, char *infoLog))\
X(glGetProgramiv, void, (GLuint program, GLenum pname, GLint *params))\
X(glGetProgramInfoLog, void, (GLuint program, GLsizei bufSize, GLsizei *length, char *infoLog))\
X(glAttachShader, void, (GLuint program, GLuint shader))\
X(glLinkProgram, void, (GLuint program))\
X(glValidateProgram, void, (GLuint program))\
X(glDeleteShader, void, (GLuint shader))\
X(glUseProgram, void, (GLuint program))\
X(glGetUniformLocation, GLint, (GLuint program, char *name))\
X(glGetAttribLocation, GLint, (GLuint program, char *name))\
X(glEnableVertexAttribArray, void, (GLuint index))\
X(glVertexAttribPointer, void, (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer))\
X(glBufferData, void, (GLenum target, ptrdiff_t size, void *data, GLenum usage))\
X(glBufferSubData, void, (GLenum target, ptrdiff_t offset, ptrdiff_t size, const void *data))\
X(glBlendFuncSeparate, void, (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha))\
X(glUniform1f, void, (GLint location, GLfloat v0))\
X(glUniform2f, void, (GLint location, GLfloat v0, GLfloat v1))\
X(glUniform3f, void, (GLint location, GLfloat v0, GLfloat v1, GLfloat v2))\
X(glUniform4f, void, (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3))\
X(glUniformMatrix4fv, void, (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value))\
X(glUniform1i, void, (GLint location, GLint v0))\
X(glTexImage3D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels))\
X(glTexSubImage3D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels))\
X(glGenerateMipmap, void, (GLenum target))\
X(glBindAttribLocation, void, (GLuint programObj, GLuint index, char *name))\
X(glBindFragDataLocation, void, (GLuint program, GLuint color, char *name))\
X(glActiveTexture, void, (GLenum texture))\
X(glVertexAttribDivisor, void, (GLuint index, GLuint divisor))\
X(glDrawArraysInstanced, void, (GLenum mode, GLint first, GLsizei count, GLsizei instancecount))\
X(glDebugMessageCallback, void, (void (*)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam), void *user_data))\
#define X(name, r, p) typedef r name##_FunctionType p;
R_OGL_ProcedureXList
#undef X
#define X(name, r, p) global name##_FunctionType *name = 0;
R_OGL_ProcedureXList
#undef X
////////////////////////////////
//~ rjf: State Types
typedef struct R_OGL_FormatInfo R_OGL_FormatInfo;
struct R_OGL_FormatInfo
{
GLint internal_format;
GLenum format;
GLenum base_type;
};
typedef struct R_OGL_Tex2D R_OGL_Tex2D;
struct R_OGL_Tex2D
{
R_OGL_Tex2D *next;
GLuint id;
R_ResourceKind resource_kind;
R_Tex2DFormat fmt;
Vec2S32 size;
};
typedef struct R_OGL_State R_OGL_State;
struct R_OGL_State
{
Arena *arena;
R_OGL_Tex2D *free_tex2d;
GLuint shaders[R_OGL_ShaderKind_COUNT];
GLuint all_purpose_vao;
GLuint scratch_buffer_2mb;
GLuint white_texture;
};
////////////////////////////////
//~ rjf: Globals
global R_OGL_State *r_ogl_state = 0;
////////////////////////////////
//~ rjf: Helpers
internal R_Handle r_ogl_handle_from_tex2d(R_OGL_Tex2D *t);
internal R_OGL_Tex2D *r_ogl_tex2d_from_handle(R_Handle h);
internal R_OGL_FormatInfo r_ogl_format_info_from_tex2dformat(R_Tex2DFormat fmt);
internal void r_ogl_debug_message_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam);
#define glUseProgramScope(...) DeferLoop(glUseProgram(__VA_ARGS__), glUseProgram(0))
#define glBindVertexArrayScope(...) DeferLoop(glBindVertexArray(__VA_ARGS__), glBindVertexArray(0))
////////////////////////////////
//~ rjf: OS-Specific Hooks
internal VoidProc *r_ogl_os_load_procedure(char *name);
internal void r_ogl_os_init(CmdLine *cmdln);
internal R_Handle r_ogl_os_window_equip(OS_Handle window);
internal void r_ogl_os_window_unequip(OS_Handle os, R_Handle r);
internal void r_ogl_os_select_window(OS_Handle os, R_Handle r);
internal void r_ogl_os_window_swap(OS_Handle os, R_Handle r);
#endif // RENDER_OPENGL_H
+292
View File
@@ -0,0 +1,292 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Shader Table
@table(name name_lower input_atts output_atts)
R_OGL_ShaderTable:
{
{Rect rect r_ogl_rect_input_attributes r_ogl_single_color_output_attributes}
{Blur blur 0 r_ogl_single_color_output_attributes}
}
@enum R_OGL_ShaderKind:
{
@expand(R_OGL_ShaderTable a) `$(a.name)`,
COUNT
}
@data(String8) r_ogl_shader_kind_name_table:
{
@expand(R_OGL_ShaderTable a) `str8_lit_comp("$(a.name_lower)")`,
}
@data(`String8 *`) r_ogl_shader_kind_vshad_src_table:
{
@expand(R_OGL_ShaderTable a) `&r_ogl_$(a.name_lower)_vshad_src`,
}
@data(`String8 *`) r_ogl_shader_kind_pshad_src_table:
{
@expand(R_OGL_ShaderTable a) `&r_ogl_$(a.name_lower)_pshad_src`,
}
@data(R_OGL_AttributeArray) r_ogl_shader_kind_input_attributes_table:
{
@expand(R_OGL_ShaderTable a) `{ $(a.input_atts), $(a.input_atts != 0 -> "ArrayCount(" .. a.input_atts .. ")") }`,
}
@data(R_OGL_AttributeArray) r_ogl_shader_kind_output_attributes_table:
{
@expand(R_OGL_ShaderTable a) `{ $(a.output_atts), ArrayCount($(a.output_atts)) }`,
}
////////////////////////////////
//~ rjf: UI Rectangle Shaders
//- rjf: vertex
@embed_string r_ogl_rect_vshad_src:
```
#version 330 core
in vec4 c2v_dst_rect;
in vec4 c2v_src_rect;
in vec4 c2v_colors_0;
in vec4 c2v_colors_1;
in vec4 c2v_colors_2;
in vec4 c2v_colors_3;
in vec4 c2v_corner_radii;
in vec4 c2v_style; // x: border_thickness_px, y: softness_px, z: omit_texture, w: unused
out vec2 v2p_sdf_sample_pos;
out vec2 v2p_texcoord_pct;
out vec2 v2p_rect_half_size_px;
out vec4 v2p_tint;
out float v2p_corner_radius;
out float v2p_border_thickness;
out float v2p_softness;
out float v2p_omit_texture;
uniform sampler2D u_tex_color;
uniform vec2 u_viewport_size_px;
void main(void)
{
// rjf: constants
vec2 vertices[] = vec2[](vec2(-1, -1), vec2(-1, +1), vec2(+1, -1), vec2(+1, +1));
// rjf: find dst position
vec2 dst_half_size = (c2v_dst_rect.zw - c2v_dst_rect.xy) / 2;
vec2 dst_center = (c2v_dst_rect.zw + c2v_dst_rect.xy) / 2;
vec2 dst_position = vertices[gl_VertexID] * dst_half_size + dst_center;
// rjf: find src position
vec2 src_half_size = (c2v_src_rect.zw - c2v_src_rect.xy) / 2;
vec2 src_center = (c2v_src_rect.zw + c2v_src_rect.xy) / 2;
vec2 src_position = vertices[gl_VertexID] * src_half_size + src_center;
// rjf: find color
vec4 colors[] = vec4[](c2v_colors_0, c2v_colors_1, c2v_colors_2, c2v_colors_3);
vec4 color = colors[gl_VertexID];
// rjf: find corner radius
float corner_radii[] = float[](c2v_corner_radii.x, c2v_corner_radii.y, c2v_corner_radii.z, c2v_corner_radii.w);
float corner_radius = corner_radii[gl_VertexID];
// rjf: fill outputs
vec2 dst_verts_pct = vec2(((gl_VertexID >> 1) != 1) ? 1.f : 0.f,
((gl_VertexID & 1) != 0) ? 0.f : 1.f);
ivec2 u_tex_color_size_i = textureSize(u_tex_color, 0);
vec2 u_tex_color_size = vec2(float(u_tex_color_size_i.x), float(u_tex_color_size_i.y));
{
gl_Position = vec4(2 * dst_position.x / u_viewport_size_px.x - 1,
2 * (1 - dst_position.y / u_viewport_size_px.y) - 1,
0.0, 1.0);
v2p_sdf_sample_pos = (2.f * dst_verts_pct - 1.f) * dst_half_size;
v2p_texcoord_pct = src_position / u_tex_color_size;
v2p_rect_half_size_px = dst_half_size;
v2p_tint = color;
v2p_corner_radius = corner_radius;
v2p_border_thickness = c2v_style.x;
v2p_softness = c2v_style.y;
v2p_omit_texture = c2v_style.z;
}
}
```
//- rjf: pixel
@embed_string r_ogl_rect_pshad_src:
```
#version 330 core
in vec2 v2p_sdf_sample_pos;
in vec2 v2p_texcoord_pct;
in vec2 v2p_rect_half_size_px;
in vec4 v2p_tint;
in float v2p_corner_radius;
in float v2p_border_thickness;
in float v2p_softness;
in float v2p_omit_texture;
out vec4 final_color;
uniform float u_opacity;
uniform sampler2D u_tex_color;
uniform mat4 u_texture_sample_channel_map;
float rect_sdf(vec2 sample_pos, vec2 rect_half_size, float r)
{
return length(max(abs(sample_pos) - rect_half_size + r, 0.0)) - r;
}
float linear_from_srgb_f32(float x)
{
return x < 0.0404482362771082 ? x / 12.92 : pow((x + 0.055) / 1.055, 2.4);
}
vec4 linear_from_srgba(vec4 v)
{
vec4 result = vec4(linear_from_srgb_f32(v.x),
linear_from_srgb_f32(v.y),
linear_from_srgb_f32(v.z),
v.w);
return result;
}
void main(void)
{
// rjf: sample texture
vec4 albedo_sample = vec4(1, 1, 1, 1);
if(v2p_omit_texture < 1)
{
albedo_sample = u_texture_sample_channel_map * texture(u_tex_color, v2p_texcoord_pct);
albedo_sample = linear_from_srgba(albedo_sample);
}
// rjf: sample for borders
float border_sdf_t = 1;
if(v2p_border_thickness > 0)
{
float border_sdf_s = rect_sdf(v2p_sdf_sample_pos,
v2p_rect_half_size_px - vec2(v2p_softness*2.f, v2p_softness*2.f) - v2p_border_thickness,
max(v2p_corner_radius-v2p_border_thickness, 0));
border_sdf_t = smoothstep(0, 2*v2p_softness, border_sdf_s);
}
if(border_sdf_t < 0.001f)
{
discard;
}
// rjf: sample for corners
float corner_sdf_t = 1;
if(v2p_corner_radius > 0 || v2p_softness > 0.75f)
{
float corner_sdf_s = rect_sdf(v2p_sdf_sample_pos,
v2p_rect_half_size_px - vec2(v2p_softness*2.f, v2p_softness*2.f),
v2p_corner_radius);
corner_sdf_t = 1-smoothstep(0, 2*v2p_softness, corner_sdf_s);
}
// rjf: form+return final color
final_color = albedo_sample;
final_color *= v2p_tint;
final_color.a *= u_opacity;
final_color.a *= corner_sdf_t;
final_color.a *= border_sdf_t;
}
```
////////////////////////////////
//~ rjf: Blur Shaders
//- rjf: vertex
@embed_string r_ogl_blur_vshad_src:
```
#version 330 core
uniform vec4 rect;
uniform vec4 corner_radii_px;
uniform vec2 viewport_size;
uniform uint blur_count;
out vec2 texcoord;
out vec2 sdf_sample_pos;
out vec2 rect_half_size;
out float corner_radius;
void main(void)
{
vec2 vertex_positions_scrn[] = vec2[](rect.xw,
rect.xy,
rect.zw,
rect.zy);
float corner_radii_px[] = float[](corner_radii_px.y,
corner_radii_px.x,
corner_radii_px.w,
corner_radii_px.z);
vec2 cornercoords_pct = vec2((gl_VertexID >> 1) != 0 ? 1.f : 0.f,
(gl_VertexID & 1) != 0 ? 0.f : 1.f);
vec2 vertex_position_pct = vertex_positions_scrn[gl_VertexID] / viewport_size;
vec2 vertex_position_scr = 2.f * vertex_position_pct - 1.f;
vec2 rect_half_size = vec2((rect.z-rect.x)/2, (rect.w-rect.y)/2);
gl_Position = vec4(vertex_position_scr.x, -vertex_position_scr.y, 0.f, 1.f);
texcoord = vertex_position_pct;
sdf_sample_pos = (2.f * cornercoords_pct - 1.f) * rect_half_size;
rect_half_size = rect_half_size - 2.f;
corner_radius = corner_radii_px[gl_VertexID];
}
```
//- rjf: pixel
@embed_string r_ogl_blur_pshad_src:
```
#version 330 core
uniform sampler2D tex;
uniform vec4 kernel[32];
uniform int blur_count;
uniform vec2 direction;
in vec2 texcoord;
in vec2 sdf_sample_pos;
in vec2 rect_half_size;
in float corner_radius;
out vec4 final_color;
float rect_sdf(vec2 sample_pos, vec2 rect_half_size, float r)
{
return length(max(abs(sample_pos) - rect_half_size + r, 0.0)) - r;
}
void main(void)
{
// rjf: blend weighted texture samples into color
vec3 color = kernel[0].x * texture(tex, texcoord).rgb;
for(int i = 1; i < blur_count; i += 1)
{
float weight = kernel[i].x;
float offset = kernel[i].y;
color += weight * texture(tex, texcoord - offset * direction).rgb;
color += weight * texture(tex, texcoord + offset * direction).rgb;
}
// rjf: sample for corners
float corner_sdf_s = rect_sdf(sdf_sample_pos, rect_half_size, corner_radius);
float corner_sdf_t = 1-smoothstep(0, 2, corner_sdf_s);
// rjf: weight output color by sdf
// this is doing alpha testing, leave blurring only where mostly opaque pixels are
if(corner_sdf_t < 0.9f)
{
discard;
}
final_color = vec4(color, 1.f);
}
```
@@ -0,0 +1,184 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal VoidProc *
r_ogl_os_load_procedure(char *name)
{
VoidProc *p = (VoidProc*)wglGetProcAddress(name);
if(p == (VoidProc*)1 || p == (VoidProc*)2 || p == (VoidProc*)3 || p == (VoidProc*)-1)
{
p = 0;
}
return p;
}
internal void
r_ogl_os_init(CmdLine *cmdline)
{
//- rjf: create bootstrapping window
HWND bootstrap_hwnd = 0;
{
WNDCLASSEXW wndclass = { sizeof(wndclass) };
wndclass.lpfnWndProc = DefWindowProcW;
wndclass.hInstance = GetModuleHandle(0);
wndclass.lpszClassName = L"bootstrap-window";
ATOM wndatom = RegisterClassExW(&wndclass);
bootstrap_hwnd = CreateWindowExW(0, L"bootstrap-window", L"", 0,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
0, 0, wndclass.hInstance, 0);
}
//- rjf: grab dc
HDC dc = GetDC(bootstrap_hwnd);
//- rjf: build pixel format descriptor
int pf = 0;
{
PIXELFORMATDESCRIPTOR pfd = {sizeof(pfd)};
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cDepthBits = 24;
pfd.cStencilBits = 8;
pfd.iLayerType = PFD_MAIN_PLANE;
pf = ChoosePixelFormat(dc, &pfd);
BOOL describe = DescribePixelFormat(dc, pf, sizeof(pfd), &pfd);
BOOL set_pf = SetPixelFormat(dc, pf, &pfd);
}
//- rjf: make bootstrap ctx + make current
HGLRC bootstrap_ctx = wglCreateContext(dc);
wglMakeCurrent(dc, bootstrap_ctx);
//- rjf: load modern extensions
wglChoosePixelFormatARB = (FNWGLCHOOSEPIXELFORMATARBPROC*) r_ogl_os_load_procedure("wglChoosePixelFormatARB");
wglCreateContextAttribsARB = (FNWGLCREATECONTEXTATTRIBSARBPROC*)r_ogl_os_load_procedure("wglCreateContextAttribsARB");
wglSwapIntervalEXT = (FNWGLSWAPINTERVALEXTPROC*) r_ogl_os_load_procedure("wglSwapIntervalEXT");
//- rjf: set up real pixel format
{
int pf_attribs_i[] =
{
WGL_DRAW_TO_WINDOW_ARB, 1,
WGL_SUPPORT_OPENGL_ARB, 1,
WGL_DOUBLE_BUFFER_ARB, 1,
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
WGL_COLOR_BITS_ARB, 32,
WGL_DEPTH_BITS_ARB, 24,
WGL_STENCIL_BITS_ARB, 8,
0
};
UINT num_formats = 0;
wglChoosePixelFormatARB(dc, pf_attribs_i, 0, 1, &pf, &num_formats);
}
//- rjf: make real gl ctx
HGLRC real_ctx = 0;
if(pf)
{
const int context_attribs[] =
{
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB,
0
};
real_ctx = wglCreateContextAttribsARB(dc, bootstrap_ctx, context_attribs);
r_ogl_w32_hglrc = real_ctx;
}
//- rjf: clean up bootstrap context
wglMakeCurrent(dc, 0);
wglDeleteContext(bootstrap_ctx);
wglMakeCurrent(dc, real_ctx);
wglSwapIntervalEXT(1);
ReleaseDC(bootstrap_hwnd, dc);
DestroyWindow(bootstrap_hwnd);
}
internal R_Handle
r_ogl_os_window_equip(OS_Handle window)
{
//- rjf: unpack window
OS_W32_Window *w = os_w32_window_from_handle(window);
HWND hwnd = w->hwnd;
HDC hdc = GetDC(hwnd);
//- rjf: select in ctx
wglMakeCurrent(hdc, r_ogl_w32_hglrc);
//- rjf: setup real pixel format
int pixel_format = 0;
UINT num_formats = 0;
int pf_attribs_i[] =
{
WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
WGL_COLOR_BITS_ARB, 32,
WGL_DEPTH_BITS_ARB, 24,
WGL_STENCIL_BITS_ARB, 8,
0
};
wglChoosePixelFormatARB(hdc,
pf_attribs_i,
0,
1,
&pixel_format,
&num_formats);
// NOTE(rjf): This doesn't seem to be necessary for SetPixelFormat, we can
// just pass 0 for it, and SetPixelFormat needs to be called here, but the
// docs don't seem to suggest that 0 is an acceptable value, so I am just
// filling this out with the same attribs as that for the wgl function,
// and passing it.
PIXELFORMATDESCRIPTOR pfd = {sizeof(pfd)};
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cDepthBits = 24;
pfd.cStencilBits = 8;
pfd.iLayerType = PFD_MAIN_PLANE;
//- rjf: set pixel format
SetPixelFormat(hdc, pixel_format, &pfd);
//- rjf: release hdc
ReleaseDC(hwnd, hdc);
R_Handle result = {0};
return result;
}
internal void
r_ogl_os_window_unequip(OS_Handle os, R_Handle r)
{
}
internal void
r_ogl_os_select_window(OS_Handle os, R_Handle r)
{
OS_W32_Window *w = os_w32_window_from_handle(os);
if(w != 0)
{
HWND hwnd = w->hwnd;
HDC hdc = GetDC(hwnd);
wglMakeCurrent(hdc, r_ogl_w32_hglrc);
ReleaseDC(hwnd, hdc);
}
}
internal void
r_ogl_os_window_swap(OS_Handle os, R_Handle r)
{
OS_W32_Window *w = os_w32_window_from_handle(os);
if(w != 0)
{
HDC dc = GetDC(w->hwnd);
SwapBuffers(dc);
ReleaseDC(w->hwnd, dc);
}
}
@@ -0,0 +1,34 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef RENDER_OPENGL_WIN32_H
#define RENDER_OPENGL_WIN32_H
#include <GL/gl.h>
#pragma comment(lib, "opengl32")
#define WGL_DRAW_TO_WINDOW_ARB 0x2001
#define WGL_SUPPORT_OPENGL_ARB 0x2010
#define WGL_DOUBLE_BUFFER_ARB 0x2011
#define WGL_PIXEL_TYPE_ARB 0x2013
#define WGL_COLOR_BITS_ARB 0x2014
#define WGL_DEPTH_BITS_ARB 0x2022
#define WGL_STENCIL_BITS_ARB 0x2023
#define WGL_TYPE_RGBA_ARB 0x202B
#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
#define WGL_CONTEXT_FLAGS_ARB 0x2094
typedef BOOL WINAPI FNWGLCHOOSEPIXELFORMATARBPROC(HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats);
typedef HGLRC WINAPI FNWGLCREATECONTEXTATTRIBSARBPROC(HDC hDC, HGLRC hShareContext, const int *attribList);
typedef BOOL WINAPI FNWGLSWAPINTERVALEXTPROC(int interval);
FNWGLCHOOSEPIXELFORMATARBPROC *wglChoosePixelFormatARB;
FNWGLCREATECONTEXTATTRIBSARBPROC *wglCreateContextAttribsARB;
FNWGLSWAPINTERVALEXTPROC *wglSwapIntervalEXT;
global HGLRC r_ogl_w32_hglrc = 0;
#endif // RENDER_OPENGL_WIN32_H
+27
View File
@@ -6,6 +6,33 @@
#include "generated/render.meta.c"
////////////////////////////////
//~ rjf: Helpers
internal Mat4x4F32
r_sample_channel_map_from_tex2dformat(R_Tex2DFormat fmt)
{
Mat4x4F32 result =
{
{
{1, 0, 0, 0},
{0, 1, 0, 0},
{0, 0, 1, 0},
{0, 0, 0, 1},
}
};
switch(fmt)
{
default:{}break;
case R_Tex2DFormat_R8:
{
MemoryZeroArray(result.v[0]);
result.v[0][0] = result.v[0][1] = result.v[0][2] = result.v[0][3] = 1.f;
}break;
}
return result;
}
////////////////////////////////
//~ rjf: Basic Type Functions
+5
View File
@@ -194,6 +194,11 @@ struct R_PassList
U64 count;
};
////////////////////////////////
//~ rjf: Helpers
internal Mat4x4F32 r_sample_channel_map_from_tex2dformat(R_Tex2DFormat fmt);
////////////////////////////////
//~ rjf: Handle Type Functions
+2
View File
@@ -10,6 +10,8 @@
# include "stub/render_stub.c"
#elif R_BACKEND == R_BACKEND_D3D11
# include "d3d11/render_d3d11.c"
#elif R_BACKEND == R_BACKEND_OPENGL
# include "opengl/render_opengl.c"
#else
# error Renderer backend not specified.
#endif
+5
View File
@@ -9,12 +9,15 @@
#define R_BACKEND_STUB 0
#define R_BACKEND_D3D11 1
#define R_BACKEND_OPENGL 2
////////////////////////////////
//~ rjf: Decide On Backend
#if !defined(R_BACKEND) && OS_WINDOWS
# define R_BACKEND R_BACKEND_D3D11
#elif !defined(R_BACKEND) && OS_LINUX
# define R_BACKEND R_BACKEND_OPENGL
#endif
////////////////////////////////
@@ -29,6 +32,8 @@
# include "stub/render_stub.h"
#elif R_BACKEND == R_BACKEND_D3D11
# include "d3d11/render_d3d11.h"
#elif R_BACKEND == R_BACKEND_OPENGL
# include "opengl/render_opengl.h"
#else
# error Renderer backend not specified.
#endif