1#include <SDL2/SDL_opengl.h>
2#include <sokol_gfx.h>
3#include <stb_image_c89.h>
4
5#include "util.h"
6#include "types.h"
7#include "platform/platform.h"
8#include "renderer/renderer.h"
9#include "renderer_backend.h"
10
11#define MAX_MESHES (512) /* TODO; random value ig */
12#define MAX_TEXTURES (512) /* TODO; random value ig */
13
14typedef struct {
15 sg_buffer vb; /* vertex buffer */
16 sg_buffer ib; /* index buffer */
17 u32 ic; /* index count */
18 i8 used;
19} RendererMesh;
20
21typedef struct {
22 sg_image image;
23 sg_view view;
24 i8 used;
25} RendererTexture;
26
27typedef struct {
28 MGContext* ctx;
29 i8 pass; /* basically a bool; in
30 render pass or not */
31
32 sg_shader shader;
33 sg_pipeline pipeline;
34 sg_sampler sampler;
35
36 RendererMesh meshes[MAX_MESHES];
37 RendererTexture textures[MAX_MESHES];
38} RendererState;
39
40/* create the shader and render pipeline */
41static void create_pipelines(void);
42
43/* create texture sampler */
44static void create_sampler(void);
45
46/* logging function for passing to sokol */
47static void sokol_log(const char* tag, u32 level, u32 item, const char* msg, u32 ln,
48 const char* file, void* user);
49
50static RendererState ren;
51/* TODO: textures */
52static const char* vsdr = "#version 330\n"
53 "layout(location=0) in vec3 position;\n"
54 "layout(location=1) in vec2 texcoord0;\n"
55 "layout(location=2) in vec4 color0;\n"
56 "\n"
57 "uniform mat4 mvp;\n"
58 "\n"
59 "out vec2 uv;\n"
60 "out vec4 colour;\n"
61 "\n"
62 "void main(void)\n"
63 "{\n"
64 " gl_Position = mvp * vec4(position, 1.0);\n"
65 " uv = texcoord0;\n"
66 " colour = color0;\n"
67 "}\n";
68static const char* fsdr = "#version 330\n"
69 "in vec2 uv;\n"
70 "in vec4 colour;\n"
71 "\n"
72 "uniform sampler2D tex;\n"
73 "\n"
74 "out vec4 frag_colour;\n"
75 "\n"
76 "void main(void)\n"
77 "{\n"
78 " frag_colour = texture(tex, uv);\n"
79 "}\n";
80
81static void create_pipelines(void)
82{
83 sg_shader_desc shader_desc = { 0 };
84 sg_pipeline_desc pipeline_desc = { 0 };
85
86 /* shader descriptor */
87 shader_desc.vertex_func.source = vsdr;
88 shader_desc.fragment_func.source = fsdr;
89 shader_desc.attrs[0].glsl_name = "position";
90 shader_desc.attrs[1].glsl_name = "texcoord0";
91 shader_desc.attrs[2].glsl_name = "color0";
92 shader_desc.uniform_blocks[0].stage = SG_SHADERSTAGE_VERTEX;
93 shader_desc.uniform_blocks[0].size = sizeof(float) * 16; /* size of mvp matrix */
94 shader_desc.uniform_blocks[0].layout = SG_UNIFORMLAYOUT_STD140;
95 shader_desc.uniform_blocks[0].glsl_uniforms[0].glsl_name = "mvp";
96 shader_desc.uniform_blocks[0].glsl_uniforms[0].type = SG_UNIFORMTYPE_MAT4;
97
98 /* textures */
99 shader_desc.views[0].texture.stage = SG_SHADERSTAGE_FRAGMENT;
100 shader_desc.views[0].texture.image_type = SG_IMAGETYPE_2D;
101 shader_desc.views[0].texture.sample_type = SG_IMAGESAMPLETYPE_FLOAT;
102 shader_desc.samplers[0].stage = SG_SHADERSTAGE_FRAGMENT;
103 shader_desc.samplers[0].sampler_type = SG_SAMPLERTYPE_FILTERING;
104 shader_desc.texture_sampler_pairs[0].stage = SG_SHADERSTAGE_FRAGMENT;
105 shader_desc.texture_sampler_pairs[0].view_slot = 0;
106 shader_desc.texture_sampler_pairs[0].sampler_slot = 0;
107 shader_desc.texture_sampler_pairs[0].glsl_name = "tex";
108
109 ren.shader = sg_make_shader(&shader_desc);
110 if (sg_query_shader_state(ren.shader) != SG_RESOURCESTATE_VALID)
111 mg_die(MG_ERR_SHADER_CREATE, "Failed to create shader");
112
113 /* pipeline descriptor */
114 pipeline_desc.shader = ren.shader;
115 pipeline_desc.layout.buffers[0].stride = sizeof(MGVertex);
116 pipeline_desc.layout.attrs[0].format = SG_VERTEXFORMAT_FLOAT3;
117 pipeline_desc.layout.attrs[1].format = SG_VERTEXFORMAT_FLOAT2;
118 pipeline_desc.layout.attrs[2].format = SG_VERTEXFORMAT_UBYTE4N;
119 pipeline_desc.index_type = SG_INDEXTYPE_UINT16;
120 pipeline_desc.depth.compare = SG_COMPAREFUNC_LESS_EQUAL;
121 pipeline_desc.depth.write_enabled = true;
122 pipeline_desc.cull_mode = SG_CULLMODE_BACK;
123 pipeline_desc.face_winding = SG_FACEWINDING_CCW;
124
125 ren.pipeline = sg_make_pipeline(&pipeline_desc);
126 if (sg_query_pipeline_state(ren.pipeline) != SG_RESOURCESTATE_VALID)
127 mg_die(MG_ERR_PIPELINE_CREATE, "Failed to create pipeline");
128}
129
130static void create_sampler(void)
131{
132 sg_sampler_desc desc = { 0 };
133
134 desc.min_filter = SG_FILTER_NEAREST;
135 desc.mag_filter = SG_FILTER_NEAREST;
136 desc.wrap_u = SG_WRAP_REPEAT;
137 desc.wrap_v = SG_WRAP_REPEAT;
138
139 ren.sampler = sg_make_sampler(&desc);
140
141 if (sg_query_sampler_state(ren.sampler) != SG_RESOURCESTATE_VALID)
142 mg_die(MG_ERR_SAMPLER_CREATE, "Failed to create sampler");
143}
144
145static void sokol_log(const char* tag, u32 level, u32 item, const char* msg, u32 ln,
146 const char* file, void* user)
147{
148 LogType type;
149 (void)item;
150 (void)user;
151
152 type = level <= 1 ? LOG_ERR : LOG_DBG;
153
154 mg_log(type, "%s: %s [%s:%u]", tag ? tag : "Sokol", msg ? msg : "No message",
155 file ? file : "Unknown", ln);
156}
157
158void renderer_backend_create(MGContext* ctx)
159{
160 sg_desc desc = { 0 };
161
162 ren.ctx = ctx;
163 ren.pass = 0;
164
165 desc.environment.defaults.color_format = SG_PIXELFORMAT_RGBA8;
166 desc.environment.defaults.depth_format = SG_PIXELFORMAT_DEPTH;
167 desc.environment.defaults.sample_count = 1;
168 desc.logger.func = sokol_log;
169 desc.buffer_pool_size = MAX_MESHES * 2;
170
171 sg_setup(&desc);
172
173 if (!sg_isvalid())
174 mg_die(MG_ERR_REN_CREATE, "Failed to initialise renderer");
175
176 create_pipelines();
177 create_sampler();
178}
179
180void renderer_backend_begin(void)
181{
182 sg_pass pass = { 0 };
183 i32 width;
184 i32 height;
185
186 ren.pass = 0;
187
188 platform_get_drawable_size(ren.ctx, &width, &height);
189 /* (minimized window?) no need to draw */
190 if (width <= 0 || height <= 0)
191 return;
192
193 pass.action.colors[0].load_action = SG_LOADACTION_CLEAR;
194 pass.action.colors[0].clear_value.r = 0.90f;
195 pass.action.colors[0].clear_value.g = 0.80f;
196 pass.action.colors[0].clear_value.b = 0.70f;
197 pass.action.colors[0].clear_value.a = 1.00f;
198
199 pass.action.depth.load_action = SG_LOADACTION_CLEAR;
200 pass.action.depth.clear_value = 1.0f;
201
202 /* swapchain descriptor */
203 pass.swapchain.width = width;
204 pass.swapchain.height = height;
205 pass.swapchain.sample_count = 1;
206 pass.swapchain.color_format = SG_PIXELFORMAT_RGBA8;
207 pass.swapchain.depth_format = SG_PIXELFORMAT_DEPTH;
208
209 pass.swapchain.gl.framebuffer = 0;
210
211 sg_begin_pass(&pass);
212 ren.pass = 1;
213}
214
215void renderer_backend_end(void)
216{
217 if (!ren.pass)
218 return;
219
220 sg_end_pass();
221 sg_commit();
222
223 ren.pass = 0;
224}
225
226void renderer_backend_destroy(void)
227{
228 u32 id;
229
230 /* the values could be different in future so
231 probably should keep them separate */
232 for (id = 1; id < MAX_MESHES; id++)
233 renderer_backend_mesh_destroy(id);
234 for (id = 1; id < MAX_TEXTURES; id++)
235 renderer_backend_texture_destroy(id);
236
237 sg_destroy_sampler(ren.sampler);
238 sg_destroy_pipeline(ren.pipeline);
239 sg_destroy_shader(ren.shader);
240
241 if (sg_isvalid())
242 sg_shutdown();
243
244 ren.ctx = NULL;
245 ren.pass = 0;
246}
247
248MGMesh renderer_backend_mesh_create(const MGMeshDesc* desc)
249{
250 sg_buffer_desc buffer_desc = { 0 };
251 RendererMesh* mesh;
252 u32 id;
253
254 if (!desc || !desc->vtc || !desc->idc || desc->vtcc == 0 || desc->idcc == 0)
255 return MGMESH_INVALID;
256
257 /* find free mesh slot */
258 for (id = 1; id < MAX_MESHES; id++) {
259 if (!ren.meshes[id].used)
260 break;
261 }
262
263 if (id == MAX_MESHES) {
264 mg_log(LOG_ERR, "Maximum mesh limit reached");
265 return MGMESH_INVALID;
266 }
267
268 /* vertex buffer */
269 mesh = &ren.meshes[id];
270 buffer_desc.data.ptr = desc->vtc;
271 buffer_desc.data.size = desc->vtcc * sizeof(MGVertex);
272 mesh->vb = sg_make_buffer(&buffer_desc);
273
274 /* index buffer */
275 buffer_desc = (sg_buffer_desc){ 0 };
276 buffer_desc.usage.vertex_buffer = false;
277 buffer_desc.usage.index_buffer = true;
278 buffer_desc.usage.immutable = true;
279 buffer_desc.data.ptr = desc->idc;
280 buffer_desc.data.size = desc->idcc * sizeof(u16);
281 mesh->ib = sg_make_buffer(&buffer_desc);
282
283 i8 valid = sg_query_buffer_state(mesh->vb) == SG_RESOURCESTATE_VALID &&
284 sg_query_buffer_state(mesh->ib) == SG_RESOURCESTATE_VALID;
285 if (!valid) {
286 if (sg_query_buffer_state(mesh->vb) == SG_RESOURCESTATE_VALID)
287 sg_destroy_buffer(mesh->vb);
288 if (sg_query_buffer_state(mesh->ib) == SG_RESOURCESTATE_VALID)
289 sg_destroy_buffer(mesh->ib);
290
291 *mesh = (RendererMesh){ 0 };
292 mg_log(LOG_ERR, "Failed to create mesh buffers");
293 return MGMESH_INVALID;
294 }
295
296 mesh->ic = desc->idcc;
297 mesh->used = 1;
298 return id;
299}
300
301void renderer_backend_mesh_draw(MGMesh mesh, MGTexture texture, const MGMat4* mvp)
302{
303 RendererMesh* rmesh;
304 RendererTexture* rtexture;
305 sg_bindings bindings = { 0 };
306 sg_range uniforms;
307
308 if (!ren.pass || !mvp)
309 return;
310
311 if (mesh == MGMESH_INVALID || mesh >= MAX_MESHES)
312 return;
313
314 if (texture == MGTEXTURE_INVALID || texture >= MAX_TEXTURES)
315 return;
316
317 rmesh = &ren.meshes[mesh];
318 rtexture = &ren.textures[texture];
319 if (!rmesh->used || !rtexture->used)
320 return;
321
322 bindings.vertex_buffers[0] = rmesh->vb;
323 bindings.index_buffer = rmesh->ib;
324 bindings.views[0] = rtexture->view;
325 bindings.samplers[0] = ren.sampler;
326
327 uniforms.ptr = &mvp->Elements[0][0];
328 uniforms.size = sizeof(float) * 16;
329
330 sg_apply_pipeline(ren.pipeline);
331 sg_apply_bindings(&bindings);
332 sg_apply_uniforms(0, &uniforms);
333
334 sg_draw(0, rmesh->ic, 1);
335}
336
337void renderer_backend_mesh_destroy(MGMesh id)
338{
339 RendererMesh* mesh;
340
341 if (id == MGMESH_INVALID || id >= MAX_MESHES)
342 return;
343
344 mesh = &ren.meshes[id];
345 if (!mesh->used)
346 return;
347
348 sg_destroy_buffer(mesh->ib);
349 sg_destroy_buffer(mesh->vb);
350
351 *mesh = (RendererMesh){ 0 };
352}
353
354MGTexture renderer_backend_texture_load(const char* path)
355{
356 stbi_uc* pixels;
357 int width;
358 int height;
359 int channels;
360 MGTextureDesc desc;
361 MGTexture texture;
362
363 if (!path)
364 return MGTEXTURE_INVALID;
365
366 pixels = stbi_load(path, &width, &height, &channels, 4);
367 if (!pixels) {
368 mg_log(LOG_ERR, "Failed to load texture: %s", path);
369 return MGTEXTURE_INVALID;
370 }
371
372 desc.pixels = pixels;
373 desc.width = width;
374 desc.height = height;
375 texture = renderer_backend_texture_create(&desc);
376 stbi_image_free(pixels);
377 return texture;
378}
379
380MGTexture renderer_backend_texture_create(const MGTextureDesc* desc)
381{
382 sg_image_desc image_desc = { 0 };
383 sg_view_desc view_desc = { 0 };
384 RendererTexture* texture;
385 u32 id;
386
387 if (!desc || !desc->pixels || desc->width <= 0 || desc->height <= 0)
388 return MGTEXTURE_INVALID;
389
390 for (id = 1; id < MAX_TEXTURES; id++) {
391 if (!ren.textures[id].used)
392 break;
393 }
394
395 if (id == MAX_TEXTURES) {
396 mg_log(LOG_ERR, "Maximum texture limit reached");
397 return MGTEXTURE_INVALID;
398 }
399
400 texture = &ren.textures[id];
401 image_desc.width = desc->width;
402 image_desc.height = desc->height;
403 image_desc.pixel_format = SG_PIXELFORMAT_RGBA8;
404 image_desc.data.mip_levels[0].ptr = desc->pixels;
405 image_desc.data.mip_levels[0].size = (size_t)desc->width * (size_t)desc->height * 4;
406
407 texture->image = sg_make_image(&image_desc);
408 if (sg_query_image_state(texture->image) != SG_RESOURCESTATE_VALID) {
409 *texture = (RendererTexture){ 0 };
410 mg_log(LOG_ERR, "Failed to create texture");
411 return MGTEXTURE_INVALID;
412 }
413
414 view_desc.texture.image = texture->image;
415 texture->view = sg_make_view(&view_desc);
416
417 if (sg_query_view_state(texture->view) != SG_RESOURCESTATE_VALID) {
418 sg_destroy_image(texture->image);
419 *texture = (RendererTexture){ 0 };
420 mg_log(LOG_ERR, "Failed to create texture view");
421 return MGTEXTURE_INVALID;
422 }
423
424 texture->used = 1;
425
426 return id;
427}
428
429void renderer_backend_texture_destroy(MGTexture id)
430{
431 RendererTexture* texture;
432
433 if (id == MGTEXTURE_INVALID || id >= MAX_TEXTURES)
434 return;
435
436 texture = &ren.textures[id];
437
438 if (!texture->used)
439 return;
440
441 sg_destroy_view(texture->view);
442 sg_destroy_image(texture->image);
443
444 *texture = (RendererTexture){ 0 };
445}
446
447void renderer_debug_wireframe(i8 enabled)
448{
449 glPolygonMode(GL_FRONT_AND_BACK, enabled ? GL_LINE : GL_FILL);
450 sg_reset_state_cache();
451}
452