master uint/magnolia / source / renderer / renderer_sokol.c
  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