diff options
| author | Vasco <vasco.guilherme.alves@gmail.com> | 2026-08-12 17:47:50 +0100 |
|---|---|---|
| committer | Vasco <vasco.guilherme.alves@gmail.com> | 2026-08-12 17:47:50 +0100 |
| commit | 95e3e76fd9c789cbf2d4409ef49456addc71643f (patch) | |
| tree | 34161614f5f0ae909f58eb5e58a2ffd26c2f0e9f /rend.c | |
Diffstat (limited to 'rend.c')
| -rw-r--r-- | rend.c | 604 |
1 files changed, 604 insertions, 0 deletions
@@ -0,0 +1,604 @@ +#include <assert.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <sys/param.h> +#include <unistd.h> + +#include "rend.h" +#include "rend_internal.h" +#include "rend_vk.c" +#include "rend_vk_internal.h" + +/* clean up functions */ + +static void rend__renderer_destroy_recursive(RendRenderer renderer); +static void rend__pipeline_destroy_recursive(RendPipeline pipeline); +static bool rend__renderer_init_recursive(RendRenderer renderer, RendBackendType backend, bool auto_pick); + +static RendRenderer rend_renderers_head = NULL; + +// TODO: automatically clear buffers and textures +// static RendBuffer rend_buffer_head = NULL; +// static RendTexture rend_texture_head = NULL; + +bool rend_backend_vk14_initialized = false; + +RendVTable rend_vtables[] = { + [REND_BACKEND_VULKAN_14] = { + .renderer_create = rend_vk14_renderer_create, + .renderer_destroy = rend_vk14_renderer_destroy, + .renderer_frame_begin = rend_vk14_renderer_frame_begin, + .renderer_frame_end = rend_vk14_renderer_frame_end, + + .buffer_create_lifetime = rend_vk14_buffer_create_lifetime, + .buffer_destroy = rend_vk14_buffer_destroy, + .buffer_copy = rend_vk14_buffer_copy, + + .texture_create = rend_vk14_texture_create, + .texture_destroy = rend_vk14_texture_destroy, + .texture_copy_buffer = rend_vk14_texture_copy_buffer, + .texture_blit = rend_vk14_texture_blit, + + .pipeline_create = rend_vk14_pipeline_create, + .pipeline_bind = rend_vk14_pipeline_bind, + .pipeline_push_constants = rend_vk14_pipeline_push_constants, + + .pipeline_bind_vertex_buffer = rend_vk14_pipeline_bind_vertex_buffer, + .pipeline_bind_index_buffer = rend_vk14_pipeline_bind_index_buffer, + + .pipeline_dispatch = rend_vk14_pipeline_dispatch, + .pipeline_draw = rend_vk14_pipeline_draw, + .pipeline_draw_indexed = rend_vk14_pipeline_draw_indexed, + .pipeline_set_blend = rend_vk14_pipeline_set_blend, + + .renderer_render_pass_begin = rend_vk14_renderer_render_pass_begin, + .renderer_render_pass_begin_texture = rend_vk14_renderer_render_pass_begin_texture, + .renderer_render_pass_end = rend_vk14_renderer_render_pass_end, + .renderer_render_pass_end_texture = rend_vk14_renderer_render_pass_end_texture, + + .descriptor_write_buffer = rend_vk14_descriptor_write_buffer, + .descriptor_write_texture = rend_vk14_descriptor_write_texture, + }, +}; + +extern void +rend_quit() +{ + rend__renderer_destroy_recursive(rend_renderers_head); + + if (rend_backend_vk14_initialized) { + rend_vk14_quit(); + rend_backend_vk14_initialized = false; + } + +} + +extern RendRenderer +rend_renderer_create(P_Window *target, RendBackendType backend, void* device, bool vsync, RendBindingInfo *bind_info) +{ + RendRenderer rend = rmalloc(sizeof *rend); + rend->next = rend_renderers_head; + rend->prev = NULL; + rend_renderers_head = rend; + if(rend->next) { + rend->next->prev = rend; + } + + /* create vtable for backend */ + rend__renderer_init_recursive(rend, backend, false); + + rend->vsync = vsync; + rend->bind_info = *bind_info; + rend->window = target; + + rend->pipeline_head = 0; + + if (rend_vtables[rend->backend].renderer_create(rend, target)) { + return rend; + } + + return NULL; +} + +extern void +rend_renderer_destroy(RendRenderer renderer) +{ + if (!renderer) return; + + rend__pipeline_destroy_recursive(renderer->pipeline_head); + + if (renderer->backend != 0) rend_vtables[renderer->backend].renderer_destroy(renderer); + + if (renderer->prev) renderer->prev->next = renderer->next; + else rend_renderers_head = renderer->next; + + if (renderer->next) renderer->next->prev = renderer->prev; + + rfree(renderer); +} + +extern bool +rend_renderer_frame_begin(RendRenderer renderer) +{ + renderer->in_frame = 1; + return rend_vtables[renderer->backend].renderer_frame_begin(renderer); +} + +extern void +rend_renderer_frame_end(RendRenderer renderer, float *delta) +{ + renderer->in_frame = 0; + rend_vtables[renderer->backend].renderer_frame_end(renderer, delta); + renderer->frame_count++; +} + +extern void +rend_renderer_render_pass_begin(RendRenderer renderer, float r, float g ,float b, float a) +{ + renderer->in_pass = 1; + rend_vtables[renderer->backend].renderer_render_pass_begin(renderer, r, g, b, a); +} + +extern void +rend_renderer_render_pass_begin_texture(RendRenderer renderer, RendTexture *texture) +{ + renderer->in_pass = 1; + rend_vtables[renderer->backend].renderer_render_pass_begin_texture(renderer, texture); +} + +extern void +rend_renderer_render_pass_end_texture(RendRenderer renderer, RendTexture *texture) +{ + renderer->in_pass = 0; + rend_vtables[renderer->backend].renderer_render_pass_end_texture(renderer, texture); +} + + +extern void +rend_renderer_render_pass_end(RendRenderer renderer) +{ + renderer->in_pass = 0; + rend_vtables[renderer->backend].renderer_render_pass_end(renderer); +} + + +extern void +rend_descriptor_write_ubo(RendRenderer renderer, RendBuffer ubo, uint32_t binding, uint32_t slot) +{ + rend_vtables[renderer->backend].descriptor_write_buffer(renderer, ubo, binding, slot, 0, ubo.size, true); +} + +extern void +rend_descriptor_write_ssbo(RendRenderer renderer, RendBuffer ssbo, uint32_t binding, uint32_t slot) +{ + rend_vtables[renderer->backend].descriptor_write_buffer(renderer, ssbo, binding, slot, 0, ssbo.size, false); +} + +extern RendBuffer +rend_buffer_create(RendRenderer renderer, size_t size, RendBufferType type, bool gpu) { + RendBuffer buffer = rend_vtables[renderer->backend].buffer_create_lifetime(renderer, size, type, gpu, REND_LIFETIME_PERMANENT); + buffer.backend = renderer->backend; + return buffer; +} + +extern void +rend_buffer_destroy(RendBuffer *buffer) { + rend_vtables[buffer->backend].buffer_destroy(buffer); +} + +extern void +rend_buffer_write(RendRenderer renderer, RendBuffer *buffer, const void *data, size_t size, size_t offset) +{ + if (buffer->mapped_memory) { + uint8_t *ptr = buffer->mapped_memory; + memcpy(ptr + offset, data, size); + } else { + /* if the memory is bound to the device we must create and deallocate a transfer buffer */ + RendBuffer transfer_buffer = rend_vtables[buffer->backend].buffer_create_lifetime(renderer, size, REND_BUFFER_TRANSFER, false, REND_LIFETIME_FRAME); + memcpy(transfer_buffer.mapped_memory, data, size); + rend_vtables[buffer->backend].buffer_copy(renderer, buffer, offset, &transfer_buffer, 0, size); + rend_vtables[buffer->backend].buffer_destroy(&transfer_buffer); + } +} + +extern void +rend_buffer_copy(RendRenderer renderer, RendBuffer *dest, size_t dest_offset, RendBuffer *src, size_t src_offset, size_t bytes) +{ + rend_vtables[renderer->backend].buffer_copy(renderer, dest, dest_offset, src, src_offset, bytes); +} + +extern uint64_t +rend_buffer_address(RendBuffer *buffer) +{ + assert(buffer != NULL); + return buffer->gpu_address; +} + +extern RendTexture +rend_texture_create(RendRenderer renderer, uint32_t width, uint32_t height, uint32_t depth, uint32_t mip_levels, uint32_t layers, RendFormat format) +{ + RendTexture tex = rend_vtables[renderer->backend].texture_create(renderer, width, height, depth, mip_levels, layers, format); + tex.backend = renderer->backend; + return tex; +} + +extern RendTexture +rend_texture_create_from_data(RendRenderer renderer, const void *data, uint32_t width, uint32_t height, RendFormat format) +{ + RendTexture tex = rend_vtables[renderer->backend].texture_create(renderer, width, height, 1, 1, 1, format); + tex.backend = renderer->backend; + + uint32_t size = width * height * rend_format_size[format]; + + RendBuffer staging_buffer = rend_vtables[renderer->backend].buffer_create_lifetime(renderer, size, REND_BUFFER_TRANSFER, false, REND_LIFETIME_FRAME); + memcpy(staging_buffer.mapped_memory, data, size); + rend_vtables[renderer->backend].texture_copy_buffer(renderer, &tex, &staging_buffer); + rend_vtables[renderer->backend].buffer_destroy(&staging_buffer); + return tex; +} + +extern void +rend_texture_copy_data(RendRenderer renderer, RendTexture *texture, const void *data, size_t size) +{ + RendBuffer staging_buffer = rend_vtables[renderer->backend].buffer_create_lifetime(renderer, size, REND_BUFFER_TRANSFER, false, REND_LIFETIME_FRAME); + memcpy(staging_buffer.mapped_memory, data, size); + rend_vtables[renderer->backend].texture_copy_buffer(renderer, texture, &staging_buffer); + rend_vtables[renderer->backend].buffer_destroy(&staging_buffer); +} + + +extern void +rend_texture_copy_buffer(RendRenderer renderer, RendTexture *texture, RendBuffer *buffer) +{ + rend_vtables[renderer->backend].texture_copy_buffer(renderer, texture, buffer); +} + +extern void +rend_texture_blit(RendRenderer renderer, RendTexture *src, RendTexture *dst, uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h, uint32_t dst_x, uint32_t dst_y, uint32_t dst_w, uint32_t dst_h) +{ + rend_vtables[renderer->backend].texture_blit(renderer, src, dst, src_x, src_y, src_w, src_h, dst_x, dst_y, dst_w, dst_h); +} + +extern uint64_t +rend_texture_id(RendTexture *texture) +{ + return texture->id; +} + +extern void +rend_texture_destroy(RendRenderer renderer, RendTexture *tex) +{ + rend_vtables[renderer->backend].texture_destroy(renderer, tex); +} + +extern void +rend_pipeline_push_constants(RendPipeline pipeline, void *push_data, size_t size) +{ + assert(pipeline != NULL && "Pipeline handle is NULL!"); + assert(rend_vtables[pipeline->backend].pipeline_push_constants && "Backend function not implemented!"); + rend_vtables[pipeline->backend].pipeline_push_constants(pipeline, push_data, size); +} + +extern void +rend_pipeline_bind_vertex_buffer(RendPipeline pipeline, uint32_t binding, RendBuffer buffer, size_t offset) +{ + rend_vtables[pipeline->backend].pipeline_bind_vertex_buffer(pipeline, binding, buffer, offset); +} + +extern void +rend_pipeline_bind_index_buffer(RendPipeline pipeline, RendBuffer buffer, size_t offset, RendIndexType index_type) +{ + rend_vtables[pipeline->backend].pipeline_bind_index_buffer(pipeline, buffer, offset, index_type); +} + +extern void +rend_pipeline_bind_texture(RendPipeline pipeline, RendTexture *texture, uint32_t binding, uint32_t slot) +{ + rend_vtables[pipeline->backend].descriptor_write_texture(pipeline->backend_ctx, texture, binding, slot); +} + +extern void +rend_descriptor_write_texture(RendRenderer renderer, RendTexture *texture, uint32_t binding, uint32_t slot) +{ + rend_vtables[renderer->backend].descriptor_write_texture(renderer->context, texture, binding, slot); +} + + +extern RendPipeline +rend_pipeline_create_graphics_spirv(RendRenderer renderer, uint8_t *vertex_bytes, size_t vertex_size, uint8_t *frag_bytes, size_t frag_size, const RendVertexBinding *vertex_bindings, uint32_t vertex_binding_count, const RendVertexAttributes *vertex_attributes, uint32_t vertex_attribute_count, const RendPushConstantInfo *push_constants, uint32_t push_constant_count, RendPolygonMode polygon_mode, RendCullMode cull_mode, RendTopology topology, RendFormat color_format, bool depth_test_enable) +{ + RendPipeline pipeline = rmalloc(sizeof *pipeline); + + Rend__PipelineConfig config = { + .vertex_bindings = vertex_bindings, + .vertex_binding_count = vertex_binding_count, + .vertex_attributes = vertex_attributes, + .vertex_attribute_count = vertex_attribute_count, + .push_constants = push_constants, + .push_constant_count = push_constant_count, + .polygon_mode = polygon_mode, + .cull_mode = cull_mode, + .topology = topology, + .depth_test_enable = depth_test_enable, + + .color_format = color_format, + }; + + if (rend_vtables[renderer->backend].pipeline_create(renderer, pipeline, config, REND__PIPELINE_GRAPHICS, vertex_bytes, vertex_size, frag_bytes, frag_size, NULL, 0)) { + pipeline->backend = renderer->backend; + + pipeline->next = renderer->pipeline_head; + pipeline->prev = NULL; + renderer->pipeline_head = pipeline; + pipeline->type = REND__PIPELINE_GRAPHICS; + if(pipeline->next) { + pipeline->next->prev = pipeline; + } + + return pipeline; + } + rfree(pipeline); + return NULL; +} + +extern RendPipeline +rend_pipeline_create_graphics_bindless_spirv(RendRenderer renderer, uint8_t *vertex_bytes, size_t vertex_size, uint8_t *frag_bytes, size_t frag_size, const RendPushConstantInfo *push_constants, uint32_t push_constant_count, RendPolygonMode polygon_mode, RendCullMode cull_mode, RendTopology topology, RendFormat color_format, bool depth_test_enable) +{ + RendPipeline pipeline = rmalloc(sizeof *pipeline); + + Rend__PipelineConfig config = { + .vertex_bindings = NULL, + .vertex_binding_count = 0, + .vertex_attributes = NULL, + .vertex_attribute_count = 0, + .push_constants = push_constants, + .push_constant_count = push_constant_count, + .polygon_mode = polygon_mode, + .cull_mode = cull_mode, + .topology = topology, + .depth_test_enable = depth_test_enable, + + .color_format = color_format, + }; + + if (rend_vtables[renderer->backend].pipeline_create(renderer, pipeline, config, REND__PIPELINE_GRAPHICS, vertex_bytes, vertex_size, frag_bytes, frag_size, NULL, 0)) { + pipeline->backend = renderer->backend; + + pipeline->next = renderer->pipeline_head; + pipeline->prev = NULL; + renderer->pipeline_head = pipeline; + pipeline->type = REND__PIPELINE_GRAPHICS; + if(pipeline->next) { + pipeline->next->prev = pipeline; + } + + return pipeline; + } + rfree(pipeline); + return NULL; +} + +extern RendPipeline +rend_pipeline_create_compute_spirv(RendRenderer renderer, const uint8_t *compute_bytes, size_t compute_size, const RendPushConstantInfo *push_constants, uint32_t push_constant_count) +{ + RendPipeline pipeline = rmalloc(sizeof *pipeline); + Rend__PipelineConfig config = { + .push_constants = push_constants, + .push_constant_count = push_constant_count, + }; + + if (rend_vtables[renderer->backend].pipeline_create(renderer, pipeline, config, REND__PIPELINE_COMPUTE, compute_bytes, compute_size, NULL, 0, NULL, 0)) { + pipeline->backend = renderer->backend; + pipeline->type = REND__PIPELINE_COMPUTE; + + pipeline->next = renderer->pipeline_head; + pipeline->prev = NULL; + renderer->pipeline_head = pipeline; + if(pipeline->next) { + pipeline->next->prev = pipeline; + } + + return pipeline; + } + rfree(pipeline); + return NULL; +} + +extern RendPipeline +rend_pipeline_create_meshlet_spirv(RendRenderer renderer, uint8_t *meshlet_bytes, size_t meshlet_size, uint8_t *frag_bytes, size_t frag_size, const RendPushConstantInfo *push_constants, uint32_t push_constant_count, RendPolygonMode polygon_mode, RendCullMode cull_mode, bool depth_test_enable) +{ + RendPipeline pipeline = rmalloc(sizeof *pipeline); + Rend__PipelineConfig config = { + .push_constants = push_constants, + .push_constant_count = push_constant_count, + .polygon_mode = polygon_mode, + .cull_mode = cull_mode, + .depth_test_enable = depth_test_enable + }; + if (rend_vtables[renderer->backend].pipeline_create(renderer, pipeline, config, REND__PIPELINE_MESH, meshlet_bytes, meshlet_size, frag_bytes, frag_size, NULL, 0)) { + pipeline->backend = renderer->backend; + pipeline->type = REND__PIPELINE_MESH; + + pipeline->next = renderer->pipeline_head; + pipeline->prev = NULL; + renderer->pipeline_head = pipeline; + if(pipeline->next) { + pipeline->next->prev = pipeline; + } + + return pipeline; + } + rfree(pipeline); + return NULL; +} + +extern void +rend_pipeline_bind(RendPipeline pipeline) +{ + rend_vtables[pipeline->backend].pipeline_bind(pipeline); +} + +extern void +rend_pipeline_dispatch(RendPipeline pipeline, uint32_t x, uint32_t y, uint32_t z) +{ + rend_vtables[pipeline->backend].pipeline_dispatch(pipeline, x, y, z); +} + +extern void +rend_pipeline_draw(RendPipeline pipeline, size_t count, uint32_t instance_count) +{ + rend_vtables[pipeline->backend].pipeline_draw(pipeline, count, instance_count); +} + +extern void +rend_pipeline_draw_indexed(RendPipeline pipeline, uint32_t index_count, uint32_t first_index, int32_t vertex_offset, uint32_t instance_count) +{ + rend_vtables[pipeline->backend].pipeline_draw_indexed(pipeline, index_count, first_index, vertex_offset, instance_count); +} + +extern void +rend_pipeline_set_blend(RendPipeline pipeline, bool blend) +{ + rend_vtables[pipeline->backend].pipeline_set_blend(pipeline, blend); +} + +extern void +rend_cmd_render_begin(RendRenderer renderer, float r, float g, float b, float a) +{ + renderer->in_pass = 1; + rend_vtables[renderer->backend].renderer_render_pass_begin(renderer, r, g, b, a); +} + +extern void +rend_cmd_render_begin_texture(RendRenderer renderer, RendTexture *texture) +{ + renderer->in_pass = 1; + rend_vtables[renderer->backend].renderer_render_pass_begin_texture(renderer, texture); +} + +extern void +rend_cmd_render_end(RendRenderer renderer) +{ + renderer->in_pass = 0; + rend_vtables[renderer->backend].renderer_render_pass_end(renderer); +} + +extern void +rend_cmd_render_end_texture(RendRenderer renderer, RendTexture *texture) +{ + renderer->in_pass = 0; + rend_vtables[renderer->backend].renderer_render_pass_end_texture(renderer, texture); +} + +extern void +rend_cmd_bind_pipeline(RendPipeline pipeline) +{ + rend_vtables[pipeline->backend].pipeline_bind(pipeline); +} + +extern void +rend_cmd_bind_vertex_buffer(RendPipeline pipeline, uint32_t binding, RendBuffer buffer, size_t offset) +{ + rend_vtables[pipeline->backend].pipeline_bind_vertex_buffer(pipeline, binding, buffer, offset); +} + +extern void +rend_cmd_bind_index_buffer(RendPipeline pipeline, RendBuffer buffer, size_t offset, RendIndexType index_type) +{ + rend_vtables[pipeline->backend].pipeline_bind_index_buffer(pipeline, buffer, offset, index_type); +} + +extern void +rend_cmd_push_constants(RendPipeline pipeline, void *push_data, size_t size) +{ + assert(pipeline != NULL && "Pipeline handle is NULL!"); + assert(rend_vtables[pipeline->backend].pipeline_push_constants && "Backend function not implemented!"); + rend_vtables[pipeline->backend].pipeline_push_constants(pipeline, push_data, size); +} + +extern void +rend_cmd_dispatch(RendPipeline pipeline, uint32_t x, uint32_t y, uint32_t z) +{ + rend_vtables[pipeline->backend].pipeline_dispatch(pipeline, x, y, z); +} + +extern void +rend_cmd_draw(RendPipeline pipeline, size_t count, uint32_t instance_count) +{ + rend_vtables[pipeline->backend].pipeline_draw(pipeline, count, instance_count); +} + +extern void +rend_cmd_draw_indexed(RendPipeline pipeline, uint32_t index_count, uint32_t first_index, int32_t vertex_offset, uint32_t instance_count) +{ + rend_vtables[pipeline->backend].pipeline_draw_indexed(pipeline, index_count, first_index, vertex_offset, instance_count); +} + +extern void +rend_cmd_blit(RendRenderer renderer, RendTexture *src, RendTexture *dst, uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h, uint32_t dst_x, uint32_t dst_y, uint32_t dst_w, uint32_t dst_h) +{ + assert(renderer->in_frame && "Must be called while rendering a frame!"); + assert(!renderer->in_pass && "Must be called outside a render pass!"); + renderer->in_pass = true; + rend_vtables[renderer->backend].texture_blit(renderer, src, dst, src_x, src_y, src_w, src_h, dst_x, dst_y, dst_w, dst_h); +} + +static bool +rend__renderer_init_recursive(RendRenderer renderer, RendBackendType backend, bool auto_pick) +{ + /* set autopick to true and set backend to first backend*/ + if (backend == REND_BACKEND_AUTO) { + backend++; + auto_pick = true; + } + + switch (backend) { + default: + /* unless using auto pick to pick renderers, crash */ + if (!auto_pick) REND__CRASH("Invalid backend type!"); + + case REND_BACKEND_VULKAN_14: + if (rend_vk14_init()) { + rend_backend_vk14_initialized = true; + renderer->backend = REND_BACKEND_VULKAN_14; + return true; + } + break; + } + + if (auto_pick) { + backend++; + if (backend >= REND_BACKEND_COUNT) { + return false; + } + + /* recursively try next backend */ + return rend__renderer_init_recursive(renderer, backend, auto_pick); + } + + return false; +} + +static void +rend__renderer_destroy_recursive(RendRenderer renderer) +{ + if (renderer == NULL) return; + + rend__renderer_destroy_recursive(renderer->next); + + if (renderer->backend != 0) { + rend_vtables[renderer->backend].renderer_destroy(renderer); + } + + rend__pipeline_destroy_recursive(renderer->pipeline_head); + rfree(renderer); +} + +static void +rend__pipeline_destroy_recursive(RendPipeline pipeline) +{ + if (pipeline == NULL) return; + rend__pipeline_destroy_recursive(pipeline->next); + rfree(pipeline); +} |
