From cc3429220b19df8c5ca6abf28a02cd38d08de32d Mon Sep 17 00:00:00 2001 From: Vasco Date: Mon, 24 Aug 2026 17:47:30 +0100 Subject: Cool 0.0.0. Rend 1.0.4. --- Rend/rend.c | 125 +- Rend/rend.h | 3 +- Rend/rend_internal.h | 29 +- Rend/rend_vk.c | 3637 ++++++++++++++++++++++------------------------ Rend/rend_vk_allocator.c | 118 -- Rend/rend_vk_arena.c | 264 ---- Rend/rend_vk_device.c | 377 ----- Rend/rend_vk_image.c | 125 -- Rend/rend_vk_internal.c | 1014 +++++++++++++ Rend/rend_vk_internal.h | 177 --- 10 files changed, 2793 insertions(+), 3076 deletions(-) delete mode 100644 Rend/rend_vk_allocator.c delete mode 100644 Rend/rend_vk_arena.c delete mode 100644 Rend/rend_vk_device.c delete mode 100644 Rend/rend_vk_image.c create mode 100644 Rend/rend_vk_internal.c delete mode 100644 Rend/rend_vk_internal.h (limited to 'Rend') diff --git a/Rend/rend.c b/Rend/rend.c index 4d85745..f390de2 100644 --- a/Rend/rend.c +++ b/Rend/rend.c @@ -8,12 +8,10 @@ #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 void rend__pipeline_free_list(RendPipeline pipeline); static bool rend__renderer_init_recursive(RendRenderer renderer, RendBackendType backend, bool auto_pick); static RendRenderer rend_renderers_head = NULL; @@ -65,40 +63,44 @@ RendVTable rend_vtables[] = { extern void rend_quit() { - rend__renderer_destroy_recursive(rend_renderers_head); + while (rend_renderers_head) + rend_renderer_destroy(rend_renderers_head); if (rend_backend_vk_initialized) { rend_vk_quit(); rend_backend_vk_initialized = false; } - } extern RendRenderer rend_renderer_create(PeakWindow *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; - } + if (!rend) return NULL; + memset(rend, 0, sizeof *rend); - rend__renderer_init_recursive(rend, backend, false); + if (!rend__renderer_init_recursive(rend, backend, false)) { + rfree(rend); + return NULL; + } rend->vsync = vsync; rend->bind_info = *bind_info; rend->window = target; - rend->pipeline_head = 0; rend->context = rend_vtables[rend->backend].renderer_create(target, bind_info); - if (rend->context) { - return rend; + if (!rend->context) { + rfree(rend); + return NULL; } - return NULL; + rend->next = rend_renderers_head; + rend->prev = NULL; + rend_renderers_head = rend; + if (rend->next) + rend->next->prev = rend; + + return rend; } extern void @@ -107,10 +109,12 @@ rend_renderer_destroy(RendRenderer renderer) RASSERT(renderer && renderer->context, "Invalid renderer."); if (!renderer) return; - rend__pipeline_destroy_recursive(renderer->pipeline_head); + rend__pipeline_free_list(renderer->pipeline_head); + renderer->pipeline_head = NULL; - if (renderer->backend != 0) { + if (renderer->backend != 0 && renderer->context) { rend_vtables[renderer->backend].renderer_destroy(renderer->context); + renderer->context = NULL; } if (renderer->prev) renderer->prev->next = renderer->next; @@ -118,6 +122,8 @@ rend_renderer_destroy(RendRenderer renderer) if (renderer->next) renderer->next->prev = renderer->prev; + renderer->next = NULL; + renderer->prev = NULL; rfree(renderer); } @@ -153,21 +159,21 @@ 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); + rend_vtables[renderer->backend].renderer_render_pass_begin(renderer->context, 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); + rend_vtables[renderer->backend].renderer_render_pass_begin_texture(renderer->context, 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); + rend_vtables[renderer->backend].renderer_render_pass_end_texture(renderer->context, texture); } @@ -175,25 +181,25 @@ extern void rend_renderer_render_pass_end(RendRenderer renderer) { renderer->in_pass = 0; - rend_vtables[renderer->backend].renderer_render_pass_end(renderer); + rend_vtables[renderer->backend].renderer_render_pass_end(renderer->context); } 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); + rend_vtables[renderer->backend].descriptor_write_buffer(renderer->context, 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); + rend_vtables[renderer->backend].descriptor_write_buffer(renderer->context, 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); + RendBuffer buffer = rend_vtables[renderer->backend].buffer_create_lifetime(renderer->context, size, type, gpu, REND_LIFETIME_PERMANENT); buffer.backend = renderer->backend; return buffer; } @@ -211,9 +217,9 @@ rend_buffer_write(RendRenderer renderer, RendBuffer *buffer, const void *data, s 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); + RendBuffer transfer_buffer = rend_vtables[buffer->backend].buffer_create_lifetime(renderer->context, 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_copy(renderer->context, buffer, offset, &transfer_buffer, 0, size); rend_vtables[buffer->backend].buffer_destroy(&transfer_buffer); } } @@ -221,7 +227,7 @@ rend_buffer_write(RendRenderer renderer, RendBuffer *buffer, const void *data, s 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); + rend_vtables[renderer->backend].buffer_copy(renderer->context, dest, dest_offset, src, src_offset, bytes); } extern uint64_t @@ -234,7 +240,7 @@ rend_buffer_address(RendBuffer *buffer) 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); + RendTexture tex = rend_vtables[renderer->backend].texture_create(renderer->context, width, height, depth, mip_levels, layers, format); tex.backend = renderer->backend; return tex; } @@ -242,14 +248,14 @@ rend_texture_create(RendRenderer renderer, uint32_t width, uint32_t height, uint 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); + RendTexture tex = rend_vtables[renderer->backend].texture_create(renderer->context, 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); + RendBuffer staging_buffer = rend_vtables[renderer->backend].buffer_create_lifetime(renderer->context, 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].texture_copy_buffer(renderer->context, &tex, &staging_buffer); rend_vtables[renderer->backend].buffer_destroy(&staging_buffer); return tex; } @@ -257,9 +263,9 @@ rend_texture_create_from_data(RendRenderer renderer, const void *data, uint32_t 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); + RendBuffer staging_buffer = rend_vtables[renderer->backend].buffer_create_lifetime(renderer->context, 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].texture_copy_buffer(renderer->context, texture, &staging_buffer); rend_vtables[renderer->backend].buffer_destroy(&staging_buffer); } @@ -267,13 +273,13 @@ rend_texture_copy_data(RendRenderer renderer, RendTexture *texture, const void * extern void rend_texture_copy_buffer(RendRenderer renderer, RendTexture *texture, RendBuffer *buffer) { - rend_vtables[renderer->backend].texture_copy_buffer(renderer, texture, buffer); + rend_vtables[renderer->backend].texture_copy_buffer(renderer->context, 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); + rend_vtables[renderer->backend].texture_blit(renderer->context, src, dst, src_x, src_y, src_w, src_h, dst_x, dst_y, dst_w, dst_h); } extern uint64_t @@ -285,7 +291,7 @@ rend_texture_id(RendTexture *texture) extern void rend_texture_destroy(RendRenderer renderer, RendTexture *tex) { - rend_vtables[renderer->backend].texture_destroy(renderer, tex); + rend_vtables[renderer->backend].texture_destroy(renderer->context, tex); } extern void @@ -341,7 +347,7 @@ rend_pipeline_create_graphics_spirv(RendRenderer renderer, uint8_t *vertex_bytes .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)) { + if (rend_vtables[renderer->backend].pipeline_create(renderer->context, pipeline, config, REND__PIPELINE_GRAPHICS, vertex_bytes, vertex_size, frag_bytes, frag_size, NULL, 0)) { pipeline->backend = renderer->backend; pipeline->next = renderer->pipeline_head; @@ -378,7 +384,7 @@ rend_pipeline_create_graphics_bindless_spirv(RendRenderer renderer, uint8_t *ver .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)) { + if (rend_vtables[renderer->backend].pipeline_create(renderer->context, pipeline, config, REND__PIPELINE_GRAPHICS, vertex_bytes, vertex_size, frag_bytes, frag_size, NULL, 0)) { pipeline->backend = renderer->backend; pipeline->next = renderer->pipeline_head; @@ -404,7 +410,7 @@ rend_pipeline_create_compute_spirv(RendRenderer renderer, const uint8_t *compute .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)) { + if (rend_vtables[renderer->backend].pipeline_create(renderer->context, pipeline, config, REND__PIPELINE_COMPUTE, compute_bytes, compute_size, NULL, 0, NULL, 0)) { pipeline->backend = renderer->backend; pipeline->type = REND__PIPELINE_COMPUTE; @@ -432,7 +438,7 @@ rend_pipeline_create_meshlet_spirv(RendRenderer renderer, uint8_t *meshlet_bytes .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)) { + if (rend_vtables[renderer->backend].pipeline_create(renderer->context, pipeline, config, REND__PIPELINE_MESH, meshlet_bytes, meshlet_size, frag_bytes, frag_size, NULL, 0)) { pipeline->backend = renderer->backend; pipeline->type = REND__PIPELINE_MESH; @@ -483,28 +489,28 @@ 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); + rend_vtables[renderer->backend].renderer_render_pass_begin(renderer->context, 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); + rend_vtables[renderer->backend].renderer_render_pass_begin_texture(renderer->context, texture); } extern void rend_cmd_render_end(RendRenderer renderer) { renderer->in_pass = 0; - rend_vtables[renderer->backend].renderer_render_pass_end(renderer); + rend_vtables[renderer->backend].renderer_render_pass_end(renderer->context); } 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); + rend_vtables[renderer->backend].renderer_render_pass_end_texture(renderer->context, texture); } extern void @@ -557,7 +563,7 @@ rend_cmd_blit(RendRenderer renderer, RendTexture *src, RendTexture *dst, uint32_ RASSERT(renderer->in_frame && "Must be called while rendering a frame!"); RASSERT(!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); + rend_vtables[renderer->backend].texture_blit(renderer->context, src, dst, src_x, src_y, src_w, src_h, dst_x, dst_y, dst_w, dst_h); } static bool @@ -597,24 +603,13 @@ rend__renderer_init_recursive(RendRenderer renderer, RendBackendType backend, bo } static void -rend__renderer_destroy_recursive(RendRenderer renderer) +rend__pipeline_free_list(RendPipeline pipeline) { - if (renderer == NULL) return; - - rend__renderer_destroy_recursive(renderer->next); - - if (renderer->backend != 0) { - rend_vtables[renderer->backend].renderer_destroy(renderer); + while (pipeline) { + RendPipeline next = pipeline->next; + pipeline->next = NULL; + pipeline->prev = NULL; + rfree(pipeline); + pipeline = next; } - - 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); } diff --git a/Rend/rend.h b/Rend/rend.h index c4ba1d4..489686a 100644 --- a/Rend/rend.h +++ b/Rend/rend.h @@ -15,7 +15,7 @@ #define REND_MAJOR 1 // breaking API changes #define REND_MINOR 0 // non-breaking features -#define REND_PATCH 3 // non-breaking patches and bug fixes +#define REND_PATCH 4 // non-breaking patches and bug fixes #ifndef PEAK_VULKAN #define PEAK_VULKAN @@ -284,6 +284,7 @@ enum RendBufferType_t { * 1.0.1 - @vasco - render pass that targets textures * 1.0.2 - @vasco - Peak instead of Podium * 1.0.3 - @vasco - frame_begin no longer sticks in_frame or burns timeline on OUT_OF_DATE + * 1.0.4 - @vasco - vulkan backend collapsed; renderer create fails cleanly * * 1.0.0 finished API release * diff --git a/Rend/rend_internal.h b/Rend/rend_internal.h index 4993d92..78275cc 100644 --- a/Rend/rend_internal.h +++ b/Rend/rend_internal.h @@ -1,9 +1,6 @@ #ifndef REND_INTERNAL_H #define REND_INTERNAL_H -#include "rend_vk_internal.h" - - #if defined(REND_DEBUG) #ifndef P_LOG_DEBUG_ENABLED #define P_LOG_DEBUG_ENABLED 1 @@ -78,19 +75,19 @@ typedef struct { bool (*renderer_frame_begin)(RendContextHandle); void (*renderer_frame_end)(RendContextHandle, float *delta); - void (*descriptor_write_buffer)(RendRenderer renderer, RendBuffer ubo, uint32_t binding, uint32_t slot, uint32_t offset, uint32_t size, bool is_ubo); - void (*descriptor_write_texture)(void *ctx, RendTexture *texture, uint32_t binding, uint32_t slot); + void (*descriptor_write_buffer)(RendContextHandle handle, RendBuffer ubo, uint32_t binding, uint32_t slot, uint32_t offset, uint32_t size, bool is_ubo); + void (*descriptor_write_texture)(RendContextHandle handle, RendTexture *texture, uint32_t binding, uint32_t slot); - RendBuffer (*buffer_create_lifetime)(RendRenderer renderer, size_t size, RendBufferType type, bool gpu, int lifetime); + RendBuffer (*buffer_create_lifetime)(RendContextHandle handle, size_t size, RendBufferType type, bool gpu, int lifetime); void (*buffer_destroy)(RendBuffer *buffer); - void (*buffer_copy)(RendRenderer renderer, RendBuffer *dest, size_t dest_offset, RendBuffer *src, size_t src_offset, size_t bytes); + void (*buffer_copy)(RendContextHandle handle, RendBuffer *dest, size_t dest_offset, RendBuffer *src, size_t src_offset, size_t bytes); - RendTexture (*texture_create)(RendRenderer renderer, uint32_t width, uint32_t height, uint32_t depth, uint32_t mip_levels, uint32_t layers, RendFormat format); - void (*texture_destroy)(RendRenderer renderer, RendTexture *tex); - void (*texture_copy_buffer)(RendRenderer renderer, RendTexture *texture, RendBuffer *buffer); - void (*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); + RendTexture (*texture_create)(RendContextHandle handle, uint32_t width, uint32_t height, uint32_t depth, uint32_t mip_levels, uint32_t layers, RendFormat format); + void (*texture_destroy)(RendContextHandle handle, RendTexture *tex); + void (*texture_copy_buffer)(RendContextHandle handle, RendTexture *texture, RendBuffer *buffer); + void (*texture_blit)(RendContextHandle handle, 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); - bool (*pipeline_create)(RendRenderer, RendPipeline, Rend__PipelineConfig, uint8_t type, const uint8_t *shader1, size_t bytes1, const uint8_t *shader2, size_t bytes2, const uint8_t *shader3, size_t bytes3); + bool (*pipeline_create)(RendContextHandle, RendPipeline, Rend__PipelineConfig, uint8_t type, const uint8_t *shader1, size_t bytes1, const uint8_t *shader2, size_t bytes2, const uint8_t *shader3, size_t bytes3); void (*pipeline_bind)(RendPipeline); void (*pipeline_push_constants)(RendPipeline pipeline, void *push_data, size_t size); @@ -102,10 +99,10 @@ typedef struct { void (*pipeline_draw_indexed)(RendPipeline pipeline, uint32_t index_count, uint32_t first_index, int32_t vertex_offset, uint32_t instance_count); void (*pipeline_set_blend)(RendPipeline, bool); - void (*renderer_render_pass_begin)(RendRenderer renderer, float r, float g, float b, float a); - void (*renderer_render_pass_begin_texture)(RendRenderer, RendTexture*); - void (*renderer_render_pass_end)(RendRenderer renderer); - void (*renderer_render_pass_end_texture)(RendRenderer renderer, RendTexture*); + void (*renderer_render_pass_begin)(RendContextHandle handle, float r, float g, float b, float a); + void (*renderer_render_pass_begin_texture)(RendContextHandle, RendTexture*); + void (*renderer_render_pass_end)(RendContextHandle handle); + void (*renderer_render_pass_end_texture)(RendContextHandle handle, RendTexture*); } RendVTable; diff --git a/Rend/rend_vk.c b/Rend/rend_vk.c index 8fac97b..cfce9d4 100644 --- a/Rend/rend_vk.c +++ b/Rend/rend_vk.c @@ -1,53 +1,46 @@ -#pragma once -#include "rend.h" -#include "rend_internal.h" +/* + * Vulkan 1.4 backend. Per-renderer context is RendVk14Context. + * Instance, allocator, and device are process-global (see rend_vk_internal.c). + * + * * 1.0.3 - @vasco - backend functions take RendContextHandle + */ + #include #include #include -#include "rend_vk_internal.h" -#include "rend_vk_allocator.c" -#include "rend_vk_device.c" -#include "rend_vk_arena.c" -#include "rend_vk_image.c" +#include "rend.h" +#include "rend_internal.h" +#include "rend_vk_internal.c" -#define REND_MIN_FRAMES_IN_FLIGHT 2 // double buffering! -#define REND_MAX_FRAMES_IN_FLIGHT 4 // quadruple buffering! +#define REND_MIN_FRAMES_IN_FLIGHT 2 /* double buffering! */ +#define REND_MAX_FRAMES_IN_FLIGHT 4 /* quadruple buffering! */ #define REND_VK_MAX_PIPELINES 100 typedef struct { - VkSwapchainKHR handle; - VkSurfaceFormatKHR format; - VkExtent2D extent; + VkSwapchainKHR handle; + VkSurfaceFormatKHR format; + VkExtent2D extent; - uint32_t image_count; - VkImage *images; - VkImageView *views; + uint32_t image_count; + VkImage *images; + VkImageView *views; - RendVkImage depth_attachment; + RendVkImage depth_attachment; } RendVkSwapchain; typedef struct { - VkCommandPool command_pool; - VkCommandBuffer command_buffer; - VkSemaphore image_acquired_semaphore; + VkCommandPool command_pool; + VkCommandBuffer command_buffer; + VkSemaphore image_acquired_semaphore; } RendVkFrameResources; -typedef struct { - uint8_t push_data[128]; - uint32_t size; -} RendVkResourceSet; - typedef struct RendVkPipeline { - void *ctx; - VkPipeline handle; - VkPipelineLayout layout; - uint32_t vertex_count; - uint32_t vertex_stride; - uint32_t push_constants_range; - - bool blend_enable; + VkPipeline handle; + VkPipelineLayout layout; + uint32_t push_constants_range; + bool blend_enable; } RendVkPipeline; @@ -56,1851 +49,1627 @@ typedef struct RendVkPipeline { * such as the instance, the allocator, the devices etc... */ typedef struct RendVk14Context { + PeakWindow *window; + VkSurfaceKHR surface; + RendVkPipeline pipelines[REND_VK_MAX_PIPELINES]; + RendVkFrameResources frame_resources[REND_MAX_FRAMES_IN_FLIGHT]; + RendVkSwapchain swapchain; + VkSemaphore timeline_semaphore; + VkSemaphore render_complete_semaphores[REND_MAX_FRAMES_IN_FLIGHT]; - RendVkDevice device; // SPEC: All objects created or allocated from a VkDevice are private to that device, and must not be used on other devices. - - PeakWindow *window; - VkSurfaceKHR surface; - RendVkPipeline pipelines[REND_VK_MAX_PIPELINES]; - RendVkFrameResources frame_resources[REND_MAX_FRAMES_IN_FLIGHT]; - RendVkSwapchain swapchain; - VkSemaphore timeline_semaphore; - VkSemaphore render_complete_semaphores[REND_MAX_FRAMES_IN_FLIGHT]; + VkCommandPool upload_command_pool; + VkCommandPool graphics_command_pool; - VkCommandPool upload_command_pool; - VkCommandPool graphics_command_pool; + RendVkArenaAllocator arena_persistent; /* magical arena allocator for every type of memory */ + RendVkArenaAllocator arena_frame; - RendVkArenaAllocator arena_persistent; // magical arena allocator for every type of memory - RendVkArenaAllocator arena_frame; + uint64_t frame; + uint64_t frame_index; + uint64_t signal_value; + uint64_t next_signal_value; + uint64_t max_frames_in_flight; + uint32_t pipeline_count; + uint32_t image_index; - uint64_t frame; - uint64_t frame_index; - uint64_t signal_value; - uint64_t next_signal_value; - uint64_t max_frames_in_flight; - uint32_t pipeline_count; - uint32_t image_index; + bool require_swapchain_recreation; - bool require_swapchain_recreation; + bool vsync; + bool in_frame; - bool vsync; - bool in_frame; - - VkDescriptorPool descriptor_pool; - VkDescriptorSet desc_set; - VkDescriptorSetLayout desc_layout; + VkDescriptorPool descriptor_pool; + VkDescriptorSet desc_set; + VkDescriptorSetLayout desc_layout; } RendVk14Context; -/* function declarations */ -extern bool rend_vk_init(); -extern void rend_vk_quit(); - -extern RendContextHandle rend_vk_renderer_create(PeakWindow *window, RendBindingInfo *bind_info); -extern void rend_vk_renderer_destroy(RendContextHandle handle); -extern bool rend_vk_renderer_frame_begin(RendContextHandle handle); -extern void rend_vk_renderer_frame_end(RendContextHandle handle, float *delta); - -static inline void rend_vk__renderer_render_pass_begin_internal(RendRenderer renderer, float r, float g, float b, float a, uint64_t view_handle, uint64_t depth_attachment_view_handle, uint32_t offset_x, uint32_t offset_y, uint32_t width, uint32_t height); - -extern void rend_vk_renderer_render_pass_begin(RendRenderer renderer, float r, float g, float b, float a); -extern void rend_vk_renderer_render_pass_begin_texture(RendRenderer renderer, RendTexture *texture); -extern void rend_vk_renderer_render_pass_end(RendRenderer renderer); -extern void rend_vk_renderer_render_pass_end_texture(RendRenderer renderer, RendTexture *texture); - -extern void rend_vk_descriptor_write_buffer(RendRenderer renderer, RendBuffer ubo, uint32_t binding, uint32_t slot, uint32_t offset, uint32_t size, bool is_ubo); -extern void rend_vk_descriptor_write_texture(void *ctx, RendTexture *texture, uint32_t binding, uint32_t slot); - -extern RendBuffer rend_vk_buffer_create_lifetime(RendRenderer renderer, size_t size, RendBufferType type, bool gpu, int lifetime); -extern void rend_vk_buffer_destroy(RendBuffer *buffer); -extern void rend_vk_buffer_copy(RendRenderer renderer, RendBuffer *dest, size_t dest_offset, RendBuffer *src, size_t src_offset, size_t bytes); - -extern RendTexture rend_vk_texture_create(RendRenderer renderer, uint32_t width, uint32_t height, uint32_t depth, uint32_t mip_levels, uint32_t layers, RendFormat format); -extern void rend_vk_texture_destroy(RendRenderer renderer, RendTexture *tex); -extern void rend_vk_texture_transition_layout(RendRenderer renderer, VkCommandBuffer cmd, RendTexture *texture, VkImageLayout new_layout); -extern void rend_vk_texture_transfer_ownership_release(RendRenderer renderer, VkCommandBuffer cmd, RendTexture *texture, uint32_t src_family, uint32_t dst_family, VkImageLayout new_layout); -extern void rend_vk_texture_transfer_ownership_acquire(RendRenderer renderer, VkCommandBuffer cmd, RendTexture *texture, uint32_t src_family, uint32_t dst_family, VkImageLayout new_layout); -extern void rend_vk_texture_copy_buffer(RendRenderer renderer, RendTexture *texture, RendBuffer *buffer); -extern void rend_vk_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); - +static void rend_vk_texture_transition_layout(RendContextHandle handle, VkCommandBuffer cmd, RendTexture *texture, VkImageLayout new_layout); static VkCommandBuffer rend_vk_cmdbuffer_single_use_begin(VkCommandPool pool); static void rend_vk_cmdbuffer_single_use_end(VkCommandPool pool, VkCommandBuffer cmd, VkQueue q); - -extern bool rend_vk_pipeline_create(RendRenderer renderer, RendPipeline pipeline, Rend__PipelineConfig config, uint8_t type, const uint8_t *shader1, size_t bytes1, const uint8_t *shader2, size_t bytes2, const uint8_t *shader3, size_t bytes3); -extern void rend_vk_pipeline_bind(RendPipeline); -extern void rend_vk_pipeline_push_constants(RendPipeline pipeline, void *push_data, size_t size); - -extern void rend_vk_pipeline_bind_vertex_buffer(RendPipeline pipeline, uint32_t binding, RendBuffer buffer, size_t offset); -extern void rend_vk_pipeline_bind_index_buffer(RendPipeline pipeline, RendBuffer buffer, size_t offset, RendIndexType index_type); - -extern void rend_vk_pipeline_dispatch(RendPipeline pipeline, uint32_t x, uint32_t y, uint32_t z); -extern void rend_vk_pipeline_draw(RendPipeline, size_t count, uint32_t instance_count); -extern void rend_vk_pipeline_draw_indexed(RendPipeline pipeline, uint32_t index_count, uint32_t first_index, int32_t vertex_offset, uint32_t instance_count); -extern void rend_vk_pipeline_set_blend(RendPipeline, bool); - -VKAPI_ATTR VkBool32 VKAPI_CALL rend_vk_debug_func( VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, VkDebugUtilsMessageTypeFlagsEXT message_types, const VkDebugUtilsMessengerCallbackDataEXT *callback_data, void *user_data); static void rend_vk_pipeline_destroy(RendVkPipeline *pipeline); static void rend_vk_swapchain_create(RendVk14Context *ctx, RendVkSwapchain *swapchain); static void rend_vk_swapchain_destroy(RendVk14Context *ctx, RendVkSwapchain *swapchain); - static VkShaderModule rend_vk_shader_module_create(const void *data, size_t size); static uint32_t rend_vk_get_heap_index(uint32_t memory_type_bits, uint32_t preferred_index); +VKAPI_ATTR VkBool32 VKAPI_CALL rend_vk_debug_func(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, VkDebugUtilsMessageTypeFlagsEXT message_types, const VkDebugUtilsMessengerCallbackDataEXT *callback_data, void *user_data); -extern bool -rend_vk_init() +bool +rend_vk_init(void) { - RASSERT(sizeof(void*) == 8 && sizeof(int64_t) == 8 && sizeof(double) == 8, "SPEC: Host device 64-bit integer and floating-point types."); - - /* trying to init another renderer with the same backend */ - if (vk_instance) { - return true; - } - - /* create instance if does not exist */ - VkApplicationInfo vk_app_info = {VK_STRUCTURE_TYPE_APPLICATION_INFO}; - vk_app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; - vk_app_info.pApplicationName = "REND"; - vk_app_info.applicationVersion = VK_MAKE_VERSION(REND_MAJOR, REND_MINOR, REND_PATCH); - vk_app_info.apiVersion = VK_API_VERSION_1_4; - vk_app_info.pEngineName = "REND Renderer"; - vk_app_info.engineVersion = VK_MAKE_VERSION(REND_MAJOR, REND_MINOR, REND_PATCH); - - uint32_t peak_ext_count = 0; - const char **peak_exts = peak_vulkan_get_extensions(&peak_ext_count); - const char *extensions[8]; - uint32_t ext_count = 0; - uint32_t ei; - for (ei = 0; ei < peak_ext_count && ext_count < 8; ei++) - extensions[ext_count++] = peak_exts[ei]; + RASSERT(sizeof(void*) == 8 && sizeof(int64_t) == 8 && sizeof(double) == 8, "SPEC: Host device 64-bit integer and floating-point types."); + + /* trying to init another renderer with the same backend */ + if (vk_instance) { + return true; + } + + /* create instance if does not exist */ + VkApplicationInfo vk_app_info = {VK_STRUCTURE_TYPE_APPLICATION_INFO}; + vk_app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + vk_app_info.pApplicationName = "REND"; + vk_app_info.applicationVersion = VK_MAKE_VERSION(REND_MAJOR, REND_MINOR, REND_PATCH); + vk_app_info.apiVersion = VK_API_VERSION_1_4; + vk_app_info.pEngineName = "REND Renderer"; + vk_app_info.engineVersion = VK_MAKE_VERSION(REND_MAJOR, REND_MINOR, REND_PATCH); + + uint32_t peak_ext_count = 0; + const char **peak_exts = peak_vulkan_get_extensions(&peak_ext_count); + const char *extensions[8]; + uint32_t ext_count = 0; + uint32_t ei; + for (ei = 0; ei < peak_ext_count && ext_count < 8; ei++) + extensions[ext_count++] = peak_exts[ei]; #ifdef REND_DEBUG - if (ext_count < 8) - extensions[ext_count++] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; - PDEBUG("Vulkan Extensions: "); - for (ei = 0; ei < ext_count; ei++) - PDEBUG("%s", extensions[ei]); + if (ext_count < 8) + extensions[ext_count++] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; + PDEBUG("Vulkan Extensions: "); + for (ei = 0; ei < ext_count; ei++) + PDEBUG("%s", extensions[ei]); #endif #ifdef REND_DEBUG - const char *required_validation_layers[] = { "VK_LAYER_KHRONOS_validation" }; - uint32_t layer_count = 1; - - PDEBUG("Required Validation Layers: "); - for (ei = 0; ei < layer_count; ei++) { - PDEBUG(" - %s", required_validation_layers[ei]); - } - - VkValidationFeatureEnableEXT enabled_validation_features[] = { - // VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT, - // VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_RESERVE_BINDING_SLOT_EXT, - }; - - VkValidationFeaturesEXT validation_features = { - .sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT, - .pNext = NULL, - // .enabledValidationFeatureCount = sizeof(enabled_validation_features) / sizeof(enabled_validation_features[0]), - // .pEnabledValidationFeatures = enabled_validation_features, - }; + const char *required_validation_layers[] = { "VK_LAYER_KHRONOS_validation" }; + uint32_t layer_count = 1; + + PDEBUG("Required Validation Layers: "); + for (ei = 0; ei < layer_count; ei++) { + PDEBUG(" - %s", required_validation_layers[ei]); + } + + VkValidationFeaturesEXT validation_features = { + .sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT, + .pNext = NULL, + /* GPU-assisted validation: VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT */ + }; #endif - VkInstanceCreateInfo vk_create_info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO}; - vk_create_info.pApplicationInfo = &vk_app_info; - vk_create_info.ppEnabledExtensionNames = extensions; - vk_create_info.enabledExtensionCount = ext_count; - vk_create_info.pApplicationInfo = &vk_app_info; + VkInstanceCreateInfo vk_create_info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO}; + vk_create_info.pApplicationInfo = &vk_app_info; + vk_create_info.ppEnabledExtensionNames = extensions; + vk_create_info.enabledExtensionCount = ext_count; + vk_create_info.pApplicationInfo = &vk_app_info; #ifdef REND_DEBUG - vk_create_info.enabledLayerCount = layer_count; - vk_create_info.ppEnabledLayerNames = required_validation_layers; - vk_create_info.pNext = &validation_features; + vk_create_info.enabledLayerCount = layer_count; + vk_create_info.ppEnabledLayerNames = required_validation_layers; + vk_create_info.pNext = &validation_features; #else - vk_create_info.enabledLayerCount = 0; - vk_create_info.ppEnabledLayerNames = NULL; + vk_create_info.enabledLayerCount = 0; + vk_create_info.ppEnabledLayerNames = NULL; #endif - VkResult res = vkCreateInstance(&vk_create_info, vk_allocator, &vk_instance); - CHECK_VK_RESULT(res); + VkResult res = vkCreateInstance(&vk_create_info, vk_allocator, &vk_instance); + CHECK_VK_RESULT(res); - PDEBUG("Vulkan instance created!"); + PDEBUG("Vulkan instance created!"); #ifdef REND_DEBUG - uint32_t log_severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; - VkDebugUtilsMessengerCreateInfoEXT debug_create_info = { VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT}; - debug_create_info.messageSeverity = log_severity; - debug_create_info.messageType = - VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT; - debug_create_info.pfnUserCallback = rend_vk_debug_func; - - PFN_vkCreateDebugUtilsMessengerEXT func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr( vk_instance, "vkCreateDebugUtilsMessengerEXT"); - if (!func) { - PDEBUG("Failed to create vulkan debug messenger!"); - return false; - } - func(vk_instance, &debug_create_info, vk_allocator, &vk_debug_messenger); + uint32_t log_severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; + VkDebugUtilsMessengerCreateInfoEXT debug_create_info = { VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT}; + debug_create_info.messageSeverity = log_severity; + debug_create_info.messageType = + VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT; + debug_create_info.pfnUserCallback = rend_vk_debug_func; + + PFN_vkCreateDebugUtilsMessengerEXT func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(vk_instance, "vkCreateDebugUtilsMessengerEXT"); + if (!func) { + PDEBUG("Failed to create vulkan debug messenger!"); + return false; + } + func(vk_instance, &debug_create_info, vk_allocator, &vk_debug_messenger); #endif - return true; + return true; } -extern void -rend_vk_quit() +void +rend_vk_quit(void) { - PDEBUG("[REND_VK14] Destroying device."); + PDEBUG("[REND_VK14] Destroying device."); - /* we destroy the device on quit */ - if (vk_device.logical_device != 0) { - rend_vk_device_destroy(&vk_device); - } + /* we destroy the device on quit */ + if (vk_device.logical_device != 0) { + rend_vk_device_destroy(&vk_device); + } - if (vk_debug_messenger) { - PFN_vkDestroyDebugUtilsMessengerEXT func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr( vk_instance, "vkDestroyDebugUtilsMessengerEXT"); - func(vk_instance, vk_debug_messenger, vk_allocator); - vk_debug_messenger = 0; - } + if (vk_debug_messenger) { + PFN_vkDestroyDebugUtilsMessengerEXT func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(vk_instance, "vkDestroyDebugUtilsMessengerEXT"); + func(vk_instance, vk_debug_messenger, vk_allocator); + vk_debug_messenger = 0; + } - vkDestroyInstance(vk_instance, vk_allocator); - vk_instance = 0; + vkDestroyInstance(vk_instance, vk_allocator); + vk_instance = 0; } -extern RendContextHandle +RendContextHandle rend_vk_renderer_create(PeakWindow *window, RendBindingInfo *bind_info) { - RendVk14Context *ctx = rmalloc(sizeof(*ctx)); - if (!ctx) { - PERROR("Failed to allocate internal vulkan context."); - return NULL; - } - - memset(ctx, 0, sizeof *ctx); - ctx->window = window; - - /* get surface from window */ - if (!peak_vulkan_create_surface(window, vk_instance, vk_allocator, &ctx->surface)) { - PERROR("Failed to create vulkan surface!"); - rfree(ctx); - return NULL; - } - - RendSpecs specs = { - .sampler_anisotropy = true, - .graphics = true, - .transfer = true, - .discrete_gpu = false, // even though its not a requirement, I expect discrete gpu to be picked - }; - - /* we lazily create the logical device only after creating the first renderer - * because we need the surface first */ - if (vk_device.logical_device == 0) { - rend_vk_device_create(ctx->surface, specs, &vk_device, rend_vk_device_score_default); - } - - /* we must create arena before swapchain */ - ctx->arena_persistent = rend_vk_arena_create( - vk_device.logical_device, - vk_device.physical_device, - vk_device.properties.limits, - vk_allocator); - - ctx->arena_frame = rend_vk_arena_create( - vk_device.logical_device, - vk_device.physical_device, - vk_device.properties.limits, - vk_allocator); - - /* create swapchain */ - rend_vk_swapchain_create(ctx, &ctx->swapchain); - - /* timeline semaphore */ - VkSemaphoreTypeCreateInfo timeline_type_info = {VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO}; - timeline_type_info.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE; - timeline_type_info.initialValue = ctx->max_frames_in_flight; - - VkSemaphoreCreateInfo timeline_semaphore_info = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO}; - timeline_semaphore_info.pNext = &timeline_type_info; - - if (vkCreateSemaphore(vk_device.logical_device, &timeline_semaphore_info, vk_allocator, &ctx->timeline_semaphore) != VK_SUCCESS) { - PERROR("Unable to create the timeline semaphore for the renderer!"); - rfree(ctx); - return NULL; - } - - /* create per frame semaphores */ - VkSemaphoreCreateInfo frame_semaphore_info = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO}; - for (uint32_t u = 0; u < REND_MAX_FRAMES_IN_FLIGHT; ++u) { - if (vkCreateSemaphore( - vk_device.logical_device, - &frame_semaphore_info, - vk_allocator, - &ctx->frame_resources[u].image_acquired_semaphore) != VK_SUCCESS) { - PERROR("Unable to create semaphore for frame #%u!", u); - rfree(ctx); - return NULL; - } - - if (vkCreateSemaphore( - vk_device.logical_device, - &frame_semaphore_info, - vk_allocator, - &ctx->render_complete_semaphores[u]) != VK_SUCCESS) { - PERROR("Unable to create render complete semaphore #%u!", u); - rfree(ctx); - return NULL; - } - - VkCommandPoolCreateInfo pool_info = {VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO}; - pool_info.queueFamilyIndex = vk_device.graphics_family_index; - if (vkCreateCommandPool(vk_device.logical_device, &pool_info, vk_allocator, &ctx->frame_resources[u].command_pool) != VK_SUCCESS) { - PERROR("Unable to create command pool for frame #%u!", u); - rfree(ctx); - return NULL; - } - - VkCommandBufferAllocateInfo cmdbuf_info = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO}; - cmdbuf_info.commandPool = ctx->frame_resources[u].command_pool; - cmdbuf_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - cmdbuf_info.commandBufferCount = 1; - if (vkAllocateCommandBuffers(vk_device.logical_device, &cmdbuf_info, &ctx->frame_resources[u].command_buffer) != VK_SUCCESS) { - PERROR("Unable to create command buffer for frame #%u!", u); - rfree(ctx); - return NULL; - } - - } - - VkCommandPoolCreateInfo pool_info = { - .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, - .queueFamilyIndex = vk_device.transfer_family_index, - .flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT - }; - - if (vkCreateCommandPool(vk_device.logical_device, &pool_info, vk_allocator, &ctx->upload_command_pool) != VK_SUCCESS) { - PERROR("Unable to create upload command pool!"); - rfree(ctx); - return NULL; - } - - pool_info.queueFamilyIndex = vk_device.graphics_family_index; - if (vkCreateCommandPool(vk_device.logical_device, &pool_info, vk_allocator, &ctx->graphics_command_pool) != VK_SUCCESS) { - PERROR("Unable to create graphics command pool!"); - rfree(ctx); - return NULL; - } - - ctx->frame = 0; - ctx->frame_index = 0; - ctx->next_signal_value = ctx->max_frames_in_flight + 1; /* start at frame zero */ - - /* - * Descriptor Pool - */ - { - RendBindingInfo bind_info_local = {0}; - if (bind_info) bind_info_local = *bind_info; - RendBindingInfo bind_info = bind_info_local; - - uint32_t total_ubos = 0; - for (uint32_t i = 0; i < bind_info.ubo_binding_count; ++i) { - total_ubos += bind_info.ubo_array_sizes[i]; - } - - uint32_t total_ssbos = 0; - for (uint32_t i = 0; i < bind_info.ssbo_binding_count; ++i) { - total_ssbos += bind_info.ssbo_array_sizes[i]; - } - - uint32_t total_textures = 0; - for (uint32_t i = 0; i < bind_info.texture_binding_count; ++i) { - total_textures += bind_info.texture_array_sizes[i]; - } - - const uint32_t pool_count = 3; - VkDescriptorPoolSize pool_sizes[3] = { - { .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .descriptorCount = (total_ubos > 0) ? total_ubos : 1 }, - { .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, .descriptorCount = (total_ssbos > 0) ? total_ssbos : 1 }, - { .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .descriptorCount = (total_textures > 0) ? total_textures : 1 } - }; - - VkDescriptorPoolCreateInfo pool_info = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, - .flags = 0, - .maxSets = 1, - .poolSizeCount = pool_count, - .pPoolSizes = pool_sizes, - .pNext = NULL, - }; - - VkResult result = vkCreateDescriptorPool(vk_device.logical_device, &pool_info, vk_allocator, &ctx->descriptor_pool); - if (result != VK_SUCCESS) { - rfree(ctx); - return NULL; - } - - - /* - * Descriptor Sets!!!!!! - */ - - const uint32_t binding_count = bind_info.ubo_binding_count + bind_info.ssbo_binding_count + bind_info.texture_binding_count; - VkDescriptorSetLayoutBinding binding_array[binding_count]; - - for (uint32_t i = 0; i < bind_info.ubo_binding_count; ++i) { - binding_array[i] = (VkDescriptorSetLayoutBinding) { - .binding = bind_info.ubo_bindings[i], - .descriptorCount = bind_info.ubo_array_sizes[i], - .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - .stageFlags = VK_SHADER_STAGE_ALL, - .pImmutableSamplers = 0, - }; - }; - - uint32_t offset = bind_info.ubo_binding_count; - for (uint32_t i = 0; i < bind_info.ssbo_binding_count; ++i) { - binding_array[i + offset] = (VkDescriptorSetLayoutBinding) { - .binding = bind_info.ssbo_bindings[i], - .descriptorCount = bind_info.ssbo_array_sizes[i], - .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - .stageFlags = VK_SHADER_STAGE_ALL, - .pImmutableSamplers = 0, - }; - }; - - offset += bind_info.ssbo_binding_count; - for (uint32_t i = 0; i < bind_info.texture_binding_count; ++i) { - binding_array[i + offset] = (VkDescriptorSetLayoutBinding) { - .binding = bind_info.texture_bindings[i], - .descriptorCount = bind_info.texture_array_sizes[i], - .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .stageFlags = VK_SHADER_STAGE_ALL, - .pImmutableSamplers = 0, - }; - }; - - VkDescriptorBindingFlags binding_flags[binding_count]; - for (uint32_t u = 0; u < binding_count; ++u) { - binding_flags[u] = VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT; - } - - VkDescriptorSetLayoutBindingFlagsCreateInfo desc_flags_info = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO, - .bindingCount = binding_count, - .pBindingFlags = binding_flags - }; - - VkDescriptorSetLayoutCreateInfo desc_layout_info = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, - .flags = 0, - .bindingCount = binding_count, - .pBindings = binding_array, - .pNext = &desc_flags_info, - }; - - result = vkCreateDescriptorSetLayout(vk_device.logical_device, &desc_layout_info, vk_allocator, &ctx->desc_layout); - if (result != VK_SUCCESS) { - PERROR("Failed to create descriptor set layout!"); - rfree(ctx); - return NULL; - } - - VkDescriptorSetAllocateInfo alloc_info = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, - .descriptorPool = ctx->descriptor_pool, - .descriptorSetCount = 1, // ONLY ONE DESCRIPTOR SET - .pSetLayouts = &ctx->desc_layout, - .pNext = NULL, - }; - - result = vkAllocateDescriptorSets(vk_device.logical_device, &alloc_info, &ctx->desc_set); - if (result != VK_SUCCESS) { - PERROR("Failed to allocate descriptors!!!"); - rfree(ctx); - return NULL; - } - } - - return ctx; + uint32_t u; + uint32_t i; + RendVk14Context *ctx = rmalloc(sizeof(*ctx)); + if (!ctx) { + PERROR("Failed to allocate internal vulkan context."); + return NULL; + } + + memset(ctx, 0, sizeof(*ctx)); + ctx->window = window; + + /* get surface from window */ + if (!peak_vulkan_create_surface(window, vk_instance, vk_allocator, &ctx->surface)) { + PERROR("Failed to create vulkan surface!"); + rfree(ctx); + return NULL; + } + + RendSpecs specs = { + .sampler_anisotropy = true, + .graphics = true, + .transfer = true, + .discrete_gpu = false, /* even though its not a requirement, I expect discrete gpu to be picked */ + }; + + /* we lazily create the logical device only after creating the first renderer + * because we need the surface first */ + if (vk_device.logical_device == 0) { + rend_vk_device_create(ctx->surface, specs, &vk_device); + } + + /* we must create arena before swapchain */ + ctx->arena_persistent = rend_vk_arena_create( + vk_device.logical_device, + vk_device.physical_device, + vk_device.properties.limits, + vk_allocator); + + ctx->arena_frame = rend_vk_arena_create( + vk_device.logical_device, + vk_device.physical_device, + vk_device.properties.limits, + vk_allocator); + + /* create swapchain */ + rend_vk_swapchain_create(ctx, &ctx->swapchain); + + /* timeline semaphore */ + VkSemaphoreTypeCreateInfo timeline_type_info = {VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO}; + timeline_type_info.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE; + timeline_type_info.initialValue = ctx->max_frames_in_flight; + + VkSemaphoreCreateInfo timeline_semaphore_info = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO}; + timeline_semaphore_info.pNext = &timeline_type_info; + + if (vkCreateSemaphore(vk_device.logical_device, &timeline_semaphore_info, vk_allocator, &ctx->timeline_semaphore) != VK_SUCCESS) { + PERROR("Unable to create the timeline semaphore for the renderer!"); + rfree(ctx); + return NULL; + } + + /* create per frame semaphores */ + VkSemaphoreCreateInfo frame_semaphore_info = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO}; + for (u = 0; u < REND_MAX_FRAMES_IN_FLIGHT; ++u) { + if (vkCreateSemaphore( + vk_device.logical_device, + &frame_semaphore_info, + vk_allocator, + &ctx->frame_resources[u].image_acquired_semaphore) != VK_SUCCESS) { + PERROR("Unable to create semaphore for frame #%u!", u); + rfree(ctx); + return NULL; + } + + if (vkCreateSemaphore( + vk_device.logical_device, + &frame_semaphore_info, + vk_allocator, + &ctx->render_complete_semaphores[u]) != VK_SUCCESS) { + PERROR("Unable to create render complete semaphore #%u!", u); + rfree(ctx); + return NULL; + } + + VkCommandPoolCreateInfo pool_info = {VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO}; + pool_info.queueFamilyIndex = vk_device.graphics_family_index; + if (vkCreateCommandPool(vk_device.logical_device, &pool_info, vk_allocator, &ctx->frame_resources[u].command_pool) != VK_SUCCESS) { + PERROR("Unable to create command pool for frame #%u!", u); + rfree(ctx); + return NULL; + } + + VkCommandBufferAllocateInfo cmdbuf_info = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO}; + cmdbuf_info.commandPool = ctx->frame_resources[u].command_pool; + cmdbuf_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + cmdbuf_info.commandBufferCount = 1; + if (vkAllocateCommandBuffers(vk_device.logical_device, &cmdbuf_info, &ctx->frame_resources[u].command_buffer) != VK_SUCCESS) { + PERROR("Unable to create command buffer for frame #%u!", u); + rfree(ctx); + return NULL; + } + + } + + VkCommandPoolCreateInfo pool_info = { + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .queueFamilyIndex = vk_device.transfer_family_index, + .flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT + }; + + if (vkCreateCommandPool(vk_device.logical_device, &pool_info, vk_allocator, &ctx->upload_command_pool) != VK_SUCCESS) { + PERROR("Unable to create upload command pool!"); + rfree(ctx); + return NULL; + } + + pool_info.queueFamilyIndex = vk_device.graphics_family_index; + if (vkCreateCommandPool(vk_device.logical_device, &pool_info, vk_allocator, &ctx->graphics_command_pool) != VK_SUCCESS) { + PERROR("Unable to create graphics command pool!"); + rfree(ctx); + return NULL; + } + + ctx->frame = 0; + ctx->frame_index = 0; + ctx->next_signal_value = ctx->max_frames_in_flight + 1; /* start at frame zero */ + + /* + * Descriptor Pool + */ + { + RendBindingInfo bind_info_local = {0}; + if (bind_info) bind_info_local = *bind_info; + RendBindingInfo bind_info = bind_info_local; + + uint32_t total_ubos = 0; + for (i = 0; i < bind_info.ubo_binding_count; ++i) { + total_ubos += bind_info.ubo_array_sizes[i]; + } + + uint32_t total_ssbos = 0; + for (i = 0; i < bind_info.ssbo_binding_count; ++i) { + total_ssbos += bind_info.ssbo_array_sizes[i]; + } + + uint32_t total_textures = 0; + for (i = 0; i < bind_info.texture_binding_count; ++i) { + total_textures += bind_info.texture_array_sizes[i]; + } + + const uint32_t pool_count = 3; + VkDescriptorPoolSize pool_sizes[3] = { + { .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .descriptorCount = (total_ubos > 0) ? total_ubos : 1 }, + { .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, .descriptorCount = (total_ssbos > 0) ? total_ssbos : 1 }, + { .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .descriptorCount = (total_textures > 0) ? total_textures : 1 } + }; + + VkDescriptorPoolCreateInfo pool_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .flags = 0, + .maxSets = 1, + .poolSizeCount = pool_count, + .pPoolSizes = pool_sizes, + .pNext = NULL, + }; + + VkResult result = vkCreateDescriptorPool(vk_device.logical_device, &pool_info, vk_allocator, &ctx->descriptor_pool); + if (result != VK_SUCCESS) { + rfree(ctx); + return NULL; + } + + + /* + * Descriptor Sets!!!!!! + */ + + const uint32_t binding_count = bind_info.ubo_binding_count + bind_info.ssbo_binding_count + bind_info.texture_binding_count; + VkDescriptorSetLayoutBinding binding_array[binding_count]; + + for (i = 0; i < bind_info.ubo_binding_count; ++i) { + binding_array[i] = (VkDescriptorSetLayoutBinding) { + .binding = bind_info.ubo_bindings[i], + .descriptorCount = bind_info.ubo_array_sizes[i], + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .stageFlags = VK_SHADER_STAGE_ALL, + .pImmutableSamplers = 0, + }; + }; + + uint32_t offset = bind_info.ubo_binding_count; + for (i = 0; i < bind_info.ssbo_binding_count; ++i) { + binding_array[i + offset] = (VkDescriptorSetLayoutBinding) { + .binding = bind_info.ssbo_bindings[i], + .descriptorCount = bind_info.ssbo_array_sizes[i], + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .stageFlags = VK_SHADER_STAGE_ALL, + .pImmutableSamplers = 0, + }; + }; + + offset += bind_info.ssbo_binding_count; + for (i = 0; i < bind_info.texture_binding_count; ++i) { + binding_array[i + offset] = (VkDescriptorSetLayoutBinding) { + .binding = bind_info.texture_bindings[i], + .descriptorCount = bind_info.texture_array_sizes[i], + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .stageFlags = VK_SHADER_STAGE_ALL, + .pImmutableSamplers = 0, + }; + }; + + VkDescriptorBindingFlags binding_flags[binding_count]; + for (u = 0; u < binding_count; ++u) { + binding_flags[u] = VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT; + } + + VkDescriptorSetLayoutBindingFlagsCreateInfo desc_flags_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO, + .bindingCount = binding_count, + .pBindingFlags = binding_flags + }; + + VkDescriptorSetLayoutCreateInfo desc_layout_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .flags = 0, + .bindingCount = binding_count, + .pBindings = binding_array, + .pNext = &desc_flags_info, + }; + + result = vkCreateDescriptorSetLayout(vk_device.logical_device, &desc_layout_info, vk_allocator, &ctx->desc_layout); + if (result != VK_SUCCESS) { + PERROR("Failed to create descriptor set layout!"); + rfree(ctx); + return NULL; + } + + VkDescriptorSetAllocateInfo alloc_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .descriptorPool = ctx->descriptor_pool, + .descriptorSetCount = 1, /* ONLY ONE DESCRIPTOR SET */ + .pSetLayouts = &ctx->desc_layout, + .pNext = NULL, + }; + + result = vkAllocateDescriptorSets(vk_device.logical_device, &alloc_info, &ctx->desc_set); + if (result != VK_SUCCESS) { + PERROR("Failed to allocate descriptors!!!"); + rfree(ctx); + return NULL; + } + } + + return ctx; } -extern void +void rend_vk_renderer_destroy(RendContextHandle handle) { - RASSERT(handle, "Invalid context handle."); + uint32_t u; + RASSERT(handle, "Invalid context handle."); - RendVk14Context *ctx = (RendVk14Context *)handle; + RendVk14Context *ctx = (RendVk14Context *)handle; - VkDevice dev = vk_device.logical_device; + VkDevice dev = vk_device.logical_device; - PDEBUG("[REND] Waiting for device..."); - vkDeviceWaitIdle(dev); + PDEBUG("[REND] Waiting for device..."); + vkDeviceWaitIdle(dev); - PDEBUG("[REND] Destroying renderer..."); - vkDestroySemaphore(dev, ctx->timeline_semaphore, vk_allocator); + PDEBUG("[REND] Destroying renderer..."); + vkDestroySemaphore(dev, ctx->timeline_semaphore, vk_allocator); - /* destroy per frame semaphores */ - for (uint32_t u = 0; u < REND_MAX_FRAMES_IN_FLIGHT; ++u) { - vkDestroySemaphore(dev, ctx->frame_resources[u].image_acquired_semaphore, vk_allocator); - vkDestroySemaphore(dev, ctx->render_complete_semaphores[u], vk_allocator); - vkDestroyCommandPool(dev, ctx->frame_resources[u].command_pool, vk_allocator); - ctx->frame_resources[u].image_acquired_semaphore = 0; - ctx->frame_resources[u].command_pool = 0; - ctx->frame_resources[u].command_buffer = 0; - } + /* destroy per frame semaphores */ + for (u = 0; u < REND_MAX_FRAMES_IN_FLIGHT; ++u) { + vkDestroySemaphore(dev, ctx->frame_resources[u].image_acquired_semaphore, vk_allocator); + vkDestroySemaphore(dev, ctx->render_complete_semaphores[u], vk_allocator); + vkDestroyCommandPool(dev, ctx->frame_resources[u].command_pool, vk_allocator); + ctx->frame_resources[u].image_acquired_semaphore = 0; + ctx->frame_resources[u].command_pool = 0; + ctx->frame_resources[u].command_buffer = 0; + } - vkDestroyCommandPool(vk_device.logical_device, ctx->upload_command_pool, vk_allocator); - vkDestroyCommandPool(vk_device.logical_device, ctx->graphics_command_pool, vk_allocator); + vkDestroyCommandPool(vk_device.logical_device, ctx->upload_command_pool, vk_allocator); + vkDestroyCommandPool(vk_device.logical_device, ctx->graphics_command_pool, vk_allocator); - rend_vk_arena_destroy(&ctx->arena_persistent); - rend_vk_arena_destroy(&ctx->arena_frame); + rend_vk_arena_destroy(&ctx->arena_persistent); + rend_vk_arena_destroy(&ctx->arena_frame); - vkDestroyDescriptorSetLayout(dev, ctx->desc_layout, vk_allocator); - vkDestroyDescriptorPool(dev, ctx->descriptor_pool, vk_allocator); + vkDestroyDescriptorSetLayout(dev, ctx->desc_layout, vk_allocator); + vkDestroyDescriptorPool(dev, ctx->descriptor_pool, vk_allocator); - PDEBUG("[REND] Destroying pipelines..."); - for (uint32_t u = 0; u < ctx->pipeline_count; ++u) { - rend_vk_pipeline_destroy(&ctx->pipelines[u]); - } + PDEBUG("[REND] Destroying pipelines..."); + for (u = 0; u < ctx->pipeline_count; ++u) { + rend_vk_pipeline_destroy(&ctx->pipelines[u]); + } - PDEBUG("[REND] Destroying swapchain..."); - rend_vk_swapchain_destroy(ctx, &ctx->swapchain); - ctx->swapchain.handle = 0; + PDEBUG("[REND] Destroying swapchain..."); + rend_vk_swapchain_destroy(ctx, &ctx->swapchain); + ctx->swapchain.handle = 0; - PDEBUG("[REND] Destroying surface..."); - RASSERT(vk_instance); - vkDestroySurfaceKHR(vk_instance, ctx->surface, vk_allocator); - ctx->surface = 0; + PDEBUG("[REND] Destroying surface..."); + RASSERT(vk_instance); + vkDestroySurfaceKHR(vk_instance, ctx->surface, vk_allocator); + ctx->surface = 0; - /* handle is guarranteed to be allocated */ - rfree(handle); + /* handle is guarranteed to be allocated */ + rfree(handle); } -extern bool +bool rend_vk_renderer_frame_begin(RendContextHandle handle) { - RendVk14Context *ctx; - VkDevice dev; - uint32_t retries; - - RASSERT(handle, "Invalid handle."); - - ctx = (RendVk14Context *) handle; - dev = vk_device.logical_device; - - for (retries = 0; retries < 4; retries++) { - uint64_t frame_res_index; - uint64_t wait_value; - VkResult acquire_image; - RendVkFrameResources frame_resource; - VkSemaphoreWaitInfo timeline_wait_info; - - if (ctx->require_swapchain_recreation) { - if (ctx->window && (ctx->window->width == 0 || ctx->window->height == 0)) - return false; - PDEBUG("[REND] Awaiting device..."); - vkDeviceWaitIdle(vk_device.logical_device); - PDEBUG("[REND] Recreating swapchain..."); - rend_vk_swapchain_destroy(ctx, &ctx->swapchain); - rend_vk_swapchain_create(ctx, &ctx->swapchain); - ctx->require_swapchain_recreation = false; - } - - if (ctx->max_frames_in_flight == 0) - return false; - - /* wait on timeline semaphore */ - frame_res_index = ctx->frame % ctx->max_frames_in_flight; - ctx->frame_index = frame_res_index; - - wait_value = ctx->next_signal_value - ctx->max_frames_in_flight; - - timeline_wait_info = (VkSemaphoreWaitInfo) { - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO, - .semaphoreCount = 1, - .pSemaphores = &ctx->timeline_semaphore, - .pValues = &wait_value - }; - - vkWaitSemaphores(vk_device.logical_device, &timeline_wait_info, UINT64_MAX); - - frame_resource = ctx->frame_resources[frame_res_index]; - vkResetCommandPool(dev, frame_resource.command_pool, 0); - - /* acquire next image */ - acquire_image = vkAcquireNextImageKHR( - vk_device.logical_device, - ctx->swapchain.handle, - UINT64_MAX, - frame_resource.image_acquired_semaphore, - VK_NULL_HANDLE, - &ctx->image_index); - - if (acquire_image == VK_ERROR_OUT_OF_DATE_KHR) { - ctx->require_swapchain_recreation = true; - continue; - } - if (acquire_image == VK_SUBOPTIMAL_KHR) { - ctx->require_swapchain_recreation = true; - } else if (acquire_image != VK_SUCCESS) { - return false; - } - - ctx->signal_value = ctx->next_signal_value++; - - { - VkCommandBufferBeginInfo cmd_begin_info = { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT - }; - - vkBeginCommandBuffer(frame_resource.command_buffer, &cmd_begin_info); - - static const size_t NUM_LAYOUT_BARRIERS = 2; - VkImageMemoryBarrier2 layout_barriers[NUM_LAYOUT_BARRIERS]; - layout_barriers[0] = (VkImageMemoryBarrier2) { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, - - .srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, - .srcAccessMask = 0, - - .dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, - .dstAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT_KHR, - - .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - - .image = ctx->swapchain.images[ctx->image_index], - - .subresourceRange = (VkImageSubresourceRange) { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1 - }, - - // .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - // .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - }; - layout_barriers[1] = (VkImageMemoryBarrier2) { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, - - .srcStageMask = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT, - .srcAccessMask = 0, - - .dstStageMask = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT, - .dstAccessMask = VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, - - .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .newLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, - - .image = ctx->swapchain.depth_attachment.handle, - - .subresourceRange = (VkImageSubresourceRange) { - .aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1 - }, - }; - - VkMemoryBarrier2 memory_barrier = { - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, - .srcStageMask = VK_PIPELINE_STAGE_2_HOST_BIT, - .srcAccessMask = VK_ACCESS_2_HOST_WRITE_BIT, - .dstStageMask = VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT, - .dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT, - }; - - VkDependencyInfo dep_info = {VK_STRUCTURE_TYPE_DEPENDENCY_INFO}; - dep_info.memoryBarrierCount = 1; - dep_info.pMemoryBarriers = &memory_barrier; - dep_info.imageMemoryBarrierCount = (uint32_t) NUM_LAYOUT_BARRIERS; - dep_info.pImageMemoryBarriers = layout_barriers; - - vkCmdPipelineBarrier2(frame_resource.command_buffer, &dep_info); - } - - ctx->in_frame = true; - return true; - } - - return false; + RendVk14Context *ctx; + VkDevice dev; + uint32_t retries; + + RASSERT(handle, "Invalid handle."); + + ctx = (RendVk14Context *)handle; + dev = vk_device.logical_device; + + for (retries = 0; retries < 4; retries++) { + uint64_t frame_res_index; + uint64_t wait_value; + VkResult acquire_image; + RendVkFrameResources frame_resource; + VkSemaphoreWaitInfo timeline_wait_info; + + if (ctx->require_swapchain_recreation) { + if (ctx->window && (ctx->window->width == 0 || ctx->window->height == 0)) + return false; + PDEBUG("[REND] Awaiting device..."); + vkDeviceWaitIdle(vk_device.logical_device); + PDEBUG("[REND] Recreating swapchain..."); + rend_vk_swapchain_destroy(ctx, &ctx->swapchain); + rend_vk_swapchain_create(ctx, &ctx->swapchain); + ctx->require_swapchain_recreation = false; + } + + if (ctx->max_frames_in_flight == 0) + return false; + + /* wait on timeline semaphore */ + frame_res_index = ctx->frame % ctx->max_frames_in_flight; + ctx->frame_index = frame_res_index; + + wait_value = ctx->next_signal_value - ctx->max_frames_in_flight; + + timeline_wait_info = (VkSemaphoreWaitInfo) { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO, + .semaphoreCount = 1, + .pSemaphores = &ctx->timeline_semaphore, + .pValues = &wait_value + }; + + vkWaitSemaphores(vk_device.logical_device, &timeline_wait_info, UINT64_MAX); + + frame_resource = ctx->frame_resources[frame_res_index]; + vkResetCommandPool(dev, frame_resource.command_pool, 0); + + /* acquire next image */ + acquire_image = vkAcquireNextImageKHR( + vk_device.logical_device, + ctx->swapchain.handle, + UINT64_MAX, + frame_resource.image_acquired_semaphore, + VK_NULL_HANDLE, + &ctx->image_index); + + if (acquire_image == VK_ERROR_OUT_OF_DATE_KHR) { + ctx->require_swapchain_recreation = true; + continue; + } + if (acquire_image == VK_SUBOPTIMAL_KHR) { + ctx->require_swapchain_recreation = true; + } else if (acquire_image != VK_SUCCESS) { + return false; + } + + ctx->signal_value = ctx->next_signal_value++; + + { + VkCommandBufferBeginInfo cmd_begin_info = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT + }; + + vkBeginCommandBuffer(frame_resource.command_buffer, &cmd_begin_info); + + static const size_t NUM_LAYOUT_BARRIERS = 2; + VkImageMemoryBarrier2 layout_barriers[NUM_LAYOUT_BARRIERS]; + layout_barriers[0] = (VkImageMemoryBarrier2) { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + + .srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + .srcAccessMask = 0, + + .dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + .dstAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT_KHR, + + .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + + .image = ctx->swapchain.images[ctx->image_index], + + .subresourceRange = (VkImageSubresourceRange) { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1 + }, + + /* .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, */ + /* .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, */ + }; + layout_barriers[1] = (VkImageMemoryBarrier2) { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + + .srcStageMask = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT, + .srcAccessMask = 0, + + .dstStageMask = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT, + .dstAccessMask = VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + + .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, + + .image = ctx->swapchain.depth_attachment.handle, + + .subresourceRange = (VkImageSubresourceRange) { + .aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1 + }, + }; + + VkMemoryBarrier2 memory_barrier = { + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, + .srcStageMask = VK_PIPELINE_STAGE_2_HOST_BIT, + .srcAccessMask = VK_ACCESS_2_HOST_WRITE_BIT, + .dstStageMask = VK_PIPELINE_STAGE_2_VERTEX_SHADER_BIT, + .dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT, + }; + + VkDependencyInfo dep_info = {VK_STRUCTURE_TYPE_DEPENDENCY_INFO}; + dep_info.memoryBarrierCount = 1; + dep_info.pMemoryBarriers = &memory_barrier; + dep_info.imageMemoryBarrierCount = (uint32_t) NUM_LAYOUT_BARRIERS; + dep_info.pImageMemoryBarriers = layout_barriers; + + vkCmdPipelineBarrier2(frame_resource.command_buffer, &dep_info); + } + + ctx->in_frame = true; + return true; + } + + return false; } -extern void +void rend_vk_renderer_frame_end(RendContextHandle handle, float *delta) { - RendVk14Context *ctx = (RendVk14Context *)handle; - RASSERT(ctx, "Uninitialized renderer."); - - RendVkFrameResources res = ctx->frame_resources[ctx->frame_index]; - - VkImageMemoryBarrier2 present_barrier; - present_barrier = (VkImageMemoryBarrier2) { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, - - .srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, - .srcAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, - - .dstStageMask = VK_PIPELINE_STAGE_2_NONE, - .dstAccessMask = 0, - - .oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - - .image = ctx->swapchain.images[ctx->image_index], - - .subresourceRange = (VkImageSubresourceRange) { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1 - } - - }; - - VkDependencyInfo dep_info = {VK_STRUCTURE_TYPE_DEPENDENCY_INFO}; - dep_info.imageMemoryBarrierCount = 1; - dep_info.pImageMemoryBarriers = &present_barrier; - - vkCmdPipelineBarrier2(res.command_buffer, &dep_info); - vkEndCommandBuffer(res.command_buffer); - - /* ensure swapchain image is available */ - VkSemaphoreSubmitInfo image_acquire_await_info = { - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, - .semaphore = res.image_acquired_semaphore, - .stageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT - }; - - /* signal that the image is presented */ - VkSemaphoreSubmitInfo semaphore_signals[2] = { - [0] = { - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, - .semaphore = ctx->render_complete_semaphores[ctx->image_index], - .stageMask = VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT - }, - [1] = { - .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, - .semaphore = ctx->timeline_semaphore, - .value = ctx->signal_value, - .stageMask = VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT - }, - }; - - VkCommandBufferSubmitInfo cmd_submit_info = { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO, - .commandBuffer = res.command_buffer, - }; - - VkSubmitInfo2 submit_info = { - .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2, - .waitSemaphoreInfoCount = 1, - .pWaitSemaphoreInfos = &image_acquire_await_info, - .commandBufferInfoCount = 1, - .pCommandBufferInfos = &cmd_submit_info, - .signalSemaphoreInfoCount = 2, - .pSignalSemaphoreInfos = semaphore_signals - }; - - vkQueueSubmit2(vk_device.graphics_queue, 1, &submit_info, VK_NULL_HANDLE); - - VkPresentInfoKHR present_info = { - .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, - .waitSemaphoreCount = 1, - .pWaitSemaphores = &ctx->render_complete_semaphores[ctx->image_index], - .swapchainCount = 1, - .pSwapchains = &ctx->swapchain.handle, - .pImageIndices = &ctx->image_index, - .pResults = NULL, - }; - - vkQueuePresentKHR(vk_device.graphics_queue, &present_info); - - ctx->frame++; - ctx->in_frame = false; - uint64_t f = ctx->frame_index; - - /* clear host mapped memory */ - rend_vk_arena_clear_all(&ctx->arena_frame); - rend_vk_arena_clear(&ctx->arena_persistent, vk_device.host_index); + RendVk14Context *ctx = (RendVk14Context *)handle; + RASSERT(ctx, "Uninitialized renderer."); + + RendVkFrameResources res = ctx->frame_resources[ctx->frame_index]; + + VkImageMemoryBarrier2 present_barrier; + present_barrier = (VkImageMemoryBarrier2) { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + + .srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + .srcAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, + + .dstStageMask = VK_PIPELINE_STAGE_2_NONE, + .dstAccessMask = 0, + + .oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + + .image = ctx->swapchain.images[ctx->image_index], + + .subresourceRange = (VkImageSubresourceRange) { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1 + } + + }; + + VkDependencyInfo dep_info = {VK_STRUCTURE_TYPE_DEPENDENCY_INFO}; + dep_info.imageMemoryBarrierCount = 1; + dep_info.pImageMemoryBarriers = &present_barrier; + + vkCmdPipelineBarrier2(res.command_buffer, &dep_info); + vkEndCommandBuffer(res.command_buffer); + + /* ensure swapchain image is available */ + VkSemaphoreSubmitInfo image_acquire_await_info = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, + .semaphore = res.image_acquired_semaphore, + .stageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT + }; + + /* signal that the image is presented */ + VkSemaphoreSubmitInfo semaphore_signals[2] = { + [0] = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, + .semaphore = ctx->render_complete_semaphores[ctx->image_index], + .stageMask = VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT + }, + [1] = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, + .semaphore = ctx->timeline_semaphore, + .value = ctx->signal_value, + .stageMask = VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT + }, + }; + + VkCommandBufferSubmitInfo cmd_submit_info = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO, + .commandBuffer = res.command_buffer, + }; + + VkSubmitInfo2 submit_info = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2, + .waitSemaphoreInfoCount = 1, + .pWaitSemaphoreInfos = &image_acquire_await_info, + .commandBufferInfoCount = 1, + .pCommandBufferInfos = &cmd_submit_info, + .signalSemaphoreInfoCount = 2, + .pSignalSemaphoreInfos = semaphore_signals + }; + + vkQueueSubmit2(vk_device.graphics_queue, 1, &submit_info, VK_NULL_HANDLE); + + VkPresentInfoKHR present_info = { + .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + .waitSemaphoreCount = 1, + .pWaitSemaphores = &ctx->render_complete_semaphores[ctx->image_index], + .swapchainCount = 1, + .pSwapchains = &ctx->swapchain.handle, + .pImageIndices = &ctx->image_index, + .pResults = NULL, + }; + + vkQueuePresentKHR(vk_device.graphics_queue, &present_info); + + ctx->frame++; + ctx->in_frame = false; + + /* clear host mapped memory */ + rend_vk_arena_clear_all(&ctx->arena_frame); + rend_vk_arena_clear(&ctx->arena_persistent, vk_device.host_index); } static inline void -rend_vk__renderer_render_pass_begin_internal(RendRenderer renderer, float r, float g, float b, float a, uint64_t view_handle, uint64_t depth_attachment_view_handle, uint32_t offset_x, uint32_t offset_y, uint32_t width, uint32_t height) +rend_vk__renderer_render_pass_begin_internal(RendContextHandle handle, float r, float g, float b, float a, uint64_t view_handle, uint64_t depth_attachment_view_handle, uint32_t offset_x, uint32_t offset_y, uint32_t width, uint32_t height, VkAttachmentLoadOp color_load) { - RendVk14Context *ctx = (RendVk14Context*) renderer->context; - RASSERT(ctx->in_frame && "must begin render pass inside a frame"); - - RendVkFrameResources frame_resource = ctx->frame_resources[ctx->frame_index]; - - VkRenderingAttachmentInfo color_attachment = { - .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, - .imageView = (VkImageView) view_handle, - .imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, // clear image - .storeOp = VK_ATTACHMENT_STORE_OP_STORE, // store for presentation - .clearValue = (VkClearValue) { - .color = {r, g, b, a}, - } - }; - - VkRenderingAttachmentInfo depth_attachment = { - .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, - .imageView = (VkImageView) depth_attachment_view_handle, - .imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, - .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, // clear depth data - .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, // don't care after rendering - .clearValue = (VkClearValue) { - .depthStencil = (VkClearDepthStencilValue) {1.0f, 0}, - }, - }; - - VkRenderingInfo rendering_info = { - .sType = VK_STRUCTURE_TYPE_RENDERING_INFO, - .renderArea.offset = (VkOffset2D) {offset_x, offset_y}, - .renderArea.extent = (VkExtent2D) {width, height}, - .layerCount = 1, - .colorAttachmentCount = 1, - .pColorAttachments = &color_attachment, - .pDepthAttachment = &depth_attachment, - }; - - vkCmdBeginRendering(frame_resource.command_buffer, &rendering_info); - - VkViewport viewport = { - .x = offset_x, - .y = offset_y, - .width = width, - .height = height, - .minDepth = 0.0f, - .maxDepth = 1.0f - }; - - vkCmdSetViewport(frame_resource.command_buffer, 0, 1, &viewport); - - VkRect2D scissor = {{offset_x, offset_y}, {width, height}}; - vkCmdSetScissor(frame_resource.command_buffer, 0, 1, &scissor); + RendVk14Context *ctx = (RendVk14Context *)handle; + RASSERT(ctx->in_frame && "must begin render pass inside a frame"); + + RendVkFrameResources frame_resource = ctx->frame_resources[ctx->frame_index]; + + VkRenderingAttachmentInfo color_attachment = { + .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, + .imageView = (VkImageView)view_handle, + .imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + .loadOp = color_load, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .clearValue = { + .color = {{r, g, b, a}}, + } + }; + + VkRenderingAttachmentInfo depth_attachment = { + .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, + .imageView = (VkImageView)depth_attachment_view_handle, + .imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, + .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, /* clear depth data */ + .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, /* don't care after rendering */ + .clearValue = (VkClearValue) { + .depthStencil = (VkClearDepthStencilValue) {1.0f, 0}, + }, + }; + + VkRenderingInfo rendering_info = { + .sType = VK_STRUCTURE_TYPE_RENDERING_INFO, + .renderArea.offset = (VkOffset2D) {offset_x, offset_y}, + .renderArea.extent = (VkExtent2D) {width, height}, + .layerCount = 1, + .colorAttachmentCount = 1, + .pColorAttachments = &color_attachment, + .pDepthAttachment = &depth_attachment, + }; + + vkCmdBeginRendering(frame_resource.command_buffer, &rendering_info); + + VkViewport viewport = { + .x = offset_x, + .y = offset_y, + .width = width, + .height = height, + .minDepth = 0.0f, + .maxDepth = 1.0f + }; + + vkCmdSetViewport(frame_resource.command_buffer, 0, 1, &viewport); + + VkRect2D scissor = {{offset_x, offset_y}, {width, height}}; + vkCmdSetScissor(frame_resource.command_buffer, 0, 1, &scissor); } -extern void -rend_vk_renderer_render_pass_begin(RendRenderer renderer, float r, float g, float b, float a) +void +rend_vk_renderer_render_pass_begin(RendContextHandle handle, float r, float g, float b, float a) { - - RendVk14Context *ctx = (RendVk14Context*) renderer->context; - - rend_vk__renderer_render_pass_begin_internal( - renderer, - r, g, b, a, - (uint64_t) ctx->swapchain.views[ctx->image_index], - (uint64_t) ctx->swapchain.depth_attachment.view, - 0, 0, - ctx->swapchain.extent.width, ctx->swapchain.extent.height - ); + RendVk14Context *ctx = (RendVk14Context *)handle; + + rend_vk__renderer_render_pass_begin_internal( + handle, + r, g, b, a, + (uint64_t) ctx->swapchain.views[ctx->image_index], + (uint64_t) ctx->swapchain.depth_attachment.view, + 0, 0, + ctx->swapchain.extent.width, ctx->swapchain.extent.height, + VK_ATTACHMENT_LOAD_OP_CLEAR + ); } -extern void -rend_vk_renderer_render_pass_begin_texture(RendRenderer renderer, RendTexture *texture) +void +rend_vk_renderer_render_pass_begin_texture(RendContextHandle handle, RendTexture *texture) { - - RendVk14Context *ctx = (RendVk14Context*) renderer->context; - - RendVkFrameResources frame_resource = ctx->frame_resources[ctx->frame_index]; - - rend_vk_texture_transition_layout(renderer, ctx->frame_resources[ctx->frame_index].command_buffer, texture, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - - VkRenderingAttachmentInfo color_attachment = { - .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, - .imageView = (VkImageView) texture->view, - .imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, // load image instead of clearing - .storeOp = VK_ATTACHMENT_STORE_OP_STORE, // store for presentation - }; - - VkRenderingAttachmentInfo depth_attachment = { - .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, - .imageView = (VkImageView) ctx->swapchain.depth_attachment.view, - .imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, - .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, // clear depth data - .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, // don't care after rendering - .clearValue = (VkClearValue) { - .depthStencil = (VkClearDepthStencilValue) {1.0f, 0}, - }, - }; - - VkRenderingInfo rendering_info = { - .sType = VK_STRUCTURE_TYPE_RENDERING_INFO, - .renderArea.offset = (VkOffset2D) {0, 0}, - .renderArea.extent = (VkExtent2D) {texture->width, texture->height}, - .layerCount = 1, - .colorAttachmentCount = 1, - .pColorAttachments = &color_attachment, - .pDepthAttachment = &depth_attachment, - }; - - vkCmdBeginRendering(frame_resource.command_buffer, &rendering_info); - - VkViewport viewport = { - .x = 0, - .y = 0, - .width = texture->width, - .height = texture->height, - .minDepth = 0.0f, - .maxDepth = 1.0f - }; - - vkCmdSetViewport(frame_resource.command_buffer, 0, 1, &viewport); - - VkRect2D scissor = {{0, 0}, {texture->width, texture->height}}; - vkCmdSetScissor(frame_resource.command_buffer, 0, 1, &scissor); + RendVk14Context *ctx = (RendVk14Context *)handle; + + rend_vk_texture_transition_layout(handle, ctx->frame_resources[ctx->frame_index].command_buffer, texture, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + rend_vk__renderer_render_pass_begin_internal( + handle, + 0, 0, 0, 0, + (uint64_t)texture->view, + (uint64_t)ctx->swapchain.depth_attachment.view, + 0, 0, + texture->width, texture->height, + VK_ATTACHMENT_LOAD_OP_LOAD + ); } -extern void -rend_vk_renderer_render_pass_end_texture(RendRenderer renderer, RendTexture *texture) +void +rend_vk_renderer_render_pass_end(RendContextHandle handle) { - - RendVk14Context *ctx = (RendVk14Context*) renderer->context; - rend_vk_texture_transition_layout(renderer, ctx->frame_resources[ctx->frame_index].command_buffer, texture, VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL); - rend_vk_renderer_render_pass_end(renderer); + RendVk14Context *ctx = (RendVk14Context *)handle; + RASSERT(ctx->in_frame && "must end render pass inside a frame"); + RendVkFrameResources res = ctx->frame_resources[ctx->frame_index]; + vkCmdEndRendering(res.command_buffer); } - -extern void -rend_vk_renderer_render_pass_end(RendRenderer renderer) +void +rend_vk_renderer_render_pass_end_texture(RendContextHandle handle, RendTexture *texture) { - RendVk14Context *ctx = (RendVk14Context*) renderer->context; - RASSERT(ctx->in_frame && "must end render pass inside a frame"); - RendVkFrameResources res = ctx->frame_resources[ctx->frame_index]; - vkCmdEndRendering(res.command_buffer); + RendVk14Context *ctx = (RendVk14Context *)handle; + rend_vk_texture_transition_layout(handle, ctx->frame_resources[ctx->frame_index].command_buffer, texture, VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL); + rend_vk_renderer_render_pass_end(handle); } -extern void -rend_vk_descriptor_write_buffer(RendRenderer renderer, RendBuffer ubo, uint32_t binding, uint32_t slot, uint32_t offset, uint32_t size, bool is_ubo) +void +rend_vk_descriptor_write_buffer(RendContextHandle handle, RendBuffer ubo, uint32_t binding, uint32_t slot, uint32_t offset, uint32_t size, bool is_ubo) { - RendVk14Context *ctx = renderer->context; - - VkDescriptorBufferInfo buffer_info = { - .buffer = (VkBuffer) ubo.handle, - .offset = offset, - .range = size, - }; - - VkWriteDescriptorSet descriptor_write = { - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .dstSet = ctx->desc_set, - .dstBinding = binding, - .dstArrayElement = slot, // write texture to slot - .descriptorCount = 1, - .descriptorType = (is_ubo) ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER : VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - .pBufferInfo = &buffer_info, - }; - - vkUpdateDescriptorSets(vk_device.logical_device, 1, &descriptor_write, 0, NULL); + RendVk14Context *ctx = (RendVk14Context *)handle; + + VkDescriptorBufferInfo buffer_info = { + .buffer = (VkBuffer)ubo.handle, + .offset = offset, + .range = size, + }; + + VkWriteDescriptorSet descriptor_write = { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = ctx->desc_set, + .dstBinding = binding, + .dstArrayElement = slot, /* write texture to slot */ + .descriptorCount = 1, + .descriptorType = (is_ubo) ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER : VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .pBufferInfo = &buffer_info, + }; + + vkUpdateDescriptorSets(vk_device.logical_device, 1, &descriptor_write, 0, NULL); } -extern RendBuffer -rend_vk_buffer_create_lifetime(RendRenderer renderer, size_t size, RendBufferType type, bool gpu, int lifetime) +RendBuffer +rend_vk_buffer_create_lifetime(RendContextHandle handle, size_t size, RendBufferType type, bool gpu, int lifetime) { - RendVk14Context *ctx = (RendVk14Context *)renderer->context; - int32_t index = (gpu) ? vk_device.device_index : vk_device.host_index; - - VkBufferUsageFlags vk_usage = 0; - switch (type) { - case REND_BUFFER_VERTEX: - vk_usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; - break; - case REND_BUFFER_INDEX: - vk_usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; - break; - case REND_BUFFER_UNIFORM: - vk_usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; - break; - case REND_BUFFER_STORAGE: - vk_usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - break; - case REND_BUFFER_TRANSFER: - vk_usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; - break; - default: - REND__CRASH("Invalid buffer type!"); - break; - } - - RendBuffer buffer = {0}; - buffer.usage = vk_usage | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; - - bool is_concurrent = (gpu && (vk_device.graphics_family_index != vk_device.transfer_family_index)); - int32_t family[] = { vk_device.graphics_family_index, vk_device.transfer_family_index }; - - VkBufferCreateInfo buffer_info = { - .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, - .size = size, - .usage = buffer.usage, - .sharingMode = is_concurrent ? VK_SHARING_MODE_CONCURRENT : VK_SHARING_MODE_EXCLUSIVE, - .queueFamilyIndexCount = is_concurrent ? 2 : 1, - .pQueueFamilyIndices = is_concurrent ? family : NULL, - }; - - if (vkCreateBuffer(vk_device.logical_device, &buffer_info, vk_allocator, (VkBuffer*) &buffer.handle) != VK_SUCCESS) { - REND__CRASH("Failed to create VkBuffer!"); - } - - VkMemoryRequirements mem_reqs; - vkGetBufferMemoryRequirements(vk_device.logical_device, (VkBuffer) buffer.handle, &mem_reqs); - - RASSERT((mem_reqs.memoryTypeBits & (1u << index)) && "Buffer incompatible with chosen memory type!"); - - RendVkArenaAllocator *arena = (lifetime == REND_LIFETIME_FRAME) ? &ctx->arena_frame : &ctx->arena_persistent; - RendMemory vk_memory = rend_vk_arena_alloc(arena, mem_reqs.size, index); - - if (vkBindBufferMemory(vk_device.logical_device, (VkBuffer) buffer.handle, (VkDeviceMemory) vk_memory.device_memory, vk_memory.offset) != VK_SUCCESS) { - REND__CRASH("Failed to bind VkBuffer memory!"); - } - - buffer.memory = vk_memory; - - VkBufferDeviceAddressInfo address_info = { - .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, - .buffer = (VkBuffer) buffer.handle, - }; - - if (!gpu && vk_memory.host_mapped_memory) { - buffer.mapped_memory = vk_memory.host_mapped_memory; - } else { - buffer.mapped_memory = NULL; - } - - buffer.gpu_address = vkGetBufferDeviceAddress(vk_device.logical_device, &address_info); - buffer.size = size; - return buffer; + RendVk14Context *ctx = (RendVk14Context *)handle; + int32_t index = (gpu) ? vk_device.device_index : vk_device.host_index; + + if (type >= REND_BUFFER_COUNT || !vk_buffer_usage[type]) { + REND__CRASH("Invalid buffer type!"); + } + VkBufferUsageFlags vk_usage = vk_buffer_usage[type]; + + RendBuffer buffer = {0}; + buffer.usage = vk_usage | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; + + bool is_concurrent = (gpu && (vk_device.graphics_family_index != vk_device.transfer_family_index)); + uint32_t family[] = { vk_device.graphics_family_index, vk_device.transfer_family_index }; + + VkBufferCreateInfo buffer_info = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .size = size, + .usage = buffer.usage, + .sharingMode = is_concurrent ? VK_SHARING_MODE_CONCURRENT : VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = is_concurrent ? 2 : 1, + .pQueueFamilyIndices = is_concurrent ? family : NULL, + }; + + if (vkCreateBuffer(vk_device.logical_device, &buffer_info, vk_allocator, (VkBuffer *) &buffer.handle) != VK_SUCCESS) { + REND__CRASH("Failed to create VkBuffer!"); + } + + VkMemoryRequirements mem_reqs; + vkGetBufferMemoryRequirements(vk_device.logical_device, (VkBuffer)buffer.handle, &mem_reqs); + + RASSERT((mem_reqs.memoryTypeBits & (1u << index)) && "Buffer incompatible with chosen memory type!"); + + RendVkArenaAllocator *arena = (lifetime == REND_LIFETIME_FRAME) ? &ctx->arena_frame : &ctx->arena_persistent; + RendMemory vk_memory = rend_vk_arena_alloc(arena, mem_reqs.size, index); + + if (vkBindBufferMemory(vk_device.logical_device, (VkBuffer)buffer.handle, (VkDeviceMemory)vk_memory.device_memory, vk_memory.offset) != VK_SUCCESS) { + REND__CRASH("Failed to bind VkBuffer memory!"); + } + + buffer.memory = vk_memory; + + VkBufferDeviceAddressInfo address_info = { + .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, + .buffer = (VkBuffer)buffer.handle, + }; + + if (!gpu && vk_memory.host_mapped_memory) { + buffer.mapped_memory = vk_memory.host_mapped_memory; + } else { + buffer.mapped_memory = NULL; + } + + buffer.gpu_address = vkGetBufferDeviceAddress(vk_device.logical_device, &address_info); + buffer.size = size; + return buffer; } -extern void +void rend_vk_buffer_destroy(RendBuffer *buffer) { - // NOTE: this function is meant to be called by the user - // when freeing his buffers mid frame, there may be a better - // way using fences perhaps? or by checking the timeline semaphore? - vkDeviceWaitIdle(vk_device.logical_device); - vkDestroyBuffer(vk_device.logical_device, (VkBuffer) buffer->handle, vk_allocator); - memset(buffer, 0xC0FFEE, sizeof *buffer); // fill buffer with coffee + /* NOTE: this function is meant to be called by the user */ + /* when freeing his buffers mid frame, there may be a better */ + /* way using fences perhaps? or by checking the timeline semaphore? */ + vkDeviceWaitIdle(vk_device.logical_device); + vkDestroyBuffer(vk_device.logical_device, (VkBuffer)buffer->handle, vk_allocator); + memset(buffer, 0xC0FFEE, sizeof(*buffer)); /* fill buffer with coffee */ } -extern void -rend_vk_buffer_copy(RendRenderer renderer, RendBuffer *dest, size_t dest_offset, RendBuffer *src, size_t src_offset, size_t bytes) +void +rend_vk_buffer_copy(RendContextHandle handle, RendBuffer *dest, size_t dest_offset, RendBuffer *src, size_t src_offset, size_t bytes) { - RASSERT(src && dest); // check that im not sending null pointers - RASSERT(src->usage & VK_BUFFER_USAGE_TRANSFER_SRC_BIT); // source buffer must be marked as transfer src - RASSERT(dest->usage & VK_BUFFER_USAGE_TRANSFER_DST_BIT); // dest buffer must be marked as transfer dest - - RendVk14Context *ctx = (RendVk14Context *)renderer->context; - VkBuffer src_vk = (VkBuffer)(uintptr_t)src->handle; - VkBuffer dest_vk = (VkBuffer)(uintptr_t)dest->handle; - - VkCommandBuffer transfer_cmd = VK_NULL_HANDLE; - - VkCommandBufferAllocateInfo alloc_info = { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, - .commandBufferCount = 1, - .commandPool = ctx->upload_command_pool, - .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, - .pNext = NULL, - }; - - if (vkAllocateCommandBuffers(vk_device.logical_device, &alloc_info, &transfer_cmd) != VK_SUCCESS) { - REND__CRASH("Failed to allocate transfer command buffer!"); - } - - VkCommandBufferBeginInfo begin_info = { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, - }; - vkBeginCommandBuffer(transfer_cmd, &begin_info); - - VkBufferCopy buffer_copy = { - .srcOffset = (VkDeviceSize)src_offset, - .dstOffset = (VkDeviceSize)dest_offset, - .size = (VkDeviceSize)bytes, - }; - - vkCmdCopyBuffer(transfer_cmd, src_vk, dest_vk, 1, &buffer_copy); - vkEndCommandBuffer(transfer_cmd); - - VkCommandBufferSubmitInfo cmd_info = { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO, - .commandBuffer = transfer_cmd, - .deviceMask = 0, - }; - - VkSubmitInfo2 submit_info = { - .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2, - .commandBufferInfoCount = 1, - .pCommandBufferInfos = &cmd_info, - }; - - vkQueueSubmit2(vk_device.transfer_queue, 1, &submit_info, VK_NULL_HANDLE); - - vkQueueWaitIdle(vk_device.transfer_queue); - vkFreeCommandBuffers(vk_device.logical_device, ctx->upload_command_pool, 1, &transfer_cmd); + RASSERT(src && dest); /* check that im not sending null pointers */ + RASSERT(src->usage & VK_BUFFER_USAGE_TRANSFER_SRC_BIT); /* source buffer must be marked as transfer src */ + RASSERT(dest->usage & VK_BUFFER_USAGE_TRANSFER_DST_BIT); /* dest buffer must be marked as transfer dest */ + + RendVk14Context *ctx = (RendVk14Context *)handle; + VkBuffer src_vk = (VkBuffer)(uintptr_t)src->handle; + VkBuffer dest_vk = (VkBuffer)(uintptr_t)dest->handle; + + VkCommandBuffer transfer_cmd = VK_NULL_HANDLE; + + VkCommandBufferAllocateInfo alloc_info = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .commandBufferCount = 1, + .commandPool = ctx->upload_command_pool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .pNext = NULL, + }; + + if (vkAllocateCommandBuffers(vk_device.logical_device, &alloc_info, &transfer_cmd) != VK_SUCCESS) { + REND__CRASH("Failed to allocate transfer command buffer!"); + } + + VkCommandBufferBeginInfo begin_info = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + }; + vkBeginCommandBuffer(transfer_cmd, &begin_info); + + VkBufferCopy buffer_copy = { + .srcOffset = (VkDeviceSize)src_offset, + .dstOffset = (VkDeviceSize)dest_offset, + .size = (VkDeviceSize)bytes, + }; + + vkCmdCopyBuffer(transfer_cmd, src_vk, dest_vk, 1, &buffer_copy); + vkEndCommandBuffer(transfer_cmd); + + VkCommandBufferSubmitInfo cmd_info = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO, + .commandBuffer = transfer_cmd, + .deviceMask = 0, + }; + + VkSubmitInfo2 submit_info = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2, + .commandBufferInfoCount = 1, + .pCommandBufferInfos = &cmd_info, + }; + + vkQueueSubmit2(vk_device.transfer_queue, 1, &submit_info, VK_NULL_HANDLE); + + vkQueueWaitIdle(vk_device.transfer_queue); + vkFreeCommandBuffers(vk_device.logical_device, ctx->upload_command_pool, 1, &transfer_cmd); } -extern RendTexture -rend_vk_texture_create(RendRenderer renderer, uint32_t width, uint32_t height, uint32_t depth, uint32_t mip_levels, uint32_t layers, RendFormat format) +RendTexture +rend_vk_texture_create(RendContextHandle handle, uint32_t width, uint32_t height, uint32_t depth, uint32_t mip_levels, uint32_t layers, RendFormat format) { - RASSERT(format < REND_FORMAT_COUNT && "Invalid format!"); - - RendVk14Context *ctx = (RendVk14Context*) renderer->context; - VkFormat vk_format = vk_format_from_rend_format[format]; - - uint32_t safe_depth = (depth > 0) ? depth : 1; - uint32_t safe_mips = (mip_levels > 0) ? mip_levels : 1; - uint32_t safe_layers = (layers > 0) ? layers : 1; - - RendTexture tex = { - .handle = 0, - .width = width, - .height = height, - .depth = safe_depth, - .mip_levels = safe_mips, - .layers = safe_layers, - .format = vk_format, - .layout = VK_IMAGE_LAYOUT_UNDEFINED, - }; - - VkImageCreateInfo image_info = { - .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, - .imageType = (safe_depth > 1) ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D, - .extent = { .width = width, .height = height, .depth = safe_depth }, - .mipLevels = tex.mip_levels, - .arrayLayers = tex.layers, - .format = vk_format, - .tiling = VK_IMAGE_TILING_OPTIMAL, - .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, - .sharingMode = VK_SHARING_MODE_EXCLUSIVE, - .samples = VK_SAMPLE_COUNT_1_BIT, - .flags = 0, - }; - - if (vkCreateImage(vk_device.logical_device, &image_info, vk_allocator, (VkImage*)&tex.handle) != VK_SUCCESS) { - REND__CRASH("failed to create image!"); - return tex; - } - - VkMemoryRequirements mem_requirements; - vkGetImageMemoryRequirements(vk_device.logical_device, (VkImage)tex.handle, &mem_requirements); - - uint32_t index = rend_vk_get_heap_index(mem_requirements.memoryTypeBits, vk_device.device_index); - tex.memory = rend_vk_arena_alloc(&ctx->arena_persistent, mem_requirements.size, index); - - vkBindImageMemory(vk_device.logical_device, (VkImage)tex.handle, (VkDeviceMemory)tex.memory.device_memory, (VkDeviceSize)tex.memory.offset); - - // Correct ImageView creation using strict VkImageViewType and derived format - VkImageViewCreateInfo view_info = { - .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .image = (VkImage)tex.handle, - .viewType = (safe_depth > 1) ? VK_IMAGE_VIEW_TYPE_3D : VK_IMAGE_VIEW_TYPE_2D, - .format = vk_format, - .subresourceRange = { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = tex.mip_levels, - .baseArrayLayer = 0, - .layerCount = tex.layers, - }, - }; - - if (vkCreateImageView(vk_device.logical_device, &view_info, vk_allocator, (VkImageView*)&tex.view) != VK_SUCCESS) { - REND__CRASH("failed to create image view!"); - return tex; - } - - /* per-texture sampler */ - VkSamplerCreateInfo sampler_info = { - .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, - .magFilter = VK_FILTER_LINEAR, // force sharp upscaling - .minFilter = VK_FILTER_LINEAR, // TODO: add filter setting to texture - .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT, - .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT, - .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT, - - .mipmapMode = (tex.mip_levels > 1) ? VK_SAMPLER_MIPMAP_MODE_LINEAR : VK_SAMPLER_MIPMAP_MODE_NEAREST, - .minLod = 0.0f, - .maxLod = (tex.mip_levels > 1) ? (float)tex.mip_levels : 0.0f, - - .anisotropyEnable = (tex.mip_levels > 1 && vk_device.features.samplerAnisotropy) ? VK_TRUE : VK_FALSE, - .maxAnisotropy = 8.0f, - }; - - if (vkCreateSampler(vk_device.logical_device, &sampler_info, vk_allocator, (VkSampler*)&tex.sampler) != VK_SUCCESS) { - REND__CRASH("failed to create sampler!"); - } - - return tex; + RASSERT(format < REND_FORMAT_COUNT && "Invalid format!"); + + RendVk14Context *ctx = (RendVk14Context *)handle; + VkFormat vk_format = vk_format_from_rend_format[format]; + + uint32_t safe_depth = (depth > 0) ? depth : 1; + uint32_t safe_mips = (mip_levels > 0) ? mip_levels : 1; + uint32_t safe_layers = (layers > 0) ? layers : 1; + + RendTexture tex = { + .handle = 0, + .width = width, + .height = height, + .depth = safe_depth, + .mip_levels = safe_mips, + .layers = safe_layers, + .format = vk_format, + .layout = VK_IMAGE_LAYOUT_UNDEFINED, + }; + + VkImageCreateInfo image_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .imageType = (safe_depth > 1) ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D, + .extent = { .width = width, .height = height, .depth = safe_depth }, + .mipLevels = tex.mip_levels, + .arrayLayers = tex.layers, + .format = vk_format, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .samples = VK_SAMPLE_COUNT_1_BIT, + .flags = 0, + }; + + if (vkCreateImage(vk_device.logical_device, &image_info, vk_allocator, (VkImage *)&tex.handle) != VK_SUCCESS) { + REND__CRASH("failed to create image!"); + return tex; + } + + VkMemoryRequirements mem_requirements; + vkGetImageMemoryRequirements(vk_device.logical_device, (VkImage)tex.handle, &mem_requirements); + + uint32_t index = rend_vk_get_heap_index(mem_requirements.memoryTypeBits, vk_device.device_index); + tex.memory = rend_vk_arena_alloc(&ctx->arena_persistent, mem_requirements.size, index); + + vkBindImageMemory(vk_device.logical_device, (VkImage)tex.handle, (VkDeviceMemory)tex.memory.device_memory, (VkDeviceSize)tex.memory.offset); + + /* Correct ImageView creation using strict VkImageViewType and derived format */ + VkImageViewCreateInfo view_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = (VkImage)tex.handle, + .viewType = (safe_depth > 1) ? VK_IMAGE_VIEW_TYPE_3D : VK_IMAGE_VIEW_TYPE_2D, + .format = vk_format, + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = tex.mip_levels, + .baseArrayLayer = 0, + .layerCount = tex.layers, + }, + }; + + if (vkCreateImageView(vk_device.logical_device, &view_info, vk_allocator, (VkImageView *)&tex.view) != VK_SUCCESS) { + REND__CRASH("failed to create image view!"); + return tex; + } + + /* per-texture sampler */ + VkSamplerCreateInfo sampler_info = { + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .magFilter = VK_FILTER_LINEAR, /* force sharp upscaling */ + .minFilter = VK_FILTER_LINEAR, /* TODO: add filter setting to texture */ + .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT, + + .mipmapMode = (tex.mip_levels > 1) ? VK_SAMPLER_MIPMAP_MODE_LINEAR : VK_SAMPLER_MIPMAP_MODE_NEAREST, + .minLod = 0.0f, + .maxLod = (tex.mip_levels > 1) ? (float)tex.mip_levels : 0.0f, + + .anisotropyEnable = (tex.mip_levels > 1 && vk_device.features.samplerAnisotropy) ? VK_TRUE : VK_FALSE, + .maxAnisotropy = 8.0f, + }; + + if (vkCreateSampler(vk_device.logical_device, &sampler_info, vk_allocator, (VkSampler *)&tex.sampler) != VK_SUCCESS) { + REND__CRASH("failed to create sampler!"); + } + + return tex; } -extern void -rend_vk_texture_destroy(RendRenderer renderer, RendTexture *tex) +void +rend_vk_texture_destroy(RendContextHandle handle, RendTexture *tex) { - RASSERT(renderer && tex); - RendVk14Context *ctx = (RendVk14Context*)renderer->context; + RASSERT(handle && tex); - if (tex->handle) { - vkDestroyImage(vk_device.logical_device, (VkImage) tex->handle, vk_allocator); - tex->handle = 0; - } + if (tex->handle) { + vkDestroyImage(vk_device.logical_device, (VkImage)tex->handle, vk_allocator); + tex->handle = 0; + } - if (tex->view) { - vkDestroyImageView(vk_device.logical_device, (VkImageView) tex->view, vk_allocator); - tex->view = 0; - } + if (tex->view) { + vkDestroyImageView(vk_device.logical_device, (VkImageView)tex->view, vk_allocator); + tex->view = 0; + } - if (tex->sampler) { - vkDestroySampler(vk_device.logical_device, (VkSampler)tex->sampler, vk_allocator); - tex->sampler = 0; - } + if (tex->sampler) { + vkDestroySampler(vk_device.logical_device, (VkSampler)tex->sampler, vk_allocator); + tex->sampler = 0; + } - // memset(tex, 0xBABE, sizeof *tex); + /* memset(tex, 0xBABE, sizeof(*tex)); */ } -extern void -rend_vk_texture_transition_layout(RendRenderer renderer, VkCommandBuffer cmd, RendTexture *texture, VkImageLayout new_layout) +static void +rend_vk_texture_barrier(VkCommandBuffer cmd, RendTexture *texture, VkImageLayout new_layout, uint32_t src_family, uint32_t dst_family, VkAccessFlags2 src_access, VkAccessFlags2 dst_access, VkPipelineStageFlags2 src_stage, VkPipelineStageFlags2 dst_stage) { - RendVk14Context *ctx = renderer->context; - VkCommandPool pool = ctx->upload_command_pool; - - VkImageMemoryBarrier2 barrier = { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, - .oldLayout = texture->layout, - .newLayout = new_layout, - - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - - .image = (VkImage) texture->handle, - - .subresourceRange = (VkImageSubresourceRange) { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = texture->mip_levels, - .baseArrayLayer = 0, - .layerCount = texture->layers, - }, - - .srcAccessMask = 0, // TODO - .dstAccessMask = 0, // TODO - - }; - - if (texture->layout == VK_IMAGE_LAYOUT_UNDEFINED && new_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - barrier.srcAccessMask = 0; - barrier.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; - barrier.srcStageMask = VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT; - barrier.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT; - } else if (texture->layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && new_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { - barrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT; - barrier.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT; - barrier.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT; - } else { - barrier.srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT; - barrier.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT; - barrier.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT; - } - - VkDependencyInfo dep = { - .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, - .pImageMemoryBarriers = &barrier, - .imageMemoryBarrierCount = 1, - }; - - texture->layout = new_layout; - vkCmdPipelineBarrier2(cmd, &dep); + VkImageMemoryBarrier2 barrier = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + .oldLayout = texture->layout, + .newLayout = new_layout, + .srcQueueFamilyIndex = src_family, + .dstQueueFamilyIndex = dst_family, + .image = (VkImage)texture->handle, + .srcAccessMask = src_access, + .dstAccessMask = dst_access, + .srcStageMask = src_stage, + .dstStageMask = dst_stage, + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = texture->mip_levels, + .baseArrayLayer = 0, + .layerCount = texture->layers, + }, + }; + VkDependencyInfo dep = { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pImageMemoryBarriers = &barrier, + .imageMemoryBarrierCount = 1, + }; + texture->layout = new_layout; + vkCmdPipelineBarrier2(cmd, &dep); } -extern void -rend_vk_texture_transfer_ownership_release(RendRenderer renderer, VkCommandBuffer cmd, RendTexture *texture, uint32_t src_family, uint32_t dst_family, VkImageLayout new_layout) +void +rend_vk_texture_transition_layout(RendContextHandle handle, VkCommandBuffer cmd, RendTexture *texture, VkImageLayout new_layout) { - RendVk14Context *ctx = renderer->context; - (void) ctx; // reserved in case you need it for stage/access lookups later - - VkImageMemoryBarrier2 barrier = { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, - .oldLayout = texture->layout, - .newLayout = new_layout, - .srcQueueFamilyIndex = src_family, - .dstQueueFamilyIndex = dst_family, - .image = (VkImage) texture->handle, - .subresourceRange = (VkImageSubresourceRange) { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = texture->mip_levels, - .baseArrayLayer = 0, - .layerCount = texture->layers, - }, - }; - - if (texture->layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && new_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { - barrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; - barrier.dstAccessMask = 0; - barrier.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT; - barrier.dstStageMask = VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT; - } else { - barrier.srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT; - barrier.dstAccessMask = 0; - barrier.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT; - barrier.dstStageMask = VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT; - } - - VkDependencyInfo dep = { - .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, - .pImageMemoryBarriers = &barrier, - .imageMemoryBarrierCount = 1, - }; - - texture->layout = new_layout; - vkCmdPipelineBarrier2(cmd, &dep); + (void)handle; + if (texture->layout == VK_IMAGE_LAYOUT_UNDEFINED && new_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + rend_vk_texture_barrier(cmd, texture, new_layout, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, + 0, VK_ACCESS_2_TRANSFER_WRITE_BIT, + VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_2_TRANSFER_BIT); + } else if (texture->layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && new_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + rend_vk_texture_barrier(cmd, texture, new_layout, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, + VK_ACCESS_2_TRANSFER_WRITE_BIT, VK_ACCESS_2_SHADER_READ_BIT, + VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT); + } else { + rend_vk_texture_barrier(cmd, texture, new_layout, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, + VK_ACCESS_2_MEMORY_WRITE_BIT, VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT); + } } -extern void -rend_vk_texture_transfer_ownership_acquire(RendRenderer renderer, VkCommandBuffer cmd, RendTexture *texture, uint32_t src_family, uint32_t dst_family, VkImageLayout new_layout) +void +rend_vk_texture_transfer_ownership_release(RendContextHandle handle, VkCommandBuffer cmd, RendTexture *texture, uint32_t src_family, uint32_t dst_family, VkImageLayout new_layout) { - RendVk14Context *ctx = renderer->context; - (void) ctx; - - VkImageMemoryBarrier2 barrier = { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, - .oldLayout = texture->layout, - .newLayout = new_layout, - .srcQueueFamilyIndex = src_family, - .dstQueueFamilyIndex = dst_family, - .image = (VkImage) texture->handle, - - .subresourceRange = (VkImageSubresourceRange) { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = texture->mip_levels, - .baseArrayLayer = 0, - .layerCount = texture->layers, - }, - }; - - if (texture->layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && new_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { - barrier.srcAccessMask = 0; - barrier.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT; - barrier.srcStageMask = VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT; - barrier.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT; - } else { - barrier.srcAccessMask = 0; - barrier.dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT; - barrier.srcStageMask = VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT; - barrier.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT; - } - - VkDependencyInfo dep = { - .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, - .pImageMemoryBarriers = &barrier, - .imageMemoryBarrierCount = 1, - }; - - texture->layout = new_layout; - vkCmdPipelineBarrier2(cmd, &dep); + (void)handle; + if (texture->layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && new_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + rend_vk_texture_barrier(cmd, texture, new_layout, src_family, dst_family, + VK_ACCESS_2_TRANSFER_WRITE_BIT, 0, + VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT); + } else { + rend_vk_texture_barrier(cmd, texture, new_layout, src_family, dst_family, + VK_ACCESS_2_MEMORY_WRITE_BIT, 0, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT); + } } -extern void -rend_vk_texture_copy_buffer(RendRenderer renderer, RendTexture *texture, RendBuffer *buffer) +void +rend_vk_texture_transfer_ownership_acquire(RendContextHandle handle, VkCommandBuffer cmd, RendTexture *texture, uint32_t src_family, uint32_t dst_family, VkImageLayout new_layout) { - RendVk14Context *ctx = renderer->context; - - VkCommandBuffer cmd_transfer = rend_vk_cmdbuffer_single_use_begin(ctx->upload_command_pool); { - rend_vk_texture_transition_layout(renderer, cmd_transfer, texture, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - VkBufferImageCopy region = { - .imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .imageSubresource.layerCount = texture->layers, - .imageExtent = (VkExtent3D) { texture->width, texture->height, texture->depth }, - }; - - vkCmdCopyBufferToImage(cmd_transfer, (VkBuffer) buffer->handle, (VkImage) texture->handle, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); - rend_vk_texture_transfer_ownership_release(renderer, cmd_transfer, texture, vk_device.transfer_family_index, vk_device.graphics_family_index, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - } rend_vk_cmdbuffer_single_use_end(ctx->upload_command_pool, cmd_transfer, vk_device.transfer_queue); - - VkCommandBuffer cmd_graphics = rend_vk_cmdbuffer_single_use_begin(ctx->graphics_command_pool); { - rend_vk_texture_transfer_ownership_acquire(renderer, cmd_graphics, texture, vk_device.transfer_family_index, vk_device.graphics_family_index, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - } rend_vk_cmdbuffer_single_use_end(ctx->graphics_command_pool, cmd_graphics, vk_device.graphics_queue); + (void)handle; + if (texture->layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && new_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + rend_vk_texture_barrier(cmd, texture, new_layout, src_family, dst_family, + 0, VK_ACCESS_2_SHADER_READ_BIT, + VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT); + } else { + rend_vk_texture_barrier(cmd, texture, new_layout, src_family, dst_family, + 0, VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT, + VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT); + } } -extern void -rend_vk_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) +void +rend_vk_texture_copy_buffer(RendContextHandle handle, RendTexture *texture, RendBuffer *buffer) { + RendVk14Context *ctx = (RendVk14Context *)handle; + + VkCommandBuffer cmd_transfer = rend_vk_cmdbuffer_single_use_begin(ctx->upload_command_pool); { + rend_vk_texture_transition_layout(handle, cmd_transfer, texture, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + VkBufferImageCopy region = { + .imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .imageSubresource.layerCount = texture->layers, + .imageExtent = (VkExtent3D) { texture->width, texture->height, texture->depth }, + }; + + vkCmdCopyBufferToImage(cmd_transfer, (VkBuffer)buffer->handle, (VkImage)texture->handle, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + rend_vk_texture_transfer_ownership_release(handle, cmd_transfer, texture, vk_device.transfer_family_index, vk_device.graphics_family_index, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + } rend_vk_cmdbuffer_single_use_end(ctx->upload_command_pool, cmd_transfer, vk_device.transfer_queue); + + VkCommandBuffer cmd_graphics = rend_vk_cmdbuffer_single_use_begin(ctx->graphics_command_pool); { + rend_vk_texture_transfer_ownership_acquire(handle, cmd_graphics, texture, vk_device.transfer_family_index, vk_device.graphics_family_index, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + } rend_vk_cmdbuffer_single_use_end(ctx->graphics_command_pool, cmd_graphics, vk_device.graphics_queue); +} - RendVk14Context *ctx = (RendVk14Context*) renderer->context; - VkCommandBuffer cmd = ctx->frame_resources[ctx->frame_index].command_buffer; - - rend_vk_texture_transition_layout(renderer, cmd, src, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - rend_vk_texture_transition_layout(renderer, cmd, dst, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - - VkImageBlit2 blit_region = { - .sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2, - .srcSubresource = { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .mipLevel = 0, - .baseArrayLayer = 0, - .layerCount = 1, - }, - .srcOffsets = { - { src_x, src_y, 0 }, - { src_x + src_w, src_y + src_h, 1 } - }, - .dstSubresource = { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .mipLevel = 0, - .baseArrayLayer = 0, - .layerCount = 1, - }, - .dstOffsets = { - { dst_x, dst_y, 0 }, - { dst_x + dst_w, dst_y + dst_h, 1 } - } - }; - - VkBlitImageInfo2 blit_info = { - .sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2, - .srcImage = (VkImage) src->handle, - .srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - .dstImage = (VkImage) dst->handle, - .dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - .regionCount = 1, - .pRegions = &blit_region, - - .filter = VK_FILTER_NEAREST // crispy - }; - - vkCmdBlitImage2(cmd, &blit_info); +void +rend_vk_texture_blit(RendContextHandle handle, 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) +{ + RendVk14Context *ctx = (RendVk14Context *)handle; + VkCommandBuffer cmd = ctx->frame_resources[ctx->frame_index].command_buffer; + + rend_vk_texture_transition_layout(handle, cmd, src, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + rend_vk_texture_transition_layout(handle, cmd, dst, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + VkImageBlit2 blit_region = { + .sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2, + .srcSubresource = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .srcOffsets = { + { src_x, src_y, 0 }, + { src_x + src_w, src_y + src_h, 1 } + }, + .dstSubresource = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .dstOffsets = { + { dst_x, dst_y, 0 }, + { dst_x + dst_w, dst_y + dst_h, 1 } + } + }; + + VkBlitImageInfo2 blit_info = { + .sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2, + .srcImage = (VkImage)src->handle, + .srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + .dstImage = (VkImage)dst->handle, + .dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .regionCount = 1, + .pRegions = &blit_region, + + .filter = VK_FILTER_NEAREST /* crispy */ + }; + + vkCmdBlitImage2(cmd, &blit_info); } static VkCommandBuffer rend_vk_cmdbuffer_single_use_begin(VkCommandPool pool) { - VkCommandBufferAllocateInfo alloc_info = { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, - .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, - .commandPool = pool, - .commandBufferCount = 1, - }; + VkCommandBufferAllocateInfo alloc_info = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandPool = pool, + .commandBufferCount = 1, + }; - VkCommandBuffer cmd; - vkAllocateCommandBuffers(vk_device.logical_device, &alloc_info, &cmd); + VkCommandBuffer cmd; + vkAllocateCommandBuffers(vk_device.logical_device, &alloc_info, &cmd); - VkCommandBufferBeginInfo begin = { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, - }; + VkCommandBufferBeginInfo begin = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + }; - vkBeginCommandBuffer(cmd, &begin); + vkBeginCommandBuffer(cmd, &begin); - return cmd; + return cmd; } static void rend_vk_cmdbuffer_single_use_end(VkCommandPool pool, VkCommandBuffer cmd, VkQueue q) { - vkEndCommandBuffer(cmd); + vkEndCommandBuffer(cmd); - VkSubmitInfo submit_info = { - .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, - .commandBufferCount = 1, - .pCommandBuffers = &cmd, - }; + VkSubmitInfo submit_info = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .commandBufferCount = 1, + .pCommandBuffers = &cmd, + }; - vkQueueSubmit(q, 1, &submit_info, VK_NULL_HANDLE); - vkQueueWaitIdle(q); + vkQueueSubmit(q, 1, &submit_info, VK_NULL_HANDLE); + vkQueueWaitIdle(q); - vkFreeCommandBuffers(vk_device.logical_device, pool, 1, &cmd); + vkFreeCommandBuffers(vk_device.logical_device, pool, 1, &cmd); } -extern bool -rend_vk_pipeline_create(RendRenderer renderer, RendPipeline pipeline, Rend__PipelineConfig config, uint8_t type, const uint8_t *shader1, size_t bytes1, const uint8_t *shader2, size_t bytes2, const uint8_t *shader3, size_t bytes3) +bool +rend_vk_pipeline_create(RendContextHandle handle, RendPipeline pipeline, Rend__PipelineConfig config, uint8_t type, const uint8_t *shader1, size_t bytes1, const uint8_t *shader2, size_t bytes2, const uint8_t *shader3, size_t bytes3) { - - RendVk14Context *ctx = (RendVk14Context *)renderer->context; - pipeline->idx = ctx->pipeline_count++; - pipeline->backend_ctx = ctx; // useful for when we only have RendPipeline as an argument - - RendVkPipeline *vk_pipeline = &ctx->pipelines[pipeline->idx]; - vk_pipeline->blend_enable = false; - - /* - * set color and depth format - */ - VkFormat color_format = (config.color_format != REND_FORMAT_UNDEFINED) - ? vk_format_from_rend_format[config.color_format] - : ctx->swapchain.format.format; - - VkFormat depth_format = (config.depth_format != REND_FORMAT_UNDEFINED) - ? vk_format_from_rend_format[config.depth_format] - : vk_device.depth_format; - - /* - * Pipeline Layout - */ - - vk_pipeline->push_constants_range = 0; - uint32_t total_size = 0; - for (uint32_t u = 0; u < config.push_constant_count; ++u) { - total_size += config.push_constants[u].size; - } - - vk_pipeline->push_constants_range = total_size; - - VkPushConstantRange pc_range = { - .offset = 0, - .size = total_size, - .stageFlags = VK_SHADER_STAGE_ALL, - }; - - VkPipelineLayoutCreateInfo layout_info = { - .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - .pSetLayouts = &ctx->desc_layout, - .setLayoutCount = 1, - .pPushConstantRanges = (total_size > 0) ? &pc_range : NULL, - .pushConstantRangeCount = (total_size > 0) ? 1 : 0, - }; - - CHECK_VK_RESULT(vkCreatePipelineLayout(vk_device.logical_device, &layout_info, vk_allocator, &vk_pipeline->layout)); - - /* - * Setup shaders based on pipeline type - */ - VkPipelineShaderStageCreateInfo shader_stages[3] = {0}; - VkShaderModule shader_modules[3] = {0}; - uint32_t shader_count = 0; - - if (type == REND__PIPELINE_GRAPHICS) { - shader_count = 2; - shader_modules[0] = rend_vk_shader_module_create(shader1, bytes1); - shader_modules[1] = rend_vk_shader_module_create(shader2, bytes2); - - shader_stages[0] = (VkPipelineShaderStageCreateInfo) { - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .stage = VK_SHADER_STAGE_VERTEX_BIT, - .module = shader_modules[0], - .pName = "main" - }; - - shader_stages[1] = (VkPipelineShaderStageCreateInfo) { - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .stage = VK_SHADER_STAGE_FRAGMENT_BIT, - .module = shader_modules[1], - .pName = "main", - }; - - } else if (type == REND__PIPELINE_MESH) { - shader_count = 2; - shader_modules[0] = rend_vk_shader_module_create(shader1, bytes1); - shader_modules[1] = rend_vk_shader_module_create(shader2, bytes2); - - shader_stages[0] = (VkPipelineShaderStageCreateInfo) { - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .stage = VK_SHADER_STAGE_MESH_BIT_EXT, - .module = shader_modules[0], - .pName = "main" - }; - - shader_stages[1] = (VkPipelineShaderStageCreateInfo) { - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .stage = VK_SHADER_STAGE_FRAGMENT_BIT, - .module = shader_modules[1], - .pName = "main", - }; - - } else if (type == REND__PIPELINE_COMPUTE) { - shader_count = 1; - shader_modules[0] = rend_vk_shader_module_create(shader1, bytes1); - - shader_stages[0] = (VkPipelineShaderStageCreateInfo) { - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .stage = VK_SHADER_STAGE_COMPUTE_BIT, - .module = shader_modules[0], - .pName = "main" - }; - } else { - REND__CRASH("Invalid pipeline type"); - } - - /* - * Compute Pipeline Branch - */ - if (type == REND__PIPELINE_COMPUTE) { - VkComputePipelineCreateInfo compute_pipeline_info = { - .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, - .stage = shader_stages[0], - .layout = vk_pipeline->layout, - .basePipelineHandle = VK_NULL_HANDLE, - .basePipelineIndex = -1, - }; - - CHECK_VK_RESULT(vkCreateComputePipelines(vk_device.logical_device, VK_NULL_HANDLE, 1, &compute_pipeline_info, vk_allocator, &vk_pipeline->handle)); - - PINFO("Successfully created compute pipeline!"); - } - /* - * Graphics / Mesh Pipeline Branch - */ - else { - VkPipelineRenderingCreateInfo rendering_info = {VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO}; - rendering_info.colorAttachmentCount = 1; - rendering_info.pColorAttachmentFormats = &color_format; - rendering_info.depthAttachmentFormat = depth_format; - - VkPipelineViewportStateCreateInfo viewport_state = {VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO}; - viewport_state.viewportCount = 1; - viewport_state.scissorCount = 1; - - VkPipelineRasterizationStateCreateInfo rasterizer = {VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO}; - rasterizer.depthClampEnable = VK_FALSE; - rasterizer.rasterizerDiscardEnable = VK_FALSE; - rasterizer.polygonMode = vk_polymode[config.polygon_mode]; - rasterizer.lineWidth = 1.0f; - rasterizer.cullMode = vk_cullflags[config.cull_mode]; - rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; - rasterizer.depthBiasEnable = VK_FALSE; - - VkPipelineMultisampleStateCreateInfo multisampling = {VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO}; - multisampling.sampleShadingEnable = VK_FALSE; - multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - - VkPipelineColorBlendAttachmentState color_blend_attachment = {0}; - color_blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; - color_blend_attachment.blendEnable = VK_FALSE; - - VkPipelineColorBlendStateCreateInfo color_blending = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO}; - color_blending.logicOpEnable = VK_FALSE; - color_blending.logicOp = VK_LOGIC_OP_COPY; - color_blending.attachmentCount = 1; - color_blending.pAttachments = &color_blend_attachment; - - VkPipelineDepthStencilStateCreateInfo depth_stencil = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO}; - depth_stencil.depthTestEnable = config.depth_test_enable; - depth_stencil.depthWriteEnable = VK_TRUE; - depth_stencil.depthCompareOp = VK_COMPARE_OP_LESS; - depth_stencil.depthBoundsTestEnable = VK_FALSE; - depth_stencil.stencilTestEnable = VK_FALSE; - - VkDynamicState dynamic_states[] = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR}; - VkPipelineDynamicStateCreateInfo dynamic_state = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO}; - dynamic_state.dynamicStateCount = 2; - dynamic_state.pDynamicStates = dynamic_states; - - VkPipelineVertexInputStateCreateInfo vertex_input_info = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO}; - VkPipelineInputAssemblyStateCreateInfo input_assembly = {VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO}; - VkVertexInputBindingDescription* vk_bindings = NULL; - VkVertexInputAttributeDescription* vk_attributes = NULL; - - if (type == REND__PIPELINE_GRAPHICS) { - uint32_t binding_count = config.vertex_binding_count; - uint32_t attribute_count = config.vertex_attribute_count; - - /* - * Bind Vertex Attributes - */ - if (binding_count > 0) { - vk_bindings = rmalloc(sizeof(VkVertexInputBindingDescription) * binding_count); - for (uint32_t i = 0; i < binding_count; i++) { - RendVertexBinding rb = config.vertex_bindings[i]; - vk_bindings[i].binding = rb.binding; - vk_bindings[i].stride = rb.stride; - vk_bindings[i].inputRate = (rb.input_rate == REND_INPUT_RATE_INSTANCE) ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; - } - } - - if (attribute_count > 0) { - vk_attributes = rmalloc(sizeof(VkVertexInputAttributeDescription) * attribute_count); - for (uint32_t i = 0; i < attribute_count; i++) { - RendVertexAttributes ra = config.vertex_attributes[i]; - vk_attributes[i].binding = ra.binding; - vk_attributes[i].location = ra.location; - vk_attributes[i].offset = ra.offset; - vk_attributes[i].format = vk_format_from_rend_format[ra.format]; - } - } - - vertex_input_info.vertexBindingDescriptionCount = binding_count; - vertex_input_info.pVertexBindingDescriptions = vk_bindings; - vertex_input_info.vertexAttributeDescriptionCount = attribute_count; - vertex_input_info.pVertexAttributeDescriptions = vk_attributes; - - input_assembly.topology = vk_topology[config.topology]; - input_assembly.primitiveRestartEnable = VK_FALSE; - } - - VkGraphicsPipelineCreateInfo pipeline_info = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO}; - pipeline_info.pNext = &rendering_info; - pipeline_info.stageCount = shader_count; - pipeline_info.pStages = shader_stages; - pipeline_info.pVertexInputState = &vertex_input_info; - pipeline_info.pInputAssemblyState = &input_assembly; - pipeline_info.pViewportState = &viewport_state; - pipeline_info.pRasterizationState = &rasterizer; - pipeline_info.pMultisampleState = &multisampling; - pipeline_info.pColorBlendState = &color_blending; - pipeline_info.pDepthStencilState = &depth_stencil; - pipeline_info.pDynamicState = &dynamic_state; - pipeline_info.layout = vk_pipeline->layout; - pipeline_info.renderPass = VK_NULL_HANDLE; - - CHECK_VK_RESULT(vkCreateGraphicsPipelines(vk_device.logical_device, VK_NULL_HANDLE, 1, &pipeline_info, vk_allocator, &vk_pipeline->handle)); - - if (vk_bindings) rfree(vk_bindings); - if (vk_attributes) rfree(vk_attributes); - - } - - /* destroy shader modules */ - for (uint32_t i = 0; i < shader_count; ++i) { - if (shader_modules[i] != VK_NULL_HANDLE) { - vkDestroyShaderModule(vk_device.logical_device, shader_modules[i], vk_allocator); - } - } - - return true; + uint32_t u; + uint32_t i; + RendVk14Context *ctx = (RendVk14Context *)handle; + pipeline->idx = ctx->pipeline_count++; + pipeline->backend_ctx = ctx; /* useful for when we only have RendPipeline as an argument */ + + RendVkPipeline *vk_pipeline = &ctx->pipelines[pipeline->idx]; + vk_pipeline->blend_enable = false; + + /* + * set color and depth format + */ + VkFormat color_format = (config.color_format != REND_FORMAT_UNDEFINED) + ? vk_format_from_rend_format[config.color_format] + : ctx->swapchain.format.format; + + VkFormat depth_format = (config.depth_format != REND_FORMAT_UNDEFINED) + ? vk_format_from_rend_format[config.depth_format] + : vk_device.depth_format; + + /* + * Pipeline Layout + */ + + uint32_t total_size = 0; + for (u = 0; u < config.push_constant_count; ++u) { + total_size += config.push_constants[u].size; + } + vk_pipeline->push_constants_range = total_size; + + VkPushConstantRange pc_range = { + .offset = 0, + .size = total_size, + .stageFlags = VK_SHADER_STAGE_ALL, + }; + + VkPipelineLayoutCreateInfo layout_info = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .pSetLayouts = &ctx->desc_layout, + .setLayoutCount = 1, + .pPushConstantRanges = (total_size > 0) ? &pc_range : NULL, + .pushConstantRangeCount = (total_size > 0) ? 1 : 0, + }; + + CHECK_VK_RESULT(vkCreatePipelineLayout(vk_device.logical_device, &layout_info, vk_allocator, &vk_pipeline->layout)); + + VkPipelineShaderStageCreateInfo shader_stages[3] = {0}; + VkShaderModule shader_modules[3] = {0}; + const uint8_t *shader_bytes[3] = { shader1, shader2, shader3 }; + size_t shader_sizes[3] = { bytes1, bytes2, bytes3 }; + uint32_t shader_count = 0; + + if (type != REND__PIPELINE_GRAPHICS && type != REND__PIPELINE_MESH && type != REND__PIPELINE_COMPUTE) { + REND__CRASH("Invalid pipeline type"); + } + for (i = 0; i < 3 && vk_pipeline_stages[type][i]; ++i) { + shader_modules[i] = rend_vk_shader_module_create(shader_bytes[i], shader_sizes[i]); + shader_stages[i] = (VkPipelineShaderStageCreateInfo) { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .stage = vk_pipeline_stages[type][i], + .module = shader_modules[i], + .pName = "main" + }; + shader_count++; + } + + /* + * Compute Pipeline Branch + */ + if (type == REND__PIPELINE_COMPUTE) { + VkComputePipelineCreateInfo compute_pipeline_info = { + .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, + .stage = shader_stages[0], + .layout = vk_pipeline->layout, + .basePipelineHandle = VK_NULL_HANDLE, + .basePipelineIndex = -1, + }; + + CHECK_VK_RESULT(vkCreateComputePipelines(vk_device.logical_device, VK_NULL_HANDLE, 1, &compute_pipeline_info, vk_allocator, &vk_pipeline->handle)); + + PINFO("Successfully created compute pipeline!"); + } + /* + * Graphics / Mesh Pipeline Branch + */ + else { + VkPipelineRenderingCreateInfo rendering_info = {VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO}; + rendering_info.colorAttachmentCount = 1; + rendering_info.pColorAttachmentFormats = &color_format; + rendering_info.depthAttachmentFormat = depth_format; + + VkPipelineViewportStateCreateInfo viewport_state = {VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO}; + viewport_state.viewportCount = 1; + viewport_state.scissorCount = 1; + + VkPipelineRasterizationStateCreateInfo rasterizer = {VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO}; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = vk_polymode[config.polygon_mode]; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = vk_cullflags[config.cull_mode]; + rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + + VkPipelineMultisampleStateCreateInfo multisampling = {VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO}; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + VkPipelineColorBlendAttachmentState color_blend_attachment = {0}; + color_blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + color_blend_attachment.blendEnable = VK_FALSE; + + VkPipelineColorBlendStateCreateInfo color_blending = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO}; + color_blending.logicOpEnable = VK_FALSE; + color_blending.logicOp = VK_LOGIC_OP_COPY; + color_blending.attachmentCount = 1; + color_blending.pAttachments = &color_blend_attachment; + + VkPipelineDepthStencilStateCreateInfo depth_stencil = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO}; + depth_stencil.depthTestEnable = config.depth_test_enable; + depth_stencil.depthWriteEnable = VK_TRUE; + depth_stencil.depthCompareOp = VK_COMPARE_OP_LESS; + depth_stencil.depthBoundsTestEnable = VK_FALSE; + depth_stencil.stencilTestEnable = VK_FALSE; + + VkDynamicState dynamic_states[] = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR}; + VkPipelineDynamicStateCreateInfo dynamic_state = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO}; + dynamic_state.dynamicStateCount = 2; + dynamic_state.pDynamicStates = dynamic_states; + + VkPipelineVertexInputStateCreateInfo vertex_input_info = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO}; + VkPipelineInputAssemblyStateCreateInfo input_assembly = {VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO}; + VkVertexInputBindingDescription* vk_bindings = NULL; + VkVertexInputAttributeDescription* vk_attributes = NULL; + + if (type == REND__PIPELINE_GRAPHICS) { + uint32_t binding_count = config.vertex_binding_count; + uint32_t attribute_count = config.vertex_attribute_count; + + /* + * Bind Vertex Attributes + */ + if (binding_count > 0) { + vk_bindings = rmalloc(sizeof(VkVertexInputBindingDescription) * binding_count); + for (i = 0; i < binding_count; i++) { + RendVertexBinding rb = config.vertex_bindings[i]; + vk_bindings[i].binding = rb.binding; + vk_bindings[i].stride = rb.stride; + vk_bindings[i].inputRate = (rb.input_rate == REND_INPUT_RATE_INSTANCE) ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; + } + } + + if (attribute_count > 0) { + vk_attributes = rmalloc(sizeof(VkVertexInputAttributeDescription) * attribute_count); + for (i = 0; i < attribute_count; i++) { + RendVertexAttributes ra = config.vertex_attributes[i]; + vk_attributes[i].binding = ra.binding; + vk_attributes[i].location = ra.location; + vk_attributes[i].offset = ra.offset; + vk_attributes[i].format = vk_format_from_rend_format[ra.format]; + } + } + + vertex_input_info.vertexBindingDescriptionCount = binding_count; + vertex_input_info.pVertexBindingDescriptions = vk_bindings; + vertex_input_info.vertexAttributeDescriptionCount = attribute_count; + vertex_input_info.pVertexAttributeDescriptions = vk_attributes; + + input_assembly.topology = vk_topology[config.topology]; + input_assembly.primitiveRestartEnable = VK_FALSE; + } + + VkGraphicsPipelineCreateInfo pipeline_info = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO}; + pipeline_info.pNext = &rendering_info; + pipeline_info.stageCount = shader_count; + pipeline_info.pStages = shader_stages; + pipeline_info.pVertexInputState = &vertex_input_info; + pipeline_info.pInputAssemblyState = &input_assembly; + pipeline_info.pViewportState = &viewport_state; + pipeline_info.pRasterizationState = &rasterizer; + pipeline_info.pMultisampleState = &multisampling; + pipeline_info.pColorBlendState = &color_blending; + pipeline_info.pDepthStencilState = &depth_stencil; + pipeline_info.pDynamicState = &dynamic_state; + pipeline_info.layout = vk_pipeline->layout; + pipeline_info.renderPass = VK_NULL_HANDLE; + + CHECK_VK_RESULT(vkCreateGraphicsPipelines(vk_device.logical_device, VK_NULL_HANDLE, 1, &pipeline_info, vk_allocator, &vk_pipeline->handle)); + + if (vk_bindings) rfree(vk_bindings); + if (vk_attributes) rfree(vk_attributes); + + } + + /* destroy shader modules */ + for (i = 0; i < shader_count; ++i) { + if (shader_modules[i] != VK_NULL_HANDLE) { + vkDestroyShaderModule(vk_device.logical_device, shader_modules[i], vk_allocator); + } + } + + return true; } -extern void +void rend_vk_pipeline_bind(RendPipeline pipeline) { - RendVk14Context *ctx = pipeline->backend_ctx; - RendVkPipeline vk_pipeline = ctx->pipelines[pipeline->idx]; - VkCommandBuffer cmd = ctx->frame_resources[ctx->frame_index].command_buffer; - VkPipelineBindPoint bind_point = (pipeline->type == REND__PIPELINE_COMPUTE) ? VK_PIPELINE_BIND_POINT_COMPUTE : VK_PIPELINE_BIND_POINT_GRAPHICS; - vkCmdBindPipeline(cmd, bind_point, vk_pipeline.handle); - vkCmdBindDescriptorSets(cmd, bind_point, vk_pipeline.layout, 0, 1, &ctx->desc_set, 0, NULL); + RendVk14Context *ctx = pipeline->backend_ctx; + RendVkPipeline vk_pipeline = ctx->pipelines[pipeline->idx]; + VkCommandBuffer cmd = ctx->frame_resources[ctx->frame_index].command_buffer; + VkPipelineBindPoint bind_point = (pipeline->type == REND__PIPELINE_COMPUTE) ? VK_PIPELINE_BIND_POINT_COMPUTE : VK_PIPELINE_BIND_POINT_GRAPHICS; + vkCmdBindPipeline(cmd, bind_point, vk_pipeline.handle); + vkCmdBindDescriptorSets(cmd, bind_point, vk_pipeline.layout, 0, 1, &ctx->desc_set, 0, NULL); } -extern void +void rend_vk_pipeline_push_constants(RendPipeline pipeline, void *push_data, size_t size) { - RASSERT(pipeline && push_data); + RASSERT(pipeline && push_data); - RendVk14Context *ctx = pipeline->backend_ctx; - RendVkPipeline p = ctx->pipelines[pipeline->idx]; - RASSERT(size <= p.push_constants_range && "Size exceeds bound push constant range!"); - vkCmdPushConstants(ctx->frame_resources[ctx->frame_index].command_buffer, p.layout, VK_SHADER_STAGE_ALL, 0, size, push_data); + RendVk14Context *ctx = pipeline->backend_ctx; + RendVkPipeline p = ctx->pipelines[pipeline->idx]; + RASSERT(size <= p.push_constants_range && "Size exceeds bound push constant range!"); + vkCmdPushConstants(ctx->frame_resources[ctx->frame_index].command_buffer, p.layout, VK_SHADER_STAGE_ALL, 0, size, push_data); } -extern void +void rend_vk_pipeline_bind_vertex_buffer(RendPipeline pipeline, uint32_t binding, RendBuffer buffer, size_t offset) { - RendVk14Context *ctx = pipeline->backend_ctx; - VkBuffer buf = (VkBuffer)(uintptr_t)buffer.handle; - VkCommandBuffer cmd = ctx->frame_resources[ctx->frame_index].command_buffer; - - VkDeviceSize vk_offset = (VkDeviceSize)offset; - vkCmdBindVertexBuffers(cmd, binding, 1, &buf, &vk_offset); + RendVk14Context *ctx = pipeline->backend_ctx; + VkBuffer buf = (VkBuffer)(uintptr_t)buffer.handle; + VkCommandBuffer cmd = ctx->frame_resources[ctx->frame_index].command_buffer; + + VkDeviceSize vk_offset = (VkDeviceSize)offset; + vkCmdBindVertexBuffers(cmd, binding, 1, &buf, &vk_offset); } -extern void +void rend_vk_pipeline_bind_index_buffer(RendPipeline pipeline, RendBuffer buffer, size_t offset, RendIndexType index_type) { - RendVk14Context *ctx = pipeline->backend_ctx; - VkBuffer buf = (VkBuffer)(uintptr_t)buffer.handle; - VkCommandBuffer cmd = ctx->frame_resources[ctx->frame_index].command_buffer; - - VkIndexType vk_index_type = (index_type == REND_INDEX_UINT16) ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32; - - vkCmdBindIndexBuffer(cmd, buf, (VkDeviceSize)offset, vk_index_type); + RendVk14Context *ctx = pipeline->backend_ctx; + VkBuffer buf = (VkBuffer)(uintptr_t)buffer.handle; + VkCommandBuffer cmd = ctx->frame_resources[ctx->frame_index].command_buffer; + + VkIndexType vk_index_type = (index_type == REND_INDEX_UINT16) ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32; + + vkCmdBindIndexBuffer(cmd, buf, (VkDeviceSize)offset, vk_index_type); } -extern void -rend_vk_descriptor_write_texture(void *ctx, RendTexture *texture, uint32_t binding, uint32_t slot) +void +rend_vk_descriptor_write_texture(RendContextHandle handle, RendTexture *texture, uint32_t binding, uint32_t slot) { - RendVk14Context *vk_ctx = (RendVk14Context*) ctx; - - VkDescriptorImageInfo image_info = { - .sampler = (VkSampler) texture->sampler, - .imageView = (VkImageView) texture->view, - .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - }; - - VkWriteDescriptorSet descriptor_write = { - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .dstSet = vk_ctx->desc_set, - .dstBinding = binding, - .dstArrayElement = slot, // write texture to slot - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .pImageInfo = &image_info, - }; - - vkUpdateDescriptorSets(vk_device.logical_device, 1, &descriptor_write, 0, NULL); + RendVk14Context *vk_ctx = (RendVk14Context *)handle; + + VkDescriptorImageInfo image_info = { + .sampler = (VkSampler)texture->sampler, + .imageView = (VkImageView)texture->view, + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + }; + + VkWriteDescriptorSet descriptor_write = { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = vk_ctx->desc_set, + .dstBinding = binding, + .dstArrayElement = slot, /* write texture to slot */ + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .pImageInfo = &image_info, + }; + + vkUpdateDescriptorSets(vk_device.logical_device, 1, &descriptor_write, 0, NULL); } -extern void +void rend_vk_pipeline_dispatch(RendPipeline pipeline, uint32_t x, uint32_t y, uint32_t z) { - RendVk14Context *ctx = pipeline->backend_ctx; - VkCommandBuffer cmd = ctx->frame_resources[ctx->frame_index].command_buffer; - vkCmdDispatch(cmd, x, y, z); + RendVk14Context *ctx = pipeline->backend_ctx; + VkCommandBuffer cmd = ctx->frame_resources[ctx->frame_index].command_buffer; + vkCmdDispatch(cmd, x, y, z); } -extern void +void rend_vk_pipeline_draw(RendPipeline pipeline, size_t count, uint32_t instance_count) { - RendVk14Context *ctx = pipeline->backend_ctx; - VkCommandBuffer cmd = ctx->frame_resources[ctx->frame_index].command_buffer; - vkCmdDraw(cmd, count, instance_count, 0, 0); + RendVk14Context *ctx = pipeline->backend_ctx; + VkCommandBuffer cmd = ctx->frame_resources[ctx->frame_index].command_buffer; + vkCmdDraw(cmd, count, instance_count, 0, 0); } -extern void +void rend_vk_pipeline_draw_indexed(RendPipeline pipeline, uint32_t index_count, uint32_t first_index, int32_t vertex_offset, uint32_t instance_count) { - RendVk14Context *ctx = pipeline->backend_ctx; - VkCommandBuffer cmd = ctx->frame_resources[ctx->frame_index].command_buffer; - vkCmdDrawIndexed(cmd, index_count, instance_count, first_index, vertex_offset, 0); + RendVk14Context *ctx = pipeline->backend_ctx; + VkCommandBuffer cmd = ctx->frame_resources[ctx->frame_index].command_buffer; + vkCmdDrawIndexed(cmd, index_count, instance_count, first_index, vertex_offset, 0); } -extern void +void rend_vk_pipeline_set_blend(RendPipeline pipeline, bool blend) { - RendVk14Context *ctx = pipeline->backend_ctx; - RendVkPipeline *vk_pipeline = &ctx->pipelines[pipeline->idx]; - vk_pipeline->blend_enable = blend; + RendVk14Context *ctx = pipeline->backend_ctx; + RendVkPipeline *vk_pipeline = &ctx->pipelines[pipeline->idx]; + vk_pipeline->blend_enable = blend; } -extern void -rend_vk_pipeline_draw_instanced(RendPipeline pipeline, uint32_t index_count, uint32_t instance_count) -{ - RendVk14Context *ctx = pipeline->backend_ctx; - VkCommandBuffer cmd = ctx->frame_resources[ctx->frame_index].command_buffer; - vkCmdDraw(cmd, index_count, instance_count, 0, 0); -} - -extern VKAPI_ATTR VkBool32 VKAPI_CALL +VKAPI_ATTR VkBool32 VKAPI_CALL rend_vk_debug_func(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, VkDebugUtilsMessageTypeFlagsEXT message_types, const VkDebugUtilsMessengerCallbackDataEXT *callback_data, void *user_data) { - switch (message_severity) { - default: - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: - PERROR(callback_data->pMessage); - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: - PWARN(callback_data->pMessage); - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: - PINFO(callback_data->pMessage); - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: - PTRACE(callback_data->pMessage); - break; - } - return VK_FALSE; + switch (message_severity) { + default: + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: + PERROR(callback_data->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: + PWARN(callback_data->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: + PINFO(callback_data->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: + PTRACE(callback_data->pMessage); + break; + } + return VK_FALSE; } @@ -1908,278 +1677,280 @@ rend_vk_debug_func(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, VkDe static void rend_vk_pipeline_destroy(RendVkPipeline *pipeline) { - VkDevice dev = vk_device.logical_device; + VkDevice dev = vk_device.logical_device; - if (pipeline->handle != VK_NULL_HANDLE) { - vkDestroyPipeline(dev, pipeline->handle, vk_allocator); - } + if (pipeline->handle != VK_NULL_HANDLE) { + vkDestroyPipeline(dev, pipeline->handle, vk_allocator); + } - if (pipeline->layout != VK_NULL_HANDLE) { - vkDestroyPipelineLayout(dev, pipeline->layout, vk_allocator); - } + if (pipeline->layout != VK_NULL_HANDLE) { + vkDestroyPipelineLayout(dev, pipeline->layout, vk_allocator); + } } static void rend_vk_swapchain_create(RendVk14Context *ctx, RendVkSwapchain *swapchain) { - RASSERT(ctx && "No context provided."); - RASSERT(swapchain && "No swapchain provided."); - - VkSurfaceCapabilitiesKHR surface_caps; - uint32_t width; - uint32_t height; - VkExtent2D swapchain_extent; - - vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk_device.physical_device, ctx->surface, &surface_caps); - width = surface_caps.currentExtent.width; - height = surface_caps.currentExtent.height; - - /* 0xffffffff means the surface size is defined by the swapchain extent */ - if (width == 0xffffffffu || height == 0xffffffffu) { - width = ctx->window ? ctx->window->width : 0; - height = ctx->window ? ctx->window->height : 0; - } - if (width == 0) width = 1; - if (height == 0) height = 1; - - swapchain_extent = (VkExtent2D){width, height}; - - ctx->max_frames_in_flight = REND_MIN_FRAMES_IN_FLIGHT; - - if (surface_caps.minImageCount > ctx->max_frames_in_flight) { - ctx->max_frames_in_flight = surface_caps.minImageCount; - } - - /* maxImageCount == 0 means there is no maximum */ - if (surface_caps.maxImageCount > 0 && surface_caps.maxImageCount < ctx->max_frames_in_flight) { - ctx->max_frames_in_flight = surface_caps.maxImageCount; - } - if (ctx->max_frames_in_flight > REND_MAX_FRAMES_IN_FLIGHT) { - ctx->max_frames_in_flight = REND_MAX_FRAMES_IN_FLIGHT; - } - if (ctx->max_frames_in_flight == 0) { - ctx->max_frames_in_flight = 1; - } - - bool found = false; - for (uint32_t i = 0; i < vk_device.swapchain_support.format_count; i++) { - VkSurfaceFormatKHR format = vk_device.swapchain_support.format[i]; - - /* preferred format */ - if (format.format == VK_FORMAT_B8G8R8A8_UNORM && - format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { - swapchain->format = format; - found = true; - break; - } - } - - /* default format */ - if (!found) { - swapchain->format = vk_device.swapchain_support.format[0]; - } - - /* NOTE: mailbox is probably the best for most applications - * but I may want the ability to pick a different mode in - * very niche circumstances. - * - * We also may want immediate mode if we want to disable VSYNC. - */ - - /* default present mode */ - VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR; - for (uint32_t i = 0; i < vk_device.swapchain_support.present_mode_count; i++) { - VkPresentModeKHR pres = vk_device.swapchain_support.present_modes[i]; - /* preferred format is mailbox */ - if (ctx->vsync) { - if (pres == VK_PRESENT_MODE_MAILBOX_KHR) { - present_mode = pres; - break; - } - } else { - if (pres == VK_PRESENT_MODE_IMMEDIATE_KHR) { - present_mode = pres; - break; - } - } - } + uint32_t i; + RASSERT(ctx && "No context provided."); + RASSERT(swapchain && "No swapchain provided."); + + VkSurfaceCapabilitiesKHR surface_caps; + uint32_t width; + uint32_t height; + VkExtent2D swapchain_extent; + + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk_device.physical_device, ctx->surface, &surface_caps); + width = surface_caps.currentExtent.width; + height = surface_caps.currentExtent.height; + + /* 0xffffffff means the surface size is defined by the swapchain extent */ + if (width == 0xffffffffu || height == 0xffffffffu) { + width = ctx->window ? ctx->window->width : 0; + height = ctx->window ? ctx->window->height : 0; + } + if (width == 0) width = 1; + if (height == 0) height = 1; + + swapchain_extent = (VkExtent2D){width, height}; + + ctx->max_frames_in_flight = REND_MIN_FRAMES_IN_FLIGHT; + + if (surface_caps.minImageCount > ctx->max_frames_in_flight) { + ctx->max_frames_in_flight = surface_caps.minImageCount; + } + + /* maxImageCount == 0 means there is no maximum */ + if (surface_caps.maxImageCount > 0 && surface_caps.maxImageCount < ctx->max_frames_in_flight) { + ctx->max_frames_in_flight = surface_caps.maxImageCount; + } + if (ctx->max_frames_in_flight > REND_MAX_FRAMES_IN_FLIGHT) { + ctx->max_frames_in_flight = REND_MAX_FRAMES_IN_FLIGHT; + } + if (ctx->max_frames_in_flight == 0) { + ctx->max_frames_in_flight = 1; + } + + bool found = false; + for (i = 0; i < vk_device.swapchain_support.format_count; i++) { + VkSurfaceFormatKHR format = vk_device.swapchain_support.format[i]; + + /* preferred format */ + if (format.format == VK_FORMAT_B8G8R8A8_UNORM && + format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { + swapchain->format = format; + found = true; + break; + } + } + + /* default format */ + if (!found) { + swapchain->format = vk_device.swapchain_support.format[0]; + } + + /* NOTE: mailbox is probably the best for most applications + * but I may want the ability to pick a different mode in + * very niche circumstances. + * + * We also may want immediate mode if we want to disable VSYNC. + */ + + /* default present mode */ + VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR; + for (i = 0; i < vk_device.swapchain_support.present_mode_count; i++) { + VkPresentModeKHR pres = vk_device.swapchain_support.present_modes[i]; + /* preferred format is mailbox */ + if (ctx->vsync) { + if (pres == VK_PRESENT_MODE_MAILBOX_KHR) { + present_mode = pres; + break; + } + } else { + if (pres == VK_PRESENT_MODE_IMMEDIATE_KHR) { + present_mode = pres; + break; + } + } + } #ifdef REND_DEBUG - static const char* present_mode_names[] = { - [VK_PRESENT_MODE_IMMEDIATE_KHR] = "IMMEDIATE", - [VK_PRESENT_MODE_MAILBOX_KHR] = "MAILBOX", - [VK_PRESENT_MODE_FIFO_KHR] = "FIFO", - [VK_PRESENT_MODE_FIFO_RELAXED_KHR] = "FIFO_RELAXED" - }; - PDEBUG("Present mode %s was chosen!", present_mode_names[present_mode]); + static const char* present_mode_names[] = { + [VK_PRESENT_MODE_IMMEDIATE_KHR] = "IMMEDIATE", + [VK_PRESENT_MODE_MAILBOX_KHR] = "MAILBOX", + [VK_PRESENT_MODE_FIFO_KHR] = "FIFO", + [VK_PRESENT_MODE_FIFO_RELAXED_KHR] = "FIFO_RELAXED" + }; + PDEBUG("Present mode %s was chosen!", present_mode_names[present_mode]); #endif - VkExtent2D min = surface_caps.minImageExtent; - VkExtent2D max = surface_caps.maxImageExtent; - swapchain_extent.width = (swapchain_extent.width < min.width) ? min.width : swapchain_extent.width; - swapchain_extent.width = (swapchain_extent.width > max.width) ? max.width : swapchain_extent.width; - swapchain_extent.height = (swapchain_extent.height < min.height) ? min.height : swapchain_extent.height; - swapchain_extent.height = (swapchain_extent.height > max.height) ? max.height : swapchain_extent.height; - - uint32_t img_count = surface_caps.minImageCount + 1; - if (surface_caps.maxImageCount > 0 && img_count > surface_caps.maxImageCount) { - img_count = surface_caps.maxImageCount; - } - - VkSwapchainCreateInfoKHR swapchain_create_info = { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR}; - swapchain_create_info.surface = ctx->surface; - swapchain_create_info.minImageCount = img_count; - swapchain_create_info.imageFormat = swapchain->format.format; - swapchain_create_info.imageColorSpace = swapchain->format.colorSpace; - swapchain_create_info.imageExtent = swapchain_extent; - swapchain_create_info.imageArrayLayers = 1; - swapchain_create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - - /* index sharing */ - if (vk_device.graphics_family_index != vk_device.present_family_index) { - uint32_t queueFamilyIndices[] = {vk_device.graphics_family_index, vk_device.present_family_index}; - swapchain_create_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT; - swapchain_create_info.queueFamilyIndexCount = 2; - swapchain_create_info.pQueueFamilyIndices = queueFamilyIndices; - } else { - swapchain_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - swapchain_create_info.queueFamilyIndexCount = 0; - swapchain_create_info.pQueueFamilyIndices = 0; - } - - swapchain_create_info.preTransform = surface_caps.currentTransform; - swapchain_create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - swapchain_create_info.presentMode = present_mode; - swapchain_create_info.clipped = VK_TRUE; - swapchain_create_info.oldSwapchain = 0; - - if (vkCreateSwapchainKHR(vk_device.logical_device, &swapchain_create_info, vk_allocator, &swapchain->handle) != VK_SUCCESS) { - REND__CRASH("Swapchain creation failed, you are probably trying to create two renderers for the same surface!"); - } - - swapchain->image_count = 0; - CHECK_VK_RESULT(vkGetSwapchainImagesKHR(vk_device.logical_device, swapchain->handle, &swapchain->image_count, 0)); - - if (!swapchain->images) { - swapchain->images = rmalloc(swapchain->image_count * sizeof *swapchain->images); - } - if (!swapchain->views) { - swapchain->views = rmalloc(swapchain->image_count * sizeof *swapchain->views); - } - - CHECK_VK_RESULT( vkGetSwapchainImagesKHR(vk_device.logical_device, swapchain->handle, &swapchain->image_count, swapchain->images)); - - for (uint32_t i = 0; i < swapchain->image_count; i++) { - - VkImageViewCreateInfo view_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO}; - view_info.image = swapchain->images[i]; - view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; - view_info.format = swapchain->format.format; - view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - view_info.subresourceRange.baseMipLevel = 0; - view_info.subresourceRange.levelCount = 1; - view_info.subresourceRange.baseArrayLayer = 0; - view_info.subresourceRange.layerCount = 1; - - CHECK_VK_RESULT(vkCreateImageView(vk_device.logical_device, &view_info, vk_allocator, &swapchain->views[i])); - } - - /* depth resources */ - if (!rend_vk_device_detect_depth_format(&vk_device)) { - vk_device.depth_format = VK_FORMAT_UNDEFINED; - PFATAL("Failed to find a supported depth buffer format!"); - } - - swapchain->depth_attachment = rend_vk_image_create( - vk_device.logical_device, - VK_IMAGE_TYPE_2D, - swapchain_extent.width, swapchain_extent.height, - vk_device.depth_format, - VK_IMAGE_TILING_OPTIMAL, - VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, - 1, // depth - 1, // mip level - 1, // layers - VK_SAMPLE_COUNT_1_BIT, - VK_SHARING_MODE_EXCLUSIVE - ); - - uint32_t mem_type = rend_vk_image_required_memory_type(&swapchain->depth_attachment); - uint32_t depth_index = rend_vk_get_heap_index(mem_type, vk_device.device_index); - - RASSERT(ctx); - RendMemory depth_mem = rend_vk_arena_alloc(&ctx->arena_persistent, swapchain->depth_attachment.requirements.size, depth_index); - - rend_vk_image_bind_memory(&swapchain->depth_attachment, &depth_mem); - - rend_vk_image_view_create( - &swapchain->depth_attachment, - VK_IMAGE_VIEW_TYPE_2D, - VK_IMAGE_ASPECT_DEPTH_BIT - ); - - swapchain->extent = swapchain_extent; + VkExtent2D min = surface_caps.minImageExtent; + VkExtent2D max = surface_caps.maxImageExtent; + swapchain_extent.width = (swapchain_extent.width < min.width) ? min.width : swapchain_extent.width; + swapchain_extent.width = (swapchain_extent.width > max.width) ? max.width : swapchain_extent.width; + swapchain_extent.height = (swapchain_extent.height < min.height) ? min.height : swapchain_extent.height; + swapchain_extent.height = (swapchain_extent.height > max.height) ? max.height : swapchain_extent.height; + + uint32_t img_count = surface_caps.minImageCount + 1; + if (surface_caps.maxImageCount > 0 && img_count > surface_caps.maxImageCount) { + img_count = surface_caps.maxImageCount; + } + + VkSwapchainCreateInfoKHR swapchain_create_info = { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR}; + swapchain_create_info.surface = ctx->surface; + swapchain_create_info.minImageCount = img_count; + swapchain_create_info.imageFormat = swapchain->format.format; + swapchain_create_info.imageColorSpace = swapchain->format.colorSpace; + swapchain_create_info.imageExtent = swapchain_extent; + swapchain_create_info.imageArrayLayers = 1; + swapchain_create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + + /* index sharing */ + if (vk_device.graphics_family_index != vk_device.present_family_index) { + uint32_t queueFamilyIndices[] = {vk_device.graphics_family_index, vk_device.present_family_index}; + swapchain_create_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + swapchain_create_info.queueFamilyIndexCount = 2; + swapchain_create_info.pQueueFamilyIndices = queueFamilyIndices; + } else { + swapchain_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swapchain_create_info.queueFamilyIndexCount = 0; + swapchain_create_info.pQueueFamilyIndices = 0; + } + + swapchain_create_info.preTransform = surface_caps.currentTransform; + swapchain_create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + swapchain_create_info.presentMode = present_mode; + swapchain_create_info.clipped = VK_TRUE; + swapchain_create_info.oldSwapchain = 0; + + if (vkCreateSwapchainKHR(vk_device.logical_device, &swapchain_create_info, vk_allocator, &swapchain->handle) != VK_SUCCESS) { + REND__CRASH("Swapchain creation failed, you are probably trying to create two renderers for the same surface!"); + } + + swapchain->image_count = 0; + CHECK_VK_RESULT(vkGetSwapchainImagesKHR(vk_device.logical_device, swapchain->handle, &swapchain->image_count, 0)); + + if (!swapchain->images) { + swapchain->images = rmalloc(swapchain->image_count * sizeof(*swapchain->images)); + } + if (!swapchain->views) { + swapchain->views = rmalloc(swapchain->image_count * sizeof(*swapchain->views)); + } + + CHECK_VK_RESULT( vkGetSwapchainImagesKHR(vk_device.logical_device, swapchain->handle, &swapchain->image_count, swapchain->images)); + + for (i = 0; i < swapchain->image_count; i++) { + + VkImageViewCreateInfo view_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO}; + view_info.image = swapchain->images[i]; + view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + view_info.format = swapchain->format.format; + view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + view_info.subresourceRange.baseMipLevel = 0; + view_info.subresourceRange.levelCount = 1; + view_info.subresourceRange.baseArrayLayer = 0; + view_info.subresourceRange.layerCount = 1; + + CHECK_VK_RESULT(vkCreateImageView(vk_device.logical_device, &view_info, vk_allocator, &swapchain->views[i])); + } + + /* depth resources */ + if (!rend_vk_device_detect_depth_format(&vk_device)) { + vk_device.depth_format = VK_FORMAT_UNDEFINED; + PFATAL("Failed to find a supported depth buffer format!"); + } + + swapchain->depth_attachment = rend_vk_image_create( + vk_device.logical_device, + VK_IMAGE_TYPE_2D, + swapchain_extent.width, swapchain_extent.height, + vk_device.depth_format, + VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, + 1, /* depth */ + 1, /* mip level */ + 1, /* layers */ + VK_SAMPLE_COUNT_1_BIT, + VK_SHARING_MODE_EXCLUSIVE + ); + + uint32_t mem_type = rend_vk_image_required_memory_type(&swapchain->depth_attachment); + uint32_t depth_index = rend_vk_get_heap_index(mem_type, vk_device.device_index); + + RASSERT(ctx); + RendMemory depth_mem = rend_vk_arena_alloc(&ctx->arena_persistent, swapchain->depth_attachment.requirements.size, depth_index); + + rend_vk_image_bind_memory(&swapchain->depth_attachment, &depth_mem); + + rend_vk_image_view_create( + &swapchain->depth_attachment, + VK_IMAGE_VIEW_TYPE_2D, + VK_IMAGE_ASPECT_DEPTH_BIT + ); + + swapchain->extent = swapchain_extent; } static void rend_vk_swapchain_destroy(RendVk14Context *ctx, RendVkSwapchain *swapchain) { - if (!swapchain || swapchain->handle == VK_NULL_HANDLE) return; - RASSERT(vk_device.logical_device); - - rend_vk_image_destroy(&swapchain->depth_attachment); - - if (swapchain->views) { - for (uint32_t i = 0; i < swapchain->image_count; i++) { - vkDestroyImageView(vk_device.logical_device, swapchain->views[i], vk_allocator); - } - rfree(swapchain->views); - swapchain->views = 0; - } - - if (swapchain->images) { - rfree(swapchain->images); - swapchain->images = 0; - } - - swapchain->image_count = 0; - vkDestroySwapchainKHR(vk_device.logical_device, swapchain->handle, vk_allocator); - swapchain->handle = VK_NULL_HANDLE; + uint32_t i; + if (!swapchain || swapchain->handle == VK_NULL_HANDLE) return; + RASSERT(vk_device.logical_device); + + rend_vk_image_destroy(&swapchain->depth_attachment); + + if (swapchain->views) { + for (i = 0; i < swapchain->image_count; i++) { + vkDestroyImageView(vk_device.logical_device, swapchain->views[i], vk_allocator); + } + rfree(swapchain->views); + swapchain->views = 0; + } + + if (swapchain->images) { + rfree(swapchain->images); + swapchain->images = 0; + } + + swapchain->image_count = 0; + vkDestroySwapchainKHR(vk_device.logical_device, swapchain->handle, vk_allocator); + swapchain->handle = VK_NULL_HANDLE; } static VkShaderModule rend_vk_shader_module_create(const void *data, size_t size) { - VkShaderModuleCreateInfo create_info = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO}; - create_info.codeSize = size; - create_info.pCode = (const uint32_t *)data; + VkShaderModuleCreateInfo create_info = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO}; + create_info.codeSize = size; + create_info.pCode = (const uint32_t *)data; - VkShaderModule module; - VkResult res = vkCreateShaderModule(vk_device.logical_device, &create_info, vk_allocator, &module); + VkShaderModule module; + VkResult res = vkCreateShaderModule(vk_device.logical_device, &create_info, vk_allocator, &module); - if (res != VK_SUCCESS) { - PWARN("Failed to create shader module for %p", data); - return VK_NULL_HANDLE; - } - - return module; + if (res != VK_SUCCESS) { + PWARN("Failed to create shader module for %p", data); + return VK_NULL_HANDLE; + } + + return module; } static uint32_t rend_vk_get_heap_index(uint32_t memory_type_bits, uint32_t preferred_index) { - if (memory_type_bits & (1u << preferred_index)) { - return preferred_index; - } - - for (uint32_t i = 0; i < 32; i++) { - if (memory_type_bits & (1u << i)) { - return i; - } - } - - return UINT32_MAX; + uint32_t i; + if (memory_type_bits & (1u << preferred_index)) { + return preferred_index; + } + + for (i = 0; i < 32; i++) { + if (memory_type_bits & (1u << i)) { + return i; + } + } + + return UINT32_MAX; } diff --git a/Rend/rend_vk_allocator.c b/Rend/rend_vk_allocator.c deleted file mode 100644 index 737e566..0000000 --- a/Rend/rend_vk_allocator.c +++ /dev/null @@ -1,118 +0,0 @@ -#pragma once -#include "rend_internal.h" -#include -#include -#include - -typedef struct RendVkAllocatorState { - const char *name; -} RendVkAllocatorState; - -typedef struct RendVkAllocatorHeader { - uint32_t offset; -} RendVkAllocatorHeader; - -static bool rend_vk_allocator_is_power_of_two(uintptr_t x); -static void *rend_vk_allocator_alloc(void *pUserData, size_t size, size_t alignment, VkSystemAllocationScope allocationScope); -static void *rend_vk_allocator_realloc(void *pUserData, void *pOriginal, size_t size, size_t alignment, VkSystemAllocationScope allocationScope); -static void rend_vk_allocator_free(void *pUserData, void *pMemory); -static void rend_vk_allocator_internal_notification(void *pUserData, size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope); -static void rend_vk_allocator_free_notification(void *pUserData, size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope); - -static VkAllocationCallbacks rend_vk_allocator = { - .pfnAllocation = rend_vk_allocator_alloc, - .pfnReallocation = rend_vk_allocator_realloc, - .pfnFree = rend_vk_allocator_free, - .pfnInternalAllocation = rend_vk_allocator_internal_notification, - .pfnInternalFree = rend_vk_allocator_free_notification, -}; - -static const char* rend_vk_allocator_scope_name[] = { - [VK_SYSTEM_ALLOCATION_SCOPE_CACHE] = "Cache", - [VK_SYSTEM_ALLOCATION_SCOPE_COMMAND] = "Command", - [VK_SYSTEM_ALLOCATION_SCOPE_DEVICE] = "Device", - [VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE] = "Instance", - [VK_SYSTEM_ALLOCATION_SCOPE_OBJECT] = "Object", -}; - - -static bool -rend_vk_allocator_is_power_of_two(uintptr_t x) -{ - return (x & (x-1)) == 0; -} - -static uintptr_t -rend_vk_allocator_align_forward(uintptr_t ptr, size_t align) -{ - uintptr_t p, a, modulo; - - assert(rend_vk_allocator_is_power_of_two(align)); - - p = ptr; - a = (uintptr_t)align; - modulo = p & (a-1); - - if (modulo != 0) { - p += a - modulo; - } - return p; -} - -static void* -rend_vk_allocator_alloc(void *pUserData, size_t size, size_t alignment, VkSystemAllocationScope allocationScope) -{ - size_t total_size = size + alignment + sizeof (RendVkAllocatorHeader); - uint8_t *raw_ptr = malloc(total_size); - uintptr_t unaligned_addr = (uintptr_t)(raw_ptr + sizeof (RendVkAllocatorHeader)); - - uint8_t *aligned_ptr = (uint8_t*)rend_vk_allocator_align_forward(unaligned_addr, alignment); - RendVkAllocatorHeader *header = (RendVkAllocatorHeader*) aligned_ptr - 1; - header->offset = aligned_ptr - raw_ptr; - - PDEBUG("[VK_ALLOC] %p - bytes %lu with alignment %lu - scope %s", - aligned_ptr, size, alignment, rend_vk_allocator_scope_name[allocationScope]); - - return (void*) aligned_ptr; -} - -static void* -rend_vk_allocator_realloc(void *pUserData, void *pOriginal, size_t size, size_t alignment, VkSystemAllocationScope allocationScope) -{ - if (!pOriginal) return rend_vk_allocator_alloc(pUserData, size, alignment, allocationScope); - if (size == 0) { - rend_vk_allocator_free(pUserData, pOriginal); - return NULL; - } - void *new_ptr = rend_vk_allocator_alloc(pUserData, size, alignment, allocationScope); - if (!new_ptr) return NULL; - memcpy(new_ptr, pOriginal, size); - rend_vk_allocator_free(pUserData, pOriginal); - return new_ptr; -} - -static void -rend_vk_allocator_free(void *pUserData, void *pMemory) -{ - RendVkAllocatorHeader *ptr = pMemory; - uint8_t *raw_ptr = pMemory; - raw_ptr -= (ptr-1)->offset; - - PDEBUG("[VK_FREE] %p", ptr); - free(raw_ptr); -} - -static void -rend_vk_allocator_internal_notification(void *pUserData, size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope) -{ - PDEBUG("[VK_ALLOC_INTERNAL] bytes %lu - scope %s", - size, rend_vk_allocator_scope_name[allocationScope]); - -} - -static void -rend_vk_allocator_free_notification(void *pUserData, size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope) -{ - PDEBUG("[VK_FREE_INTERNAL] bytes %lu - scope %s", - size, rend_vk_allocator_scope_name[allocationScope]); -} diff --git a/Rend/rend_vk_arena.c b/Rend/rend_vk_arena.c deleted file mode 100644 index b936be4..0000000 --- a/Rend/rend_vk_arena.c +++ /dev/null @@ -1,264 +0,0 @@ -#pragma once -#include "rend_internal.h" -#include "rend_vk_internal.h" -#include - -/* arenas need to be subdivided into blocks - * just like pools because we must respect page size */ -typedef struct RendVkPage { - RendMemory memory; - size_t head; - int reserved; -} RendVkPage; - -typedef struct RendVkPagedArena { - RendVkPage *page_darr; - uint32_t capacity; - uint32_t elements; -} RendVkPagedArena; - -struct RendVkArenaAllocator { - VkAllocationCallbacks *allocator; - size_t *heap_idx_alloc_sizes; // allocation size per type of memory available on the gpu - RendVkPagedArena *mem_arenas; // memory arena per type of memory available on the gpu - VkDevice logical_device; // virtual device - VkPhysicalDevice physical_device; // physical device we are allocating memory from - VkDeviceSize gpu_alignment; // allocations must respect the physical limitations of the gpu - VkDeviceSize block_min_size; // minimum size per block of memory - uint64_t total_allocations; - uint32_t heap_index_count; - VkPhysicalDeviceMemoryProperties properties; // useful for getting properties per heap index -}; - -static RendVkArenaAllocator -rend_vk_arena_create(VkDevice logical_device, VkPhysicalDevice physical_device, VkPhysicalDeviceLimits device_limits, VkAllocationCallbacks *allocator) -{ - RendVkArenaAllocator arena = { - .allocator = allocator, - .heap_idx_alloc_sizes = NULL, - .logical_device = logical_device, - .physical_device = physical_device, - .gpu_alignment = 0, - .block_min_size = 0, - .total_allocations = 0, - .heap_index_count = 0, - .mem_arenas = 0, - }; - - VkPhysicalDeviceMemoryProperties mem_properties; - vkGetPhysicalDeviceMemoryProperties(physical_device, &mem_properties); - arena.properties = mem_properties; - - uint32_t count = mem_properties.memoryTypeCount; - arena.heap_index_count = count; - - arena.heap_idx_alloc_sizes = rmalloc(count * sizeof *arena.heap_idx_alloc_sizes); - memset(arena.heap_idx_alloc_sizes, 0, count * sizeof *arena.heap_idx_alloc_sizes); - - arena.mem_arenas = rmalloc(count * sizeof *arena.mem_arenas); - for (uint32_t u = 0; u < count; ++u) { - arena.mem_arenas[u].capacity = 2; - arena.mem_arenas[u].elements = 0; - arena.mem_arenas[u].page_darr = rmalloc(2 * sizeof *arena.mem_arenas[u].page_darr); - memset(arena.mem_arenas[u].page_darr, 0, 2 * sizeof *arena.mem_arenas[u].page_darr); - } - - VkDeviceSize alignment = device_limits.bufferImageGranularity; - if (device_limits.nonCoherentAtomSize > alignment) { - alignment = device_limits.nonCoherentAtomSize; - } - - arena.gpu_alignment = alignment; - arena.block_min_size = arena.gpu_alignment * 10; - - return arena; -} - -static uint32_t -rend_vk_arena_add_page(RendVkArenaAllocator *arena, VkDeviceSize size, uint32_t heap_index, bool fit_to_alloc) -{ - VkDeviceSize new_arena_size = fit_to_alloc ? size : (size * 2); - new_arena_size = (new_arena_size < arena->block_min_size) ? arena->block_min_size : new_arena_size; - - VkMemoryPropertyFlags properties = arena->properties.memoryTypes[heap_index].propertyFlags; - - RendVkPage page = (RendVkPage) {0}; - page.head = 0; - - VkMemoryAllocateFlagsInfo flags_info = { - .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO, - .pNext = NULL, - .flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT, - .deviceMask = 0 - }; - - VkMemoryAllocateInfo alloc_info = { - .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, - .allocationSize = new_arena_size, - .pNext = &flags_info, - .memoryTypeIndex = heap_index - }; - - page.memory = (RendMemory) {0}; - page.memory.offset = 0; - page.memory.size = new_arena_size; - - VkResult res = vkAllocateMemory(arena->logical_device, &alloc_info, arena->allocator, (VkDeviceMemory*) &page.memory.device_memory); - if (res != VK_SUCCESS) { - return UINT32_MAX; - } - - if (properties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { - vkMapMemory( - arena->logical_device, - (VkDeviceMemory) page.memory.device_memory, - 0, // Offset inside VkDeviceMemory!!!1 - new_arena_size, // MUST match alloc_info.allocationSize and NOT raw size!!! - 0, - &page.memory.host_mapped_memory - ); - } - - RendVkPagedArena *mem_arena = &arena->mem_arenas[heap_index]; - - if (mem_arena->elements + 1 >= mem_arena->capacity) { - uint32_t new_capacity = mem_arena->capacity * 2; - void *new_darr = rrealloc(mem_arena->page_darr, new_capacity * sizeof *mem_arena->page_darr); - if (!new_darr) { - vkFreeMemory(arena->logical_device, (VkDeviceMemory) page.memory.device_memory, arena->allocator); - return UINT32_MAX; - } - mem_arena->page_darr = new_darr; - mem_arena->capacity = new_capacity; - } - - uint32_t page_index = mem_arena->elements; - mem_arena->page_darr[mem_arena->elements++] = page; - - arena->total_allocations++; - - return page_index; -} - -static RendMemory -rend_vk_arena_alloc(RendVkArenaAllocator *arena, VkDeviceSize size, uint32_t heap_index) -{ - assert(heap_index < 32 && "Unusual heap index. Did you pass the memory type instead?"); - RendVkPagedArena *mem_arena = &arena->mem_arenas[heap_index]; - - VkMemoryPropertyFlags properties = arena->properties.memoryTypes[heap_index].propertyFlags; - - /* align to allocation to fit page size */ - VkDeviceSize align = arena->gpu_alignment; - VkDeviceSize aligned_size = (size + align - 1) & ~(align - 1); - - /* check if fits in previous blocks */ - int whole_page = !(properties & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - uint32_t page_idx = UINT32_MAX; - for (uint32_t u = 0; u < mem_arena->elements; ++u) { - RendVkPage page = mem_arena->page_darr[u]; - int valid = (whole_page) ? (page.head == 0) : 1; - if ((page.head + aligned_size <= page.memory.size) && valid) { - page_idx = u; - break; - } - } - - /* page not found, add new page */ - if (page_idx == UINT32_MAX) { - page_idx = rend_vk_arena_add_page(arena, aligned_size, heap_index, whole_page); - if (page_idx == UINT32_MAX) { - REND__CRASH("[REND_VK] Arena page allocation failed!"); - } - } - - mem_arena->page_darr[page_idx].reserved = whole_page; - - RendMemory memory = { - .device_memory = mem_arena->page_darr[page_idx].memory.device_memory, - .size = aligned_size, - .offset = mem_arena->page_darr[page_idx].head, - .host_mapped_memory = 0, - .heap_index = heap_index, - .id = page_idx, - }; - - if (properties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { - memory.host_mapped_memory = mem_arena->page_darr[page_idx].memory.host_mapped_memory + memory.offset; - } - - /* move head */ - arena->heap_idx_alloc_sizes[heap_index] += aligned_size; - mem_arena->page_darr[page_idx].head += aligned_size; - return memory; -} - - -static void -rend_vk_arena_clear(RendVkArenaAllocator *arena, uint32_t heap_index) -{ - assert(heap_index < 32 && "Unusual heap index. Did you pass the memory type instead?"); - RendVkPagedArena mem_arena = arena->mem_arenas[heap_index]; - if (mem_arena.page_darr) { - for (uint32_t u = 0; u < mem_arena.elements; ++u) { - mem_arena.page_darr[u].head = 0; - mem_arena.page_darr[u].reserved = 0; - } - } -} - -static void -rend_vk_arena_clear_all(RendVkArenaAllocator *arena) -{ - for (uint32_t u = 0; u < arena->properties.memoryTypeCount; ++u) { - rend_vk_arena_clear(arena, u); - } -} - -static void -rend_vk_arena_shrink(RendVkArenaAllocator *arena, uint32_t heap_index) -{ - assert(heap_index < 32 && "Unusual heap index. Did you pass the memory type instead?"); - RendVkPagedArena *mem_arena = &arena->mem_arenas[heap_index]; - if (mem_arena->capacity > 4) { - uint32_t new_capacity = mem_arena->capacity / 2; - void *new_darr = rrealloc(mem_arena->page_darr, new_capacity * sizeof *mem_arena->page_darr); - if (!new_darr) return; - mem_arena->page_darr = new_darr; - mem_arena->capacity = new_capacity; - } -} - -static void -rend_vk_arena_destroy(RendVkArenaAllocator *arena) -{ - if (arena->mem_arenas) { - for (size_t u = 0; u < arena->heap_index_count; ++u) { - RendVkPagedArena *mem_arena = &arena->mem_arenas[u]; - for (uint32_t p = 0; p < mem_arena->elements; ++p) { - - RendMemory memory = mem_arena->page_darr[p].memory ; - if (memory.offset == 0) { - if (memory.host_mapped_memory) { - vkUnmapMemory(arena->logical_device, (VkDeviceMemory) memory.device_memory); - } - vkFreeMemory(arena->logical_device, (VkDeviceMemory) memory.device_memory, arena->allocator); - } else { - PWARN("[REND_VK] Attempted to free memory with an offset!"); - } - - } - if (mem_arena->page_darr) { - rfree(mem_arena->page_darr); - mem_arena->page_darr = 0; - } - } - rfree(arena->mem_arenas); - arena->mem_arenas = 0; - } - if (arena->heap_idx_alloc_sizes) { - rfree(arena->heap_idx_alloc_sizes); - arena->heap_idx_alloc_sizes = 0; - } - memset(arena, 0, sizeof *arena); -} diff --git a/Rend/rend_vk_device.c b/Rend/rend_vk_device.c deleted file mode 100644 index 6f25557..0000000 --- a/Rend/rend_vk_device.c +++ /dev/null @@ -1,377 +0,0 @@ -#pragma once -#include "rend_internal.h" -#include "rend_vk_internal.h" -#include - -// struct RendDevice { -// bool anisotropy_support; -// bool bindless_support; -// }; - - -static bool -rend_vk_device_create(VkSurfaceKHR surface, RendSpecs specs, RendVkDevice *out_device, uint32_t (*score_devices)(RendVkDevice *, RendSpecs, const char **, uint32_t)) -{ - assert(out_device); - - uint32_t device_count = 0; - CHECK_VK_RESULT(vkEnumeratePhysicalDevices(vk_instance, &device_count, VK_NULL_HANDLE)); - if (device_count == 0) { - PFATAL("No GPU with Vulkan support found!"); - return false; - } - - VkPhysicalDevice physical_devices[device_count]; - CHECK_VK_RESULT(vkEnumeratePhysicalDevices(vk_instance, &device_count, physical_devices)); - - const char *extension_names[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; - - uint32_t best_score = 1; // 1 so that devices that do not meet specs get ignored - RendVkDevice best_device = {0}; - - PDEBUG("DEVICE SCORE"); - for (uint32_t i = 0; i < device_count; i++) { - - /* populate device struct */ - RendVkDevice scoring = {0}; - scoring.surface = surface; - scoring.physical_device = physical_devices[i]; - vkGetPhysicalDeviceProperties(physical_devices[i], &scoring.properties); - vkGetPhysicalDeviceFeatures(physical_devices[i], &scoring.features); - vkGetPhysicalDeviceMemoryProperties(physical_devices[i], &scoring.memory); - - /* score device */ - uint32_t dev_score = score_devices(&scoring, specs, extension_names, 1); - PDEBUG("%-20.20s %5d", scoring.properties.deviceName, dev_score); - if (dev_score >= best_score) { - if (best_device.swapchain_support.format) { - rfree(best_device.swapchain_support.format); - } - if (best_device.swapchain_support.present_modes) { - rfree(best_device.swapchain_support.present_modes); - } - best_score = dev_score; - best_device = scoring; - } else { - if (scoring.swapchain_support.format) { - rfree(scoring.swapchain_support.format); - } - if (scoring.swapchain_support.present_modes) { - rfree(scoring.swapchain_support.present_modes); - } - } - - } - - if (best_score > 1) { - PDEBUG("Driver version %d.%d.%d", VK_VERSION_MAJOR(best_device.properties.driverVersion), VK_VERSION_MINOR(best_device.properties.driverVersion), VK_VERSION_PATCH(best_device.properties.driverVersion)); - PDEBUG("Vulkan API version %d.%d.%d", VK_VERSION_MAJOR(best_device.properties.apiVersion), VK_VERSION_MINOR(best_device.properties.apiVersion), VK_VERSION_PATCH(best_device.properties.apiVersion)); - } - - if (!best_device.physical_device) { - PERROR("No physical devices were found that meet specs!"); - return false; - } - - /* update out device to use best selected device */ - *out_device = best_device; - - PDEBUG("Graphics Family Index: %u", out_device->graphics_family_index); - PDEBUG("Present Family Index: %u", out_device->present_family_index); - PDEBUG("Compute Family Index: %u", out_device->compute_family_index); - PDEBUG("Transfer Family Index: %u", out_device->transfer_family_index); - - bool present_shares_graphics_q = out_device->present_family_index == out_device->graphics_family_index; - bool transfer_shares_graphics_q = out_device->transfer_family_index == out_device->graphics_family_index; - - uint32_t index_count = 1; - if (!present_shares_graphics_q) index_count++; - if (!transfer_shares_graphics_q) index_count++; - - uint32_t indices[index_count]; - uint8_t index = 0; - - indices[index++] = out_device->graphics_family_index; - if (!present_shares_graphics_q) { - indices[index++] = out_device->present_family_index; - } - if (!transfer_shares_graphics_q) { - indices[index++] = out_device->transfer_family_index; - } - - VkDeviceQueueCreateInfo q_create_info[index_count]; - - float queue_priority[2] = {1.0f, 1.0f}; - for (uint32_t i = 0; i < index_count; i++) { - q_create_info[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - q_create_info[i].queueFamilyIndex = indices[i]; - // TODO: request 2 queues when possible - // q_create_info[i].queueCount = (indices[i] == out_device->graphics_family_index) ? 2 : 1; - q_create_info[i].queueCount = 1; - q_create_info[i].flags = 0; - q_create_info[i].pNext = 0; - q_create_info[i].pQueuePriorities = queue_priority; - } - - // TODO: driven by the same config as the check_specs function - // used earlier - - VkPhysicalDeviceFeatures device_features = {0}; - device_features.samplerAnisotropy = specs.sampler_anisotropy; - device_features.fillModeNonSolid = VK_TRUE; - device_features.shaderInt64 = VK_TRUE; - - // vulkan 1.3 features (dynamic rendering + sync2) - VkPhysicalDeviceVulkan13Features vk13_features = {0}; - vk13_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES; - vk13_features.dynamicRendering = VK_TRUE; - vk13_features.synchronization2 = VK_TRUE; - - // vulkan 1.2 features (timeline semaphores) - VkPhysicalDeviceVulkan12Features vk12_features = {0}; - vk12_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; - vk12_features.timelineSemaphore = VK_TRUE; - - // vk12_features.descriptorIndexing = VK_TRUE; - // vk12_features.descriptorBindingSampledImageUpdateAfterBind = VK_TRUE; - vk12_features.descriptorBindingPartiallyBound = VK_TRUE; - // vk12_features.descriptorBindingUniformBufferUpdateAfterBind = VK_TRUE; - // vk12_features.descriptorBindingVariableDescriptorCount = VK_TRUE; - // vk12_features.runtimeDescriptorArray = VK_TRUE; - - vk12_features.bufferDeviceAddress = VK_TRUE; - vk12_features.scalarBlockLayout = VK_TRUE; - vk12_features.pNext = &vk13_features; - - // vulkan 1.1 features (draw parameters) - VkPhysicalDeviceVulkan11Features vk11_features = {0}; - vk11_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES; - vk11_features.shaderDrawParameters = VK_TRUE; - vk11_features.pNext = &vk12_features; - - VkDeviceCreateInfo device_create_info = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO }; - device_create_info.pNext = &vk11_features; - device_create_info.queueCreateInfoCount = index_count; - device_create_info.pQueueCreateInfos = q_create_info; - device_create_info.pEnabledFeatures = &device_features; - device_create_info.enabledExtensionCount = 1; - - const char *extention_names = VK_KHR_SWAPCHAIN_EXTENSION_NAME; - device_create_info.ppEnabledExtensionNames = &extention_names; - - /* deprecated and ignored */ - device_create_info.enabledLayerCount = 0; - device_create_info.ppEnabledLayerNames = 0; - - CHECK_VK_RESULT(vkCreateDevice(out_device->physical_device, &device_create_info, vk_allocator, &out_device->logical_device)); - - vkGetDeviceQueue(out_device->logical_device, out_device->graphics_family_index, 0, &out_device->graphics_queue); - vkGetDeviceQueue(out_device->logical_device, out_device->present_family_index, 0, &out_device->present_queue); - vkGetDeviceQueue(out_device->logical_device, out_device->transfer_family_index, 0, &out_device->transfer_queue); - - PDEBUG("GRAPHICS | PRESENT | COMPUTE | TRANSFER | DEVICE"); - PDEBUG(" %02d | %02d | %02d | %02d | %s", - out_device->graphics_family_index != UINT32_MAX, - out_device->present_family_index != UINT32_MAX, - out_device->compute_family_index != UINT32_MAX, - out_device->transfer_family_index != UINT32_MAX, - out_device->properties.deviceName); - - VkPhysicalDeviceMemoryProperties mem_props = out_device->memory; - out_device->device_index = UINT32_MAX; - for (uint32_t i = 0; i < mem_props.memoryTypeCount; i++) { - if ((mem_props.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) { - out_device->device_index = i; - break; - } - } - - VkMemoryPropertyFlags host_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; - out_device->host_index = UINT32_MAX; - for (uint32_t i = 0; i < mem_props.memoryTypeCount; i++) { - if ((mem_props.memoryTypes[i].propertyFlags & host_flags) == host_flags) { - out_device->host_index = i; - break; - } - } - - PDEBUG("Device local heap: %2u", out_device->device_index); - PDEBUG("Host mapped heap: %2u", out_device->host_index); - return true; -} - -static void -rend_vk_device_destroy(RendVkDevice *device) -{ - RASSERT(device && device->logical_device, "Invalid or uninitialized device."); - - vkDeviceWaitIdle(device->logical_device); // safe when destroying device - vkDestroyDevice(device->logical_device, vk_allocator); - if (device->swapchain_support.format) rfree(vk_device.swapchain_support.format); - if (device->swapchain_support.present_modes) rfree(vk_device.swapchain_support.present_modes); - - *device = (RendVkDevice){0}; -} - -static void -rend_vk_device_query_swapchain_support(RendVkDevice *device) -{ - RASSERT(device->physical_device, "Invalid device pointer."); - - /* surface capabilities */ - CHECK_VK_RESULT(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device->physical_device, device->surface, &device->swapchain_support.capabilities)); - - /* surface formats */ - CHECK_VK_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(device->physical_device, device->surface, &device->swapchain_support.format_count, VK_NULL_HANDLE)); - if (device->swapchain_support.format_count != 0) { - if (!device->swapchain_support.format) { - device->swapchain_support.format = rmalloc(device->swapchain_support.format_count * sizeof(*device->swapchain_support.format)); - } - CHECK_VK_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(device->physical_device, device->surface, &device->swapchain_support.format_count, device->swapchain_support.format)); - } - - /* present modes */ - CHECK_VK_RESULT(vkGetPhysicalDeviceSurfacePresentModesKHR(device->physical_device, device->surface, &device->swapchain_support.present_mode_count, VK_NULL_HANDLE)); - if (device->swapchain_support.present_mode_count != 0) { - if (!device->swapchain_support.present_modes) { - device->swapchain_support.present_modes = rmalloc(device->swapchain_support.present_mode_count * sizeof(*device->swapchain_support.present_modes)); - } - CHECK_VK_RESULT(vkGetPhysicalDeviceSurfacePresentModesKHR(device->physical_device, device->surface, &device->swapchain_support.present_mode_count, device->swapchain_support.present_modes)); - } -} - -static bool -rend_vk_device_detect_depth_format(RendVkDevice *device) -{ - const uint64_t candidate_count = 3; - VkFormat candidates[] = {VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT}; - - uint32_t flags = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT; - for (uint32_t i = 0; i < candidate_count; i++) { - VkFormatProperties properties; - vkGetPhysicalDeviceFormatProperties(device->physical_device, candidates[i], &properties); - if ((properties.linearTilingFeatures & flags) == flags) { - device->depth_format = candidates[i]; - return true; - } else if ((properties.optimalTilingFeatures & flags) == flags) { - device->depth_format = candidates[i]; - return true; - } - } - - return false; -} - -static uint32_t -rend_vk_device_score_default(RendVkDevice *device, RendSpecs minimum_specs, const char **required_extensions, uint32_t required_extension_count) -{ - /* NOTE: When we score the device we will also check for specs. Not meeting a spec - * leads to 0 score and continue. We will assume device already contains some information - * about properties when this function is called. */ - - uint32_t score = 0; - - device->graphics_family_index = UINT32_MAX; - device->present_family_index = UINT32_MAX; - device->compute_family_index = UINT32_MAX; - device->transfer_family_index = UINT32_MAX; - - if (minimum_specs.discrete_gpu) { - if (device->properties.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) { - return 0; - } - } - - uint32_t q_family_count = 0; - vkGetPhysicalDeviceQueueFamilyProperties(device->physical_device, &q_family_count, VK_NULL_HANDLE); - - VkQueueFamilyProperties q_family[q_family_count]; - vkGetPhysicalDeviceQueueFamilyProperties(device->physical_device, &q_family_count, q_family); - - uint8_t min_transfer_score = 255; - for (uint32_t i = 0; i < q_family_count; i++) { - - uint8_t transfer_score = 0; - if (q_family[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { - device->graphics_family_index = i; - transfer_score++; - } - - if (q_family[i].queueFlags & VK_QUEUE_COMPUTE_BIT) { - device->compute_family_index = i; - transfer_score++; - } - - if (q_family[i].queueFlags & VK_QUEUE_TRANSFER_BIT) { - // take the index if the transfer score is minimal - if (transfer_score <= min_transfer_score) { - min_transfer_score = transfer_score; - device->transfer_family_index = i; - } - } - - VkBool32 supports_present = VK_FALSE; - CHECK_VK_RESULT(vkGetPhysicalDeviceSurfaceSupportKHR(device->physical_device, i, device->surface, &supports_present)); - if (supports_present) { - device->present_family_index = i; - } - } - - if ( (minimum_specs.graphics && device->graphics_family_index == UINT32_MAX) || - (minimum_specs.present && device->present_family_index == UINT32_MAX) || - (minimum_specs.compute && device->compute_family_index == UINT32_MAX) || - (minimum_specs.transfer && device->transfer_family_index == UINT32_MAX)) { - return 0; - } - - // swapchain support - rend_vk_device_query_swapchain_support(device); - - if (device->swapchain_support.format_count < 1 || device->swapchain_support.present_mode_count < 1) { - return 0; - } - - // check for extension specs - if (required_extensions) { - uint32_t available_extentions_count = 0; - VkExtensionProperties *available_extentions = NULL; - CHECK_VK_RESULT(vkEnumerateDeviceExtensionProperties(device->physical_device, VK_NULL_HANDLE, &available_extentions_count, VK_NULL_HANDLE)); - - if (available_extentions_count != 0) { - - available_extentions = rmalloc(available_extentions_count * sizeof(*available_extentions)); - CHECK_VK_RESULT(vkEnumerateDeviceExtensionProperties(device->physical_device, VK_NULL_HANDLE, &available_extentions_count, available_extentions)); - - bool overall_found = true; - for (uint32_t i = 0; i < required_extension_count; ++i) { - bool found = false; - for (uint32_t j = 0; j < available_extentions_count; ++j) { - if (strcmp(required_extensions[i], available_extentions[j].extensionName) == 0) { - found = true; - break; - } - } - if (!found) { - overall_found = false; - break; - } - } - - rfree(available_extentions); - if (!overall_found) { - return 0; - } - } - } - - if (minimum_specs.sampler_anisotropy && !device->features.samplerAnisotropy) { - return 0; - } - - score = 10; - if (device->properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) { - score += 1000; - } - - return score; -} diff --git a/Rend/rend_vk_image.c b/Rend/rend_vk_image.c deleted file mode 100644 index e842bf0..0000000 --- a/Rend/rend_vk_image.c +++ /dev/null @@ -1,125 +0,0 @@ -#pragma once -#include "rend_internal.h" -#include "rend_vk_internal.h" -#include - -struct RendVkImage { - VkDevice logical_device; - VkImage handle; - VkImageView view; - - VkMemoryRequirements requirements; - - VkImageType img_type; - uint32_t width, height; - VkFormat format; - VkImageTiling tiling; - VkImageUsageFlags usage; - uint32_t depth; - uint32_t mip_levels; - uint32_t layers; - VkSampleCountFlags sample_count_flags; - VkSharingMode sharing_mode; - - RendMemory *memory; -}; - -static RendVkImage -rend_vk_image_create(VkDevice logical_device, VkImageType img_type, uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, - VkImageUsageFlags usage, VkMemoryPropertyFlags memory_flags, uint32_t depth, uint32_t mip_levels, uint32_t layers, - VkSampleCountFlags sample_count_flags, VkSharingMode sharing_mode) -{ - RendVkImage image = { - .handle = VK_NULL_HANDLE, - .memory = VK_NULL_HANDLE, - .logical_device = logical_device, - .img_type = img_type, - .width = width, - .height = height, - .format = format, - .tiling = tiling, - .usage = usage, - .depth = depth, - .mip_levels = mip_levels, - .layers = layers, - .sample_count_flags = sample_count_flags, - .sharing_mode = sharing_mode - }; - - VkImageCreateInfo img_create_info = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; - img_create_info.imageType = img_type; - img_create_info.extent.width = width; - img_create_info.extent.height = height; - img_create_info.extent.depth = depth; - img_create_info.mipLevels = mip_levels; - img_create_info.arrayLayers = layers; - img_create_info.format = format; - img_create_info.tiling = tiling; - img_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - img_create_info.usage = usage; - img_create_info.samples = sample_count_flags; - img_create_info.sharingMode = sharing_mode; - - - if (vkCreateImage(logical_device, &img_create_info, vk_allocator, &image.handle) != VK_SUCCESS) { - return image; - } - - return image; -} - -static uint32_t -rend_vk_image_required_memory_type(RendVkImage *img) -{ - assert(img->memory == NULL && "Image already bound to memory"); - VkMemoryRequirements memory_requirements = {0}; - vkGetImageMemoryRequirements(img->logical_device, img->handle, &memory_requirements); - img->requirements = memory_requirements; - return memory_requirements.memoryTypeBits; -} - -static void -rend_vk_image_bind_memory(RendVkImage *img, RendMemory *memory) -{ - assert(img->memory == NULL && "Image already bound to memory"); - vkBindImageMemory(img->logical_device, img->handle, (VkDeviceMemory) memory->device_memory, memory->offset); -} - -static void -rend_vk_image_destroy(RendVkImage *img) -{ - if (img->view) { - vkDestroyImageView(img->logical_device, img->view, vk_allocator); - img->view = 0; - } - - if (img->handle) { - vkDestroyImage(img->logical_device, img->handle, vk_allocator); - img->handle = 0; - } -} - -static void -rend_vk_image_view_create(RendVkImage *image, VkImageViewType view_type, VkImageAspectFlags view_aspect_flags) -{ - VkImageViewCreateInfo view_create_info = { - .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .image = image->handle, - .format = image->format, - .viewType = view_type, - .subresourceRange.aspectMask = view_aspect_flags, - .subresourceRange.baseMipLevel = 0, // offset starts at 0 - .subresourceRange.levelCount = image->mip_levels, // number of mips - .subresourceRange.baseArrayLayer = 0, // offset starts at 0 - .subresourceRange.layerCount = image->layers, // number of layers - }; - - vkCreateImageView(image->logical_device, &view_create_info, vk_allocator, &image->view); -} - -static uint64_t -rend_vk_image_address(RendVkImage *image) -{ - assert(image->memory == NULL && "Image already bound to memory"); - return (uint64_t) image->memory->device_memory + image->memory->offset; -} diff --git a/Rend/rend_vk_internal.c b/Rend/rend_vk_internal.c new file mode 100644 index 0000000..4f1679f --- /dev/null +++ b/Rend/rend_vk_internal.c @@ -0,0 +1,1014 @@ +#include +#include +#include +#include +#include +#include + +#include "rend.h" +#include "rend_internal.h" + +#define CHECK_VK_RESULT(r) \ +{ \ + assert(r == VK_SUCCESS && __LINE__); \ +} + +typedef struct RendVkImage RendVkImage; +typedef struct RendVkArenaAllocator RendVkArenaAllocator; + +typedef struct { + VkSurfaceCapabilitiesKHR capabilities; + VkSurfaceFormatKHR *format; + VkPresentModeKHR *present_modes; + uint32_t format_count; + uint32_t present_mode_count; +} RendVkSwapchainSupport; + +typedef struct { + VkDevice logical_device; + + VkPhysicalDeviceProperties properties; + VkPhysicalDeviceMemoryProperties memory; + VkPhysicalDevice physical_device; + VkPhysicalDeviceFeatures features; + + VkSurfaceKHR surface; + RendVkSwapchainSupport swapchain_support; + VkFormat depth_format; + + VkQueue graphics_queue; + VkQueue present_queue; + VkQueue compute_queue; + VkQueue transfer_queue; + uint32_t graphics_family_index; + uint32_t present_family_index; + uint32_t compute_family_index; + uint32_t transfer_family_index; + + uint32_t host_index; + uint32_t device_index; +} RendVkDevice; + +typedef struct RendVkPage { + RendMemory memory; + size_t head; + int reserved; +} RendVkPage; + +typedef struct RendVkPagedArena { + RendVkPage *page_darr; + uint32_t capacity; + uint32_t elements; +} RendVkPagedArena; + +struct RendVkArenaAllocator { + VkAllocationCallbacks *allocator; + RendVkPagedArena *mem_arenas; + VkDevice logical_device; + VkPhysicalDevice physical_device; + VkDeviceSize gpu_alignment; + VkDeviceSize block_min_size; + uint32_t heap_index_count; + VkPhysicalDeviceMemoryProperties properties; +}; + +struct RendVkImage { + VkDevice logical_device; + VkImage handle; + VkImageView view; + + VkMemoryRequirements requirements; + + VkImageType img_type; + uint32_t width, height; + VkFormat format; + VkImageTiling tiling; + VkImageUsageFlags usage; + uint32_t depth; + uint32_t mip_levels; + uint32_t layers; + VkSampleCountFlags sample_count_flags; + VkSharingMode sharing_mode; + + RendMemory *memory; +}; + +typedef struct RendVkAllocatorState { + const char *name; +} RendVkAllocatorState; + +typedef struct RendVkAllocatorHeader { + uint32_t offset; +} RendVkAllocatorHeader; + +static bool rend_vk_allocator_is_power_of_two(uintptr_t x); +static uintptr_t rend_vk_allocator_align_forward(uintptr_t ptr, size_t align); +static void *rend_vk_allocator_alloc(void *pUserData, size_t size, size_t alignment, VkSystemAllocationScope allocationScope); +static void *rend_vk_allocator_realloc(void *pUserData, void *pOriginal, size_t size, size_t alignment, VkSystemAllocationScope allocationScope); +static void rend_vk_allocator_free(void *pUserData, void *pMemory); +static void rend_vk_allocator_internal_notification(void *pUserData, size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope); +static void rend_vk_allocator_free_notification(void *pUserData, size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope); + +static VkAllocationCallbacks rend_vk_allocator = { + .pfnAllocation = rend_vk_allocator_alloc, + .pfnReallocation = rend_vk_allocator_realloc, + .pfnFree = rend_vk_allocator_free, + .pfnInternalAllocation = rend_vk_allocator_internal_notification, + .pfnInternalFree = rend_vk_allocator_free_notification, +}; + +static const char *rend_vk_allocator_scope_name[] = { + [VK_SYSTEM_ALLOCATION_SCOPE_CACHE] = "Cache", + [VK_SYSTEM_ALLOCATION_SCOPE_COMMAND] = "Command", + [VK_SYSTEM_ALLOCATION_SCOPE_DEVICE] = "Device", + [VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE] = "Instance", + [VK_SYSTEM_ALLOCATION_SCOPE_OBJECT] = "Object", +}; + +static VkInstance vk_instance = 0; +static VkDebugUtilsMessengerEXT vk_debug_messenger = 0; +static RendVkDevice vk_device = {0}; +static VkAllocationCallbacks *vk_allocator = &rend_vk_allocator; + +static VkFormat vk_format_from_rend_format[] = { + [REND_FORMAT_R8_UNORM] = VK_FORMAT_R8_UNORM, + [REND_FORMAT_R8G8_UNORM] = VK_FORMAT_R8G8_UNORM, + [REND_FORMAT_R8G8B8A8_UNORM] = VK_FORMAT_R8G8B8A8_UNORM, + [REND_FORMAT_B8G8R8A8_UNORM] = VK_FORMAT_B8G8R8A8_UNORM, + [REND_FORMAT_R8G8B8A8_SRGB] = VK_FORMAT_R8G8B8A8_SRGB, + [REND_FORMAT_B8G8R8A8_SRGB] = VK_FORMAT_B8G8R8A8_SRGB, + [REND_FORMAT_R32_SFLOAT] = VK_FORMAT_R32_SFLOAT, + [REND_FORMAT_R32G32_SFLOAT] = VK_FORMAT_R32G32_SFLOAT, + [REND_FORMAT_R32G32B32_SFLOAT] = VK_FORMAT_R32G32B32_SFLOAT, + [REND_FORMAT_R32G32B32A32_SFLOAT]= VK_FORMAT_R32G32B32A32_SFLOAT, + [REND_FORMAT_R16_SFLOAT] = VK_FORMAT_R16_SFLOAT, + [REND_FORMAT_R16G16_SFLOAT] = VK_FORMAT_R16G16_SFLOAT, + [REND_FORMAT_R16G16B16A16_SFLOAT]= VK_FORMAT_R16G16B16A16_SFLOAT, + [REND_FORMAT_R32_UINT] = VK_FORMAT_R32_UINT, + [REND_FORMAT_R32_SINT] = VK_FORMAT_R32_SINT, + [REND_FORMAT_R32G32B32A32_UINT] = VK_FORMAT_R32G32B32A32_UINT, + [REND_FORMAT_R16G16B16A16_UINT] = VK_FORMAT_R16G16B16A16_UINT, + [REND_FORMAT_R8G8B8A8_UINT] = VK_FORMAT_R8G8B8A8_UINT, + [REND_FORMAT_D32_SFLOAT] = VK_FORMAT_D32_SFLOAT, + [REND_FORMAT_D24_UNORM_S8_UINT] = VK_FORMAT_D24_UNORM_S8_UINT, + [REND_FORMAT_D32_SFLOAT_S8_UINT] = VK_FORMAT_D32_SFLOAT_S8_UINT, +}; + +static VkPrimitiveTopology vk_topology[] = { + [REND_TOPOLOGY_TRIANGLE_LIST] = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, + [REND_TOPOLOGY_TRIANGLE_STRIP] = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, + [REND_TOPOLOGY_LINE_LIST] = VK_PRIMITIVE_TOPOLOGY_LINE_LIST, + [REND_TOPOLOGY_LINE_STRIP] = VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, + [REND_TOPOLOGY_POINT_LIST] = VK_PRIMITIVE_TOPOLOGY_POINT_LIST +}; + +static VkPolygonMode vk_polymode[] = { + [REND_POLYGON_MODE_FILL] = VK_POLYGON_MODE_FILL, + [REND_POLYGON_MODE_LINE] = VK_POLYGON_MODE_LINE, + [REND_POLYGON_MODE_POINT] = VK_POLYGON_MODE_POINT, +}; + +static VkCullModeFlags vk_cullflags[] = { + [REND_CULL_MODE_NONE] = VK_CULL_MODE_NONE, + [REND_CULL_MODE_FRONT] = VK_CULL_MODE_FRONT_BIT, + [REND_CULL_MODE_BACK] = VK_CULL_MODE_BACK_BIT, + [REND_CULL_MODE_FRONT_AND_BACK] = VK_CULL_MODE_FRONT_AND_BACK, +}; + +static const VkBufferUsageFlags vk_buffer_usage[] = { + [REND_BUFFER_VERTEX] = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + [REND_BUFFER_INDEX] = VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + [REND_BUFFER_UNIFORM] = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + [REND_BUFFER_STORAGE] = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + [REND_BUFFER_TRANSFER] = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, +}; + +static const VkShaderStageFlagBits vk_pipeline_stages[][3] = { + [REND__PIPELINE_GRAPHICS] = { VK_SHADER_STAGE_VERTEX_BIT, VK_SHADER_STAGE_FRAGMENT_BIT }, + [REND__PIPELINE_COMPUTE] = { VK_SHADER_STAGE_COMPUTE_BIT }, + [REND__PIPELINE_MESH] = { VK_SHADER_STAGE_MESH_BIT_EXT, VK_SHADER_STAGE_FRAGMENT_BIT }, +}; + +/* --- host allocator --- */ + +static bool +rend_vk_allocator_is_power_of_two(uintptr_t x) +{ + return (x & (x - 1)) == 0; +} + +static uintptr_t +rend_vk_allocator_align_forward(uintptr_t ptr, size_t align) +{ + uintptr_t p, a, modulo; + + assert(rend_vk_allocator_is_power_of_two(align)); + + p = ptr; + a = (uintptr_t)align; + modulo = p & (a - 1); + + if (modulo != 0) { + p += a - modulo; + } + return p; +} + +static void * +rend_vk_allocator_alloc(void *pUserData, size_t size, size_t alignment, VkSystemAllocationScope allocationScope) +{ + size_t total_size; + uint8_t *raw_ptr; + uintptr_t unaligned_addr; + uint8_t *aligned_ptr; + RendVkAllocatorHeader *header; + + (void)pUserData; + total_size = size + alignment + sizeof(RendVkAllocatorHeader); + raw_ptr = malloc(total_size); + unaligned_addr = (uintptr_t)(raw_ptr + sizeof(RendVkAllocatorHeader)); + + aligned_ptr = (uint8_t *)rend_vk_allocator_align_forward(unaligned_addr, alignment); + header = (RendVkAllocatorHeader *)aligned_ptr - 1; + header->offset = aligned_ptr - raw_ptr; + + PDEBUG("[VK_ALLOC] %p - bytes %lu with alignment %lu - scope %s", + aligned_ptr, size, alignment, rend_vk_allocator_scope_name[allocationScope]); + + return (void *)aligned_ptr; +} + +static void * +rend_vk_allocator_realloc(void *pUserData, void *pOriginal, size_t size, size_t alignment, VkSystemAllocationScope allocationScope) +{ + void *new_ptr; + + if (!pOriginal) + return rend_vk_allocator_alloc(pUserData, size, alignment, allocationScope); + if (size == 0) { + rend_vk_allocator_free(pUserData, pOriginal); + return NULL; + } + new_ptr = rend_vk_allocator_alloc(pUserData, size, alignment, allocationScope); + if (!new_ptr) + return NULL; + memcpy(new_ptr, pOriginal, size); + rend_vk_allocator_free(pUserData, pOriginal); + return new_ptr; +} + +static void +rend_vk_allocator_free(void *pUserData, void *pMemory) +{ + RendVkAllocatorHeader *ptr; + uint8_t *raw_ptr; + + (void)pUserData; + if (!pMemory) + return; + ptr = pMemory; + raw_ptr = pMemory; + raw_ptr -= (ptr - 1)->offset; + + PDEBUG("[VK_FREE] %p", ptr); + free(raw_ptr); +} + +static void +rend_vk_allocator_internal_notification(void *pUserData, size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope) +{ + (void)pUserData; + (void)allocationType; + PDEBUG("[VK_ALLOC_INTERNAL] bytes %lu - scope %s", + size, rend_vk_allocator_scope_name[allocationScope]); +} + +static void +rend_vk_allocator_free_notification(void *pUserData, size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope) +{ + (void)pUserData; + (void)allocationType; + PDEBUG("[VK_FREE_INTERNAL] bytes %lu - scope %s", + size, rend_vk_allocator_scope_name[allocationScope]); +} + +/* --- device --- */ + +static void +rend_vk_device_query_swapchain_support(RendVkDevice *device) +{ + RASSERT(device->physical_device, "Invalid device pointer."); + + CHECK_VK_RESULT(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device->physical_device, device->surface, &device->swapchain_support.capabilities)); + + CHECK_VK_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(device->physical_device, device->surface, &device->swapchain_support.format_count, VK_NULL_HANDLE)); + if (device->swapchain_support.format_count != 0) { + if (!device->swapchain_support.format) { + device->swapchain_support.format = rmalloc(device->swapchain_support.format_count * sizeof(*device->swapchain_support.format)); + } + CHECK_VK_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(device->physical_device, device->surface, &device->swapchain_support.format_count, device->swapchain_support.format)); + } + + CHECK_VK_RESULT(vkGetPhysicalDeviceSurfacePresentModesKHR(device->physical_device, device->surface, &device->swapchain_support.present_mode_count, VK_NULL_HANDLE)); + if (device->swapchain_support.present_mode_count != 0) { + if (!device->swapchain_support.present_modes) { + device->swapchain_support.present_modes = rmalloc(device->swapchain_support.present_mode_count * sizeof(*device->swapchain_support.present_modes)); + } + CHECK_VK_RESULT(vkGetPhysicalDeviceSurfacePresentModesKHR(device->physical_device, device->surface, &device->swapchain_support.present_mode_count, device->swapchain_support.present_modes)); + } +} + +static uint32_t +rend_vk_device_score_default(RendVkDevice *device, RendSpecs minimum_specs, const char **required_extensions, uint32_t required_extension_count) +{ + uint32_t score; + uint32_t q_family_count; + uint8_t min_transfer_score; + uint32_t i; + + score = 0; + + device->graphics_family_index = UINT32_MAX; + device->present_family_index = UINT32_MAX; + device->compute_family_index = UINT32_MAX; + device->transfer_family_index = UINT32_MAX; + + if (minimum_specs.discrete_gpu) { + if (device->properties.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) { + return 0; + } + } + + q_family_count = 0; + vkGetPhysicalDeviceQueueFamilyProperties(device->physical_device, &q_family_count, VK_NULL_HANDLE); + + { + VkQueueFamilyProperties q_family[q_family_count]; + + vkGetPhysicalDeviceQueueFamilyProperties(device->physical_device, &q_family_count, q_family); + + min_transfer_score = 255; + for (i = 0; i < q_family_count; i++) { + uint8_t transfer_score; + VkBool32 supports_present; + + transfer_score = 0; + if (q_family[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { + device->graphics_family_index = i; + transfer_score++; + } + + if (q_family[i].queueFlags & VK_QUEUE_COMPUTE_BIT) { + device->compute_family_index = i; + transfer_score++; + } + + if (q_family[i].queueFlags & VK_QUEUE_TRANSFER_BIT) { + if (transfer_score <= min_transfer_score) { + min_transfer_score = transfer_score; + device->transfer_family_index = i; + } + } + + supports_present = VK_FALSE; + CHECK_VK_RESULT(vkGetPhysicalDeviceSurfaceSupportKHR(device->physical_device, i, device->surface, &supports_present)); + if (supports_present) { + device->present_family_index = i; + } + } + } + + if ((minimum_specs.graphics && device->graphics_family_index == UINT32_MAX) || + (minimum_specs.present && device->present_family_index == UINT32_MAX) || + (minimum_specs.compute && device->compute_family_index == UINT32_MAX) || + (minimum_specs.transfer && device->transfer_family_index == UINT32_MAX)) { + return 0; + } + + rend_vk_device_query_swapchain_support(device); + + if (device->swapchain_support.format_count < 1 || device->swapchain_support.present_mode_count < 1) { + return 0; + } + + if (required_extensions) { + uint32_t available_extentions_count; + VkExtensionProperties *available_extentions; + + available_extentions_count = 0; + available_extentions = NULL; + CHECK_VK_RESULT(vkEnumerateDeviceExtensionProperties(device->physical_device, VK_NULL_HANDLE, &available_extentions_count, VK_NULL_HANDLE)); + + if (available_extentions_count != 0) { + bool overall_found; + uint32_t j; + + available_extentions = rmalloc(available_extentions_count * sizeof(*available_extentions)); + CHECK_VK_RESULT(vkEnumerateDeviceExtensionProperties(device->physical_device, VK_NULL_HANDLE, &available_extentions_count, available_extentions)); + + overall_found = true; + for (i = 0; i < required_extension_count; ++i) { + bool found; + + found = false; + for (j = 0; j < available_extentions_count; ++j) { + if (strcmp(required_extensions[i], available_extentions[j].extensionName) == 0) { + found = true; + break; + } + } + if (!found) { + overall_found = false; + break; + } + } + + rfree(available_extentions); + if (!overall_found) { + return 0; + } + } + } + + if (minimum_specs.sampler_anisotropy && !device->features.samplerAnisotropy) { + return 0; + } + + score = 10; + if (device->properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) { + score += 1000; + } + + return score; +} + +static bool +rend_vk_device_create(VkSurfaceKHR surface, RendSpecs specs, RendVkDevice *out_device) +{ + uint32_t device_count; + const char *extension_names[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; + uint32_t best_score; + RendVkDevice best_device; + uint32_t i; + bool present_shares_graphics_q; + bool transfer_shares_graphics_q; + uint32_t index_count; + uint8_t index; + float queue_priority[2]; + VkPhysicalDeviceFeatures device_features; + VkPhysicalDeviceVulkan13Features vk13_features; + VkPhysicalDeviceVulkan12Features vk12_features; + VkPhysicalDeviceVulkan11Features vk11_features; + VkDeviceCreateInfo device_create_info; + const char *extention_names; + VkPhysicalDeviceMemoryProperties mem_props; + VkMemoryPropertyFlags host_flags; + + assert(out_device); + + device_count = 0; + CHECK_VK_RESULT(vkEnumeratePhysicalDevices(vk_instance, &device_count, VK_NULL_HANDLE)); + if (device_count == 0) { + PFATAL("No GPU with Vulkan support found!"); + return false; + } + + { + VkPhysicalDevice physical_devices[device_count]; + + CHECK_VK_RESULT(vkEnumeratePhysicalDevices(vk_instance, &device_count, physical_devices)); + + best_score = 1; + best_device = (RendVkDevice){0}; + + PDEBUG("DEVICE SCORE"); + for (i = 0; i < device_count; i++) { + RendVkDevice scoring; + uint32_t dev_score; + + scoring = (RendVkDevice){0}; + scoring.surface = surface; + scoring.physical_device = physical_devices[i]; + vkGetPhysicalDeviceProperties(physical_devices[i], &scoring.properties); + vkGetPhysicalDeviceFeatures(physical_devices[i], &scoring.features); + vkGetPhysicalDeviceMemoryProperties(physical_devices[i], &scoring.memory); + + dev_score = rend_vk_device_score_default(&scoring, specs, extension_names, 1); + PDEBUG("%-20.20s %5d", scoring.properties.deviceName, dev_score); + if (dev_score >= best_score) { + if (best_device.swapchain_support.format) { + rfree(best_device.swapchain_support.format); + } + if (best_device.swapchain_support.present_modes) { + rfree(best_device.swapchain_support.present_modes); + } + best_score = dev_score; + best_device = scoring; + } else { + if (scoring.swapchain_support.format) { + rfree(scoring.swapchain_support.format); + } + if (scoring.swapchain_support.present_modes) { + rfree(scoring.swapchain_support.present_modes); + } + } + } + } + + if (best_score > 1) { + PDEBUG("Driver version %d.%d.%d", VK_VERSION_MAJOR(best_device.properties.driverVersion), VK_VERSION_MINOR(best_device.properties.driverVersion), VK_VERSION_PATCH(best_device.properties.driverVersion)); + PDEBUG("Vulkan API version %d.%d.%d", VK_VERSION_MAJOR(best_device.properties.apiVersion), VK_VERSION_MINOR(best_device.properties.apiVersion), VK_VERSION_PATCH(best_device.properties.apiVersion)); + } + + if (!best_device.physical_device) { + PERROR("No physical devices were found that meet specs!"); + return false; + } + + *out_device = best_device; + + PDEBUG("Graphics Family Index: %u", out_device->graphics_family_index); + PDEBUG("Present Family Index: %u", out_device->present_family_index); + PDEBUG("Compute Family Index: %u", out_device->compute_family_index); + PDEBUG("Transfer Family Index: %u", out_device->transfer_family_index); + + present_shares_graphics_q = out_device->present_family_index == out_device->graphics_family_index; + transfer_shares_graphics_q = out_device->transfer_family_index == out_device->graphics_family_index; + + index_count = 1; + if (!present_shares_graphics_q) + index_count++; + if (!transfer_shares_graphics_q) + index_count++; + + { + uint32_t indices[index_count]; + VkDeviceQueueCreateInfo q_create_info[index_count]; + + index = 0; + indices[index++] = out_device->graphics_family_index; + if (!present_shares_graphics_q) { + indices[index++] = out_device->present_family_index; + } + if (!transfer_shares_graphics_q) { + indices[index++] = out_device->transfer_family_index; + } + + queue_priority[0] = 1.0f; + queue_priority[1] = 1.0f; + for (i = 0; i < index_count; i++) { + q_create_info[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + q_create_info[i].queueFamilyIndex = indices[i]; + q_create_info[i].queueCount = 1; + q_create_info[i].flags = 0; + q_create_info[i].pNext = 0; + q_create_info[i].pQueuePriorities = queue_priority; + } + + device_features = (VkPhysicalDeviceFeatures){0}; + device_features.samplerAnisotropy = specs.sampler_anisotropy; + device_features.fillModeNonSolid = VK_TRUE; + device_features.shaderInt64 = VK_TRUE; + + vk13_features = (VkPhysicalDeviceVulkan13Features){0}; + vk13_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES; + vk13_features.dynamicRendering = VK_TRUE; + vk13_features.synchronization2 = VK_TRUE; + + vk12_features = (VkPhysicalDeviceVulkan12Features){0}; + vk12_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; + vk12_features.timelineSemaphore = VK_TRUE; + vk12_features.descriptorBindingPartiallyBound = VK_TRUE; + vk12_features.bufferDeviceAddress = VK_TRUE; + vk12_features.scalarBlockLayout = VK_TRUE; + vk12_features.pNext = &vk13_features; + + vk11_features = (VkPhysicalDeviceVulkan11Features){0}; + vk11_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES; + vk11_features.shaderDrawParameters = VK_TRUE; + vk11_features.pNext = &vk12_features; + + device_create_info = (VkDeviceCreateInfo){ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO }; + device_create_info.pNext = &vk11_features; + device_create_info.queueCreateInfoCount = index_count; + device_create_info.pQueueCreateInfos = q_create_info; + device_create_info.pEnabledFeatures = &device_features; + device_create_info.enabledExtensionCount = 1; + + extention_names = VK_KHR_SWAPCHAIN_EXTENSION_NAME; + device_create_info.ppEnabledExtensionNames = &extention_names; + + device_create_info.enabledLayerCount = 0; + device_create_info.ppEnabledLayerNames = 0; + + CHECK_VK_RESULT(vkCreateDevice(out_device->physical_device, &device_create_info, vk_allocator, &out_device->logical_device)); + } + + vkGetDeviceQueue(out_device->logical_device, out_device->graphics_family_index, 0, &out_device->graphics_queue); + vkGetDeviceQueue(out_device->logical_device, out_device->present_family_index, 0, &out_device->present_queue); + vkGetDeviceQueue(out_device->logical_device, out_device->transfer_family_index, 0, &out_device->transfer_queue); + + PDEBUG("GRAPHICS | PRESENT | COMPUTE | TRANSFER | DEVICE"); + PDEBUG(" %02d | %02d | %02d | %02d | %s", + out_device->graphics_family_index != UINT32_MAX, + out_device->present_family_index != UINT32_MAX, + out_device->compute_family_index != UINT32_MAX, + out_device->transfer_family_index != UINT32_MAX, + out_device->properties.deviceName); + + mem_props = out_device->memory; + out_device->device_index = UINT32_MAX; + for (i = 0; i < mem_props.memoryTypeCount; i++) { + if ((mem_props.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) { + out_device->device_index = i; + break; + } + } + + host_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + out_device->host_index = UINT32_MAX; + for (i = 0; i < mem_props.memoryTypeCount; i++) { + if ((mem_props.memoryTypes[i].propertyFlags & host_flags) == host_flags) { + out_device->host_index = i; + break; + } + } + + PDEBUG("Device local heap: %2u", out_device->device_index); + PDEBUG("Host mapped heap: %2u", out_device->host_index); + return true; +} + +static void +rend_vk_device_destroy(RendVkDevice *device) +{ + RASSERT(device && device->logical_device, "Invalid or uninitialized device."); + + vkDeviceWaitIdle(device->logical_device); + vkDestroyDevice(device->logical_device, vk_allocator); + if (device->swapchain_support.format) + rfree(vk_device.swapchain_support.format); + if (device->swapchain_support.present_modes) + rfree(vk_device.swapchain_support.present_modes); + + *device = (RendVkDevice){0}; +} + +static bool +rend_vk_device_detect_depth_format(RendVkDevice *device) +{ + const uint64_t candidate_count = 3; + VkFormat candidates[] = {VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT}; + uint32_t flags; + uint32_t i; + VkFormatProperties properties; + + flags = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT; + for (i = 0; i < candidate_count; i++) { + vkGetPhysicalDeviceFormatProperties(device->physical_device, candidates[i], &properties); + if ((properties.linearTilingFeatures & flags) == flags) { + device->depth_format = candidates[i]; + return true; + } else if ((properties.optimalTilingFeatures & flags) == flags) { + device->depth_format = candidates[i]; + return true; + } + } + + return false; +} + +/* --- arena --- */ + +static RendVkArenaAllocator +rend_vk_arena_create(VkDevice logical_device, VkPhysicalDevice physical_device, VkPhysicalDeviceLimits device_limits, VkAllocationCallbacks *allocator) +{ + RendVkArenaAllocator arena; + VkPhysicalDeviceMemoryProperties mem_properties; + VkDeviceSize alignment; + uint32_t count; + uint32_t u; + + arena = (RendVkArenaAllocator){ + .allocator = allocator, + .logical_device = logical_device, + .physical_device = physical_device, + .gpu_alignment = 0, + .block_min_size = 0, + .heap_index_count = 0, + .mem_arenas = 0, + }; + + vkGetPhysicalDeviceMemoryProperties(physical_device, &mem_properties); + arena.properties = mem_properties; + + count = mem_properties.memoryTypeCount; + arena.heap_index_count = count; + + arena.mem_arenas = rmalloc(count * sizeof(*arena.mem_arenas)); + for (u = 0; u < count; ++u) { + arena.mem_arenas[u].capacity = 2; + arena.mem_arenas[u].elements = 0; + arena.mem_arenas[u].page_darr = rmalloc(2 * sizeof(*arena.mem_arenas[u].page_darr)); + memset(arena.mem_arenas[u].page_darr, 0, 2 * sizeof(*arena.mem_arenas[u].page_darr)); + } + + alignment = device_limits.bufferImageGranularity; + if (device_limits.nonCoherentAtomSize > alignment) { + alignment = device_limits.nonCoherentAtomSize; + } + + arena.gpu_alignment = alignment; + arena.block_min_size = arena.gpu_alignment * 10; + + return arena; +} + +static uint32_t +rend_vk_arena_add_page(RendVkArenaAllocator *arena, VkDeviceSize size, uint32_t heap_index, bool fit_to_alloc) +{ + VkDeviceSize new_arena_size; + VkMemoryPropertyFlags properties; + RendVkPage page; + VkMemoryAllocateFlagsInfo flags_info; + VkMemoryAllocateInfo alloc_info; + VkResult res; + RendVkPagedArena *mem_arena; + uint32_t page_index; + uint32_t new_capacity; + void *new_darr; + + new_arena_size = fit_to_alloc ? size : (size * 2); + new_arena_size = (new_arena_size < arena->block_min_size) ? arena->block_min_size : new_arena_size; + + properties = arena->properties.memoryTypes[heap_index].propertyFlags; + + page = (RendVkPage){0}; + page.head = 0; + + flags_info = (VkMemoryAllocateFlagsInfo){ + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO, + .pNext = NULL, + .flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT, + .deviceMask = 0 + }; + + alloc_info = (VkMemoryAllocateInfo){ + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .allocationSize = new_arena_size, + .pNext = &flags_info, + .memoryTypeIndex = heap_index + }; + + page.memory = (RendMemory){0}; + page.memory.offset = 0; + page.memory.size = new_arena_size; + + res = vkAllocateMemory(arena->logical_device, &alloc_info, arena->allocator, (VkDeviceMemory *)&page.memory.device_memory); + if (res != VK_SUCCESS) { + return UINT32_MAX; + } + + if (properties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { + vkMapMemory( + arena->logical_device, + (VkDeviceMemory)page.memory.device_memory, + 0, + new_arena_size, + 0, + &page.memory.host_mapped_memory + ); + } + + mem_arena = &arena->mem_arenas[heap_index]; + + if (mem_arena->elements + 1 >= mem_arena->capacity) { + new_capacity = mem_arena->capacity * 2; + new_darr = rrealloc(mem_arena->page_darr, new_capacity * sizeof(*mem_arena->page_darr)); + if (!new_darr) { + vkFreeMemory(arena->logical_device, (VkDeviceMemory)page.memory.device_memory, arena->allocator); + return UINT32_MAX; + } + mem_arena->page_darr = new_darr; + mem_arena->capacity = new_capacity; + } + + page_index = mem_arena->elements; + mem_arena->page_darr[mem_arena->elements++] = page; + + return page_index; +} + +static RendMemory +rend_vk_arena_alloc(RendVkArenaAllocator *arena, VkDeviceSize size, uint32_t heap_index) +{ + RendVkPagedArena *mem_arena; + VkMemoryPropertyFlags properties; + VkDeviceSize align; + VkDeviceSize aligned_size; + int whole_page; + uint32_t page_idx; + uint32_t u; + RendVkPage page; + int valid; + RendMemory memory; + + assert(heap_index < 32 && "Unusual heap index. Did you pass the memory type instead?"); + mem_arena = &arena->mem_arenas[heap_index]; + + properties = arena->properties.memoryTypes[heap_index].propertyFlags; + + align = arena->gpu_alignment; + aligned_size = (size + align - 1) & ~(align - 1); + + whole_page = !(properties & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + page_idx = UINT32_MAX; + for (u = 0; u < mem_arena->elements; ++u) { + page = mem_arena->page_darr[u]; + valid = (whole_page) ? (page.head == 0) : 1; + if ((page.head + aligned_size <= page.memory.size) && valid) { + page_idx = u; + break; + } + } + + if (page_idx == UINT32_MAX) { + page_idx = rend_vk_arena_add_page(arena, aligned_size, heap_index, whole_page); + if (page_idx == UINT32_MAX) { + REND__CRASH("[REND_VK] Arena page allocation failed!"); + } + } + + mem_arena->page_darr[page_idx].reserved = whole_page; + + memory = (RendMemory){ + .device_memory = mem_arena->page_darr[page_idx].memory.device_memory, + .size = aligned_size, + .offset = mem_arena->page_darr[page_idx].head, + .host_mapped_memory = 0, + .heap_index = heap_index, + .id = page_idx, + }; + + if (properties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { + memory.host_mapped_memory = mem_arena->page_darr[page_idx].memory.host_mapped_memory + memory.offset; + } + + mem_arena->page_darr[page_idx].head += aligned_size; + return memory; +} + +static void +rend_vk_arena_clear(RendVkArenaAllocator *arena, uint32_t heap_index) +{ + RendVkPagedArena mem_arena; + uint32_t u; + + assert(heap_index < 32 && "Unusual heap index. Did you pass the memory type instead?"); + mem_arena = arena->mem_arenas[heap_index]; + if (mem_arena.page_darr) { + for (u = 0; u < mem_arena.elements; ++u) { + mem_arena.page_darr[u].head = 0; + mem_arena.page_darr[u].reserved = 0; + } + } +} + +static void +rend_vk_arena_clear_all(RendVkArenaAllocator *arena) +{ + uint32_t u; + + for (u = 0; u < arena->properties.memoryTypeCount; ++u) { + rend_vk_arena_clear(arena, u); + } +} + +static void +rend_vk_arena_destroy(RendVkArenaAllocator *arena) +{ + size_t u; + uint32_t p; + RendVkPagedArena *mem_arena; + RendMemory memory; + + if (arena->mem_arenas) { + for (u = 0; u < arena->heap_index_count; ++u) { + mem_arena = &arena->mem_arenas[u]; + for (p = 0; p < mem_arena->elements; ++p) { + memory = mem_arena->page_darr[p].memory; + if (memory.offset == 0) { + if (memory.host_mapped_memory) { + vkUnmapMemory(arena->logical_device, (VkDeviceMemory)memory.device_memory); + } + vkFreeMemory(arena->logical_device, (VkDeviceMemory)memory.device_memory, arena->allocator); + } else { + PWARN("[REND_VK] Attempted to free memory with an offset!"); + } + } + if (mem_arena->page_darr) { + rfree(mem_arena->page_darr); + mem_arena->page_darr = 0; + } + } + rfree(arena->mem_arenas); + arena->mem_arenas = 0; + } + memset(arena, 0, sizeof(*arena)); +} + +/* --- image --- */ + +static RendVkImage +rend_vk_image_create(VkDevice logical_device, VkImageType img_type, uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, + VkImageUsageFlags usage, uint32_t depth, uint32_t mip_levels, uint32_t layers, + VkSampleCountFlags sample_count_flags, VkSharingMode sharing_mode) +{ + RendVkImage image = { + .handle = VK_NULL_HANDLE, + .memory = VK_NULL_HANDLE, + .logical_device = logical_device, + .img_type = img_type, + .width = width, + .height = height, + .format = format, + .tiling = tiling, + .usage = usage, + .depth = depth, + .mip_levels = mip_levels, + .layers = layers, + .sample_count_flags = sample_count_flags, + .sharing_mode = sharing_mode + }; + + VkImageCreateInfo img_create_info = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; + img_create_info.imageType = img_type; + img_create_info.extent.width = width; + img_create_info.extent.height = height; + img_create_info.extent.depth = depth; + img_create_info.mipLevels = mip_levels; + img_create_info.arrayLayers = layers; + img_create_info.format = format; + img_create_info.tiling = tiling; + img_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + img_create_info.usage = usage; + img_create_info.samples = sample_count_flags; + img_create_info.sharingMode = sharing_mode; + + if (vkCreateImage(logical_device, &img_create_info, vk_allocator, &image.handle) != VK_SUCCESS) { + return image; + } + + return image; +} + +static uint32_t +rend_vk_image_required_memory_type(RendVkImage *img) +{ + VkMemoryRequirements memory_requirements; + + assert(img->memory == NULL && "Image already bound to memory"); + memory_requirements = (VkMemoryRequirements){0}; + vkGetImageMemoryRequirements(img->logical_device, img->handle, &memory_requirements); + img->requirements = memory_requirements; + return memory_requirements.memoryTypeBits; +} + +static void +rend_vk_image_bind_memory(RendVkImage *img, RendMemory *memory) +{ + assert(img->memory == NULL && "Image already bound to memory"); + vkBindImageMemory(img->logical_device, img->handle, (VkDeviceMemory) memory->device_memory, memory->offset); +} + +static void +rend_vk_image_destroy(RendVkImage *img) +{ + if (img->view) { + vkDestroyImageView(img->logical_device, img->view, vk_allocator); + img->view = 0; + } + + if (img->handle) { + vkDestroyImage(img->logical_device, img->handle, vk_allocator); + img->handle = 0; + } +} + +static void +rend_vk_image_view_create(RendVkImage *image, VkImageViewType view_type, VkImageAspectFlags view_aspect_flags) +{ + VkImageViewCreateInfo view_create_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = image->handle, + .format = image->format, + .viewType = view_type, + .subresourceRange.aspectMask = view_aspect_flags, + .subresourceRange.baseMipLevel = 0, + .subresourceRange.levelCount = image->mip_levels, + .subresourceRange.baseArrayLayer = 0, + .subresourceRange.layerCount = image->layers, + }; + + vkCreateImageView(image->logical_device, &view_create_info, vk_allocator, &image->view); +} diff --git a/Rend/rend_vk_internal.h b/Rend/rend_vk_internal.h deleted file mode 100644 index 5a03aea..0000000 --- a/Rend/rend_vk_internal.h +++ /dev/null @@ -1,177 +0,0 @@ -#ifndef REND_VK_INTERNAL_H -#define REND_VK_INTERNAL_H - -#include -#include -#include - -#define CHECK_VK_RESULT(r) \ -{ \ - assert(r == VK_SUCCESS && __LINE__); \ -} - - -typedef struct RendVkImage RendVkImage; -typedef struct RendVkArenaAllocator RendVkArenaAllocator; -typedef struct RendVkSbta RendVkSbta; - -typedef struct RendVkPendingCmd RendVkPendingCmd; -typedef struct RendVkDeferredCmds RendVkDeferredCmds; - -typedef struct { - const char **extention_names_darray; - bool sampler_anisotropy; - bool discrete_gpu; - bool graphics; - bool present; - bool compute; - bool transfer; -} RendVkDevicespecs; - -typedef struct { - uint32_t graphics_family_index; - uint32_t present_family_index; - uint32_t compute_family_index; - uint32_t transfer_family_index; -} RendVkQueueFamily; - -typedef struct { - VkSurfaceCapabilitiesKHR capabilities; - VkSurfaceFormatKHR *format; - VkPresentModeKHR *present_modes; - uint32_t format_count; - uint32_t present_mode_count; -} RendVkSwapchainSupport; - -typedef struct { - /* logical device handle */ - VkDevice logical_device; - - /* physical device limitation */ - VkPhysicalDeviceProperties properties; - VkPhysicalDeviceMemoryProperties memory; - VkPhysicalDevice physical_device; - VkPhysicalDeviceFeatures features; - VkPhysicalDeviceDescriptorBufferPropertiesEXT desc_props; - - /* swapchain support */ - VkSurfaceKHR surface; - RendVkSwapchainSupport swapchain_support; - VkFormat depth_format; - - /* queues */ - VkQueue graphics_queue; - VkQueue present_queue; - VkQueue compute_queue; - VkQueue transfer_queue; - uint32_t graphics_family_index; - uint32_t present_family_index; - uint32_t compute_family_index; - uint32_t transfer_family_index; - - /* memory heap indices */ - uint32_t host_index; - uint32_t device_index; -} RendVkDevice; - -static VkInstance vk_instance = 0; -static VkDebugUtilsMessengerEXT vk_debug_messenger = 0; -static RendVkDevice vk_device = {0}; - -/* helper functions */ -static int32_t rend_vk_memory_find_index(VkPhysicalDevice device, uint32_t type_filter, VkMemoryPropertyFlags property_flags); - -/* rend_vk_device.c */ -static bool rend_vk_device_create(VkSurfaceKHR surface, RendSpecs specs, RendVkDevice *out_device, uint32_t (*score_devices)(RendVkDevice *, RendSpecs, const char **, uint32_t)); -static uint32_t rend_vk_device_score_default(RendVkDevice *device, RendSpecs minimum_specs, const char **required_extensions, uint32_t required_extension_count); -static void rend_vk_physical_device_query_swapchain_support(RendVkDevice *device); -static bool rend_vk_device_detect_depth_format(RendVkDevice *device); -static void rend_vk_device_destroy(RendVkDevice *device); - -/* rend_vk_arena.c */ -static RendVkArenaAllocator rend_vk_arena_create(VkDevice logical_device, VkPhysicalDevice physical_device, VkPhysicalDeviceLimits device_limits, VkAllocationCallbacks *allocator); -static uint32_t rend_vk_arena_add_page(RendVkArenaAllocator *arena, VkDeviceSize size, uint32_t heap_index, bool fit_to_alloc); -static RendMemory rend_vk_arena_alloc(RendVkArenaAllocator *arena, VkDeviceSize size, uint32_t heap_index); -static void rend_vk_arena_clear(RendVkArenaAllocator *arena, uint32_t memory_type); -static void rend_vk_arena_clear_all(RendVkArenaAllocator *arena); -static void rend_vk_arena_shrink(RendVkArenaAllocator *arena, uint32_t memory_type); -static void rend_vk_arena_destroy(RendVkArenaAllocator *arena); - -/* rend_vk_command_queue.c */ -static inline RendVkDeferredCmds rend_vk_cmdbuf_deferred_create(void); -static inline void rend_vk_cmdbuf_deferred_destroy(RendVkDeferredCmds *tracker); -static inline uint64_t rend_vk_cmdbuf_deferred_submit(RendVkDeferredCmds *tracker, VkCommandPool pool, VkCommandBuffer cmd, VkQueue queue, uint64_t wait_value, VkPipelineStageFlags2 wait_stage, VkPipelineStageFlags2 signal_stage); -static inline void rend_vk_cmdbuf_deferred_push(RendVkDeferredCmds *tracker, VkCommandPool pool, VkCommandBuffer cmd, uint64_t wait_value); -static inline void rend_vk_cmdbuf_deferred_flush(RendVkDeferredCmds *tracker); - -/* rend_vk_image.c */ -static RendVkImage rend_vk_image_create(VkDevice logical_device, VkImageType img_type, uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags memory_flags, uint32_t depth, uint32_t mip_levels, uint32_t layers, VkSampleCountFlags sample_count_flags, VkSharingMode sharing_mode); -static uint32_t rend_vk_image_required_memory_type(RendVkImage *img); -static void rend_vk_image_bind_memory(RendVkImage *image, RendMemory *memory); -static void rend_vk_image_destroy(RendVkImage *img); -static void rend_vk_image_view_create(RendVkImage *image, VkImageViewType view_type, VkImageAspectFlags view_aspect_flags); -static uint64_t rend_vk_image_address(RendVkImage *image); - - -static VkAllocationCallbacks *vk_allocator = 0; // we are only ever going to use one, either the default one or the custom one - -static VkFormat vk_format_from_rend_format[] = { - [REND_FORMAT_R8_UNORM] = VK_FORMAT_R8_UNORM, - [REND_FORMAT_R8G8_UNORM] = VK_FORMAT_R8G8_UNORM, - [REND_FORMAT_R8G8B8A8_UNORM] = VK_FORMAT_R8G8B8A8_UNORM, - [REND_FORMAT_B8G8R8A8_UNORM] = VK_FORMAT_B8G8R8A8_UNORM, - [REND_FORMAT_R8G8B8A8_SRGB] = VK_FORMAT_R8G8B8A8_SRGB, - [REND_FORMAT_B8G8R8A8_SRGB] = VK_FORMAT_B8G8R8A8_SRGB, - [REND_FORMAT_R32_SFLOAT] = VK_FORMAT_R32_SFLOAT, - [REND_FORMAT_R32G32_SFLOAT] = VK_FORMAT_R32G32_SFLOAT, - [REND_FORMAT_R32G32B32_SFLOAT] = VK_FORMAT_R32G32B32_SFLOAT, - [REND_FORMAT_R32G32B32A32_SFLOAT]= VK_FORMAT_R32G32B32A32_SFLOAT, - [REND_FORMAT_R16_SFLOAT] = VK_FORMAT_R16_SFLOAT, - [REND_FORMAT_R16G16_SFLOAT] = VK_FORMAT_R16G16_SFLOAT, - [REND_FORMAT_R16G16B16A16_SFLOAT]= VK_FORMAT_R16G16B16A16_SFLOAT, - [REND_FORMAT_R32_UINT] = VK_FORMAT_R32_UINT, - [REND_FORMAT_R32_SINT] = VK_FORMAT_R32_SINT, - [REND_FORMAT_R32G32B32A32_UINT] = VK_FORMAT_R32G32B32A32_UINT, - [REND_FORMAT_R16G16B16A16_UINT] = VK_FORMAT_R16G16B16A16_UINT, - [REND_FORMAT_R8G8B8A8_UINT] = VK_FORMAT_R8G8B8A8_UINT, - [REND_FORMAT_D32_SFLOAT] = VK_FORMAT_D32_SFLOAT, - [REND_FORMAT_D24_UNORM_S8_UINT] = VK_FORMAT_D24_UNORM_S8_UINT, - [REND_FORMAT_D32_SFLOAT_S8_UINT] = VK_FORMAT_D32_SFLOAT_S8_UINT, -}; - -static VkPrimitiveTopology vk_topology[] = { - [REND_TOPOLOGY_TRIANGLE_LIST] = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, - [REND_TOPOLOGY_TRIANGLE_STRIP] = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, - [REND_TOPOLOGY_LINE_LIST] = VK_PRIMITIVE_TOPOLOGY_LINE_LIST, - [REND_TOPOLOGY_LINE_STRIP] = VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, - [REND_TOPOLOGY_POINT_LIST] = VK_PRIMITIVE_TOPOLOGY_POINT_LIST -}; - -static VkPolygonMode vk_polymode[] = { - [REND_POLYGON_MODE_FILL] = VK_POLYGON_MODE_FILL, - [REND_POLYGON_MODE_LINE] = VK_POLYGON_MODE_LINE, - [REND_POLYGON_MODE_POINT] = VK_POLYGON_MODE_POINT, -}; - -static VkCullModeFlags vk_cullflags[] = { - [REND_CULL_MODE_NONE] = VK_CULL_MODE_NONE, - [REND_CULL_MODE_FRONT] = VK_CULL_MODE_FRONT_BIT, - [REND_CULL_MODE_BACK] = VK_CULL_MODE_BACK_BIT, - [REND_CULL_MODE_FRONT_AND_BACK] = VK_CULL_MODE_FRONT_AND_BACK, -}; - -static int32_t -rend_vk_memory_find_index(VkPhysicalDevice device, uint32_t type_filter, VkMemoryPropertyFlags property_flags) -{ - VkPhysicalDeviceMemoryProperties memory_properties; - vkGetPhysicalDeviceMemoryProperties(device, &memory_properties); - for (uint32_t i = 0; i < memory_properties.memoryTypeCount; i++) { - if (type_filter & (1 << i) && (memory_properties.memoryTypes[i].propertyFlags & property_flags) == property_flags) { - return i; - } - } - - return -1; -} - -#endif -- cgit v1.3.1