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