small3dlib 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, const char* file, void* user);
 47
 48static RendererState ren;
 49/* TODO: textures */
 50static const char* vsdr =
 51"#version 410\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 =
 68"#version 410\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, 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(
154		type, "%s: %s [%s:%u]",
155		tag ? tag : "Sokol",
156		msg ? msg : "No message",
157		file ? file : "Unknown",
158		ln
159	);
160}
161
162void renderer_backend_create(MGContext* ctx)
163{
164	sg_desc desc = {0};
165
166	ren.ctx = ctx;
167	ren.pass = 0;
168
169	desc.environment.defaults.color_format = SG_PIXELFORMAT_RGBA8;
170	desc.environment.defaults.depth_format = SG_PIXELFORMAT_DEPTH;
171	desc.environment.defaults.sample_count = 1;
172	desc.logger.func = sokol_log;
173
174	sg_setup(&desc);
175
176	if (!sg_isvalid())
177		mg_die(MG_ERR_REN_CREATE, "Failed to initialise renderer");
178
179	create_pipelines();
180	create_sampler();
181}
182
183void renderer_backend_begin(void)
184{
185	sg_pass pass = {0};
186	i32 width;
187	i32 height;
188
189	ren.pass = 0;
190
191	platform_get_drawable_size(ren.ctx, &width, &height);
192	/* (minimized window?) no need to draw */
193	if (width <= 0 || height <= 0)
194		return;
195
196	pass.action.colors[0].load_action = SG_LOADACTION_CLEAR;
197	pass.action.colors[0].clear_value.r = 0.90f;
198	pass.action.colors[0].clear_value.g = 0.80f;
199	pass.action.colors[0].clear_value.b = 0.70f;
200	pass.action.colors[0].clear_value.a = 1.00f;
201
202	pass.action.depth.load_action = SG_LOADACTION_CLEAR;
203	pass.action.depth.clear_value = 1.0f;
204
205	/* swapchain descriptor */
206	pass.swapchain.width = width;
207	pass.swapchain.height = height;
208	pass.swapchain.sample_count = 1;
209	pass.swapchain.color_format = SG_PIXELFORMAT_RGBA8;
210	pass.swapchain.depth_format = SG_PIXELFORMAT_DEPTH;
211
212	pass.swapchain.gl.framebuffer = 0;
213
214	sg_begin_pass(&pass);
215	ren.pass = 1;
216}
217
218void renderer_backend_end(void)
219{
220	if (!ren.pass)
221		return;
222
223	sg_end_pass();
224	sg_commit();
225
226	ren.pass = 0;
227}
228
229void renderer_backend_destroy(void)
230{
231	u32 id;
232
233	/* the values could be different in future so
234	   probably should keep them separate */
235	for (id = 1; id < MAX_MESHES; id++)
236		renderer_backend_mesh_destroy(id);
237	for (id = 1; id < MAX_TEXTURES; id++)
238		renderer_backend_texture_destroy(id);
239
240	sg_destroy_sampler(ren.sampler);
241	sg_destroy_pipeline(ren.pipeline);
242	sg_destroy_shader(ren.shader);
243
244	if (sg_isvalid())
245		sg_shutdown();
246
247	ren.ctx = NULL;
248	ren.pass = 0;
249}
250
251MGMesh renderer_backend_mesh_create(const MGMeshDesc* desc)
252{
253	sg_buffer_desc buffer_desc = {0};
254	RendererMesh* mesh;
255	u32 id;
256
257	if (!desc || !desc->vtc || !desc->idc || desc->vtcc == 0 || desc->idcc == 0)
258		return MGMESH_INVALID;
259
260	/* find free mesh slot */
261	for (id = 1; id < MAX_MESHES; id++) {
262		if (!ren.meshes[id].used)
263			break;
264	}
265
266	if (id == MAX_MESHES) {
267		mg_log(LOG_ERR, "Maximum mesh limit reached");
268		return MGMESH_INVALID;
269	}
270
271	/* vertex buffer */
272	mesh = &ren.meshes[id];
273	buffer_desc.data.ptr = desc->vtc;
274	buffer_desc.data.size = desc->vtcc * sizeof(MGVertex);
275	mesh->vb = sg_make_buffer(&buffer_desc);
276
277	/* index buffer */
278	buffer_desc = (sg_buffer_desc){0};
279	buffer_desc.usage.vertex_buffer = false;
280	buffer_desc.usage.index_buffer = true;
281	buffer_desc.usage.immutable = true;
282	buffer_desc.data.ptr = desc->idc;
283	buffer_desc.data.size = desc->idcc * sizeof(u16);
284	mesh->ib = sg_make_buffer(&buffer_desc);
285
286	i8 valid = sg_query_buffer_state(mesh->vb) == SG_RESOURCESTATE_VALID &&
287	           sg_query_buffer_state(mesh->ib) == SG_RESOURCESTATE_VALID;
288	if (!valid) {
289		if (sg_query_buffer_state(mesh->vb) == SG_RESOURCESTATE_VALID)
290			sg_destroy_buffer(mesh->vb);
291		if (sg_query_buffer_state(mesh->ib) == SG_RESOURCESTATE_VALID)
292			sg_destroy_buffer(mesh->ib);
293
294		*mesh = (RendererMesh){0};
295		mg_log(LOG_ERR, "Failed to create mesh buffers");
296		return MGMESH_INVALID;
297	}
298
299	mesh->ic = desc->idcc;
300	mesh->used = 1;
301	return id;
302}
303
304void renderer_backend_mesh_draw(MGMesh mesh, MGTexture texture, const MGMat4* mvp)
305{
306	RendererMesh* rmesh;
307	RendererTexture* rtexture;
308	sg_bindings bindings = {0};
309	sg_range uniforms;
310
311	if (!ren.pass || !mvp)
312		return;
313
314	if (mesh == MGMESH_INVALID || mesh >= MAX_MESHES)
315		return;
316
317	if (texture == MGTEXTURE_INVALID || texture >= MAX_TEXTURES)
318		return;
319
320	rmesh = &ren.meshes[mesh];
321	rtexture = &ren.textures[texture];
322	if (!rmesh->used || !rtexture->used)
323		return;
324
325	bindings.vertex_buffers[0] = rmesh->vb;
326	bindings.index_buffer = rmesh->ib;
327	bindings.views[0] = rtexture->view;
328	bindings.samplers[0] = ren.sampler;
329
330	uniforms.ptr = &mvp->Elements[0][0];
331	uniforms.size = sizeof(float) * 16;
332
333	sg_apply_pipeline(ren.pipeline);
334	sg_apply_bindings(&bindings);
335	sg_apply_uniforms(0, &uniforms);
336
337	sg_draw(0, rmesh->ic, 1);
338}
339
340void renderer_backend_mesh_destroy(MGMesh id)
341{
342	RendererMesh* mesh;
343
344	if (id == MGMESH_INVALID || id >= MAX_MESHES)
345		return;
346
347	mesh = &ren.meshes[id];
348	if (!mesh->used)
349		return;
350
351	sg_destroy_buffer(mesh->ib);
352	sg_destroy_buffer(mesh->vb);
353
354	*mesh = (RendererMesh){0};
355}
356
357MGTexture renderer_backend_texture_load(const char* path)
358{
359	sg_image_desc image_desc = {0};
360	sg_view_desc view_desc = {0};
361	RendererTexture* texture;
362	stbi_uc* pixels;
363	int width;
364	int height;
365	int channels;
366	u32 id;
367
368	if (!path)
369		return MGTEXTURE_INVALID;
370
371	/* find free texture slot */
372	for (id = 1; id < MAX_TEXTURES; id++) {
373		if (!ren.textures[id].used)
374			break;
375	}
376
377	if (id == MAX_TEXTURES) {
378		mg_log(LOG_ERR, "Maximum texture limit reached");
379		return MGTEXTURE_INVALID;
380	}
381
382	texture = &ren.textures[id];
383	pixels = stbi_load(path, &width, &height, &channels, 4);
384	if (!pixels) {
385		mg_log(LOG_ERR, "Failed to load texture: %s", path);
386		return MGTEXTURE_INVALID;
387	}
388
389	image_desc.width = width;
390	image_desc.height = height;
391	image_desc.pixel_format = SG_PIXELFORMAT_RGBA8;
392	image_desc.data.mip_levels[0].ptr = pixels;
393	image_desc.data.mip_levels[0].size =width * height * 4;
394
395	texture->image = sg_make_image(&image_desc);
396	stbi_image_free(pixels);
397	if (sg_query_image_state(texture->image) != SG_RESOURCESTATE_VALID) {
398		*texture = (RendererTexture){0};
399		mg_log(LOG_ERR, "Failed to create texture: %s", path);
400		return MGTEXTURE_INVALID;
401	}
402
403	view_desc.texture.image = texture->image;
404	texture->view = sg_make_view(&view_desc);
405
406	if (sg_query_view_state(texture->view) != SG_RESOURCESTATE_VALID) {
407		sg_destroy_image(texture->image);
408		*texture = (RendererTexture){0};
409		mg_log(LOG_ERR, "Failed to create texture view: %s", path);
410		return MGTEXTURE_INVALID;
411	}
412
413	texture->used = 1;
414
415	return id;
416}
417
418void renderer_backend_texture_destroy(MGTexture id)
419{
420	RendererTexture* texture;
421
422	if (id == MGTEXTURE_INVALID || id >= MAX_TEXTURES)
423		return;
424
425	texture = &ren.textures[id];
426
427	if (!texture->used)
428		return;
429
430	sg_destroy_view(texture->view);
431	sg_destroy_image(texture->image);
432
433	*texture = (RendererTexture){0};
434}