From 7d8d503cb458f8addeaa20f61696814cefc2328d Mon Sep 17 00:00:00 2001 From: omniscient <17525998+omnisci3nce@users.noreply.github.com> Date: Fri, 22 Mar 2024 08:29:58 +1100 Subject: misc comments + cleanup --- src/renderer/render.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/renderer/render.c') diff --git a/src/renderer/render.c b/src/renderer/render.c index 7884db6..8e67fa6 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -38,7 +38,7 @@ bool renderer_init(renderer* ren) { GLFWwindow* window = glfwCreateWindow(ren->config.scr_width, ren->config.scr_height, ren->config.window_name, NULL, NULL); if (window == NULL) { - printf("Failed to create GLFW window\n"); + ERROR("Failed to create GLFW window\n"); glfwTerminate(); return false; } -- cgit v1.2.3-70-g09d2 From 411992fad179c4e45d044cd3dab86dc460ce15ac Mon Sep 17 00:00:00 2001 From: omnisci3nce <17525998+omnisci3nce@users.noreply.github.com> Date: Sat, 23 Mar 2024 17:53:42 +1100 Subject: vk debugger and physical device selection omg. --- src/renderer/backends/backend_vulkan.c | 176 ++++++++++++++++++++++++++++++++- src/renderer/backends/vulkan_helpers.h | 149 +++++++++++++++++++++++++++- src/renderer/render.c | 6 +- 3 files changed, 325 insertions(+), 6 deletions(-) (limited to 'src/renderer/render.c') diff --git a/src/renderer/backends/backend_vulkan.c b/src/renderer/backends/backend_vulkan.c index c21dfc2..481792a 100644 --- a/src/renderer/backends/backend_vulkan.c +++ b/src/renderer/backends/backend_vulkan.c @@ -1,5 +1,9 @@ -#include "str.h" #define CEL_PLATFORM_LINUX +#include +#include +#include +#include +#include "str.h" #include "darray.h" #include "defines.h" @@ -17,12 +21,29 @@ #include #include -#include -#include + +typedef struct vulkan_device { + VkPhysicalDevice physical_device; + VkDevice logical_device; + vulkan_swapchain_support_info swapchain_support; + i32 graphics_queue_index; + i32 present_queue_index; + i32 compute_queue_index; + i32 transfer_queue_index; + VkPhysicalDeviceProperties properties; + VkPhysicalDeviceFeatures features; + VkPhysicalDeviceMemoryProperties memory; +} vulkan_device; typedef struct vulkan_context { VkInstance instance; VkAllocationCallbacks* allocator; + VkSurfaceKHR surface; + vulkan_device device; + +#if defined(DEBUG) + VkDebugUtilsMessengerEXT vk_debugger; +#endif } vulkan_context; static vulkan_context context; @@ -33,6 +54,85 @@ typedef struct vulkan_state { KITC_DECL_TYPED_ARRAY(VkLayerProperties) +bool select_physical_device(vulkan_context* ctx) { + u32 physical_device_count = 0; + VK_CHECK(vkEnumeratePhysicalDevices(ctx->instance, &physical_device_count, 0)); + if (physical_device_count == 0) { + FATAL("No devices that support vulkan were found"); + return false; + } + TRACE("Number of devices found %d", physical_device_count); + + VkPhysicalDevice physical_devices[physical_device_count]; + VK_CHECK(vkEnumeratePhysicalDevices(ctx->instance, &physical_device_count, physical_devices)); + + for (u32 i = 0; i < physical_device_count; i++) { + VkPhysicalDeviceProperties properties; + vkGetPhysicalDeviceProperties(physical_devices[i], &properties); + + VkPhysicalDeviceFeatures features; + vkGetPhysicalDeviceFeatures(physical_devices[i], &features); + + VkPhysicalDeviceMemoryProperties memory; + vkGetPhysicalDeviceMemoryProperties(physical_devices[i], &memory); + + vulkan_physical_device_requirements requirements = {}; + requirements.graphics = true; + requirements.present = true; + requirements.compute = true; + requirements.transfer = true; + + requirements.sampler_anistropy = true; + requirements.discrete_gpu = true; + requirements.device_ext_names[0] = str8lit(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + requirements.device_ext_name_count = 1; + + vulkan_physical_device_queue_family_info queue_info = {}; + + bool result = physical_device_meets_requirements(physical_devices[i], ctx->surface, &properties, + &features, &requirements, &queue_info, + &ctx->device.swapchain_support); + + if (result) { + INFO("GPU Driver version: %d.%d.%d", VK_VERSION_MAJOR(properties.driverVersion), + VK_VERSION_MINOR(properties.driverVersion), VK_VERSION_PATCH(properties.driverVersion)); + + INFO("Vulkan API version: %d.%d.%d", VK_VERSION_MAJOR(properties.apiVersion), + VK_VERSION_MINOR(properties.apiVersion), VK_VERSION_PATCH(properties.apiVersion)); + + // TODO: print gpu memory information - + // https://youtu.be/6Kj3O2Ov1RU?si=pXfP5NvXXcXjJsrG&t=2439 + + ctx->device.physical_device = physical_devices[i]; + ctx->device.graphics_queue_index = queue_info.graphics_family_index; + ctx->device.present_queue_index = queue_info.present_family_index; + ctx->device.compute_queue_index = queue_info.compute_family_index; + ctx->device.transfer_queue_index = queue_info.transfer_family_index; + ctx->device.properties = properties; + ctx->device.features = features; + ctx->device.memory = memory; + break; + } + } + + if (!ctx->device.physical_device) { + ERROR("No suitable physical devices were found :("); + return false; + } + + INFO("Physical device selected: %s\n", ctx->device.properties.deviceName); + return true; +} + +bool vulkan_device_create(vulkan_context* ctx) { + if (!select_physical_device(ctx)) { + return false; + } + + return true; +} +void vulkan_device_destroy(vulkan_context* ctx) {} + bool gfx_backend_init(renderer* ren) { INFO("loading Vulkan backend"); @@ -111,11 +211,58 @@ bool gfx_backend_init(renderer* ren) { return false; } + // Debugger +#if defined(DEBUG) + DEBUG("Creating Vulkan debugger") + u32 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 = vk_debug_callback; + + PFN_vkCreateDebugUtilsMessengerEXT func = + (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(context.instance, + "vkCreateDebugUtilsMessengerEXT"); + assert(func); + VK_CHECK(func(context.instance, &debug_create_info, context.allocator, &context.vk_debugger)); + DEBUG("Vulkan debugger created"); + +#endif + + // Surface creation + DEBUG("Create SurfaceKHR") + VkSurfaceKHR surface; + VK_CHECK(glfwCreateWindowSurface(context.instance, ren->window, NULL, &surface)); + context.surface = surface; + DEBUG("Vulkan surface created") + + // Device creation + if (!vulkan_device_create(&context)) { + FATAL("device creation failed"); + return false; + } + INFO("Vulkan renderer initialisation succeeded"); return true; } -void gfx_backend_shutdown(renderer* ren) {} +void gfx_backend_shutdown(renderer* ren) { + DEBUG("Destroying Vulkan debugger"); + if (context.vk_debugger) { + PFN_vkDestroyDebugUtilsMessengerEXT func = + (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr( + context.instance, "vkDestroyDebugUtilsMessengerEXT"); + func(context.instance, context.vk_debugger, context.allocator); + } + + DEBUG("Destroying Vulkan instance..."); + vkDestroyInstance(context.instance, context.allocator); +} void clear_screen(vec3 colour) {} @@ -131,4 +278,25 @@ void uniform_f32(u32 program_id, const char* uniform_name, f32 value) {} void uniform_i32(u32 program_id, const char* uniform_name, i32 value) {} void uniform_mat4f(u32 program_id, const char* uniform_name, mat4* value) {} +VKAPI_ATTR VkBool32 VKAPI_CALL vk_debug_callback( + VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT flags, + const VkDebugUtilsMessengerCallbackDataEXT callback_data, void* user_data) { + switch (severity) { + default: + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: + ERROR(callback_data.pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: + WARN(callback_data.pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: + INFO(callback_data.pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: + TRACE(callback_data.pMessage); + break; + } + return VK_FALSE; +} + #endif \ No newline at end of file diff --git a/src/renderer/backends/vulkan_helpers.h b/src/renderer/backends/vulkan_helpers.h index 058ea91..8f4d48a 100644 --- a/src/renderer/backends/vulkan_helpers.h +++ b/src/renderer/backends/vulkan_helpers.h @@ -2,9 +2,14 @@ #include #include +#include #include "darray.h" #include "defines.h" +#include "log.h" +#include "str.h" + +#define VULKAN_PHYS_DEVICE_MAX_EXTENSION_NAMES 36 DECL_TYPED_ARRAY(const char*, cstr) @@ -16,4 +21,146 @@ static void plat_get_required_extension_names(cstr_darray* extensions) { // TODO(omni): port to using internal assert functions #define VK_CHECK(vulkan_expr) \ - { assert(vulkan_expr == VK_SUCCESS); } \ No newline at end of file + { assert(vulkan_expr == VK_SUCCESS); } + +// TODO: typedef struct vk_debugger {} vk_debugger; + +typedef struct vulkan_physical_device_requirements { + bool graphics; + bool present; + bool compute; + bool transfer; + str8 device_ext_names[VULKAN_PHYS_DEVICE_MAX_EXTENSION_NAMES]; + size_t device_ext_name_count; + bool sampler_anistropy; + bool discrete_gpu; +} vulkan_physical_device_requirements; + +typedef struct vulkan_physical_device_queue_family_info { + u32 graphics_family_index; + u32 present_family_index; + u32 compute_family_index; + u32 transfer_family_index; +} vulkan_physical_device_queue_family_info; + +#define VULKAN_MAX_DEFAULT 32 + +typedef struct vulkan_swapchain_support_info { + VkSurfaceCapabilitiesKHR capabilities; + VkSurfaceFormatKHR formats[VULKAN_MAX_DEFAULT]; + u32 format_count; + VkPresentModeKHR present_modes[VULKAN_MAX_DEFAULT]; + u32 mode_count; +} vulkan_swapchain_support_info; + +VKAPI_ATTR VkBool32 VKAPI_CALL vk_debug_callback( + VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT flags, + const VkDebugUtilsMessengerCallbackDataEXT callback_data, void* user_data); + +void vulkan_device_query_swapchain_support(VkPhysicalDevice device, VkSurfaceKHR surface, + vulkan_swapchain_support_info* out_support_info) { + // TODO: add VK_CHECK to these calls! + + // Surface capabilities + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &out_support_info->capabilities); + + // Surface formats + vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &out_support_info->format_count, + 0); // Get number of formats + if (out_support_info->format_count > 0) { + vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &out_support_info->format_count, + out_support_info->formats); + } + + // Present Modes + vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &out_support_info->mode_count, + 0); // Get number of formats + if (out_support_info->mode_count > 0) { + vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &out_support_info->mode_count, + out_support_info->present_modes); + } +} + +static bool physical_device_meets_requirements( + VkPhysicalDevice device, VkSurfaceKHR surface, const VkPhysicalDeviceProperties* properties, + const VkPhysicalDeviceFeatures* features, + const vulkan_physical_device_requirements* requirements, + vulkan_physical_device_queue_family_info* out_queue_info, + vulkan_swapchain_support_info* out_swapchain_support) { + // TODO: pass in an arena + + out_queue_info->graphics_family_index = -1; + out_queue_info->present_family_index = -1; + out_queue_info->compute_family_index = -1; + out_queue_info->transfer_family_index = -1; + + if (requirements->discrete_gpu) { + if (properties->deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) { + TRACE("Device is not a physical GPU. Skipping."); + return false; + } + } + + u32 queue_family_count = 0; + vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, 0); + VkQueueFamilyProperties queue_families[queue_family_count]; + vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, queue_families); + + INFO("Graphics | Present | Compute | Transfer | Name"); + u8 min_transfer_score = 255; + for (u32 i = 0; i < queue_family_count; i++) { + u8 current_transfer_score = 0; + + // Graphics queue + if (queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { + out_queue_info->graphics_family_index = i; + current_transfer_score++; + } + + // Compute queue + if (queue_families[i].queueFlags & VK_QUEUE_COMPUTE_BIT) { + out_queue_info->compute_family_index = i; + current_transfer_score++; + } + + // Transfer queue + if (queue_families[i].queueFlags & VK_QUEUE_TRANSFER_BIT) { + // always take the lowest score transfer index + if (current_transfer_score <= min_transfer_score) { + min_transfer_score = current_transfer_score; + out_queue_info->transfer_family_index = i; + } + } + + // Present Queue + VkBool32 supports_present = VK_FALSE; + vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &supports_present); + if (supports_present) { + out_queue_info->present_family_index = i; + } + } + + INFO(" %d | %d | %d | %d | %s", + out_queue_info->graphics_family_index != -1, out_queue_info->present_family_index != -1, + out_queue_info->compute_family_index != -1, out_queue_info->transfer_family_index != -1, + properties->deviceName); + TRACE("Graphics Family queue index: %d", out_queue_info->graphics_family_index); + TRACE("Present Family queue index: %d", out_queue_info->present_family_index); + TRACE("Compute Family queue index: %d", out_queue_info->compute_family_index); + TRACE("Transfer Family queue index: %d", out_queue_info->transfer_family_index); + + if ((!requirements->graphics || + (requirements->graphics && out_queue_info->graphics_family_index != -1))) { + INFO("Physical device meets our requirements! Proceed."); + + vulkan_device_query_swapchain_support( + device, surface, out_swapchain_support + + // TODO: error handling i.e. format count = 0 or present mode = 0 + + ); + return true; + } + + return false; +} \ No newline at end of file diff --git a/src/renderer/render.c b/src/renderer/render.c index 8e67fa6..45762d4 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -28,11 +28,14 @@ bool renderer_init(renderer* ren) { // NOTE: all platforms use GLFW at the moment but thats subject to change glfwInit(); - DEBUG("init graphics api (OpenGL) backend"); +#if defined(CEL_REND_BACKEND_OPENGL) glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); +#elif defined(CEL_REND_BACKEND_VULKAN) + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); +#endif // glfw window creation GLFWwindow* window = glfwCreateWindow(ren->config.scr_width, ren->config.scr_height, @@ -46,6 +49,7 @@ bool renderer_init(renderer* ren) { glfwMakeContextCurrent(ren->window); + DEBUG("init graphics api backend"); if (!gfx_backend_init(ren)) { FATAL("Couldnt load graphics api backend"); return false; -- cgit v1.2.3-70-g09d2 From 765a6d0f36a9f16189efc449bde815a6c0938584 Mon Sep 17 00:00:00 2001 From: omnisci3nce <17525998+omnisci3nce@users.noreply.github.com> Date: Fri, 29 Mar 2024 21:11:02 +1100 Subject: triangle --- assets/shaders/triangle.frag | 4 +- assets/shaders/triangle.vert | 3 + src/core.c | 4 +- src/platform/file.c | 50 ++-- src/renderer/backends/backend_vulkan.c | 439 +++++++++++++++++++++++++++++++-- src/renderer/render.c | 4 +- xmake.lua | 1 + 7 files changed, 458 insertions(+), 47 deletions(-) (limited to 'src/renderer/render.c') diff --git a/assets/shaders/triangle.frag b/assets/shaders/triangle.frag index d26e5c4..dd6d148 100644 --- a/assets/shaders/triangle.frag +++ b/assets/shaders/triangle.frag @@ -1,8 +1,10 @@ #version 450 #extension GL_ARB_separate_shader_objects : enable +layout(location = 0) in vec3 in_position; + layout(location = 0) out vec4 out_colour; void main() { - out_colour = vec4(1.0); + out_colour = vec4(in_position.r + 0.5, in_position.g + 0.5, in_position.b + 0.5, 1.0); } \ No newline at end of file diff --git a/assets/shaders/triangle.vert b/assets/shaders/triangle.vert index 0518c62..3dcf931 100644 --- a/assets/shaders/triangle.vert +++ b/assets/shaders/triangle.vert @@ -3,6 +3,9 @@ layout(location = 0) in vec3 in_position; +layout(location = 0) out vec3 out_position; + void main() { gl_Position = vec4(in_position, 1.0); + out_position = in_position; } \ No newline at end of file diff --git a/src/core.c b/src/core.c index 024b2d7..a0dc3b6 100644 --- a/src/core.c +++ b/src/core.c @@ -7,8 +7,8 @@ #include "render_types.h" #include "threadpool.h" -#define SCR_WIDTH 1080 -#define SCR_HEIGHT 800 +#define SCR_WIDTH 2160 +#define SCR_HEIGHT 1080 core* core_bringup() { INFO("Initiate Core bringup"); diff --git a/src/platform/file.c b/src/platform/file.c index ac5014d..6030620 100644 --- a/src/platform/file.c +++ b/src/platform/file.c @@ -63,31 +63,31 @@ str8_opt str8_from_file(arena *a, str8 path) { } FileData load_spv_file(const char *path) { - FILE *f = fopen(path, "rb"); - if (f == NULL) { - perror("Error opening file"); - return (FileData){NULL, 0}; - } - - fseek(f, 0, SEEK_END); - long fsize = ftell(f); - rewind(f); - - char *data = (char *)malloc(fsize); - if (data == NULL) { - perror("Memory allocation failed"); - fclose(f); - return (FileData){NULL, 0}; - } - - size_t bytesRead = fread(data, 1, fsize, f); - if (bytesRead < fsize) { - perror("Failed to read the entire file"); - free(data); - fclose(f); - return (FileData){NULL, 0}; - } + FILE *f = fopen(path, "rb"); + if (f == NULL) { + perror("Error opening file"); + return (FileData){ NULL, 0 }; + } + + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + rewind(f); + char *data = (char *)malloc(fsize); + if (data == NULL) { + perror("Memory allocation failed"); fclose(f); - return (FileData){data, bytesRead}; + return (FileData){ NULL, 0 }; + } + + size_t bytesRead = fread(data, 1, fsize, f); + if (bytesRead < fsize) { + perror("Failed to read the entire file"); + free(data); + fclose(f); + return (FileData){ NULL, 0 }; + } + + fclose(f); + return (FileData){ data, bytesRead }; } \ No newline at end of file diff --git a/src/renderer/backends/backend_vulkan.c b/src/renderer/backends/backend_vulkan.c index c57da9d..ac64429 100644 --- a/src/renderer/backends/backend_vulkan.c +++ b/src/renderer/backends/backend_vulkan.c @@ -1,12 +1,16 @@ +#define CDEBUG +#define CEL_PLATFORM_LINUX +// ^ Temporary + +#include +#include #include #include #include -#include "colours.h" -#define CEL_PLATFORM_LINUX -#include #include #include #include +#include "colours.h" #include "str.h" #include "darray.h" @@ -21,8 +25,8 @@ #include -#define SCR_WIDTH 1080 -#define SCR_HEIGHT 800 +#define SCR_WIDTH 2160 +#define SCR_HEIGHT 1080 #if CEL_REND_BACKEND_VULKAN @@ -137,6 +141,16 @@ typedef struct vulkan_shader { vulkan_pipeline pipeline; } vulkan_shader; +typedef struct vulkan_buffer { + u64 total_size; + VkBuffer handle; + VkBufferUsageFlagBits usage; + bool is_locked; + VkDeviceMemory memory; + i32 memory_index; + u32 memory_property_flags; +} vulkan_buffer; + typedef struct vulkan_context { VkInstance instance; VkAllocationCallbacks* allocator; @@ -146,6 +160,11 @@ typedef struct vulkan_context { u32 framebuffer_height; vulkan_swapchain swapchain; vulkan_renderpass main_renderpass; + vulkan_buffer object_vertex_buffer; + vulkan_buffer object_index_buffer; + u64 geometry_vertex_offset; + u64 geometry_index_offset; + vulkan_command_buffer_darray* gfx_command_buffers; VkSemaphore* image_available_semaphores; @@ -168,25 +187,188 @@ typedef struct vulkan_context { static vulkan_context context; +static i32 find_memory_index(vulkan_context* context, u32 type_filter, u32 property_flags) { + VkPhysicalDeviceMemoryProperties memory_properties; + vkGetPhysicalDeviceMemoryProperties(context->device.physical_device, &memory_properties); + + for (u32 i = 0; i < memory_properties.memoryTypeCount; ++i) { + // Check each memory type to see if its bit is set to 1. + if (type_filter & (1 << i) && + (memory_properties.memoryTypes[i].propertyFlags & property_flags) == property_flags) { + return i; + } + } + + WARN("Unable to find suitable memory type!"); + return -1; +} + /** @brief Internal backend state */ typedef struct vulkan_state { } vulkan_state; +typedef struct vertex_pos { + vec3 pos; +} vertex_pos; + // pipeline stuff -bool vulkan_graphics_pipeline_create( - vulkan_context* context, - vulkan_renderpass* renderpass, - u32 attribute_count, - VkVertexInputAttributeDescription* attributes, - // ... https://youtu.be/OmPmftW7Kjg?si=qn_777v_ppHKzswK&t=568 -) { +bool vulkan_graphics_pipeline_create(vulkan_context* context, vulkan_renderpass* renderpass, + u32 attribute_count, + VkVertexInputAttributeDescription* attributes, + u32 descriptor_set_layout_count, + VkDescriptorSetLayout* descriptor_set_layouts, u32 stage_count, + VkPipelineShaderStageCreateInfo* stages, VkViewport viewport, + VkRect2D scissor, bool is_wireframe, + vulkan_pipeline* out_pipeline) { + VkPipelineViewportStateCreateInfo viewport_state = { + VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO + }; + viewport_state.viewportCount = 1; + viewport_state.pViewports = &viewport; + viewport_state.scissorCount = 1; + viewport_state.pScissors = &scissor; + + // Rasterizer + VkPipelineRasterizationStateCreateInfo rasterizer_create_info = { + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO + }; + rasterizer_create_info.depthClampEnable = VK_FALSE; + rasterizer_create_info.rasterizerDiscardEnable = VK_FALSE; + rasterizer_create_info.polygonMode = is_wireframe ? VK_POLYGON_MODE_LINE : VK_POLYGON_MODE_FILL; + rasterizer_create_info.lineWidth = 1.0f; + rasterizer_create_info.cullMode = VK_CULL_MODE_BACK_BIT; + rasterizer_create_info.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterizer_create_info.depthBiasEnable = VK_FALSE; + rasterizer_create_info.depthBiasConstantFactor = 0.0; + rasterizer_create_info.depthBiasClamp = 0.0; + rasterizer_create_info.depthBiasSlopeFactor = 0.0; + + // Multisampling + VkPipelineMultisampleStateCreateInfo ms_create_info = { + VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO + }; + ms_create_info.sampleShadingEnable = VK_FALSE; + ms_create_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + ms_create_info.minSampleShading = 1.0; + ms_create_info.pSampleMask = 0; + ms_create_info.alphaToCoverageEnable = VK_FALSE; + ms_create_info.alphaToOneEnable = VK_FALSE; + + // Depth and stencil testing + VkPipelineDepthStencilStateCreateInfo depth_stencil = { + VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO + }; + depth_stencil.depthTestEnable = VK_TRUE; + depth_stencil.depthWriteEnable = VK_TRUE; + depth_stencil.depthCompareOp = VK_COMPARE_OP_LESS; + depth_stencil.depthBoundsTestEnable = VK_FALSE; + depth_stencil.stencilTestEnable = VK_FALSE; + depth_stencil.pNext = 0; + + VkPipelineColorBlendAttachmentState color_blend_attachment_state; + color_blend_attachment_state.blendEnable = VK_TRUE; + color_blend_attachment_state.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + color_blend_attachment_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + color_blend_attachment_state.colorBlendOp = VK_BLEND_OP_ADD; + color_blend_attachment_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + color_blend_attachment_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + color_blend_attachment_state.alphaBlendOp = VK_BLEND_OP_ADD; + color_blend_attachment_state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | + VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + + VkPipelineColorBlendStateCreateInfo color_blend = { + VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO + }; + color_blend.logicOpEnable = VK_FALSE; + color_blend.logicOp = VK_LOGIC_OP_COPY; + color_blend.attachmentCount = 1; + color_blend.pAttachments = &color_blend_attachment_state; + + const u32 dynamic_state_count = 3; + VkDynamicState dynamic_states[3] = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR, + VK_DYNAMIC_STATE_LINE_WIDTH, + }; + + VkPipelineDynamicStateCreateInfo dynamic_state = { + VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO + }; + dynamic_state.dynamicStateCount = dynamic_state_count; + dynamic_state.pDynamicStates = dynamic_states; + + // Vertex input + VkVertexInputBindingDescription binding_desc; + binding_desc.binding = 0; + binding_desc.stride = sizeof(vertex_pos); + binding_desc.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + VkPipelineVertexInputStateCreateInfo vertex_input_info = { + VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO + }; + vertex_input_info.vertexBindingDescriptionCount = 1; + vertex_input_info.pVertexBindingDescriptions = &binding_desc; + vertex_input_info.vertexAttributeDescriptionCount = attribute_count; + vertex_input_info.pVertexAttributeDescriptions = attributes; + + VkPipelineInputAssemblyStateCreateInfo input_assembly = { + VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO + }; + input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + input_assembly.primitiveRestartEnable = VK_FALSE; + + VkPipelineLayoutCreateInfo pipeline_layout_create_info = { + VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO + }; + pipeline_layout_create_info.setLayoutCount = descriptor_set_layout_count; + pipeline_layout_create_info.pSetLayouts = descriptor_set_layouts; + + vkCreatePipelineLayout(context->device.logical_device, &pipeline_layout_create_info, + context->allocator, &out_pipeline->layout); + + VkGraphicsPipelineCreateInfo pipeline_create_info = { + VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO + }; + pipeline_create_info.stageCount = stage_count; + pipeline_create_info.pStages = stages; + pipeline_create_info.pVertexInputState = &vertex_input_info; + pipeline_create_info.pInputAssemblyState = &input_assembly; + + pipeline_create_info.pViewportState = &viewport_state; + pipeline_create_info.pRasterizationState = &rasterizer_create_info; + pipeline_create_info.pMultisampleState = &ms_create_info; + pipeline_create_info.pDepthStencilState = &depth_stencil; + pipeline_create_info.pColorBlendState = &color_blend; + pipeline_create_info.pDynamicState = &dynamic_state; + pipeline_create_info.pTessellationState = 0; + + pipeline_create_info.layout = out_pipeline->layout; + + pipeline_create_info.renderPass = renderpass->handle; + pipeline_create_info.subpass = 0; + pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE; + pipeline_create_info.basePipelineIndex = -1; + + VkResult result = + vkCreateGraphicsPipelines(context->device.logical_device, VK_NULL_HANDLE, 1, + &pipeline_create_info, context->allocator, &out_pipeline->handle); + if (result != VK_SUCCESS) { + FATAL("graphics pipeline creation failed. its fked mate"); + ERROR_EXIT("Doomed"); + } + + return true; +} + +void vulkan_pipeline_bind(vulkan_command_buffer* command_buffer, VkPipelineBindPoint bind_point, + vulkan_pipeline* pipeline) { + vkCmdBindPipeline(command_buffer->handle, bind_point, pipeline->handle); } bool create_shader_module(vulkan_context* context, const char* filename, const char* type_str, VkShaderStageFlagBits flag, u32 stage_index, vulkan_shader_stage* shader_stages) { - memset(&shader_stages[stage_index].create_info, 0, sizeof(VkShaderModuleCreateInfo)); memset(&shader_stages[stage_index].stage_create_info, 0, sizeof(VkPipelineShaderStageCreateInfo)); @@ -225,9 +407,63 @@ bool vulkan_object_shader_create(vulkan_context* context, vulkan_shader* out_sha create_shader_module(context, stage_filenames[i], stage_type_strs[i], stage_types[i], i, out_shader->stages); } + + // TODO: descriptors + + // Pipeline creation + VkViewport viewport; + viewport.x = 0; + viewport.y = (f32)context->framebuffer_height; + viewport.width = (f32)context->framebuffer_width; + viewport.height = -(f32)context->framebuffer_height; + viewport.minDepth = 0.0; + viewport.maxDepth = 1.0; + + VkRect2D scissor; + scissor.offset.x = scissor.offset.y = 0; + scissor.extent.width = context->framebuffer_width; + scissor.extent.height = context->framebuffer_height; + + // Attributes + u32 offset = 0; + const i32 attribute_count = 1; + VkVertexInputAttributeDescription attribute_descs[1]; + // Position + VkFormat formats[1] = { VK_FORMAT_R32G32B32_SFLOAT }; + + u64 sizes[1] = { sizeof(vec3) }; + + for (u32 i = 0; i < attribute_count; i++) { + attribute_descs[i].binding = 0; + attribute_descs[i].location = i; + attribute_descs[i].format = formats[i]; + attribute_descs[i].offset = offset; + offset += sizes[i]; + } + + // TODO: Descriptor set + + // Stages + VkPipelineShaderStageCreateInfo stage_create_infos[SHADER_STAGE_COUNT]; + memset(stage_create_infos, 0, sizeof(stage_create_infos)); + for (u32 i = 0; i < SHADER_STAGE_COUNT; i++) { + stage_create_infos[i].sType = out_shader->stages[i].stage_create_info.sType; + stage_create_infos[i] = out_shader->stages[i].stage_create_info; + } + + vulkan_graphics_pipeline_create(context, &context->main_renderpass, attribute_count, + attribute_descs, 0, 0, SHADER_STAGE_COUNT, stage_create_infos, + viewport, scissor, false, &out_shader->pipeline); + INFO("Graphics pipeline created!"); + + return true; } void vulkan_object_shader_destroy(vulkan_context* context, vulkan_shader* shader) {} -void vulkan_object_shader_use(vulkan_context* context, vulkan_shader* shader) {} +void vulkan_object_shader_use(vulkan_context* context, vulkan_shader* shader) { + u32 image_index = context->image_index; + vulkan_pipeline_bind(&context->gfx_command_buffers->data[image_index], + VK_PIPELINE_BIND_POINT_GRAPHICS, &shader->pipeline); +} bool select_physical_device(vulkan_context* ctx) { u32 physical_device_count = 0; @@ -482,6 +718,68 @@ void vulkan_image_create(vulkan_context* context, VkImageType image_type, u32 wi // TODO: vulkan_image_destroy +void vulkan_buffer_bind(vulkan_context* context, vulkan_buffer* buffer, u64 offset) { + vkBindBufferMemory(context->device.logical_device, buffer->handle, buffer->memory, offset); +} + +bool vulkan_buffer_create(vulkan_context* context, u64 size, VkBufferUsageFlagBits usage, + u32 memory_property_flags, bool bind_on_create, + vulkan_buffer* out_buffer) { + memset(out_buffer, 0, sizeof(vulkan_buffer)); + out_buffer->total_size = size; + out_buffer->usage = usage; + out_buffer->memory_property_flags = memory_property_flags; + + VkBufferCreateInfo buffer_info = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; + buffer_info.size = size; + buffer_info.usage = usage; + buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + vkCreateBuffer(context->device.logical_device, &buffer_info, context->allocator, + &out_buffer->handle); + + VkMemoryRequirements requirements; + vkGetBufferMemoryRequirements(context->device.logical_device, out_buffer->handle, &requirements); + out_buffer->memory_index = + find_memory_index(context, requirements.memoryTypeBits, out_buffer->memory_property_flags); + + // Allocate + VkMemoryAllocateInfo allocate_info = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + allocate_info.allocationSize = requirements.size; + allocate_info.memoryTypeIndex = (u32)out_buffer->memory_index; + + vkAllocateMemory(context->device.logical_device, &allocate_info, context->allocator, + &out_buffer->memory); + + if (bind_on_create) { + vulkan_buffer_bind(context, out_buffer, 0); + } + + return true; +} + +// lock and unlock? + +void* vulkan_buffer_lock_memory(vulkan_context* context, vulkan_buffer* buffer, u64 offset, + u64 size, u32 flags) { + void* data; + vkMapMemory(context->device.logical_device, buffer->memory, offset, size, flags, &data); + return data; +} +void* vulkan_buffer_unlock_memory(vulkan_context* context, vulkan_buffer* buffer) { + vkUnmapMemory(context->device.logical_device, buffer->memory); +} + +void vulkan_buffer_load_data(vulkan_context* context, vulkan_buffer* buffer, u64 offset, u64 size, + u32 flags, const void* data) { + void* data_ptr = 0; + VK_CHECK(vkMapMemory(context->device.logical_device, buffer->memory, offset, size, flags, &data_ptr)); + memcpy(data_ptr, data, size); + vkUnmapMemory(context->device.logical_device, buffer->memory); +} + +// TODO: destroy + void vulkan_framebuffer_create(vulkan_context* context, vulkan_renderpass* renderpass, u32 width, u32 height, u32 attachment_count, VkImageView* attachments, vulkan_framebuffer* out_framebuffer) { @@ -577,6 +875,24 @@ void vulkan_command_buffer_end_oneshot(vulkan_context* context, VkCommandPool po vulkan_command_buffer_free(context, pool, command_buffer); } +void vulkan_buffer_copy_to(vulkan_context* context, VkCommandPool pool, VkFence fence, + VkQueue queue, VkBuffer source, u64 source_offset, VkBuffer dest, + u64 dest_offset, u64 size) { + vkQueueWaitIdle(queue); + + vulkan_command_buffer temp_cmd_buf; + vulkan_command_buffer_allocate_and_begin_oneshot(context, pool, &temp_cmd_buf); + + VkBufferCopy copy_region; + copy_region.srcOffset = source_offset; + copy_region.dstOffset = dest_offset; + copy_region.size = size; + + vkCmdCopyBuffer(temp_cmd_buf.handle, source, dest, 1, ©_region); + + vulkan_command_buffer_end_oneshot(context, pool, &temp_cmd_buf, queue); +} + void vulkan_swapchain_create(vulkan_context* context, u32 width, u32 height, vulkan_swapchain* out_swapchain) { VkExtent2D swapchain_extent = { width, height }; @@ -837,6 +1153,33 @@ void vulkan_renderpass_end(vulkan_command_buffer* command_buffer, vulkan_renderp command_buffer->state = COMMAND_BUFFER_STATE_RECORDING; } +bool create_buffers(vulkan_context* context) { + VkMemoryPropertyFlagBits mem_prop_flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + + const u64 vertex_buffer_size = sizeof(vertex_pos) * 1024 * 1024; + if (!vulkan_buffer_create(context, vertex_buffer_size, + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT, + mem_prop_flags, true, &context->object_vertex_buffer)) { + ERROR("couldnt create vertex buffer"); + return false; + } + + context->geometry_vertex_offset = 0; + + const u64 index1_buffer_size = sizeof(u32) * 1024 * 1024; + if (!vulkan_buffer_create(context, index1_buffer_size, + VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT, + mem_prop_flags, true, &context->object_index_buffer)) { + ERROR("couldnt create vertex buffer"); + return false; + } + context->geometry_index_offset = 0; + + return true; +} + void create_command_buffers(renderer* ren) { if (!context.gfx_command_buffers) { context.gfx_command_buffers = vulkan_command_buffer_darray_new(context.swapchain.image_count); @@ -848,6 +1191,25 @@ void create_command_buffers(renderer* ren) { } } +void upload_data_range(vulkan_context* context, VkCommandPool pool, VkFence fence, VkQueue queue, + vulkan_buffer* buffer, u64 offset, u64 size, void* data) { + VkBufferUsageFlags flags = + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + vulkan_buffer staging; + vulkan_buffer_create(context, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, flags, true, &staging); + TRACE("HERE"); + // load data into staging buffer + printf("Size: %ld\n", size); + vulkan_buffer_load_data(context, &staging, 0, size, 0, data); + TRACE("here"); + + // copy + vulkan_buffer_copy_to(context, pool, fence, queue, staging.handle, 0, buffer->handle, offset, + size); + + vkDestroyBuffer(context->device.logical_device, staging.handle, context->allocator); +} + void regenerate_framebuffers(renderer* ren, vulkan_swapchain* swapchain, vulkan_renderpass* renderpass) { for (u32 i = 0; i < swapchain->image_count; i++) { @@ -929,7 +1291,7 @@ bool gfx_backend_init(renderer* ren) { plat_get_required_extension_names(required_extensions); -#if defined(DEBUG) +#if defined(CDEBUG) cstr_darray_push(required_extensions, VK_EXT_DEBUG_UTILS_EXTENSION_NAME); DEBUG("Required extensions:"); @@ -944,7 +1306,7 @@ bool gfx_backend_init(renderer* ren) { // Validation layers create_info.enabledLayerCount = 0; create_info.ppEnabledLayerNames = 0; -#if defined(DEBUG) +#if defined(CDEBUG) INFO("Validation layers enabled"); cstr_darray* desired_validation_layers = cstr_darray_new(1); cstr_darray_push(desired_validation_layers, "VK_LAYER_KHRONOS_validation"); @@ -984,7 +1346,7 @@ bool gfx_backend_init(renderer* ren) { } // Debugger -#if defined(DEBUG) +#if defined(CDEBUG) DEBUG("Creating Vulkan debugger") u32 log_severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; @@ -1064,6 +1426,36 @@ bool gfx_backend_init(renderer* ren) { vulkan_object_shader_create(&context, &context.object_shader); INFO("Compiled shader modules") + create_buffers(&context); + INFO("Created buffers"); + + // TODO: temporary test code + const u32 vert_count = 4; + vertex_pos verts[4] = { 0 }; + + verts[0].pos.x = 0.0; + verts[0].pos.y = -0.5; + + verts[1].pos.x = 0.5; + verts[1].pos.y = 0.5; + + verts[2].pos.x = 0.0; + verts[2].pos.y = 0.5; + + verts[3].pos.x = 0.5; + verts[3].pos.y = -0.5; + + const u32 index_count = 6; + u32 indices[6] = { 0, 1, 2, 0, 3, 1 }; + + upload_data_range(&context, context.device.gfx_command_pool, 0, context.device.graphics_queue, + &context.object_vertex_buffer, 0, sizeof(vertex_pos) * vert_count, verts); + TRACE("Uploaded vertex data"); + upload_data_range(&context, context.device.gfx_command_pool, 0, context.device.graphics_queue, + &context.object_index_buffer, 0, sizeof(u32) * index_count, indices); + TRACE("Uploaded index data"); + // --- End test code + INFO("Vulkan renderer initialisation succeeded"); return true; } @@ -1121,6 +1513,19 @@ void backend_begin_frame(renderer* ren, f32 delta_time) { vulkan_renderpass_begin(command_buffer, &context.main_renderpass, context.swapchain.framebuffers->data[context.image_index].handle); + + // TODO: temp test code + vulkan_object_shader_use(&context, &context.object_shader); + + VkDeviceSize offsets[1] = { 0 }; + vkCmdBindVertexBuffers(command_buffer->handle, 0, 1, &context.object_vertex_buffer.handle, + (VkDeviceSize*)offsets); + + vkCmdBindIndexBuffer(command_buffer->handle, context.object_index_buffer.handle, 0, + VK_INDEX_TYPE_UINT32); + + vkCmdDrawIndexed(command_buffer->handle, 6, 1, 0, 0, 0); + // --- End temp test code } void backend_end_frame(renderer* ren, f32 delta_time) { diff --git a/src/renderer/render.c b/src/renderer/render.c index 45762d4..de1a775 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -17,8 +17,8 @@ // FIXME: get rid of these and store dynamic screen realestate // in renderer -#define SCR_WIDTH 1080 -#define SCR_HEIGHT 800 +#define SCR_WIDTH 2160 +#define SCR_HEIGHT 1080 material DEFAULT_MATERIAL = { 0 }; diff --git a/xmake.lua b/xmake.lua index 5457ccb..3005713 100644 --- a/xmake.lua +++ b/xmake.lua @@ -14,6 +14,7 @@ add_cflags("-Wall", "-Wextra", "-Wundef", "-Wdouble-promotion") if is_mode("debug") then add_cflags("-g") -- Add debug symbols in debug mode + add_defines("CDEBUG") elseif is_mode("release") then add_defines("CRELEASE") end -- cgit v1.2.3-70-g09d2 From 57185c5e59e92775dd45b3e2966debb4ce5641e9 Mon Sep 17 00:00:00 2001 From: Omniscient <17525998+omnisci3nce@users.noreply.github.com> Date: Sat, 30 Mar 2024 10:52:10 +1100 Subject: get it working on other PC. hacky code in place --- src/renderer/backends/backend_vulkan.c | 80 ++++++++++++++++++++++------------ src/renderer/render.c | 4 +- 2 files changed, 53 insertions(+), 31 deletions(-) (limited to 'src/renderer/render.c') diff --git a/src/renderer/backends/backend_vulkan.c b/src/renderer/backends/backend_vulkan.c index ac64429..3e5137e 100644 --- a/src/renderer/backends/backend_vulkan.c +++ b/src/renderer/backends/backend_vulkan.c @@ -25,8 +25,8 @@ #include -#define SCR_WIDTH 2160 -#define SCR_HEIGHT 1080 +#define SCR_WIDTH 1000 +#define SCR_HEIGHT 1000 #if CEL_REND_BACKEND_VULKAN @@ -544,26 +544,42 @@ bool vulkan_device_create(vulkan_context* context) { // Logical device - NOTE: mutates the context directly // queues -#define VULKAN_QUEUES_COUNT 3 - const char* queue_names[VULKAN_QUEUES_COUNT] = { "GRAPHICS", "PRESENT", - // "COMPUTE", - "TRANSFER" }; - i32 indices[VULKAN_QUEUES_COUNT] = { context->device.graphics_queue_index, - context->device.present_queue_index, - // context->device.compute_queue_index, - context->device.transfer_queue_index }; - VkDeviceQueueCreateInfo - queue_create_info[VULKAN_QUEUES_COUNT]; // one for each of graphics,present,compute,transfer - for (int i = 0; i < VULKAN_QUEUES_COUNT; i++) { - TRACE("Configure %s queue", queue_names[i]); - queue_create_info[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queue_create_info[i].queueFamilyIndex = indices[i]; - queue_create_info[i].queueCount = 1; // make just one of them - queue_create_info[i].flags = 0; - queue_create_info[i].pNext = 0; - f32 priority = 1.0; - queue_create_info[i].pQueuePriorities = &priority; - } +#define VULKAN_QUEUES_COUNT 2 + const char* queue_names[VULKAN_QUEUES_COUNT] = { + "GRAPHICS", + "TRANSFER", + }; + i32 indices[VULKAN_QUEUES_COUNT] = { + context->device.graphics_queue_index, + context->device.transfer_queue_index, + }; + f32 prio_one = 1.0; + VkDeviceQueueCreateInfo queue_create_info[2]; // HACK: make 2 queues, graphics and transfer + // graphics + queue_create_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_create_info[0].queueFamilyIndex = 0; + queue_create_info[0].queueCount = 1; + queue_create_info[0].flags = 0; + queue_create_info[0].pNext = 0; + queue_create_info[0].pQueuePriorities = &prio_one; + // transfer + queue_create_info[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_create_info[1].queueFamilyIndex = 1; + queue_create_info[1].queueCount = 1; + queue_create_info[1].flags = 0; + queue_create_info[1].pNext = 0; + queue_create_info[1].pQueuePriorities = &prio_one; + + // for (int i = 0; i < 2; i++) { + // TRACE("Configure %s queue", queue_names[i]); + // queue_create_info[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + // queue_create_info[i].queueFamilyIndex = indices[i]; + // queue_create_info[i].queueCount = 1; // make just one of them + // queue_create_info[i].flags = 0; + // queue_create_info[i].pNext = 0; + // f32 priority = 1.0; + // queue_create_info[i].pQueuePriorities = &priority; + // } // features VkPhysicalDeviceFeatures device_features = {}; @@ -593,10 +609,10 @@ bool vulkan_device_create(vulkan_context* context) { // get queues vkGetDeviceQueue(context->device.logical_device, context->device.graphics_queue_index, 0, &context->device.graphics_queue); - vkGetDeviceQueue(context->device.logical_device, context->device.present_queue_index, 0, - &context->device.present_queue); - vkGetDeviceQueue(context->device.logical_device, context->device.compute_queue_index, 0, - &context->device.compute_queue); + // vkGetDeviceQueue(context->device.logical_device, context->device.present_queue_index, 0, + // &context->device.present_queue); + // vkGetDeviceQueue(context->device.logical_device, context->device.compute_queue_index, 0, + // &context->device.compute_queue); vkGetDeviceQueue(context->device.logical_device, context->device.transfer_queue_index, 0, &context->device.transfer_queue); @@ -773,7 +789,8 @@ void* vulkan_buffer_unlock_memory(vulkan_context* context, vulkan_buffer* buffer void vulkan_buffer_load_data(vulkan_context* context, vulkan_buffer* buffer, u64 offset, u64 size, u32 flags, const void* data) { void* data_ptr = 0; - VK_CHECK(vkMapMemory(context->device.logical_device, buffer->memory, offset, size, flags, &data_ptr)); + VK_CHECK( + vkMapMemory(context->device.logical_device, buffer->memory, offset, size, flags, &data_ptr)); memcpy(data_ptr, data, size); vkUnmapMemory(context->device.logical_device, buffer->memory); } @@ -925,6 +942,7 @@ void vulkan_swapchain_create(vulkan_context* context, u32 width, u32 height, swapchain_create_info.minImageCount = image_count; swapchain_create_info.imageFormat = out_swapchain->image_format.format; swapchain_create_info.imageColorSpace = out_swapchain->image_format.colorSpace; + DEBUG("Image extend %d %d\n", swapchain_extent.width, swapchain_extent.height); swapchain_create_info.imageExtent = swapchain_extent; swapchain_create_info.imageArrayLayers = 1; swapchain_create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; @@ -1024,7 +1042,11 @@ void vulkan_swapchain_present(vulkan_context* context, vulkan_swapchain* swapcha VkResult result = vkQueuePresentKHR(present_queue, &present_info); if (result != VK_SUCCESS) { - FATAL("Failed to present swapchain iamge"); + if (result == VK_SUBOPTIMAL_KHR) { + // WARN("Swapchain suboptimal - maybe resize needed?"); + } else { + FATAL("Failed to present swapchain iamge"); + } } // advance the current frame @@ -1562,7 +1584,7 @@ void backend_end_frame(renderer* ren, f32 delta_time) { vulkan_command_buffer_update_submitted(command_buffer); vulkan_swapchain_present( - &context, &context.swapchain, context.device.graphics_queue, context.device.present_queue, + &context, &context.swapchain, context.device.graphics_queue, context.device.graphics_queue, context.queue_complete_semaphores[context.current_frame], context.image_index); } diff --git a/src/renderer/render.c b/src/renderer/render.c index de1a775..74b98cf 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -17,8 +17,8 @@ // FIXME: get rid of these and store dynamic screen realestate // in renderer -#define SCR_WIDTH 2160 -#define SCR_HEIGHT 1080 +#define SCR_WIDTH 1000 +#define SCR_HEIGHT 1000 material DEFAULT_MATERIAL = { 0 }; -- cgit v1.2.3-70-g09d2 From f7b91c2eae24ecb7a20b638246fb849d6c63615a Mon Sep 17 00:00:00 2001 From: Omniscient <17525998+omnisci3nce@users.noreply.github.com> Date: Sat, 30 Mar 2024 21:39:31 +1100 Subject: start adding mouse input processing --- assets/shaders/object.vert | 10 ++- examples/input/ex_input.c | 113 +++++++++++++++++++++++++++++++++ examples/main_loop/ex_main_loop.c | 6 +- src/core.c | 13 ++++ src/core.h | 1 + src/maths/maths.h | 5 ++ src/renderer/backends/backend_vulkan.c | 63 +++++++++++------- src/renderer/render.c | 2 + src/renderer/render_backend.h | 6 +- src/systems/input.c | 19 ++++++ src/systems/keys.h | 15 ++++- xmake.lua | 11 +++- 12 files changed, 235 insertions(+), 29 deletions(-) create mode 100644 examples/input/ex_input.c (limited to 'src/renderer/render.c') diff --git a/assets/shaders/object.vert b/assets/shaders/object.vert index 9aaefed..a5097d4 100644 --- a/assets/shaders/object.vert +++ b/assets/shaders/object.vert @@ -9,4 +9,12 @@ layout(set = 0, binding = 0) uniform global_object_uniform { } global_ubo; -void main() { gl_Position = global_ubo.projection * global_ubo.view * vec4(in_position, 1.0); } \ No newline at end of file +layout(push_constant) uniform push_constants { + mat4 model; // 64 bytes +} +u_push_constants; + +void main() { + gl_Position = + global_ubo.projection * global_ubo.view * u_push_constants.model * vec4(in_position, 1.0); +} \ No newline at end of file diff --git a/examples/input/ex_input.c b/examples/input/ex_input.c new file mode 100644 index 0000000..d3d1fbd --- /dev/null +++ b/examples/input/ex_input.c @@ -0,0 +1,113 @@ +#include + +#include "camera.h" +#include "core.h" +#include "input.h" +#include "keys.h" +#include "maths.h" +#include "maths_types.h" +#include "render.h" +#include "render_backend.h" + +typedef struct game_state { + camera camera; + vec3 camera_euler; + bool first_mouse_update; +} game_state; + +void update_camera_rotation(input_state* input, game_state* game, camera* cam); + +int main() { + core* core = core_bringup(); + + game_state game = { + .camera = camera_create(vec3_create(0, 0, 30), vec3_create(0, 0, -1), VEC3_Y, deg_to_rad(45.0)), + .camera_euler = vec3_create(90, 0, 0), + .first_mouse_update = true, + }; + + printf("Starting look direction: "); + print_vec3(game.camera.front); + + // Main loop + const f32 camera_speed = 0.4; + + while (!should_exit(core)) { + input_update(&core->input); + + vec3 translation = VEC3_ZERO; + if (key_is_pressed(KEYCODE_W) || key_is_pressed(KEYCODE_KEY_UP)) { + printf("Move Forwards\n"); + translation = vec3_mult(game.camera.front, camera_speed); + } else if (key_is_pressed(KEYCODE_S) || key_is_pressed(KEYCODE_KEY_DOWN)) { + printf("Move Backwards\n"); + translation = vec3_mult(game.camera.front, -camera_speed); + } else if (key_is_pressed(KEYCODE_A) || key_is_pressed(KEYCODE_KEY_LEFT)) { + printf("Move Left\n"); + vec3 lateral = vec3_normalise(vec3_cross(game.camera.front, game.camera.up)); + translation = vec3_mult(lateral, -camera_speed); + } else if (key_is_pressed(KEYCODE_D) || key_is_pressed(KEYCODE_KEY_RIGHT)) { + printf("Move Right\n"); + vec3 lateral = vec3_normalise(vec3_cross(game.camera.front, game.camera.up)); + translation = vec3_mult(lateral, camera_speed); + } + game.camera.position = vec3_add(game.camera.position, translation); + update_camera_rotation(&core->input, &game, &game.camera); + + // UNUSED: threadpool_process_results(&core->threadpool, 1); + + render_frame_begin(&core->renderer); + + mat4 model = mat4_translation(VEC3_ZERO); + + gfx_backend_draw_frame(&core->renderer, &game.camera, model); + + render_frame_end(&core->renderer); + } + + core_shutdown(core); + + return 0; +} + +void update_camera_rotation(input_state* input, game_state* game, camera* cam) { + float xoffset = -input->mouse.x_delta; // xpos - lastX; + float yoffset = -input->mouse.y_delta; // reversed since y-coordinates go from bottom to top + if (game->first_mouse_update) { + xoffset = 0.0; + yoffset = 0.0; + game->first_mouse_update = false; + } + + printf("x offset: %f y offset %f\n", xoffset, yoffset); + + float sensitivity = 0.1f; // change this value to your liking + xoffset *= sensitivity; + yoffset *= sensitivity; + + // x = yaw + game->camera_euler.x += xoffset; + // y = pitch + game->camera_euler.y += yoffset; + // we dont update roll + + f32 yaw = game->camera_euler.x; + f32 pitch = game->camera_euler.y; + + // make sure that when pitch is out of bounds, screen doesn't get flipped + if (game->camera_euler.y > 89.0f) game->camera_euler.y = 89.0f; + if (game->camera_euler.y < -89.0f) game->camera_euler.y = -89.0f; + + vec3 front = cam->front; + front.x = cos(deg_to_rad(yaw) * cos(deg_to_rad(pitch))); + front.y = sin(deg_to_rad(pitch)); + front.z = sin(deg_to_rad(yaw)) * cos(deg_to_rad(pitch)); + + front = vec3_normalise(front); + // save it back + cam->front.x = front.x; + cam->front.y = front.y; + // cam->front.z = front.z; + + print_vec3(cam->front); +} diff --git a/examples/main_loop/ex_main_loop.c b/examples/main_loop/ex_main_loop.c index 9a6f98c..25dbf4a 100644 --- a/examples/main_loop/ex_main_loop.c +++ b/examples/main_loop/ex_main_loop.c @@ -15,7 +15,11 @@ int main() { render_frame_begin(&core->renderer); - gfx_backend_draw_frame(&core->renderer); + static f32 x = 0.0; + x += 0.01; + mat4 model = mat4_translation(vec3(x, 0, 0)); + + gfx_backend_draw_frame(&core->renderer, model); // insert work here diff --git a/src/core.c b/src/core.c index 4b3bd1b..0db8962 100644 --- a/src/core.c +++ b/src/core.c @@ -2,6 +2,9 @@ #include +#include "glfw3.h" +#include "input.h" +#include "keys.h" #include "log.h" #include "render.h" #include "render_types.h" @@ -49,3 +52,13 @@ core* core_bringup() { return c; } + +void core_shutdown(core* core) { + // threadpool_destroy(&core->threadpool); + input_system_shutdown(&core->input); + renderer_shutdown(&core->renderer); +} + +bool should_exit(core* core) { + return key_just_released(KEYCODE_ESCAPE) || glfwWindowShouldClose(core->renderer.window); +} \ No newline at end of file diff --git a/src/core.h b/src/core.h index f54631e..05e3aed 100644 --- a/src/core.h +++ b/src/core.h @@ -20,5 +20,6 @@ typedef struct core { // --- Lifecycle core* core_bringup(); void core_shutdown(core* core); +bool should_exit(core* core); void core_input_update(core* core); diff --git a/src/maths/maths.h b/src/maths/maths.h index 6816415..baa75e9 100644 --- a/src/maths/maths.h +++ b/src/maths/maths.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include "maths_types.h" // --- Helpers @@ -48,6 +49,10 @@ static inline vec3 vec3_cross(vec3 a, vec3 b) { #define VEC3_Z ((vec3){ .x = 0.0, .y = 0.0, .z = 1.0 }) #define VEC3_NEG_Z ((vec3){ .x = 0.0, .y = 0.0, .z = -1.0 }) +static inline void print_vec3(vec3 v) { + printf("{ x: %f, y: %f, z: %f )\n", v.x, v.y, v.z); +} + // TODO: Dimension 2 static inline vec2 vec2_create(f32 x, f32 y) { return (vec2){ x, y }; } diff --git a/src/renderer/backends/backend_vulkan.c b/src/renderer/backends/backend_vulkan.c index 7be5918..43ea658 100644 --- a/src/renderer/backends/backend_vulkan.c +++ b/src/renderer/backends/backend_vulkan.c @@ -1,3 +1,4 @@ +#include "camera.h" #define CDEBUG #define CEL_PLATFORM_LINUX // ^ Temporary @@ -337,6 +338,16 @@ bool vulkan_graphics_pipeline_create(vulkan_context* context, vulkan_renderpass* VkPipelineLayoutCreateInfo pipeline_layout_create_info = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; + + // Pushconstants + VkPushConstantRange push_constant; + push_constant.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + push_constant.offset = sizeof(mat4) * 0; + push_constant.size = sizeof(mat4) * 2; + + pipeline_layout_create_info.pushConstantRangeCount = 1; + pipeline_layout_create_info.pPushConstantRanges = &push_constant; + pipeline_layout_create_info.setLayoutCount = descriptor_set_layout_count; pipeline_layout_create_info.pSetLayouts = descriptor_set_layouts; @@ -600,9 +611,6 @@ void vulkan_object_shader_update_global_state(vulkan_context* context, vulkan_sh VkCommandBuffer cmd_buffer = context->gfx_command_buffers->data[image_index].handle; VkDescriptorSet global_descriptors = shader->descriptor_sets[image_index]; - vkCmdBindDescriptorSets(cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, shader->pipeline.layout, 0, - 1, &global_descriptors, 0, 0); - u32 range = sizeof(global_object_uniform); u64 offset = 0; @@ -624,6 +632,28 @@ void vulkan_object_shader_update_global_state(vulkan_context* context, vulkan_sh descriptor_write.pBufferInfo = &buffer_info; vkUpdateDescriptorSets(context->device.logical_device, 1, &descriptor_write, 0, 0); + + vkCmdBindDescriptorSets(cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, shader->pipeline.layout, 0, + 1, &global_descriptors, 0, 0); +} + +void vulkan_object_shader_update_object(vulkan_context* context, vulkan_shader* shader, + mat4 model) { + u32 image_index = context->image_index; + VkCommandBuffer cmd_buffer = context->gfx_command_buffers->data[image_index].handle; + // vulkan_command_buffer* cmd_buffer = &context->gfx_command_buffers->data[context.image_index]; + + vkCmdPushConstants(cmd_buffer, shader->pipeline.layout, VK_SHADER_STAGE_VERTEX_BIT, 0, + sizeof(mat4), &model); + + // vulkan_object_shader_use(context, &context->object_shader); + VkDeviceSize offsets[1] = { 0 }; + vkCmdBindVertexBuffers(cmd_buffer, 0, 1, &context->object_vertex_buffer.handle, + (VkDeviceSize*)offsets); + + vkCmdBindIndexBuffer(cmd_buffer, context->object_index_buffer.handle, 0, VK_INDEX_TYPE_UINT32); + + vkCmdDrawIndexed(cmd_buffer, 6, 1, 0, 0, 0); } bool select_physical_device(vulkan_context* ctx) { @@ -1555,13 +1585,13 @@ bool gfx_backend_init(renderer* ren) { const f32 s = 10.0; - verts[0].pos.x = 0.0 * s; + verts[0].pos.x = -0.5 * s; verts[0].pos.y = -0.5 * s; verts[1].pos.x = 0.5 * s; verts[1].pos.y = 0.5 * s; - verts[2].pos.x = 0.0 * s; + verts[2].pos.x = -0.5 * s; verts[2].pos.y = 0.5 * s; verts[3].pos.x = 0.5 * s; @@ -1688,39 +1718,28 @@ void backend_end_frame(renderer* ren, f32 delta_time) { context.queue_complete_semaphores[context.current_frame], context.image_index); } -void gfx_backend_draw_frame(renderer* ren) { +void gfx_backend_draw_frame(renderer* ren, camera* cam, mat4 model) { backend_begin_frame(ren, 16.0); - static f32 z = -1.0; - z -= 0.2; - mat4 proj = mat4_perspective(deg_to_rad(45.0), 1000 / 1000.0, 0.1, 1000.0); - mat4 view = mat4_translation(vec3(0, 0, z)); + mat4 proj; + mat4 view; + + camera_view_projection(cam, SCR_HEIGHT, SCR_WIDTH, &view, &proj); gfx_backend_update_global_state(proj, view, VEC3_ZERO, vec4(1.0, 1.0, 1.0, 1.0), 0); + vulkan_object_shader_update_object(&context, &context.object_shader, model); backend_end_frame(ren, 16.0); } void gfx_backend_update_global_state(mat4 projection, mat4 view, vec3 view_pos, vec4 ambient_colour, i32 mode) { - vulkan_command_buffer* cmd_buffer = &context.gfx_command_buffers->data[context.image_index]; vulkan_object_shader_use(&context, &context.object_shader); vulkan_object_shader_update_global_state(&context, &context.object_shader); context.object_shader.global_ubo.projection = projection; context.object_shader.global_ubo.view = view; // TODO: other UBO properties - - // vulkan_object_shader_use(&context, &context.object_shader); - - VkDeviceSize offsets[1] = { 0 }; - vkCmdBindVertexBuffers(cmd_buffer->handle, 0, 1, &context.object_vertex_buffer.handle, - (VkDeviceSize*)offsets); - - vkCmdBindIndexBuffer(cmd_buffer->handle, context.object_index_buffer.handle, 0, - VK_INDEX_TYPE_UINT32); - - vkCmdDrawIndexed(cmd_buffer->handle, 6, 1, 0, 0, 0); } void clear_screen(vec3 colour) {} diff --git a/src/renderer/render.c b/src/renderer/render.c index 74b98cf..54b63b6 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -61,6 +61,8 @@ bool renderer_init(renderer* ren) { return true; } +void renderer_shutdown(renderer* ren) {} + void render_frame_begin(renderer* ren) { vec3 color = ren->config.clear_colour; clear_screen(color); diff --git a/src/renderer/render_backend.h b/src/renderer/render_backend.h index 69d14d8..ea84b27 100644 --- a/src/renderer/render_backend.h +++ b/src/renderer/render_backend.h @@ -3,6 +3,7 @@ */ #pragma once +#include "camera.h" #include "maths_types.h" #include "render_types.h" @@ -12,8 +13,9 @@ bool gfx_backend_init(renderer* ren); void gfx_backend_shutdown(renderer* ren); -void gfx_backend_draw_frame(renderer* ren); -void gfx_backend_update_global_state(mat4 projection, mat4 view, vec3 view_pos, vec4 ambient_colour, i32 mode); +void gfx_backend_draw_frame(renderer* ren, camera* camera, mat4 model); +void gfx_backend_update_global_state(mat4 projection, mat4 view, vec3 view_pos, vec4 ambient_colour, + i32 mode); void clear_screen(vec3 colour); diff --git a/src/systems/input.c b/src/systems/input.c index 292d438..fc62db8 100644 --- a/src/systems/input.c +++ b/src/systems/input.c @@ -1,11 +1,17 @@ #include "input.h" +#include #include +#include #include "log.h" +static input_state *g_input; // Use a global to simplify caller code + bool input_system_init(input_state *input, GLFWwindow *window) { INFO("Input init"); + memset(input, 0, sizeof(input_state)); + input->window = window; // Set everything to false. Could just set memory to zero but where's the fun in that for (int i = 0; i < KEYCODE_MAX; i++) { @@ -14,9 +20,16 @@ bool input_system_init(input_state *input, GLFWwindow *window) { input->just_released_keys[i] = false; } + g_input = input; + + assert(input->mouse.x_delta == 0); + assert(input->mouse.y_delta == 0); + return true; } +void input_system_shutdown(input_state *input) {} + void input_update(input_state *input) { // --- update keyboard input @@ -75,3 +88,9 @@ void input_update(input_state *input) { input->mouse = new_mouse_state; } + +bool key_is_pressed(keycode key) { return g_input->depressed_keys[key]; } + +bool key_just_pressed(keycode key) { return g_input->just_pressed_keys[key]; } + +bool key_just_released(keycode key) { return g_input->just_released_keys[key]; } \ No newline at end of file diff --git a/src/systems/keys.h b/src/systems/keys.h index 090bb49..a76e101 100644 --- a/src/systems/keys.h +++ b/src/systems/keys.h @@ -2,5 +2,18 @@ typedef enum keycode { // TODO: add all keycodes - KEYCODE_MAX + KEYCODE_SPACE = 32, + KEYCODE_A = 65, + KEYCODE_D = 68, + KEYCODE_S = 83, + KEYCODE_W = 87, + KEYCODE_ESCAPE = 256, + KEYCODE_ENTER = 257, + KEYCODE_TAB = 258, + KEYCODE_BACKSPACE = 259, + KEYCODE_KEY_RIGHT = 262, + KEYCODE_KEY_LEFT = 263, + KEYCODE_KEY_DOWN = 264, + KEYCODE_KEY_UP = 265, + KEYCODE_MAX = 348 } keycode; \ No newline at end of file diff --git a/xmake.lua b/xmake.lua index cb6d98a..29c1de0 100644 --- a/xmake.lua +++ b/xmake.lua @@ -71,7 +71,7 @@ rule("compile_glsl_vert_shaders") print("Compiling shader: %s to %s", sourcefile, targetfile) batchcmds:vrunv('glslc', {sourcefile, "-o", targetfile}) - batchcmds:add_depfiles(sourcefile) + -- batchcmds:add_depfiles(sourcefile) end) rule("compile_glsl_frag_shaders") set_extensions(".frag") @@ -80,7 +80,7 @@ rule("compile_glsl_frag_shaders") print("Compiling shader: %s to %s", sourcefile, targetfile) batchcmds:vrunv('glslc', {sourcefile, "-o", targetfile}) - batchcmds:add_depfiles(sourcefile) + -- batchcmds:add_depfiles(sourcefile) end) -- TODO: Metal shaders compilation @@ -144,6 +144,13 @@ target("obj") add_files("examples/obj_loading/ex_obj_loading.c") set_rundir("$(projectdir)") +target("input") + set_kind("binary") + set_group("examples") + add_deps("core_static") + add_files("examples/input/ex_input.c") + set_rundir("$(projectdir)") + target("demo") set_kind("binary") set_group("examples") -- cgit v1.2.3-70-g09d2 From b771fc06a4339f466c0864ad896d5a96aee7e35d Mon Sep 17 00:00:00 2001 From: Omniscient <17525998+omnisci3nce@users.noreply.github.com> Date: Sun, 31 Mar 2024 22:08:05 +1100 Subject: adding image creation for textures --- src/maths/primitives.h | 24 +++--- src/renderer/backends/backend_opengl.c | 23 ++++++ src/renderer/backends/backend_vulkan.c | 134 +++++++++++++++++++++++++++++---- src/renderer/render.c | 23 ------ src/renderer/render.h | 1 - src/renderer/render_backend.h | 1 + src/renderer/render_types.h | 3 +- 7 files changed, 159 insertions(+), 50 deletions(-) (limited to 'src/renderer/render.c') diff --git a/src/maths/primitives.h b/src/maths/primitives.h index 77dbbae..4fb6b4d 100644 --- a/src/maths/primitives.h +++ b/src/maths/primitives.h @@ -22,36 +22,36 @@ static mesh prim_cube_mesh_create() { // back faces vertex_darray_push( cube.vertices, - (vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_Z, .uv = (vec2){ 0 } }); + (vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_Z, .uv = (vec2){ 0, 1 } }); vertex_darray_push( cube.vertices, - (vertex){ .position = BACK_TOP_LEFT, .normal = VEC3_NEG_Z, .uv = (vec2){ 0 } }); + (vertex){ .position = BACK_TOP_LEFT, .normal = VEC3_NEG_Z, .uv = (vec2){ 0, 0 } }); vertex_darray_push( cube.vertices, - (vertex){ .position = BACK_TOP_LEFT, .normal = VEC3_NEG_Z, .uv = (vec2){ 0 } }); + (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_NEG_Z, .uv = (vec2){ 1, 0 } }); vertex_darray_push( cube.vertices, - (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_NEG_Z, .uv = (vec2){ 0 } }); + (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_NEG_Z, .uv = (vec2){ 1, 0 } }); vertex_darray_push( cube.vertices, - (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_NEG_Z, .uv = (vec2){ 0 } }); + (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_NEG_Z, .uv = (vec2){ 1,1 } }); vertex_darray_push( cube.vertices, - (vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_Z, .uv = (vec2){ 0 } }); + (vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_Z, .uv = (vec2){ 0, 1 } }); // front faces vertex_darray_push(cube.vertices, - (vertex){ .position = FRONT_BOT_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0 } }); + (vertex){ .position = FRONT_BOT_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0, 1} }); vertex_darray_push(cube.vertices, - (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 0 } }); + (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 1, 0 } }); vertex_darray_push(cube.vertices, - (vertex){ .position = FRONT_TOP_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0 } }); + (vertex){ .position = FRONT_TOP_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0, 0} }); vertex_darray_push(cube.vertices, - (vertex){ .position = FRONT_BOT_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0 } }); + (vertex){ .position = FRONT_BOT_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0, 1 } }); vertex_darray_push(cube.vertices, - (vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 0 } }); + (vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 1, 1 } }); vertex_darray_push(cube.vertices, - (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 0 } }); + (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 1, 0 } }); // top faces vertex_darray_push(cube.vertices, diff --git a/src/renderer/backends/backend_opengl.c b/src/renderer/backends/backend_opengl.c index ea6cb00..a9f7482 100644 --- a/src/renderer/backends/backend_opengl.c +++ b/src/renderer/backends/backend_opengl.c @@ -60,6 +60,29 @@ void clear_screen(vec3 colour) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } +void texture_data_upload(texture* tex) { + printf("Texture name %s\n", tex->name); + TRACE("Upload texture data"); + u32 texture_id; + glGenTextures(1, &texture_id); + glBindTexture(GL_TEXTURE_2D, texture_id); + tex->texture_id = texture_id; + + // set the texture wrapping parameters + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, + GL_REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + // set texture filtering parameters + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tex->width, tex->height, 0, tex->channel_type, + GL_UNSIGNED_BYTE, tex->image_data); + glGenerateMipmap(GL_TEXTURE_2D); + DEBUG("Freeing texture image data after uploading to GPU"); + // stbi_image_free(tex->image_data); // data is on gpu now so we dont need it around +} + void bind_texture(shader s, texture *tex, u32 slot) { // printf("bind texture slot %d with texture id %d \n", slot, tex->texture_id); glActiveTexture(GL_TEXTURE0 + slot); diff --git a/src/renderer/backends/backend_vulkan.c b/src/renderer/backends/backend_vulkan.c index d149e15..9d0ef51 100644 --- a/src/renderer/backends/backend_vulkan.c +++ b/src/renderer/backends/backend_vulkan.c @@ -6,7 +6,6 @@ // ^ Temporary #include -#include #include #include #include @@ -64,6 +63,11 @@ typedef struct vulkan_image { u32 height; } vulkan_image; +typedef struct vulkan_texture_data { + vulkan_image image; + VkSampler sampler; +} vulkan_texture_data; + typedef enum vulkan_renderpass_state { READY, RECORDING, @@ -863,6 +867,67 @@ void vulkan_image_view_create(vulkan_context* context, VkFormat format, vulkan_i &image->view); } +void vulkan_image_transition_layout( + vulkan_context* context, + vulkan_command_buffer* command_buffer, + vulkan_image* image, + VkFormat format, + VkImageLayout old_layout, + VkImageLayout new_layout +) { + VkImageMemoryBarrier barrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER}; + barrier.oldLayout = old_layout; + barrier.newLayout = new_layout; + barrier.srcQueueFamilyIndex = context->device.graphics_queue_index; + barrier.dstQueueFamilyIndex = context->device.graphics_queue_index; + barrier.image = image->handle; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + barrier.srcAccessMask = 0; + barrier.dstAccessMask = 0; + + VkPipelineStageFlags source_stage; + VkPipelineStageFlags dest_stage; + + if (old_layout == VK_IMAGE_LAYOUT_UNDEFINED && new_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + source_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + dest_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + } else if (old_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && new_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + source_stage = VK_PIPELINE_STAGE_TRANSFER_BIT; + dest_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + } else { + FATAL("Unsupported image layout transition"); + return; + } + + vkCmdPipelineBarrier(command_buffer->handle, source_stage, dest_stage, 0, 0, 0, 0, 0, 1, &barrier); +} + +void vulkan_image_copy_from_buffer( + vulkan_image* image, + VkBuffer buffer, + vulkan_command_buffer* command_buffer +) { + VkBufferImageCopy region; + region.bufferOffset = 0; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.mipLevel = 0; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = 1; + region.imageExtent.width = image->width; + region.imageExtent.height = image->height; + region.imageExtent.depth = 1; + + vkCmdCopyBufferToImage(command_buffer->handle, buffer, image->handle, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); +} + void vulkan_image_create(vulkan_context* context, VkImageType image_type, u32 width, u32 height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags memory_flags, bool create_view, @@ -1584,8 +1649,6 @@ bool gfx_backend_init(renderer* ren) { mesh cube = prim_cube_mesh_create(); - // const u32 vert_count = 36; - vertex_pos* verts = malloc(sizeof(vertex_pos) * cube.vertices->len); f32 scale = 3.0; @@ -1620,6 +1683,8 @@ bool gfx_backend_init(renderer* ren) { upload_data_range(&context, context.device.gfx_command_pool, 0, context.device.graphics_queue, &context.object_index_buffer, 0, sizeof(u32) * cube.indices_len, cube.indices); TRACE("Uploaded index data"); + vertex_darray_free(cube.vertices); + free(cube.indices); // --- End test code INFO("Vulkan renderer initialisation succeeded"); @@ -1679,21 +1744,64 @@ void backend_begin_frame(renderer* ren, f32 delta_time) { vulkan_renderpass_begin(command_buffer, &context.main_renderpass, context.swapchain.framebuffers->data[context.image_index].handle); +} - // TODO: temp test code - // vulkan_object_shader_use(&context, &context.object_shader); - - // VkDeviceSize offsets[1] = { 0 }; - // vkCmdBindVertexBuffers(command_buffer->handle, 0, 1, &context.object_vertex_buffer.handle, - // (VkDeviceSize*)offsets); +void texture_data_upload(texture* tex) { + printf("Texture name %s\n", tex->name); + tex->backend_data = malloc(sizeof(vulkan_texture_data)); + vulkan_texture_data* data = (vulkan_texture_data*)tex->backend_data; + VkDeviceSize image_size = tex->width * tex->height * tex->channel_count; - // vkCmdBindIndexBuffer(command_buffer->handle, context.object_index_buffer.handle, 0, - // VK_INDEX_TYPE_UINT32); + VkFormat image_format = VK_FORMAT_R8G8B8A8_SNORM; - // vkCmdDrawIndexed(command_buffer->handle, 6, 1, 0, 0, 0); - // --- End temp test code + VkBufferUsageFlags usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + VkMemoryPropertyFlags memory_prop_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + vulkan_buffer staging; + vulkan_buffer_create(&context, image_size, usage, memory_prop_flags, true, &staging); + vulkan_buffer_load_data(&context, &staging, 0, image_size, 0, tex->image_data); + + vulkan_image_create(&context, VK_IMAGE_TYPE_2D, tex->width, tex->height, image_format, VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT + , VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, true, VK_IMAGE_ASPECT_COLOR_BIT, &data->image); + + vulkan_command_buffer temp_buffer; + vulkan_command_buffer_allocate_and_begin_oneshot(&context, context.device.gfx_command_pool, &temp_buffer); + + vulkan_image_transition_layout(&context, &temp_buffer, &data->image, image_format, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + vulkan_image_copy_from_buffer(&data->image, staging.handle, &temp_buffer); + + vulkan_image_transition_layout(&context, &temp_buffer, &data->image, image_format, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + vulkan_command_buffer_end_oneshot(&context, context.device.gfx_command_pool, &temp_buffer, context.device.graphics_queue); + + VkSamplerCreateInfo sampler_info = {VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO}; + sampler_info.magFilter = VK_FILTER_LINEAR; + sampler_info.minFilter = VK_FILTER_LINEAR; + sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler_info.anisotropyEnable = VK_TRUE; + sampler_info.maxAnisotropy = 16; + sampler_info.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; + sampler_info.unnormalizedCoordinates = VK_FALSE; + sampler_info.compareEnable = VK_FALSE; + sampler_info.compareOp = VK_COMPARE_OP_ALWAYS; + sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + sampler_info.mipLodBias = 0.0; + sampler_info.minLod = 0.0; + sampler_info.maxLod = 0.0; + + VkResult res = vkCreateSampler(context.device.logical_device, &sampler_info, context.allocator, &data->sampler); + if (res != VK_SUCCESS) { + ERROR("Error creating texture sampler for image %s", tex->name); + return; + } + } +// TODO: destroy texture + void backend_end_frame(renderer* ren, f32 delta_time) { vulkan_command_buffer* command_buffer = &context.gfx_command_buffers->data[context.image_index]; diff --git a/src/renderer/render.c b/src/renderer/render.c index 54b63b6..9dc47c2 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -247,29 +247,6 @@ texture texture_data_load(const char* path, bool invert_y) { .image_data = data }; } -void texture_data_upload(texture* tex) { - printf("Texture name %s\n", tex->name); - TRACE("Upload texture data"); - u32 texture_id; - glGenTextures(1, &texture_id); - glBindTexture(GL_TEXTURE_2D, texture_id); - tex->texture_id = texture_id; - - // set the texture wrapping parameters - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, - GL_REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method) - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - // set texture filtering parameters - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tex->width, tex->height, 0, tex->channel_type, - GL_UNSIGNED_BYTE, tex->image_data); - glGenerateMipmap(GL_TEXTURE_2D); - DEBUG("Freeing texture image data after uploading to GPU"); - // stbi_image_free(tex->image_data); // data is on gpu now so we dont need it around -} - void dir_light_upload_uniforms(shader shader, directional_light* light) { uniform_vec3f(shader.program_id, "dirLight.direction", &light->direction); uniform_vec3f(shader.program_id, "dirLight.ambient", &light->ambient); diff --git a/src/renderer/render.h b/src/renderer/render.h index 6cd9701..b6dad00 100644 --- a/src/renderer/render.h +++ b/src/renderer/render.h @@ -37,4 +37,3 @@ void draw_mesh(renderer* ren, mesh* mesh, transform tf, material* mat, mat4* vie // --- texture texture_data_load(const char* path, bool invert_y); // #frontend -void texture_data_upload(texture* tex); // #backend diff --git a/src/renderer/render_backend.h b/src/renderer/render_backend.h index ea84b27..af62f9f 100644 --- a/src/renderer/render_backend.h +++ b/src/renderer/render_backend.h @@ -19,6 +19,7 @@ void gfx_backend_update_global_state(mat4 projection, mat4 view, vec3 view_pos, void clear_screen(vec3 colour); +void texture_data_upload(texture* tex); void bind_texture(shader s, texture* tex, u32 slot); void bind_mesh_vertex_buffer(void* backend, mesh* mesh); void draw_primitives(cel_primitive_topology primitive, u32 start_index, u32 count); diff --git a/src/renderer/render_types.h b/src/renderer/render_types.h index 2cc321c..80e1372 100644 --- a/src/renderer/render_types.h +++ b/src/renderer/render_types.h @@ -52,7 +52,8 @@ typedef struct renderer { typedef struct texture { u32 texture_id; char name[MAX_TEXTURE_NAME_LEN]; - void *image_data; + void* image_data; + void* backend_data; u32 width; u32 height; u8 channel_count; -- cgit v1.2.3-70-g09d2 From fb3e055ff500e530e9ec46317441688bf4d07e13 Mon Sep 17 00:00:00 2001 From: Omniscient <17525998+omnisci3nce@users.noreply.github.com> Date: Mon, 1 Apr 2024 01:57:11 +1100 Subject: image / textures ! --- assets/shaders/object.frag | 14 ++- assets/shaders/object.vert | 11 +- examples/input/ex_input.c | 6 +- src/colours.h | 48 +++++++++ src/maths/primitives.h | 24 ++--- src/renderer/backends/backend_opengl.c | 2 +- src/renderer/backends/backend_vulkan.c | 191 +++++++++++++++++++++++---------- src/renderer/render.c | 2 +- src/renderer/render_backend.h | 2 +- 9 files changed, 219 insertions(+), 81 deletions(-) (limited to 'src/renderer/render.c') diff --git a/assets/shaders/object.frag b/assets/shaders/object.frag index 11b33e1..8466c1d 100644 --- a/assets/shaders/object.frag +++ b/assets/shaders/object.frag @@ -1,9 +1,15 @@ #version 450 #extension GL_ARB_separate_shader_objects : enable -layout(location = 0) in vec3 in_normal; -layout(location = 1) in vec3 in_position; +layout(location = 0) in vec3 in_position; +layout(location = 1) in vec3 in_normal; +layout(location = 2) in vec2 in_tex_coord; -layout(location = 0) out vec4 out_colour; +layout(binding = 1) uniform sampler2D texSampler; -void main() { out_colour = vec4(in_normal, 1.0); } \ No newline at end of file +layout(location = 0) out vec4 out_color; + +void main() { + // out_color = vec4(1.0); + out_color = texture(texSampler, in_tex_coord); +} \ No newline at end of file diff --git a/assets/shaders/object.vert b/assets/shaders/object.vert index a7237bb..23c0ffb 100644 --- a/assets/shaders/object.vert +++ b/assets/shaders/object.vert @@ -3,9 +3,11 @@ layout(location = 0) in vec3 in_position; layout(location = 1) in vec3 in_normal; +layout(location = 2) in vec2 in_tex_coord; -layout(location = 0) out vec3 out_normal; -layout(location = 1) out vec3 out_position; +layout(location = 0) out vec3 out_position; +layout(location = 1) out vec3 out_normal; +layout(location = 2) out vec2 out_tex_coord; layout(set = 0, binding = 0) uniform global_object_uniform { mat4 projection; @@ -19,8 +21,9 @@ layout(push_constant) uniform push_constants { u_push_constants; void main() { - out_position = in_position; gl_Position = global_ubo.projection * global_ubo.view * u_push_constants.model * vec4(in_position.x, in_position.y, in_position.z, 1.0); - out_normal = in_normal; // forward normal vectors + out_position = in_position; + out_normal = in_normal; + out_tex_coord = in_tex_coord; } \ No newline at end of file diff --git a/examples/input/ex_input.c b/examples/input/ex_input.c index 3101968..576b364 100644 --- a/examples/input/ex_input.c +++ b/examples/input/ex_input.c @@ -30,6 +30,10 @@ int main() { .first_mouse_update = true, }; + // load a texture + texture tex = texture_data_load("assets/models/obj/cube/container.jpg", false); + texture_data_upload(&tex); + printf("Starting look direction: "); print_vec3(game.camera.front); @@ -65,7 +69,7 @@ int main() { mat4 model = mat4_translation(VEC3_ZERO); - gfx_backend_draw_frame(&core->renderer, &game.camera, model); + gfx_backend_draw_frame(&core->renderer, &game.camera, model, &tex); render_frame_end(&core->renderer); } diff --git a/src/colours.h b/src/colours.h index fa5f54b..a981c6c 100644 --- a/src/colours.h +++ b/src/colours.h @@ -38,3 +38,51 @@ typedef struct rgba { #define CYAN_800 ((rgba){ 0.082, 0.369, 0.459, 1.0 }) #define CYAN_900 ((rgba){ 0.086, 0.306, 0.388, 1.0 }) #define CYAN_950 ((rgba){ 0.033, 0.200, 0.263, 1.0 }) + +#define GRAY_50 ((rgba){ 0.976, 0.980, 0.984, 1.0 }) +#define GRAY_100 ((rgba){ 0.953, 0.957, 0.965, 1.0 }) +#define GRAY_200 ((rgba){ 0.898, 0.906, 0.922, 1.0 }) +#define GRAY_300 ((rgba){ 0.820, 0.835, 0.859, 1.0 }) +#define GRAY_400 ((rgba){ 0.612, 0.639, 0.686, 1.0 }) +#define GRAY_500 ((rgba){ 0.420, 0.447, 0.502, 1.0 }) +#define GRAY_600 ((rgba){ 0.294, 0.333, 0.388, 1.0 }) +#define GRAY_700 ((rgba){ 0.216, 0.255, 0.318, 1.0 }) +#define GRAY_800 ((rgba){ 0.122, 0.161, 0.216, 1.0 }) +#define GRAY_900 ((rgba){ 0.067, 0.094, 0.153, 1.0 }) +#define GRAY_950 ((rgba){ 0.012, 0.027, 0.071, 1.0 }) + +#define RED_50 ((rgba){ 0.996, 0.949, 0.949, 1.0 }) +#define RED_100 ((rgba){ 0.996, 0.886, 0.886, 1.0 }) +#define RED_200 ((rgba){ 0.996, 0.792, 0.792, 1.0 }) +#define RED_300 ((rgba){ 0.988, 0.647, 0.647, 1.0 }) +#define RED_400 ((rgba){ 0.973, 0.443, 0.443, 1.0 }) +#define RED_500 ((rgba){ 0.937, 0.267, 0.267, 1.0 }) +#define RED_600 ((rgba){ 0.863, 0.149, 0.149, 1.0 }) +#define RED_700 ((rgba){ 0.725, 0.110, 0.110, 1.0 }) +#define RED_800 ((rgba){ 0.600, 0.106, 0.106, 1.0 }) +#define RED_900 ((rgba){ 0.498, 0.114, 0.114, 1.0 }) +#define RED_950 ((rgba){ 0.271, 0.039, 0.039, 1.0 }) + +#define ORANGE_50 ((rgba){ 1.000, 0.969, 0.929, 1.0 }) +#define ORANGE_100 ((rgba){ 1.000, 0.929, 0.835, 1.0 }) +#define ORANGE_200 ((rgba){ 0.996, 0.843, 0.667, 1.0 }) +#define ORANGE_300 ((rgba){ 0.992, 0.729, 0.455, 1.0 }) +#define ORANGE_400 ((rgba){ 0.984, 0.573, 0.235, 1.0 }) +#define ORANGE_500 ((rgba){ 0.976, 0.451, 0.086, 1.0 }) +#define ORANGE_600 ((rgba){ 0.918, 0.345, 0.047, 1.0 }) +#define ORANGE_700 ((rgba){ 0.761, 0.255, 0.047, 1.0 }) +#define ORANGE_800 ((rgba){ 0.604, 0.204, 0.071, 1.0 }) +#define ORANGE_900 ((rgba){ 0.486, 0.176, 0.071, 1.0 }) +#define ORANGE_950 ((rgba){ 0.263, 0.078, 0.027, 1.0 }) + +#define AMBER_50 ((rgba){ 1.000, 0.984, 0.922, 1.0 }) +#define AMBER_100 ((rgba){ 0.996, 0.953, 0.780, 1.0 }) +#define AMBER_200 ((rgba){ 0.992, 0.902, 0.541, 1.0 }) +#define AMBER_300 ((rgba){ 0.988, 0.827, 0.302, 1.0 }) +#define AMBER_400 ((rgba){ 0.984, 0.749, 0.141, 1.0 }) +#define AMBER_500 ((rgba){ 0.961, 0.620, 0.043, 1.0 }) +#define AMBER_600 ((rgba){ 0.851, 0.467, 0.024, 1.0 }) +#define AMBER_700 ((rgba){ 0.706, 0.325, 0.035, 1.0 }) +#define AMBER_800 ((rgba){ 0.573, 0.251, 0.055, 1.0 }) +#define AMBER_900 ((rgba){ 0.471, 0.208, 0.059, 1.0 }) +#define AMBER_950 ((rgba){ 0.271, 0.102, 0.012, 1.0 }) diff --git a/src/maths/primitives.h b/src/maths/primitives.h index 4fb6b4d..b7fefa0 100644 --- a/src/maths/primitives.h +++ b/src/maths/primitives.h @@ -55,17 +55,17 @@ static mesh prim_cube_mesh_create() { // top faces vertex_darray_push(cube.vertices, - (vertex){ .position = BACK_TOP_LEFT, .normal = VEC3_Y, .uv = (vec2){ 0 } }); + (vertex){ .position = BACK_TOP_LEFT, .normal = VEC3_Y, .uv = (vec2){ 0, 0 } }); vertex_darray_push(cube.vertices, - (vertex){ .position = FRONT_TOP_LEFT, .normal = VEC3_Y, .uv = (vec2){ 0 } }); + (vertex){ .position = FRONT_TOP_LEFT, .normal = VEC3_Y, .uv = (vec2){ 0, 1 } }); vertex_darray_push(cube.vertices, - (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 0 } }); + (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 1, 1 } }); vertex_darray_push(cube.vertices, - (vertex){ .position = BACK_TOP_LEFT, .normal = VEC3_Y, .uv = (vec2){ 0 } }); + (vertex){ .position = BACK_TOP_LEFT, .normal = VEC3_Y, .uv = (vec2){ 0, 0 } }); vertex_darray_push(cube.vertices, - (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 0 } }); + (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 1, 1 } }); vertex_darray_push(cube.vertices, - (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 0 } }); + (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 1, 0 } }); // bottom faces vertex_darray_push( @@ -89,17 +89,17 @@ static mesh prim_cube_mesh_create() { // right faces vertex_darray_push(cube.vertices, - (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0 } }); + (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0, 0 } }); vertex_darray_push(cube.vertices, - (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0 } }); + (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 1, 1 } }); vertex_darray_push(cube.vertices, - (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0 } }); + (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 1, 0 } }); vertex_darray_push(cube.vertices, - (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0 } }); + (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 1,1 } }); vertex_darray_push(cube.vertices, - (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0 } }); + (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0, 0 } }); vertex_darray_push(cube.vertices, - (vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0 } }); + (vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0, 1 } }); // left faces vertex_darray_push( diff --git a/src/renderer/backends/backend_opengl.c b/src/renderer/backends/backend_opengl.c index a9f7482..e3a4fb6 100644 --- a/src/renderer/backends/backend_opengl.c +++ b/src/renderer/backends/backend_opengl.c @@ -60,7 +60,7 @@ void clear_screen(vec3 colour) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } -void texture_data_upload(texture* tex) { +void texture_data_upload(texture *tex) { printf("Texture name %s\n", tex->name); TRACE("Upload texture data"); u32 texture_id; diff --git a/src/renderer/backends/backend_vulkan.c b/src/renderer/backends/backend_vulkan.c index 9d0ef51..e0b0e4e 100644 --- a/src/renderer/backends/backend_vulkan.c +++ b/src/renderer/backends/backend_vulkan.c @@ -145,6 +145,22 @@ typedef struct global_object_uniform { f32 padding[32]; } global_object_uniform; +typedef struct object_uniform { + vec4 diffuse_colour; + vec4 v_reserved0; + vec4 v_reserved1; + vec4 v_reserved2; +} object_uniform; + +#define MAX_OBJECT_COUNT 1024 +#define VULKAN_OBJECT_SHADER_DESCRIPTOR_COUNT 1 + +typedef struct geometry_render_data { + u32 id; + mat4 model; + texture* textures[16]; +} geometry_render_data; + typedef struct vulkan_buffer { u64 total_size; VkBuffer handle; @@ -171,6 +187,8 @@ typedef struct vulkan_shader { // Data that's global for all objects drawn global_object_uniform global_ubo; + object_uniform object_ubo; + vulkan_texture_data* texture_data; } vulkan_shader; typedef struct vulkan_context { @@ -324,7 +342,7 @@ bool vulkan_graphics_pipeline_create(vulkan_context* context, vulkan_renderpass* // Vertex input VkVertexInputBindingDescription binding_desc; binding_desc.binding = 0; - binding_desc.stride = sizeof(vertex_pos); + binding_desc.stride = sizeof(vertex); binding_desc.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; VkPipelineVertexInputStateCreateInfo vertex_input_info = { @@ -436,6 +454,8 @@ bool vulkan_buffer_create(vulkan_context* context, u64 size, VkBufferUsageFlagBi vulkan_buffer_bind(context, out_buffer, 0); } + DEBUG("Created buffer."); + return true; } @@ -512,11 +532,21 @@ bool vulkan_object_shader_create(vulkan_context* context, vulkan_shader* out_sha global_ubo_layout_binding.pImmutableSamplers = 0; global_ubo_layout_binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + VkDescriptorSetLayoutBinding sampler_layout_binding; + sampler_layout_binding.binding = 1; + sampler_layout_binding.descriptorCount = 1; + sampler_layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + sampler_layout_binding.pImmutableSamplers = 0; + sampler_layout_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + VkDescriptorSetLayoutCreateInfo global_layout_info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; - global_layout_info.bindingCount = 1; - global_layout_info.pBindings = &global_ubo_layout_binding; + + VkDescriptorSetLayoutBinding bindings[2] = { global_ubo_layout_binding, sampler_layout_binding }; + + global_layout_info.bindingCount = 2; + global_layout_info.pBindings = bindings; VK_CHECK(vkCreateDescriptorSetLayout(context->device.logical_device, &global_layout_info, context->allocator, &out_shader->descriptor_set_layout)); @@ -525,9 +555,15 @@ bool vulkan_object_shader_create(vulkan_context* context, vulkan_shader* out_sha global_pool_size.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; global_pool_size.descriptorCount = 3; + VkDescriptorPoolSize sampler_pool_size; + sampler_pool_size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + sampler_pool_size.descriptorCount = 3; + + VkDescriptorPoolSize pool_sizes[2] = { global_pool_size, sampler_pool_size }; + VkDescriptorPoolCreateInfo pool_info = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; - pool_info.poolSizeCount = 1; - pool_info.pPoolSizes = &global_pool_size; + pool_info.poolSizeCount = 2; + pool_info.pPoolSizes = pool_sizes; pool_info.maxSets = 3; VK_CHECK(vkCreateDescriptorPool(context->device.logical_device, &pool_info, context->allocator, @@ -549,12 +585,13 @@ bool vulkan_object_shader_create(vulkan_context* context, vulkan_shader* out_sha // Attributes u32 offset = 0; - const i32 attribute_count = 2; - VkVertexInputAttributeDescription attribute_descs[2]; + const i32 attribute_count = 3; + VkVertexInputAttributeDescription attribute_descs[3]; // Position - VkFormat formats[2] = { VK_FORMAT_R32G32B32_SFLOAT, VK_FORMAT_R32G32B32_SFLOAT }; + VkFormat formats[3] = { VK_FORMAT_R32G32B32_SFLOAT, VK_FORMAT_R32G32B32_SFLOAT, + VK_FORMAT_R32G32_SFLOAT }; - u64 sizes[2] = { sizeof(vec3), sizeof(vec3) }; + u64 sizes[3] = { sizeof(vec3), sizeof(vec3), sizeof(vec2) }; for (u32 i = 0; i < attribute_count; i++) { attribute_descs[i].binding = 0; @@ -629,15 +666,30 @@ void vulkan_object_shader_update_global_state(vulkan_context* context, vulkan_sh buffer_info.offset = offset; buffer_info.range = range; - VkWriteDescriptorSet descriptor_write = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; - descriptor_write.dstSet = shader->descriptor_sets[image_index]; - descriptor_write.dstBinding = 0; - descriptor_write.dstArrayElement = 0; - descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - descriptor_write.descriptorCount = 1; - descriptor_write.pBufferInfo = &buffer_info; + VkDescriptorImageInfo image_info; + image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + image_info.imageView = shader->texture_data->image.view; + image_info.sampler = shader->texture_data->sampler; + + VkWriteDescriptorSet uniform_write = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; + uniform_write.dstSet = shader->descriptor_sets[image_index]; + uniform_write.dstBinding = 0; + uniform_write.dstArrayElement = 0; + uniform_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + uniform_write.descriptorCount = 1; + uniform_write.pBufferInfo = &buffer_info; + + VkWriteDescriptorSet texture_write = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; + texture_write.dstSet = shader->descriptor_sets[image_index]; + texture_write.dstBinding = 1; + texture_write.dstArrayElement = 0; + texture_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + texture_write.descriptorCount = 1; + texture_write.pImageInfo = &image_info; - vkUpdateDescriptorSets(context->device.logical_device, 1, &descriptor_write, 0, 0); + VkWriteDescriptorSet writes[2] = { uniform_write, texture_write }; + + vkUpdateDescriptorSets(context->device.logical_device, 2, writes, 0, 0); vkCmdBindDescriptorSets(cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, shader->pipeline.layout, 0, 1, &global_descriptors, 0, 0); @@ -867,15 +919,10 @@ void vulkan_image_view_create(vulkan_context* context, VkFormat format, vulkan_i &image->view); } -void vulkan_image_transition_layout( - vulkan_context* context, - vulkan_command_buffer* command_buffer, - vulkan_image* image, - VkFormat format, - VkImageLayout old_layout, - VkImageLayout new_layout -) { - VkImageMemoryBarrier barrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER}; +void vulkan_image_transition_layout(vulkan_context* context, vulkan_command_buffer* command_buffer, + vulkan_image* image, VkFormat format, VkImageLayout old_layout, + VkImageLayout new_layout) { + VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; barrier.oldLayout = old_layout; barrier.newLayout = new_layout; barrier.srcQueueFamilyIndex = context->device.graphics_queue_index; @@ -892,12 +939,16 @@ void vulkan_image_transition_layout( VkPipelineStageFlags source_stage; VkPipelineStageFlags dest_stage; - if (old_layout == VK_IMAGE_LAYOUT_UNDEFINED && new_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + if (old_layout == VK_IMAGE_LAYOUT_UNDEFINED && + new_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { barrier.srcAccessMask = 0; barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + source_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - dest_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - } else if (old_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && new_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + dest_stage = VK_PIPELINE_STAGE_TRANSFER_BIT; + + } else if (old_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && + new_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; source_stage = VK_PIPELINE_STAGE_TRANSFER_BIT; @@ -907,25 +958,30 @@ void vulkan_image_transition_layout( return; } - vkCmdPipelineBarrier(command_buffer->handle, source_stage, dest_stage, 0, 0, 0, 0, 0, 1, &barrier); + vkCmdPipelineBarrier(command_buffer->handle, source_stage, dest_stage, 0, 0, 0, 0, 0, 1, + &barrier); } -void vulkan_image_copy_from_buffer( - vulkan_image* image, - VkBuffer buffer, - vulkan_command_buffer* command_buffer -) { +void vulkan_image_copy_from_buffer(vulkan_image* image, VkBuffer buffer, + vulkan_command_buffer* command_buffer) { VkBufferImageCopy region; region.bufferOffset = 0; + region.bufferRowLength = 0; + region.bufferImageHeight = 0; region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.imageSubresource.mipLevel = 0; region.imageSubresource.baseArrayLayer = 0; region.imageSubresource.layerCount = 1; + printf("Image details width: %d height %d\n", image->width, image->height); + region.imageOffset.x = 0; + region.imageOffset.y = 0; + region.imageOffset.z = 0; region.imageExtent.width = image->width; region.imageExtent.height = image->height; region.imageExtent.depth = 1; - vkCmdCopyBufferToImage(command_buffer->handle, buffer, image->handle, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + vkCmdCopyBufferToImage(command_buffer->handle, buffer, image->handle, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); } void vulkan_image_create(vulkan_context* context, VkImageType image_type, u32 width, u32 height, @@ -942,7 +998,7 @@ void vulkan_image_create(vulkan_context* context, VkImageType image_type, u32 wi image_create_info.extent.width = width; image_create_info.extent.height = height; image_create_info.extent.depth = 1; - image_create_info.mipLevels = 4; + image_create_info.mipLevels = 1; image_create_info.arrayLayers = 1; image_create_info.format = format; image_create_info.tiling = tiling; @@ -951,8 +1007,8 @@ void vulkan_image_create(vulkan_context* context, VkImageType image_type, u32 wi image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - vkCreateImage(context->device.logical_device, &image_create_info, context->allocator, - &out_image->handle); + VK_CHECK(vkCreateImage(context->device.logical_device, &image_create_info, context->allocator, + &out_image->handle)); VkMemoryRequirements memory_reqs; vkGetImageMemoryRequirements(context->device.logical_device, out_image->handle, &memory_reqs); @@ -1649,12 +1705,13 @@ bool gfx_backend_init(renderer* ren) { mesh cube = prim_cube_mesh_create(); - vertex_pos* verts = malloc(sizeof(vertex_pos) * cube.vertices->len); + vertex* verts = malloc(sizeof(vertex) * cube.vertices->len); f32 scale = 3.0; for (size_t i = 0; i < cube.vertices->len; i++) { - verts[i].pos = vec3_mult(cube.vertices->data[i].position, scale); + verts[i].position = vec3_mult(cube.vertices->data[i].position, scale); verts[i].normal = cube.vertices->data[i].normal; + verts[i].uv = cube.vertices->data[i].uv; } // const f32 s = 1.0; @@ -1677,14 +1734,16 @@ bool gfx_backend_init(renderer* ren) { // u32 indices[6] = { 0, 1, 2, 0, 3, 1 }; upload_data_range(&context, context.device.gfx_command_pool, 0, context.device.graphics_queue, - &context.object_vertex_buffer, 0, sizeof(vertex_pos) * cube.vertices->len, - verts); + &context.object_vertex_buffer, 0, sizeof(vertex) * cube.vertices->len, verts); TRACE("Uploaded vertex data"); upload_data_range(&context, context.device.gfx_command_pool, 0, context.device.graphics_queue, &context.object_index_buffer, 0, sizeof(u32) * cube.indices_len, cube.indices); TRACE("Uploaded index data"); vertex_darray_free(cube.vertices); free(cube.indices); + + // upload texture + // --- End test code INFO("Vulkan renderer initialisation succeeded"); @@ -1750,32 +1809,46 @@ void texture_data_upload(texture* tex) { printf("Texture name %s\n", tex->name); tex->backend_data = malloc(sizeof(vulkan_texture_data)); vulkan_texture_data* data = (vulkan_texture_data*)tex->backend_data; - VkDeviceSize image_size = tex->width * tex->height * tex->channel_count; + printf("Texture (%s) details: \n width %d\n height %d\n channel count %d\n", tex->name, + tex->width, tex->height, tex->channel_count); + VkDeviceSize image_size = tex->width * tex->height * max(tex->channel_count, 4); - VkFormat image_format = VK_FORMAT_R8G8B8A8_SNORM; + TRACE("Creating buffer of size %ld", image_size); + + VkFormat image_format = VK_FORMAT_R8G8B8A8_SRGB; VkBufferUsageFlags usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - VkMemoryPropertyFlags memory_prop_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + VkMemoryPropertyFlags memory_prop_flags = + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; vulkan_buffer staging; vulkan_buffer_create(&context, image_size, usage, memory_prop_flags, true, &staging); + DEBUG("Uploading image data"); vulkan_buffer_load_data(&context, &staging, 0, image_size, 0, tex->image_data); + INFO("Loaded iamge data!"); - vulkan_image_create(&context, VK_IMAGE_TYPE_2D, tex->width, tex->height, image_format, VK_IMAGE_TILING_OPTIMAL, - VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT - , VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, true, VK_IMAGE_ASPECT_COLOR_BIT, &data->image); + vulkan_image_create( + &context, VK_IMAGE_TYPE_2D, tex->width, tex->height, image_format, VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | + VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, true, VK_IMAGE_ASPECT_COLOR_BIT, &data->image); vulkan_command_buffer temp_buffer; - vulkan_command_buffer_allocate_and_begin_oneshot(&context, context.device.gfx_command_pool, &temp_buffer); + vulkan_command_buffer_allocate_and_begin_oneshot(&context, context.device.gfx_command_pool, + &temp_buffer); - vulkan_image_transition_layout(&context, &temp_buffer, &data->image, image_format, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + vulkan_image_transition_layout(&context, &temp_buffer, &data->image, image_format, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); vulkan_image_copy_from_buffer(&data->image, staging.handle, &temp_buffer); - vulkan_image_transition_layout(&context, &temp_buffer, &data->image, image_format, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + vulkan_image_transition_layout(&context, &temp_buffer, &data->image, image_format, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - vulkan_command_buffer_end_oneshot(&context, context.device.gfx_command_pool, &temp_buffer, context.device.graphics_queue); + vulkan_command_buffer_end_oneshot(&context, context.device.gfx_command_pool, &temp_buffer, + context.device.graphics_queue); - VkSamplerCreateInfo sampler_info = {VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO}; + VkSamplerCreateInfo sampler_info = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; sampler_info.magFilter = VK_FILTER_LINEAR; sampler_info.minFilter = VK_FILTER_LINEAR; sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; @@ -1792,12 +1865,14 @@ void texture_data_upload(texture* tex) { sampler_info.minLod = 0.0; sampler_info.maxLod = 0.0; - VkResult res = vkCreateSampler(context.device.logical_device, &sampler_info, context.allocator, &data->sampler); + VkResult res = vkCreateSampler(context.device.logical_device, &sampler_info, context.allocator, + &data->sampler); if (res != VK_SUCCESS) { ERROR("Error creating texture sampler for image %s", tex->name); return; } - + + tex->image_data = (void*)data; } // TODO: destroy texture @@ -1840,7 +1915,7 @@ void backend_end_frame(renderer* ren, f32 delta_time) { context.queue_complete_semaphores[context.current_frame], context.image_index); } -void gfx_backend_draw_frame(renderer* ren, camera* cam, mat4 model) { +void gfx_backend_draw_frame(renderer* ren, camera* cam, mat4 model, texture* tex) { backend_begin_frame(ren, 16.0); mat4 proj; @@ -1848,7 +1923,9 @@ void gfx_backend_draw_frame(renderer* ren, camera* cam, mat4 model) { camera_view_projection(cam, SCR_HEIGHT, SCR_WIDTH, &view, &proj); + context.object_shader.texture_data = (vulkan_texture_data*)tex->image_data; gfx_backend_update_global_state(proj, view, cam->position, vec4(1.0, 1.0, 1.0, 1.0), 0); + vulkan_object_shader_update_object(&context, &context.object_shader, model); backend_end_frame(ren, 16.0); diff --git a/src/renderer/render.c b/src/renderer/render.c index 9dc47c2..ebade4f 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -224,7 +224,7 @@ texture texture_data_load(const char* path, bool invert_y) { stbi_set_flip_vertically_on_load(invert_y); #pragma GCC diagnostic ignored "-Wpointer-sign" - char* data = stbi_load(path, &width, &height, &num_channels, 0); + char* data = stbi_load(path, &width, &height, &num_channels, STBI_rgb_alpha); if (data) { DEBUG("loaded texture: %s", path); } else { diff --git a/src/renderer/render_backend.h b/src/renderer/render_backend.h index af62f9f..da30bcc 100644 --- a/src/renderer/render_backend.h +++ b/src/renderer/render_backend.h @@ -13,7 +13,7 @@ bool gfx_backend_init(renderer* ren); void gfx_backend_shutdown(renderer* ren); -void gfx_backend_draw_frame(renderer* ren, camera* camera, mat4 model); +void gfx_backend_draw_frame(renderer* ren, camera* camera, mat4 model, texture* tex); void gfx_backend_update_global_state(mat4 projection, mat4 view, vec3 view_pos, vec4 ambient_colour, i32 mode); -- cgit v1.2.3-70-g09d2 From 9baff5661f2ba8b57e1b0794e490e239b7ef80ca Mon Sep 17 00:00:00 2001 From: omnisci3nce <17525998+omnisci3nce@users.noreply.github.com> Date: Thu, 4 Apr 2024 22:53:13 +1100 Subject: loading animation data - step 1 --- .../property_animation/ex_property_animation.c | 4 ++ src/animation.c | 0 src/animation.h | 53 ++++++++++++++ src/maths/primitives.h | 82 +++++++++++++--------- src/renderer/backends/backend_opengl.c | 4 +- src/renderer/render.c | 16 +++++ src/renderer/render.h | 2 + src/renderer/render_types.h | 12 +++- src/resources/gltf.c | 59 ++++++++++++++++ 9 files changed, 194 insertions(+), 38 deletions(-) create mode 100644 src/animation.c create mode 100644 src/animation.h (limited to 'src/renderer/render.c') diff --git a/examples/property_animation/ex_property_animation.c b/examples/property_animation/ex_property_animation.c index 38bafd5..e175b31 100644 --- a/examples/property_animation/ex_property_animation.c +++ b/examples/property_animation/ex_property_animation.c @@ -4,6 +4,7 @@ #include "core.h" #include "input.h" #include "keys.h" +#include "log.h" #include "maths.h" #include "maths_types.h" #include "primitives.h" @@ -97,6 +98,9 @@ int main() { render_frame_end(&core->renderer); } + INFO("Shutting down"); + model_destroy(cube); + core_shutdown(core); return 0; diff --git a/src/animation.c b/src/animation.c new file mode 100644 index 0000000..e69de29 diff --git a/src/animation.h b/src/animation.h new file mode 100644 index 0000000..b7c8ca4 --- /dev/null +++ b/src/animation.h @@ -0,0 +1,53 @@ +#pragma once + +#include "darray.h" +#include "defines.h" +#include "maths_types.h" + +KITC_DECL_TYPED_ARRAY(f32) + +typedef enum interpolation { INTERPOLATION_LINEAR, INTERPOLATION_COUNT } interpolation; + +typedef enum keyframe_kind { + KEYFRAME_ROTATION, + KEYFRAME_TRANSLATION, + KEYFRAME_SCALE, + KEYFRAME_WEIGHTS, +} keyframe_kind; + +typedef union keyframe { + quat rotation; + vec3 translation; + vec3 scale; + float* weights; +} keyframe; + +typedef struct keyframes { + keyframe_kind kind; + keyframe* values; + size_t count; +} keyframes; + +typedef struct animation_spline { + f32_darray timestamps; + keyframes values; + interpolation interpolation; +} animation_spline; + +typedef struct animation_sampler { + int current_index; + animation_spline animation; +} animation_sampler; + +/** @brief Sample an animation at a given time `t` */ +keyframe animation_sample(animation_sampler sampler, f32 t); + +typedef struct animation_clip { + // A clip contains one or more animation curves + // for now I think we can just enumerate all of the properties (assuming *only* one per type is in + // a clip) NULL = this property is not animated in this clip + animation_sampler* rotation; + animation_sampler* translation; + animation_sampler* scale; + animation_sampler* weights; +} animation_clip; \ No newline at end of file diff --git a/src/maths/primitives.h b/src/maths/primitives.h index b7fefa0..ed52c8c 100644 --- a/src/maths/primitives.h +++ b/src/maths/primitives.h @@ -34,38 +34,48 @@ static mesh prim_cube_mesh_create() { (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_NEG_Z, .uv = (vec2){ 1, 0 } }); vertex_darray_push( cube.vertices, - (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_NEG_Z, .uv = (vec2){ 1,1 } }); + (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_NEG_Z, .uv = (vec2){ 1, 1 } }); vertex_darray_push( cube.vertices, (vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_Z, .uv = (vec2){ 0, 1 } }); // front faces - vertex_darray_push(cube.vertices, - (vertex){ .position = FRONT_BOT_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0, 1} }); - vertex_darray_push(cube.vertices, - (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 1, 0 } }); - vertex_darray_push(cube.vertices, - (vertex){ .position = FRONT_TOP_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0, 0} }); - vertex_darray_push(cube.vertices, - (vertex){ .position = FRONT_BOT_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0, 1 } }); - vertex_darray_push(cube.vertices, - (vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 1, 1 } }); - vertex_darray_push(cube.vertices, - (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 1, 0 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_BOT_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0, 1 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 1, 0 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_TOP_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0, 0 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_BOT_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0, 1 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 1, 1 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 1, 0 } }); // top faces vertex_darray_push(cube.vertices, (vertex){ .position = BACK_TOP_LEFT, .normal = VEC3_Y, .uv = (vec2){ 0, 0 } }); - vertex_darray_push(cube.vertices, - (vertex){ .position = FRONT_TOP_LEFT, .normal = VEC3_Y, .uv = (vec2){ 0, 1 } }); - vertex_darray_push(cube.vertices, - (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 1, 1 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_TOP_LEFT, .normal = VEC3_Y, .uv = (vec2){ 0, 1 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 1, 1 } }); vertex_darray_push(cube.vertices, (vertex){ .position = BACK_TOP_LEFT, .normal = VEC3_Y, .uv = (vec2){ 0, 0 } }); - vertex_darray_push(cube.vertices, - (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 1, 1 } }); - vertex_darray_push(cube.vertices, - (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 1, 0 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 1, 1 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 1, 0 } }); // bottom faces vertex_darray_push( @@ -88,18 +98,24 @@ static mesh prim_cube_mesh_create() { (vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_NEG_Y, .uv = (vec2){ 0 } }); // right faces - vertex_darray_push(cube.vertices, - (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0, 0 } }); - vertex_darray_push(cube.vertices, - (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 1, 1 } }); - vertex_darray_push(cube.vertices, - (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 1, 0 } }); - vertex_darray_push(cube.vertices, - (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 1,1 } }); - vertex_darray_push(cube.vertices, - (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0, 0 } }); - vertex_darray_push(cube.vertices, - (vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0, 1 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0, 0 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 1, 1 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 1, 0 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 1, 1 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0, 0 } }); + vertex_darray_push( + cube.vertices, + (vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0, 1 } }); // left faces vertex_darray_push( diff --git a/src/renderer/backends/backend_opengl.c b/src/renderer/backends/backend_opengl.c index 8df0933..b769002 100644 --- a/src/renderer/backends/backend_opengl.c +++ b/src/renderer/backends/backend_opengl.c @@ -42,9 +42,7 @@ bool gfx_backend_init(renderer *ren) { return true; } -void gfx_backend_draw_frame(renderer* ren, camera* cam, mat4 model, texture* tex) { - -} +void gfx_backend_draw_frame(renderer *ren, camera *cam, mat4 model, texture *tex) {} void gfx_backend_shutdown(renderer *ren) {} diff --git a/src/renderer/render.c b/src/renderer/render.c index 2993225..2f5e553 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -1,3 +1,4 @@ +#include "mem.h" #define STB_IMAGE_IMPLEMENTATION #include @@ -88,6 +89,21 @@ void default_material_init() { texture_data_upload(&DEFAULT_MATERIAL.specular_texture); } +void model_destroy(model* model) { + arena_free_all(&model->animation_data_arena); + arena_free_storage(&model->animation_data_arena); + mesh_darray_free(model->meshes); + material_darray_free(model->materials); + if (model->is_uploaded) { + // Delete gpu buffer data + for (u32 i = 0; i < mesh_darray_len(model->meshes); i++) { + // FIXME: dont leak Opengl + glDeleteBuffers(1, &model->meshes->data[i].vbo); + glDeleteVertexArrays(1, &model->meshes->data[i].vao); + } + } +} + void draw_model(renderer* ren, camera* camera, model* model, transform tf, scene* scene) { // TRACE("Drawing model: %s", model->name); mat4 view; diff --git a/src/renderer/render.h b/src/renderer/render.h index b6dad00..f0118b6 100644 --- a/src/renderer/render.h +++ b/src/renderer/render.h @@ -35,5 +35,7 @@ void model_upload_meshes(renderer* ren, model* model); void draw_model(renderer* ren, camera* camera, model* model, transform tf, scene* scene); void draw_mesh(renderer* ren, mesh* mesh, transform tf, material* mat, mat4* view, mat4* proj); +void model_destroy(model* model); + // --- texture texture_data_load(const char* path, bool invert_y); // #frontend diff --git a/src/renderer/render_types.h b/src/renderer/render_types.h index 80e1372..8d12183 100644 --- a/src/renderer/render_types.h +++ b/src/renderer/render_types.h @@ -7,6 +7,7 @@ */ #pragma once +#include "animation.h" #include "darray.h" #include "maths.h" #include "maths_types.h" @@ -52,8 +53,8 @@ typedef struct renderer { typedef struct texture { u32 texture_id; char name[MAX_TEXTURE_NAME_LEN]; - void* image_data; - void* backend_data; + void *image_data; + void *backend_data; u32 width; u32 height; u8 channel_count; @@ -84,6 +85,11 @@ KITC_DECL_TYPED_ARRAY(material) // creates "material_darray" #define TYPED_MATERIAL_ARRAY #endif +#ifndef TYPED_ANIMATION_CLIP_ARRAY +KITC_DECL_TYPED_ARRAY(animation_clip) // creates "material_darray" +#define TYPED_ANIMATION_CLIP_ARRAY +#endif + // lights typedef struct point_light { vec3 position; @@ -137,6 +143,8 @@ typedef struct model { mesh_darray *meshes; aabb_3d bbox; material_darray *materials; + animation_clip_darray *animations; + arena animation_data_arena; bool is_loaded; bool is_uploaded; } model; diff --git a/src/resources/gltf.c b/src/resources/gltf.c index 261c96f..b6e100f 100644 --- a/src/resources/gltf.c +++ b/src/resources/gltf.c @@ -1,11 +1,13 @@ #include #include #include +#include "animation.h" #include "core.h" #include "defines.h" #include "file.h" #include "loaders.h" #include "log.h" +#include "mem.h" #include "path.h" #include "render.h" #include "render_types.h" @@ -228,6 +230,63 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel printf("Mesh %d Mat index %d Mat name %s\n", i, mat_idx, out_model->materials->data[mat_idx].name); } + + // Animations + TRACE("Num animations %d", data->animations_count); + size_t num_animations = data->animations_count; + if (num_animations > 0) { +// Create an arena for all animation related data +#define ANIMATION_STORAGE_ARENA_SIZE (1024 * 1024) + char *animation_backing_storage = malloc(ANIMATION_STORAGE_ARENA_SIZE); + // We'll store data on this arena so we can easily free it all at once later + arena anim_arena = arena_create(animation_backing_storage, ANIMATION_STORAGE_ARENA_SIZE); + + if (!out_model->animations) { + out_model->animations = animation_clip_darray_new(num_animations); + } + + for (int anim_idx = 0; anim_idx < data->animations_count; anim_idx++) { + cgltf_animation animation = data->animations[anim_idx]; + animation_clip clip = { 0 }; + + for (size_t c = 0; c < animation.channels_count; c++) { + cgltf_animation_channel channel = animation.channels[c]; + + animation_sampler *sampler = arena_alloc(&anim_arena, sizeof(animation_sampler)); + + animation_sampler **target_property; + keyframe_kind data_type; + + switch (channel.target_path) { + case cgltf_animation_path_type_rotation: + target_property = &clip.rotation; + data_type = KEYFRAME_ROTATION; + break; + default: + WARN("unsupported animation type"); + return false; + } + *target_property = sampler; + + sampler->current_index = 0; + sampler->animation.interpolation = INTERPOLATION_LINEAR; + + // keyframe times + size_t n_frames = channel.sampler->input->count; + assert(channel.sampler->input->component_type == cgltf_component_type_r_32f); + // FIXME: CASSERT_MSG function "Expected animation sampler input component to be type f32 (keyframe times)"); + + sampler, + + // keyframe values + size_t n_values = channel.sampler->output->count; + assert(n_frames == n_values); + + sampler->animation.timestamps + } + } + } + return true; } -- cgit v1.2.3-70-g09d2 From 113b038b703aeef545e86700afd6dc2095015e84 Mon Sep 17 00:00:00 2001 From: omniscient <17525998+omnisci3nce@users.noreply.github.com> Date: Sat, 6 Apr 2024 20:14:22 +1100 Subject: use 0 in stbi_load again. need to investigate more later --- src/renderer/render.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/renderer/render.c') diff --git a/src/renderer/render.c b/src/renderer/render.c index 2f5e553..806979d 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -242,7 +242,7 @@ texture texture_data_load(const char* path, bool invert_y) { stbi_set_flip_vertically_on_load(invert_y); #pragma GCC diagnostic ignored "-Wpointer-sign" - char* data = stbi_load(path, &width, &height, &num_channels, STBI_rgb_alpha); + char* data = stbi_load(path, &width, &height, &num_channels, 0); // STBI_rgb_alpha); if (data) { DEBUG("loaded texture: %s", path); } else { -- cgit v1.2.3-70-g09d2 From d9f9479694d8a4d74822a876516282329db5ea3d Mon Sep 17 00:00:00 2001 From: Omniscient <17525998+omnisci3nce@users.noreply.github.com> Date: Sat, 6 Apr 2024 22:24:52 +1100 Subject: separate pipeline for skinned meshes --- assets/shaders/skinned.vert | 47 ++++++++ examples/gltf_loading/ex_gltf_loading.c | 1 - examples/skinned_animation/ex_skinned_animation.c | 4 +- src/animation.h | 6 + src/renderer/render.c | 134 +++++++++++++++++----- src/renderer/render.h | 2 + src/renderer/render_types.h | 18 +-- src/resources/gltf.c | 67 +++++++++-- 8 files changed, 234 insertions(+), 45 deletions(-) create mode 100644 assets/shaders/skinned.vert (limited to 'src/renderer/render.c') diff --git a/assets/shaders/skinned.vert b/assets/shaders/skinned.vert new file mode 100644 index 0000000..3d0c2cd --- /dev/null +++ b/assets/shaders/skinned.vert @@ -0,0 +1,47 @@ +#version 410 core +// Inputs +layout (location = 0) in vec3 inPos; +layout (location = 1) in vec3 inNormal; +layout (location = 2) in vec2 inTexCoords; +layout (location = 3) in ivec4 inBoneIndices; +layout (location = 4) in vec4 inWeights; + +uniform mat4 model; +uniform mat4 view; +uniform mat4 projection; +uniform mat4 lightSpaceMatrix; + +const int MAX_BONES = 100; +const int MAX_BONE_INFLUENCES = 4; +uniform mat4 finalBoneMatrices[MAX_BONES]; + +// Output +out VS_OUT { + vec3 FragPos; + vec3 Normal; + vec2 TexCoords; + vec4 FragPosLightSpace; +} vs_out; + +void main() { + vec4 totalPosition = vec4(0.0f); + for(int i = 0 ; i < MAX_BONE_INFLUENCES ; i++) { + if(inBoneIndices[i] == -1) + continue; + if(inBoneIndices[i] >=MAX_BONES) + { + totalPosition = vec4(inPos,1.0f); + break; + } + vec4 localPosition = finalBoneMatrices[inBoneIndices[i]] * vec4(inPos,1.0f); + totalPosition += localPosition * inWeights[i]; + vec3 localNormal = mat3(finalBoneMatrices[inBoneIndices[i]]) * inNormal; + vs_out.Normal = localNormal; + } + + vs_out.FragPos = vec3(model * vec4(inPos, 1.0)); + // vs_out.Normal = inNormal; + vs_out.TexCoords = inTexCoords; + vs_out.FragPosLightSpace = lightSpaceMatrix * vec4(vs_out.FragPos, 1.0); + gl_Position = projection * view * model * totalPosition; +} \ No newline at end of file diff --git a/examples/gltf_loading/ex_gltf_loading.c b/examples/gltf_loading/ex_gltf_loading.c index 7b69127..5d53e78 100644 --- a/examples/gltf_loading/ex_gltf_loading.c +++ b/examples/gltf_loading/ex_gltf_loading.c @@ -56,7 +56,6 @@ int main() { scene our_scene = { .dir_light = dir_light, .n_point_lights = 4 }; memcpy(&our_scene.point_lights, &point_lights, sizeof(point_light[4])); - while (!glfwWindowShouldClose(core->renderer.window)) { currentFrame = glfwGetTime(); deltaTime = currentFrame - lastFrame; diff --git a/examples/skinned_animation/ex_skinned_animation.c b/examples/skinned_animation/ex_skinned_animation.c index 9efffc6..3138256 100644 --- a/examples/skinned_animation/ex_skinned_animation.c +++ b/examples/skinned_animation/ex_skinned_animation.c @@ -37,7 +37,7 @@ int main() { scene our_scene = make_default_scene(); - vec3 cam_pos = vec3_create(5, 5, 5); + vec3 cam_pos = vec3_create(0, 5, 8); game_state game = { .camera = camera_create(cam_pos, vec3_negate(cam_pos), VEC3_Y, deg_to_rad(45.0)), .camera_euler = vec3_create(90, 0, 0), @@ -86,7 +86,7 @@ int main() { quat rot = quat_ident(); transform tf = transform_create(VEC3_ZERO, rot, 1.0); - draw_model(&core->renderer, &game.camera, cube, tf, &our_scene); + draw_skinned_model(&core->renderer, &game.camera, cube, tf, &our_scene); // gfx_backend_draw_frame(&core->renderer, &game.camera, model, NULL); diff --git a/src/animation.h b/src/animation.h index 81e150a..18d3ba9 100644 --- a/src/animation.h +++ b/src/animation.h @@ -28,6 +28,12 @@ typedef struct keyframes { size_t count; } keyframes; +typedef struct joint { + char* name; // optional + transform transform_components; + mat4 local_transform; +} joint; + typedef struct animation_spline { f32* timestamps; size_t n_timestamps; diff --git a/src/renderer/render.c b/src/renderer/render.c index 806979d..e420043 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -1,3 +1,7 @@ +#include +#include +#include +#include "maths_types.h" #include "mem.h" #define STB_IMAGE_IMPLEMENTATION #include @@ -59,6 +63,9 @@ bool renderer_init(renderer* ren) { ren->blinn_phong = shader_create_separate("assets/shaders/blinn_phong.vert", "assets/shaders/blinn_phong.frag"); + ren->skinned = + shader_create_separate("assets/shaders/skinned.vert", "assets/shaders/blinn_phong.frag"); + default_material_init(); return true; @@ -158,6 +165,59 @@ void draw_mesh(renderer* ren, mesh* mesh, transform tf, material* mat, mat4* vie draw_primitives(CEL_PRIMITIVE_TOPOLOGY_TRIANGLE, 0, num_vertices); } +void draw_skinned_mesh(renderer* ren, mesh* mesh, transform tf, material* mat, mat4* view, + mat4* proj) { + shader lighting_shader = ren->skinned; + + // bind buffer + bind_mesh_vertex_buffer(ren->backend_state, mesh); + + // bind textures + bind_texture(lighting_shader, &mat->diffuse_texture, 0); // bind to slot 0 + bind_texture(lighting_shader, &mat->specular_texture, 1); // bind to slot 1 + uniform_f32(lighting_shader.program_id, "material.shininess", 32.); + + // upload model transform + mat4 trans = mat4_translation(tf.position); + mat4 rot = mat4_rotation(tf.rotation); + mat4 scale = mat4_scale(tf.scale); + mat4 model_tf = mat4_mult(trans, mat4_mult(rot, scale)); + + uniform_mat4f(lighting_shader.program_id, "model", &model_tf); + // upload view & projection matrices + uniform_mat4f(lighting_shader.program_id, "view", view); + uniform_mat4f(lighting_shader.program_id, "projection", proj); + + // draw triangles + u32 num_vertices = vertex_darray_len(mesh->vertices); + draw_primitives(CEL_PRIMITIVE_TOPOLOGY_TRIANGLE, 0, num_vertices); +} + +void draw_skinned_model(renderer* ren, camera* cam, model* model, transform tf, scene* scene) { + mat4 view; + mat4 proj; + camera_view_projection(cam, SCR_HEIGHT, SCR_WIDTH, &view, &proj); + + set_shader(ren->skinned); + + // set camera uniform + uniform_vec3f(ren->blinn_phong.program_id, "viewPos", &cam->position); + // set light uniforms + dir_light_upload_uniforms(ren->blinn_phong, &scene->dir_light); + for (int i = 0; i < scene->n_point_lights; i++) { + point_light_upload_uniforms(ren->blinn_phong, &scene->point_lights[i], '0' + i); + } + + for (size_t i = 0; i < mesh_darray_len(model->meshes); i++) { + mesh* m = &model->meshes->data[i]; + if (vertex_darray_len(m->vertices) == 0) { + continue; + } + material* mat = &model->materials->data[m->material_index]; + draw_skinned_mesh(ren, m, tf, mat, &view, &proj); + } +} + void model_upload_meshes(renderer* ren, model* model) { INFO("Upload mesh vertex data to GPU for model %s", model->name); @@ -173,6 +233,7 @@ void model_upload_meshes(renderer* ren, model* model) { // upload each mesh to the GPU for (int mesh_i = 0; mesh_i < num_meshes; mesh_i++) { + mesh mesh = model->meshes->data[mesh_i]; model->meshes->data[mesh_i].vao = VAOs[mesh_i]; model->meshes->data[mesh_i].vbo = VBOs[mesh_i]; // 3. bind buffers @@ -185,34 +246,51 @@ void model_upload_meshes(renderer* ren, model* model) { // TODO: convert this garbage into a function f32 verts[num_vertices * 8]; // for each face - for (int i = 0; i < (num_vertices / 3); i++) { - // for each vert in face - for (int j = 0; j < 3; j++) { - size_t stride = (i * 24) + j * 8; - // printf("i: %d, stride: %ld, loc %d\n", i, stride, i * 3 + j); - vertex vert = model->meshes->data[mesh_i].vertices->data[i]; - // printf("pos %f %f %f\n", vert.position.x, vert.position.y, vert.position.z); - // printf("norm %f %f %f\n", vert.normal.x, vert.normal.y, vert.normal.z); - // printf("tex %f %f\n", vert.uv.x, vert.uv.y); - verts[stride + 0] = - ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].position.x; - verts[stride + 1] = - ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].position.y; - verts[stride + 2] = - ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].position.z; - verts[stride + 3] = - ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].normal.x; - verts[stride + 4] = - ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].normal.y; - verts[stride + 5] = - ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].normal.z; - verts[stride + 6] = ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].uv.x; - verts[stride + 7] = ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].uv.y; - } + // for (int i = 0; i < (num_vertices / 3); i++) { + // // for each vert in face + // for (int j = 0; j < 3; j++) { + // size_t stride = (i * 24) + j * 8; + // // printf("i: %d, stride: %ld, loc %d\n", i, stride, i * 3 + j); + // vertex vert = model->meshes->data[mesh_i].vertices->data[i]; + // // printf("pos %f %f %f\n", vert.position.x, vert.position.y, vert.position.z); + // // printf("norm %f %f %f\n", vert.normal.x, vert.normal.y, vert.normal.z); + // // printf("tex %f %f\n", vert.uv.x, vert.uv.y); + // verts[stride + 0] = + // ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].position.x; + // verts[stride + 1] = + // ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].position.y; + // verts[stride + 2] = + // ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].position.z; + // verts[stride + 3] = + // ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].normal.x; + // verts[stride + 4] = + // ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].normal.y; + // verts[stride + 5] = + // ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].normal.z; + // verts[stride + 6] = ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + + // j].uv.x; verts[stride + 7] = ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + // + j].uv.y; + // } + // } + size_t vertex_size = mesh.is_skinned + ? sizeof(vec3) * 2 + sizeof(vec2) + sizeof(u32) * 4 + sizeof(vec4) + : sizeof(vec3) * 2 + sizeof(vec2); + if (!mesh.is_skinned) { + printf("sizeof(vertex) -> %ld, vertex_size -> %ld\n", sizeof(vertex), vertex_size); + assert(vertex_size == sizeof(vertex)); + } + size_t buffer_size = vertex_size * num_vertices; + u8* bytes = malloc(buffer_size); + + for (int i = 0; i < num_vertices; i++) { + u8* p = bytes + vertex_size * i; + u8* bone_data_offset = p + sizeof(u32) * 4 + sizeof(vec4); + memcpy(p, &mesh.vertices->data[i], sizeof(vertex)); + memcpy(bone_data_offset, &mesh.vertex_bone_data->data[i], sizeof(vertex_bone_data)); } // 4. upload data - glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, buffer_size, bytes, GL_STATIC_DRAW); // 5. cont. set mesh vertex layout glBindVertexArray(model->meshes->data[mesh_i].vao); @@ -225,6 +303,10 @@ void model_upload_meshes(renderer* ren, model* model) { // tex coords glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); glEnableVertexAttribArray(2); + + // skinning (optional) + if (mesh.is_skinned) { + } } INFO("Uploaded %d submeshes with a total of %d vertices\n", num_meshes, total_verts); @@ -242,7 +324,7 @@ texture texture_data_load(const char* path, bool invert_y) { stbi_set_flip_vertically_on_load(invert_y); #pragma GCC diagnostic ignored "-Wpointer-sign" - char* data = stbi_load(path, &width, &height, &num_channels, 0); // STBI_rgb_alpha); + char* data = stbi_load(path, &width, &height, &num_channels, 0); // STBI_rgb_alpha); if (data) { DEBUG("loaded texture: %s", path); } else { diff --git a/src/renderer/render.h b/src/renderer/render.h index f0118b6..ba2cbdc 100644 --- a/src/renderer/render.h +++ b/src/renderer/render.h @@ -35,6 +35,8 @@ void model_upload_meshes(renderer* ren, model* model); void draw_model(renderer* ren, camera* camera, model* model, transform tf, scene* scene); void draw_mesh(renderer* ren, mesh* mesh, transform tf, material* mat, mat4* view, mat4* proj); +void draw_skinned_model(renderer* ren, camera* cam, model* model, transform tf, scene* scene); + void model_destroy(model* model); // --- diff --git a/src/renderer/render_types.h b/src/renderer/render_types.h index e4d3127..61571e1 100644 --- a/src/renderer/render_types.h +++ b/src/renderer/render_types.h @@ -46,6 +46,7 @@ typedef struct renderer { renderer_config config; // shaders shader blinn_phong; + shader skinned; } renderer; // --- Lighting & Materials @@ -118,24 +119,25 @@ typedef struct vertex { vec2 uv; } vertex; -typedef struct skinned_vertex { - vec3 position; - vec3 normal; - vec2 uv; +typedef struct vertex_bone_data { vec4i joints; /** @brief 4 indices of joints that influence vectors position */ vec4 weights; /** @brief weight (0,1) of each joint */ -} skinned_vertex; +} vertex_bone_data; +#include "animation.h" #ifndef TYPED_VERTEX_ARRAY KITC_DECL_TYPED_ARRAY(vertex) // creates "vertex_darray" -KITC_DECL_TYPED_ARRAY(skinned_vertex) // creates "skinned_vertex_darray" +KITC_DECL_TYPED_ARRAY(vertex_bone_data) // creates "skinned_vertex_darray" +KITC_DECL_TYPED_ARRAY(joint) #define TYPED_VERTEX_ARRAY #endif + typedef struct mesh { vertex_darray* vertices; - // skinned_vertex_darray* skinned_vertices; // only used if model needs it - // bool is_skinned; + vertex_bone_data_darray* vertex_bone_data; // only used if model needs it + joint_darray* bones; + bool is_skinned; u32 vertex_size; /** size in bytes of each vertex including necessary padding */ bool has_indices; u32 *indices; diff --git a/src/resources/gltf.c b/src/resources/gltf.c index 7fbef3b..c7c1f55 100644 --- a/src/resources/gltf.c +++ b/src/resources/gltf.c @@ -7,6 +7,7 @@ #include "file.h" #include "loaders.h" #include "log.h" +#include "maths.h" #include "maths_types.h" #include "mem.h" #include "path.h" @@ -30,6 +31,7 @@ KITC_DECL_TYPED_ARRAY(u32) KITC_DECL_TYPED_ARRAY(vec4i) KITC_DECL_TYPED_ARRAY(vec4) KITC_DECL_TYPED_ARRAY(face) +// KITC_DECL_TYPED_ARRAY(joint) bool model_load_gltf_str(const char *file_string, const char *filepath, str8 relative_path, model *out_model, bool invert_textures_y); @@ -84,9 +86,9 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel vec3_darray *tmp_positions = vec3_darray_new(1000); vec3_darray *tmp_normals = vec3_darray_new(1000); vec2_darray *tmp_uvs = vec2_darray_new(1000); - face_darray *tmp_faces = face_darray_new(1000); - vec4i_darray *tmp_joints = vec4i_darray_new(1000); + vec4i_darray *tmp_joint_indices = vec4i_darray_new(1000); vec4_darray *tmp_weights = vec4_darray_new(1000); + joint_darray *tmp_joints = joint_darray_new(256); cgltf_options options = { 0 }; cgltf_data *data = NULL; @@ -112,7 +114,33 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel if (is_skinned) { cgltf_skin *gltf_skin = data->skins; - TRACE("loading skin %s", gltf_skin->name); + DEBUG("loading skin %s", gltf_skin->name); + size_t num_joints = gltf_skin->joints_count; + DEBUG("# Joints %d", num_joints); + + // for each one we'll spit out a joint + for (size_t i = 0; i < num_joints; i++) { + cgltf_node *joint_node = gltf_skin->joints[i]; + + joint joint_i = { .name = "testjoint" }; + if (joint_node->children_count > 0 && !joint_node->has_translation && + !joint_node->has_rotation) { + WARN("joint Node with index %d is the root node", i); + joint_i.transform_components = TRANSFORM_DEFAULT; + } else { + TRACE("Storing joint transform"); + joint_i.transform_components = TRANSFORM_DEFAULT; + if (joint_node->has_translation) { + memcpy(&joint_i.transform_components.position, &joint_node->translation, 3 * sizeof(f32)); + } + if (joint_node->has_rotation) { + memcpy(&joint_i.transform_components.rotation, &joint_node->rotation, 4 * sizeof(f32)); + } + // TODO: support scaling as vec instead of float + } + joint_i.local_transform = transform_to_mat(&joint_i.transform_components); + joint_darray_push(tmp_joints, joint_i); + } } // --- Materials @@ -218,9 +246,9 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel joint_indices.y = (u32)joints_as_floats.y; joint_indices.z = (u32)joints_as_floats.z; joint_indices.w = (u32)joints_as_floats.w; - printf("Joints affecting %d %d %d %d\n", joint_indices.x, joint_indices.y, - joint_indices.z, joint_indices.w); - vec4i_darray_push(tmp_joints, joint_indices); + // printf("Joints affecting %d %d %d %d\n", joint_indices.x, joint_indices.y, + // joint_indices.z, joint_indices.w); + vec4i_darray_push(tmp_joint_indices, joint_indices); } } else if (attribute.type == cgltf_attribute_type_weights) { @@ -232,7 +260,7 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel for (cgltf_size v = 0; v < accessor->count; ++v) { vec4 weights; cgltf_accessor_read_float(accessor, v, &weights.x, 4); - printf("Weights affecting %f %f %f %f\n", weights.x, weights.y, weights.z, weights.w); + // printf("Weights affecting %f %f %f %f\n", weights.x, weights.y, weights.z, weights.w); vec4_darray_push(tmp_weights, weights); } } else { @@ -240,7 +268,7 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel } } - mesh mesh = {0}; + mesh mesh = { 0 }; mesh.vertices = vertex_darray_new(10); cgltf_accessor *indices = primitive.indices; @@ -282,7 +310,30 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel } } + if (is_skinned) { + mesh.vertex_bone_data = vertex_bone_data_darray_new(tmp_joint_indices->len); + mesh.bones = joint_darray_new(tmp_joints->len); + for (int i = 0; i < tmp_joint_indices->len; i++) { + vertex_bone_data data; + data.joints = tmp_joint_indices->data[i]; + data.weights =tmp_weights->data[i]; + vertex_bone_data_darray_push(mesh.vertex_bone_data, data); + } + for (int i = 0; i < tmp_joints->len; i++) { + joint data = tmp_joints->data[i]; + joint_darray_push(mesh.bones, data); + } + } + mesh_darray_push(out_model->meshes, mesh); + + // clear data for each mesh + vec3_darray_clear(tmp_positions); + vec3_darray_clear(tmp_normals); + vec2_darray_free(tmp_uvs); + vec4i_darray_clear(tmp_joint_indices); + vec4_darray_clear(tmp_weights); + joint_darray_clear(tmp_joints); } for (int i = 0; i < out_model->meshes->len; i++) { -- cgit v1.2.3-70-g09d2 From 42924fe7b32e93bf55ce034467ceb52e0436c303 Mon Sep 17 00:00:00 2001 From: Omniscient <17525998+omnisci3nce@users.noreply.github.com> Date: Sun, 7 Apr 2024 13:50:40 +1000 Subject: visualising bone indices --- assets/shaders/blinn_phong.frag | 4 +- assets/shaders/skinned.vert | 46 ++++++++----- examples/skinned_animation/ex_skinned_animation.c | 25 +++++-- src/animation.h | 8 +++ src/maths/maths_types.h | 6 +- src/renderer/render.c | 62 ++++++++++++----- src/renderer/render_types.h | 2 +- src/resources/gltf.c | 83 +++++++++++++---------- 8 files changed, 158 insertions(+), 78 deletions(-) (limited to 'src/renderer/render.c') diff --git a/assets/shaders/blinn_phong.frag b/assets/shaders/blinn_phong.frag index 095b19a..adcc53e 100644 --- a/assets/shaders/blinn_phong.frag +++ b/assets/shaders/blinn_phong.frag @@ -33,6 +33,7 @@ in VS_OUT { vec3 Normal; vec2 TexCoords; vec4 FragPosLightSpace; + vec4 Color; } fs_in; // --- Uniforms @@ -55,7 +56,8 @@ void main() { result += CalcPointLight(pointLights[i], norm, fs_in.FragPos, viewDir); } - FragColor = vec4(result, 1.0); + // FragColor = vec4(result, 1.0); + FragColor = fs_in.Color + 0.5; } vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir) diff --git a/assets/shaders/skinned.vert b/assets/shaders/skinned.vert index 3d0c2cd..cb2ca85 100644 --- a/assets/shaders/skinned.vert +++ b/assets/shaders/skinned.vert @@ -13,7 +13,7 @@ uniform mat4 lightSpaceMatrix; const int MAX_BONES = 100; const int MAX_BONE_INFLUENCES = 4; -uniform mat4 finalBoneMatrices[MAX_BONES]; +uniform mat4 boneMatrices[MAX_BONES]; // TODO! // Output out VS_OUT { @@ -21,27 +21,39 @@ out VS_OUT { vec3 Normal; vec2 TexCoords; vec4 FragPosLightSpace; + vec4 Color; } vs_out; void main() { - vec4 totalPosition = vec4(0.0f); - for(int i = 0 ; i < MAX_BONE_INFLUENCES ; i++) { - if(inBoneIndices[i] == -1) - continue; - if(inBoneIndices[i] >=MAX_BONES) - { - totalPosition = vec4(inPos,1.0f); - break; - } - vec4 localPosition = finalBoneMatrices[inBoneIndices[i]] * vec4(inPos,1.0f); - totalPosition += localPosition * inWeights[i]; - vec3 localNormal = mat3(finalBoneMatrices[inBoneIndices[i]]) * inNormal; - vs_out.Normal = localNormal; - } - - vs_out.FragPos = vec3(model * vec4(inPos, 1.0)); + // vec4 totalPosition = vec4(0.0f); + // for(int i = 0 ; i < MAX_BONE_INFLUENCES ; i++) { + // if(inBoneIndices[i] == 0) + // continue; + // if(inBoneIndices[i] >=MAX_BONES) + // { + // totalPosition = vec4(inPos,1.0f); + // break; + // } + // vec4 localPosition = finalBoneMatrices[inBoneIndices[i]] * vec4(inPos,1.0f); + // totalPosition += localPosition * inWeights[i]; + // vec3 localNormal = mat3(finalBoneMatrices[inBoneIndices[i]]) * inNormal; + // vs_out.Normal = localNormal; + // } + + + mat4 skinMatrix = // mat4(1.0) + // boneMatrices[int(inBoneIndices.z)]; // should just be the identtiy + inWeights.x * boneMatrices[int(inBoneIndices.x)] + + inWeights.y * boneMatrices[int(inBoneIndices.y)] + + inWeights.z * boneMatrices[int(inBoneIndices.z)] + + inWeights.w * boneMatrices[int(inBoneIndices.w)]; + + vec4 totalPosition = skinMatrix * vec4(inPos, 1.0); + + vs_out.FragPos = vec3(model * totalPosition); // vs_out.Normal = inNormal; vs_out.TexCoords = inTexCoords; vs_out.FragPosLightSpace = lightSpaceMatrix * vec4(vs_out.FragPos, 1.0); + vs_out.Color = inWeights; gl_Position = projection * view * model * totalPosition; } \ No newline at end of file diff --git a/examples/skinned_animation/ex_skinned_animation.c b/examples/skinned_animation/ex_skinned_animation.c index 3138256..43eb715 100644 --- a/examples/skinned_animation/ex_skinned_animation.c +++ b/examples/skinned_animation/ex_skinned_animation.c @@ -1,3 +1,4 @@ +#include #include #include "../example_scene.h" @@ -30,10 +31,22 @@ int main() { core* core = core_bringup(); - model_handle animated_cube_handle = + model_handle handle = model_load_gltf(core, "assets/models/gltf/SimpleSkin/glTF/SimpleSkin.gltf", false); - model* cube = &core->models->data[animated_cube_handle.raw]; - model_upload_meshes(&core->renderer, cube); + + model* simple_skin = &core->models->data[handle.raw]; + + // Okay, right here we've loaded the model. let's assert some facts + assert(simple_skin->animations->len == 1); + assert(simple_skin->animations->data[0].rotation != NULL); + assert(simple_skin->animations->data[0].translation == NULL); + assert(simple_skin->animations->data[0].scale == NULL); + + mesh* m = &simple_skin->meshes->data[0]; + assert(m->is_skinned); + assert(m->bones->len == 2); // 1 root and 1 extra joint + + model_upload_meshes(&core->renderer, simple_skin); scene our_scene = make_default_scene(); @@ -48,7 +61,7 @@ int main() { // Main loop const f32 camera_lateral_speed = 0.2; - const f32 camera_zoom_speed = 0.15; + const f32 camera_zoom_speed = 0.10; // animation // animation_clip track = cube->animations->data[0]; @@ -86,7 +99,7 @@ int main() { quat rot = quat_ident(); transform tf = transform_create(VEC3_ZERO, rot, 1.0); - draw_skinned_model(&core->renderer, &game.camera, cube, tf, &our_scene); + draw_skinned_model(&core->renderer, &game.camera, simple_skin, tf, &our_scene); // gfx_backend_draw_frame(&core->renderer, &game.camera, model, NULL); @@ -94,7 +107,7 @@ int main() { } INFO("Shutting down"); - model_destroy(cube); + model_destroy(simple_skin); core_shutdown(core); diff --git a/src/animation.h b/src/animation.h index 18d3ba9..9d5d03b 100644 --- a/src/animation.h +++ b/src/animation.h @@ -31,6 +31,7 @@ typedef struct keyframes { typedef struct joint { char* name; // optional transform transform_components; + mat4 inverse_bind_matrix; mat4 local_transform; } joint; @@ -61,4 +62,11 @@ typedef struct animation_clip { animation_sampler* weights; } animation_clip; +typedef struct skinned_animation { + mat4* joint_matrices; + size_t n_joints; +} skinned_animation; + +// void animation_update_joint_matrices(animation_clip* ) + void animation_play(animation_clip* clip); \ No newline at end of file diff --git a/src/maths/maths_types.h b/src/maths/maths_types.h index 6d38fc7..53cac55 100644 --- a/src/maths/maths_types.h +++ b/src/maths/maths_types.h @@ -64,4 +64,8 @@ typedef struct transform { typedef struct vec4i { i32 x, y, z, w; -} vec4i; \ No newline at end of file +} vec4i; + +typedef struct vec4u { + u32 x, y, z, w; +} vec4u; \ No newline at end of file diff --git a/src/renderer/render.c b/src/renderer/render.c index e420043..42f6ee4 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -97,6 +97,7 @@ void default_material_init() { } void model_destroy(model* model) { + TRACE("Freeing all data for model %s", model->name); arena_free_all(&model->animation_data_arena); arena_free_storage(&model->animation_data_arena); mesh_darray_free(model->meshes); @@ -175,19 +176,31 @@ void draw_skinned_mesh(renderer* ren, mesh* mesh, transform tf, material* mat, m // bind textures bind_texture(lighting_shader, &mat->diffuse_texture, 0); // bind to slot 0 bind_texture(lighting_shader, &mat->specular_texture, 1); // bind to slot 1 - uniform_f32(lighting_shader.program_id, "material.shininess", 32.); - // upload model transform + // Uniforms + uniform_f32(lighting_shader.program_id, "material.shininess", 32.); mat4 trans = mat4_translation(tf.position); mat4 rot = mat4_rotation(tf.rotation); mat4 scale = mat4_scale(tf.scale); mat4 model_tf = mat4_mult(trans, mat4_mult(rot, scale)); - uniform_mat4f(lighting_shader.program_id, "model", &model_tf); - // upload view & projection matrices uniform_mat4f(lighting_shader.program_id, "view", view); uniform_mat4f(lighting_shader.program_id, "projection", proj); + // bone transforms + size_t n_bones = mesh->bones->len; + + // for now assume correct ordering + mat4* bone_transforms = malloc(n_bones * sizeof(mat4)); + for (int bone_i = 0; bone_i < n_bones; bone_i++) { + bone_transforms[bone_i] = mat4_ident(); + } + + glUniformMatrix4fv(glGetUniformLocation(lighting_shader.program_id, "boneMatrices"), n_bones, + GL_FALSE, &bone_transforms->data[0]); + + free(bone_transforms); + // draw triangles u32 num_vertices = vertex_darray_len(mesh->vertices); draw_primitives(CEL_PRIMITIVE_TOPOLOGY_TRIANGLE, 0, num_vertices); @@ -201,11 +214,11 @@ void draw_skinned_model(renderer* ren, camera* cam, model* model, transform tf, set_shader(ren->skinned); // set camera uniform - uniform_vec3f(ren->blinn_phong.program_id, "viewPos", &cam->position); + uniform_vec3f(ren->skinned.program_id, "viewPos", &cam->position); // set light uniforms - dir_light_upload_uniforms(ren->blinn_phong, &scene->dir_light); + dir_light_upload_uniforms(ren->skinned, &scene->dir_light); for (int i = 0; i < scene->n_point_lights; i++) { - point_light_upload_uniforms(ren->blinn_phong, &scene->point_lights[i], '0' + i); + point_light_upload_uniforms(ren->skinned, &scene->point_lights[i], '0' + i); } for (size_t i = 0; i < mesh_darray_len(model->meshes); i++) { @@ -213,7 +226,8 @@ void draw_skinned_model(renderer* ren, camera* cam, model* model, transform tf, if (vertex_darray_len(m->vertices) == 0) { continue; } - material* mat = &model->materials->data[m->material_index]; + // material* mat = &model->materials->data[m->material_index]; + material* mat = &DEFAULT_MATERIAL; draw_skinned_mesh(ren, m, tf, mat, &view, &proj); } } @@ -272,21 +286,28 @@ void model_upload_meshes(renderer* ren, model* model) { // + j].uv.y; // } // } - size_t vertex_size = mesh.is_skinned - ? sizeof(vec3) * 2 + sizeof(vec2) + sizeof(u32) * 4 + sizeof(vec4) - : sizeof(vec3) * 2 + sizeof(vec2); - if (!mesh.is_skinned) { - printf("sizeof(vertex) -> %ld, vertex_size -> %ld\n", sizeof(vertex), vertex_size); + size_t static_vertex_size = 2 * sizeof(vec3) + sizeof(vec2); + size_t skinned_vertex_size = 2 * sizeof(vec3) + sizeof(vec2) + 4 * sizeof(u32) + sizeof(vec4); + size_t vertex_size = mesh.is_skinned ? skinned_vertex_size : static_vertex_size; + + TRACE("sizeof(vertex) -> %ld, vertex_size -> %ld\n", sizeof(vertex), vertex_size); + if (mesh.is_skinned) { + assert(vertex_size == (12 + 12 + 8 + 16 + 16)); + } else { assert(vertex_size == sizeof(vertex)); + assert(vertex_size == 8 * sizeof(float)); } size_t buffer_size = vertex_size * num_vertices; u8* bytes = malloc(buffer_size); for (int i = 0; i < num_vertices; i++) { u8* p = bytes + vertex_size * i; - u8* bone_data_offset = p + sizeof(u32) * 4 + sizeof(vec4); + u8* bone_data_offset = p + static_vertex_size; memcpy(p, &mesh.vertices->data[i], sizeof(vertex)); - memcpy(bone_data_offset, &mesh.vertex_bone_data->data[i], sizeof(vertex_bone_data)); + if (mesh.is_skinned) { + // printf("") + memcpy(bone_data_offset, &mesh.vertex_bone_data->data[i], sizeof(vertex_bone_data)); + } } // 4. upload data @@ -295,17 +316,22 @@ void model_upload_meshes(renderer* ren, model* model) { // 5. cont. set mesh vertex layout glBindVertexArray(model->meshes->data[mesh_i].vao); // position attribute - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, vertex_size, (void*)0); glEnableVertexAttribArray(0); // normal vector attribute - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float))); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, vertex_size, (void*)(3 * sizeof(float))); glEnableVertexAttribArray(1); // tex coords - glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, vertex_size, (void*)(6 * sizeof(float))); glEnableVertexAttribArray(2); // skinning (optional) if (mesh.is_skinned) { + glEnableVertexAttribArray(3); + glVertexAttribIPointer(3, 4, GL_INT, vertex_size, (void*)(8 * sizeof(float))); + + glEnableVertexAttribArray(4); + glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, vertex_size, (void*)(12 * sizeof(float))); } } diff --git a/src/renderer/render_types.h b/src/renderer/render_types.h index 61571e1..bfed18d 100644 --- a/src/renderer/render_types.h +++ b/src/renderer/render_types.h @@ -120,7 +120,7 @@ typedef struct vertex { } vertex; typedef struct vertex_bone_data { - vec4i joints; /** @brief 4 indices of joints that influence vectors position */ + vec4u joints; /** @brief 4 indices of joints that influence vectors position */ vec4 weights; /** @brief weight (0,1) of each joint */ } vertex_bone_data; diff --git a/src/resources/gltf.c b/src/resources/gltf.c index c7c1f55..7efd2bb 100644 --- a/src/resources/gltf.c +++ b/src/resources/gltf.c @@ -28,7 +28,7 @@ typedef struct face face; KITC_DECL_TYPED_ARRAY(vec3) KITC_DECL_TYPED_ARRAY(vec2) KITC_DECL_TYPED_ARRAY(u32) -KITC_DECL_TYPED_ARRAY(vec4i) +KITC_DECL_TYPED_ARRAY(vec4u) KITC_DECL_TYPED_ARRAY(vec4) KITC_DECL_TYPED_ARRAY(face) // KITC_DECL_TYPED_ARRAY(joint) @@ -86,9 +86,10 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel vec3_darray *tmp_positions = vec3_darray_new(1000); vec3_darray *tmp_normals = vec3_darray_new(1000); vec2_darray *tmp_uvs = vec2_darray_new(1000); - vec4i_darray *tmp_joint_indices = vec4i_darray_new(1000); + vec4u_darray *tmp_joint_indices = vec4u_darray_new(1000); vec4_darray *tmp_weights = vec4_darray_new(1000); joint_darray *tmp_joints = joint_darray_new(256); + vertex_bone_data_darray* tmp_vertex_bone_data = vertex_bone_data_darray_new(1000); cgltf_options options = { 0 }; cgltf_data *data = NULL; @@ -118,6 +119,8 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel size_t num_joints = gltf_skin->joints_count; DEBUG("# Joints %d", num_joints); + cgltf_accessor *gltf_inverse_bind_matrices = gltf_skin->inverse_bind_matrices; + // for each one we'll spit out a joint for (size_t i = 0; i < num_joints; i++) { cgltf_node *joint_node = gltf_skin->joints[i]; @@ -139,6 +142,8 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel // TODO: support scaling as vec instead of float } joint_i.local_transform = transform_to_mat(&joint_i.transform_components); + cgltf_accessor_read_float(gltf_inverse_bind_matrices, i, &joint_i.inverse_bind_matrix.data[0], + 16); joint_darray_push(tmp_joints, joint_i); } } @@ -238,7 +243,7 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel cgltf_accessor *accessor = attribute.data; assert(accessor->component_type == cgltf_component_type_r_16u); assert(accessor->type == cgltf_type_vec4); - vec4i joint_indices; + vec4u joint_indices; vec4 joints_as_floats; for (cgltf_size v = 0; v < accessor->count; ++v) { cgltf_accessor_read_float(accessor, v, &joints_as_floats.x, 4); @@ -246,9 +251,9 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel joint_indices.y = (u32)joints_as_floats.y; joint_indices.z = (u32)joints_as_floats.z; joint_indices.w = (u32)joints_as_floats.w; - // printf("Joints affecting %d %d %d %d\n", joint_indices.x, joint_indices.y, - // joint_indices.z, joint_indices.w); - vec4i_darray_push(tmp_joint_indices, joint_indices); + printf("Joints affecting vertex %d : %d %d %d %d\n", v, joint_indices.x, joint_indices.y, + joint_indices.z, joint_indices.w); + vec4u_darray_push(tmp_joint_indices, joint_indices); } } else if (attribute.type == cgltf_attribute_type_weights) { @@ -260,7 +265,8 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel for (cgltf_size v = 0; v < accessor->count; ++v) { vec4 weights; cgltf_accessor_read_float(accessor, v, &weights.x, 4); - // printf("Weights affecting %f %f %f %f\n", weights.x, weights.y, weights.z, weights.w); + printf("Weights affecting vertex %d : %f %f %f %f\n", v, weights.x, weights.y, weights.z, + weights.w); vec4_darray_push(tmp_weights, weights); } } else { @@ -270,9 +276,38 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel mesh mesh = { 0 }; mesh.vertices = vertex_darray_new(10); + mesh.vertex_bone_data =vertex_bone_data_darray_new(1); + + if (primitive.material != NULL) { + for (int i = 0; i < material_darray_len(out_model->materials); i++) { + printf("%s vs %s \n", primitive.material->name, out_model->materials->data[i].name); + if (strcmp(primitive.material->name, out_model->materials->data[i].name) == 0) { + TRACE("Found material"); + mesh.material_index = i; + break; + } + } + } + + if (is_skinned) { + mesh.is_skinned = true; + // mesh.vertex_bone_data = vertex_bone_data_darray_new(tmp_joint_indices->len); + mesh.bones = joint_darray_new(tmp_joints->len); + for (int i = 0; i < tmp_joint_indices->len; i++) { + vertex_bone_data data; + data.joints = tmp_joint_indices->data[i]; + data.weights = tmp_weights->data[i]; + vertex_bone_data_darray_push(tmp_vertex_bone_data, data); // Push the temp data that aligns with raw vertices + } + for (int i = 0; i < tmp_joints->len; i++) { + joint data = tmp_joints->data[i]; + joint_darray_push(mesh.bones, data); + } + } cgltf_accessor *indices = primitive.indices; if (primitive.indices > 0) { + WARN("indices!"); mesh.has_indices = true; mesh.indices = malloc(indices->count * sizeof(u32)); @@ -293,45 +328,25 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel vert.normal = tmp_normals->data[index]; vert.uv = tmp_uvs->data[index]; vertex_darray_push(mesh.vertices, vert); + + if (is_skinned) { + vertex_bone_data vbd = tmp_vertex_bone_data->data[index]; // create a copy + vertex_bone_data_darray_push(mesh.vertex_bone_data, vbd); + } + // for each vertex do the bone data } } else { mesh.has_indices = false; return false; // TODO } - if (primitive.material != NULL) { - for (int i = 0; i < material_darray_len(out_model->materials); i++) { - printf("%s vs %s \n", primitive.material->name, out_model->materials->data[i].name); - if (strcmp(primitive.material->name, out_model->materials->data[i].name) == 0) { - TRACE("Found material"); - mesh.material_index = i; - break; - } - } - } - - if (is_skinned) { - mesh.vertex_bone_data = vertex_bone_data_darray_new(tmp_joint_indices->len); - mesh.bones = joint_darray_new(tmp_joints->len); - for (int i = 0; i < tmp_joint_indices->len; i++) { - vertex_bone_data data; - data.joints = tmp_joint_indices->data[i]; - data.weights =tmp_weights->data[i]; - vertex_bone_data_darray_push(mesh.vertex_bone_data, data); - } - for (int i = 0; i < tmp_joints->len; i++) { - joint data = tmp_joints->data[i]; - joint_darray_push(mesh.bones, data); - } - } - mesh_darray_push(out_model->meshes, mesh); // clear data for each mesh vec3_darray_clear(tmp_positions); vec3_darray_clear(tmp_normals); vec2_darray_free(tmp_uvs); - vec4i_darray_clear(tmp_joint_indices); + vec4u_darray_clear(tmp_joint_indices); vec4_darray_clear(tmp_weights); joint_darray_clear(tmp_joints); } -- cgit v1.2.3-70-g09d2 From 61d96cf09e2e125f36a94a4c64ed5682fda0df1c Mon Sep 17 00:00:00 2001 From: omnisci3nce <17525998+omnisci3nce@users.noreply.github.com> Date: Sun, 7 Apr 2024 21:46:51 +1000 Subject: its bending but not deforming as expected, looks like rotating around model origin --- examples/skinned_animation/ex_skinned_animation.c | 29 +++++++++++++---------- src/animation.c | 10 ++++---- src/maths/maths.h | 8 ++++--- src/renderer/render.c | 14 ++++++++++- src/resources/gltf.c | 9 +++---- 5 files changed, 45 insertions(+), 25 deletions(-) (limited to 'src/renderer/render.c') diff --git a/examples/skinned_animation/ex_skinned_animation.c b/examples/skinned_animation/ex_skinned_animation.c index 43eb715..d0e305e 100644 --- a/examples/skinned_animation/ex_skinned_animation.c +++ b/examples/skinned_animation/ex_skinned_animation.c @@ -64,8 +64,8 @@ int main() { const f32 camera_zoom_speed = 0.10; // animation - // animation_clip track = cube->animations->data[0]; - // f64 total_time = 0.0; + animation_clip track = simple_skin->animations->data[0]; + f64 total_time = 0.0; while (!should_exit(core)) { input_update(&core->input); @@ -73,17 +73,21 @@ int main() { currentFrame = glfwGetTime(); deltaTime = currentFrame - lastFrame; lastFrame = currentFrame; - // total_time += deltaTime; + total_time += deltaTime; // printf("delta time %f\n", deltaTime); - // f64 t = fmod(total_time, 1.0); + f64 t = fmod(total_time, track.rotation->max); // INFO("Total time: %f", t); vec3 translation = VEC3_ZERO; - if (key_is_pressed(KEYCODE_W) || key_is_pressed(KEYCODE_KEY_UP)) { + if (key_is_pressed(KEYCODE_W)) { translation = vec3_mult(game.camera.front, camera_zoom_speed); + } else if (key_is_pressed(KEYCODE_KEY_UP)) { + translation = vec3_mult(game.camera.up, camera_lateral_speed); + } else if (key_is_pressed(KEYCODE_KEY_DOWN)) { + translation = vec3_mult(game.camera.up, -camera_lateral_speed); } else if (key_is_pressed(KEYCODE_S) || key_is_pressed(KEYCODE_KEY_DOWN)) { translation = vec3_mult(game.camera.front, -camera_zoom_speed); - } else if (key_is_pressed(KEYCODE_A) || key_is_pressed(KEYCODE_KEY_LEFT)) { + } else if (key_is_pressed(KEYCODE_A)) { vec3 lateral = vec3_normalise(vec3_cross(game.camera.front, game.camera.up)); translation = vec3_mult(lateral, -camera_lateral_speed); } else if (key_is_pressed(KEYCODE_D) || key_is_pressed(KEYCODE_KEY_RIGHT)) { @@ -94,14 +98,15 @@ int main() { render_frame_begin(&core->renderer); - mat4 model = mat4_translation(VEC3_ZERO); - // quat rot = animation_sample(track.rotation, t).rotation; - quat rot = quat_ident(); - transform tf = transform_create(VEC3_ZERO, rot, 1.0); + // bone rotation + quat rot = animation_sample(track.rotation, t).rotation; - draw_skinned_model(&core->renderer, &game.camera, simple_skin, tf, &our_scene); + m->bones->data[1].transform_components.rotation = rot; + + // quat rot = quat_ident(); + transform tf = transform_create(VEC3_ZERO, quat_ident(), 1.0); - // gfx_backend_draw_frame(&core->renderer, &game.camera, model, NULL); + draw_skinned_model(&core->renderer, &game.camera, simple_skin, tf, &our_scene); render_frame_end(&core->renderer); } diff --git a/src/animation.c b/src/animation.c index de7e9a2..7a79529 100644 --- a/src/animation.c +++ b/src/animation.c @@ -6,14 +6,14 @@ keyframe animation_sample(animation_sampler *sampler, f32 t) { size_t previous_index = 0; f32 previous_time = 0.0; // look forwards - DEBUG("%d\n", sampler->animation.values.kind); - TRACE("Here %d", sampler->animation.n_timestamps); - for (u32 i = 1; i < sampler->animation.n_timestamps; i++) { + // DEBUG("%d\n", sampler->animation.values.kind); + TRACE("Total timestamps %d", sampler->animation.n_timestamps); + for (u32 i = 0; i < sampler->animation.n_timestamps; i++) { f32 current_time = sampler->animation.timestamps[i]; if (current_time > t) { break; } - previous_time = current_time; + previous_time = sampler->animation.timestamps[i]; previous_index = i; } @@ -28,7 +28,7 @@ keyframe animation_sample(animation_sampler *sampler, f32 t) { f32 time_diff = sampler->animation.timestamps[next_index] - sampler->animation.timestamps[previous_index]; - f32 percent = (t - sampler->animation.timestamps[next_index]) / time_diff; + f32 percent = (t - previous_time) / time_diff; quat interpolated_rot = quat_slerp(sampler->animation.values.values[previous_index].rotation, diff --git a/src/maths/maths.h b/src/maths/maths.h index 76531f2..911b9b7 100644 --- a/src/maths/maths.h +++ b/src/maths/maths.h @@ -302,7 +302,7 @@ static inline mat4 mat4_look_at(vec3 position, vec3 target, vec3 up) { #define TRANSFORM_DEFAULT \ ((transform){ .position = VEC3_ZERO, \ - .rotation = (quat){ .x = 0., .y = 0., .z = 0., .w = 0. }, \ + .rotation = (quat){ .x = 0., .y = 0., .z = 0., .w = 1. }, \ .scale = 1.0, \ .is_dirty = false }) @@ -311,8 +311,10 @@ static transform transform_create(vec3 pos, quat rot, f32 scale) { } static inline mat4 transform_to_mat(transform *tf) { - // TODO: rotation - return mat4_mult(mat4_translation(tf->position), mat4_scale(tf->scale)); + mat4 scale = mat4_scale(tf->scale); + mat4 rotation = mat4_rotation(tf->rotation); + mat4 translation = mat4_translation(tf->position); + return mat4_mult(translation, mat4_mult(rotation, scale)); } // --- Sizing asserts diff --git a/src/renderer/render.c b/src/renderer/render.c index 42f6ee4..d7e2d48 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -1,6 +1,7 @@ #include #include #include +#include "animation.h" #include "maths_types.h" #include "mem.h" #define STB_IMAGE_IMPLEMENTATION @@ -192,8 +193,19 @@ void draw_skinned_mesh(renderer* ren, mesh* mesh, transform tf, material* mat, m // for now assume correct ordering mat4* bone_transforms = malloc(n_bones * sizeof(mat4)); + mat4 parent = mat4_ident(); for (int bone_i = 0; bone_i < n_bones; bone_i++) { - bone_transforms[bone_i] = mat4_ident(); + transform tf = mesh->bones->data[bone_i].transform_components; + mat4 local = transform_to_mat(&mesh->bones->data[bone_i].transform_components); + bone_transforms[bone_i] = mat4_mult(parent, local); + parent = bone_transforms[bone_i]; + } + + // premultiply the inverses + for (int bone_i = 0; bone_i < n_bones; bone_i++) { + joint j = mesh->bones->data[bone_i]; + // bone_transforms[bone_i] = mat4_mult(bone_transforms[bone_i], j.inverse_bind_matrix); + bone_transforms[bone_i] = mat4_mult(bone_transforms[bone_i], j.inverse_bind_matrix); } glUniformMatrix4fv(glGetUniformLocation(lighting_shader.program_id, "boneMatrices"), n_bones, diff --git a/src/resources/gltf.c b/src/resources/gltf.c index 7efd2bb..7668a49 100644 --- a/src/resources/gltf.c +++ b/src/resources/gltf.c @@ -89,7 +89,7 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel vec4u_darray *tmp_joint_indices = vec4u_darray_new(1000); vec4_darray *tmp_weights = vec4_darray_new(1000); joint_darray *tmp_joints = joint_darray_new(256); - vertex_bone_data_darray* tmp_vertex_bone_data = vertex_bone_data_darray_new(1000); + vertex_bone_data_darray *tmp_vertex_bone_data = vertex_bone_data_darray_new(1000); cgltf_options options = { 0 }; cgltf_data *data = NULL; @@ -276,7 +276,7 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel mesh mesh = { 0 }; mesh.vertices = vertex_darray_new(10); - mesh.vertex_bone_data =vertex_bone_data_darray_new(1); + mesh.vertex_bone_data = vertex_bone_data_darray_new(1); if (primitive.material != NULL) { for (int i = 0; i < material_darray_len(out_model->materials); i++) { @@ -297,7 +297,8 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel vertex_bone_data data; data.joints = tmp_joint_indices->data[i]; data.weights = tmp_weights->data[i]; - vertex_bone_data_darray_push(tmp_vertex_bone_data, data); // Push the temp data that aligns with raw vertices + vertex_bone_data_darray_push(tmp_vertex_bone_data, + data); // Push the temp data that aligns with raw vertices } for (int i = 0; i < tmp_joints->len; i++) { joint data = tmp_joints->data[i]; @@ -330,7 +331,7 @@ bool model_load_gltf_str(const char *file_string, const char *filepath, str8 rel vertex_darray_push(mesh.vertices, vert); if (is_skinned) { - vertex_bone_data vbd = tmp_vertex_bone_data->data[index]; // create a copy + vertex_bone_data vbd = tmp_vertex_bone_data->data[index]; // create a copy vertex_bone_data_darray_push(mesh.vertex_bone_data, vbd); } // for each vertex do the bone data -- cgit v1.2.3-70-g09d2 From 374567ae4b18627f9ab410e7e5a38b1e7aeb04c5 Mon Sep 17 00:00:00 2001 From: Omniscient <17525998+omnisci3nce@users.noreply.github.com> Date: Sat, 20 Apr 2024 22:02:34 +1000 Subject: it works with negating Y axis of local transform and inverse bind matrix. I have NO IDEA why --- examples/skinned_animation/ex_skinned_animation.c | 2 ++ src/renderer/render.c | 20 +++++++++++++------- 2 files changed, 15 insertions(+), 7 deletions(-) (limited to 'src/renderer/render.c') diff --git a/examples/skinned_animation/ex_skinned_animation.c b/examples/skinned_animation/ex_skinned_animation.c index d0e305e..220908a 100644 --- a/examples/skinned_animation/ex_skinned_animation.c +++ b/examples/skinned_animation/ex_skinned_animation.c @@ -46,6 +46,8 @@ int main() { assert(m->is_skinned); assert(m->bones->len == 2); // 1 root and 1 extra joint + // assert(false); + model_upload_meshes(&core->renderer, simple_skin); scene our_scene = make_default_scene(); diff --git a/src/renderer/render.c b/src/renderer/render.c index d7e2d48..d9b45cf 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -195,18 +195,24 @@ void draw_skinned_mesh(renderer* ren, mesh* mesh, transform tf, material* mat, m mat4* bone_transforms = malloc(n_bones * sizeof(mat4)); mat4 parent = mat4_ident(); for (int bone_i = 0; bone_i < n_bones; bone_i++) { + joint j = mesh->bones->data[bone_i]; transform tf = mesh->bones->data[bone_i].transform_components; - mat4 local = transform_to_mat(&mesh->bones->data[bone_i].transform_components); - bone_transforms[bone_i] = mat4_mult(parent, local); + tf.position.y = -tf.position.y; + mat4 local = transform_to_mat(&tf); + mat4 inverse = j.inverse_bind_matrix; + inverse.data[13] = -inverse.data[13]; + mat4 intemediate = mat4_mult(local, inverse); + + bone_transforms[bone_i] = intemediate; parent = bone_transforms[bone_i]; } // premultiply the inverses - for (int bone_i = 0; bone_i < n_bones; bone_i++) { - joint j = mesh->bones->data[bone_i]; - // bone_transforms[bone_i] = mat4_mult(bone_transforms[bone_i], j.inverse_bind_matrix); - bone_transforms[bone_i] = mat4_mult(bone_transforms[bone_i], j.inverse_bind_matrix); - } + // for (int bone_i = 0; bone_i < n_bones; bone_i++) { + // joint j = mesh->bones->data[bone_i]; + // // bone_transforms[bone_i] = mat4_mult(bone_transforms[bone_i], j.inverse_bind_matrix); + // bone_transforms[bone_i] = mat4_mult(bone_transforms[bone_i], j.inverse_bind_matrix); + // } glUniformMatrix4fv(glGetUniformLocation(lighting_shader.program_id, "boneMatrices"), n_bones, GL_FALSE, &bone_transforms->data[0]); -- cgit v1.2.3-70-g09d2 From 6585326fe177a0dc710ef6624a301a36db992e96 Mon Sep 17 00:00:00 2001 From: Omniscient <17525998+omnisci3nce@users.noreply.github.com> Date: Sun, 21 Apr 2024 12:15:01 +1000 Subject: minor cleanup --- examples/obj_loading/ex_obj_loading.c | 2 ++ src/renderer/render.c | 14 ++++---------- 2 files changed, 6 insertions(+), 10 deletions(-) (limited to 'src/renderer/render.c') diff --git a/examples/obj_loading/ex_obj_loading.c b/examples/obj_loading/ex_obj_loading.c index e225cb2..aaed2a0 100644 --- a/examples/obj_loading/ex_obj_loading.c +++ b/examples/obj_loading/ex_obj_loading.c @@ -1,3 +1,4 @@ +#include #include #include @@ -25,6 +26,7 @@ int main() { model_handle backpack_handle = model_load_obj(core, "assets/models/obj/backpack/backpack.obj", true); model* backpack = &core->models->data[backpack_handle.raw]; + assert(backpack->meshes->data->is_skinned == false); // 2. upload vertex data to gpu model_upload_meshes(&core->renderer, backpack); // 3. create a camera diff --git a/src/renderer/render.c b/src/renderer/render.c index 83011f8..b688613 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -176,14 +176,9 @@ void draw_mesh(renderer* ren, mesh* mesh, mat4* model_tf, material* mat, mat4* v bind_texture(lighting_shader, &mat->specular_texture, 1); // bind to slot 1 uniform_f32(lighting_shader.program_id, "material.shininess", 32.); - // upload model transform - // mat4 trans = mat4_translation(tf.position); - // mat4 rot = mat4_rotation(tf.rotation); - // mat4 scale = mat4_scale(tf.scale); - // mat4 model_tf = mat4_mult(trans, mat4_mult(rot, scale)); + // upload model, view, and projection matrices uniform_mat4f(lighting_shader.program_id, "model", model_tf); - // upload view & projection matrices uniform_mat4f(lighting_shader.program_id, "view", view); uniform_mat4f(lighting_shader.program_id, "projection", proj); @@ -333,7 +328,7 @@ void model_upload_meshes(renderer* ren, model* model) { size_t skinned_vertex_size = 2 * sizeof(vec3) + sizeof(vec2) + 4 * sizeof(u32) + sizeof(vec4); size_t vertex_size = mesh.is_skinned ? skinned_vertex_size : static_vertex_size; - TRACE("sizeof(vertex) -> %ld, vertex_size -> %ld\n", sizeof(vertex), vertex_size); + // TRACE("sizeof(vertex) -> %ld, vertex_size -> %ld\n", sizeof(vertex), vertex_size); if (mesh.is_skinned) { assert(vertex_size == (12 + 12 + 8 + 16 + 16)); } else { @@ -345,10 +340,9 @@ void model_upload_meshes(renderer* ren, model* model) { for (int i = 0; i < num_vertices; i++) { u8* p = bytes + vertex_size * i; - u8* bone_data_offset = p + static_vertex_size; - memcpy(p, &mesh.vertices->data[i], sizeof(vertex)); + memcpy(p, &mesh.vertices->data[i], static_vertex_size); if (mesh.is_skinned) { - // printf("") + u8* bone_data_offset = p + static_vertex_size; memcpy(bone_data_offset, &mesh.vertex_bone_data->data[i], sizeof(vertex_bone_data)); } } -- cgit v1.2.3-70-g09d2 From 1fc69f183bf8f105048b9d47e096ccd402107bbb Mon Sep 17 00:00:00 2001 From: Omniscient <17525998+omnisci3nce@users.noreply.github.com> Date: Sun, 21 Apr 2024 12:34:00 +1000 Subject: misc cleanup --- assets/shaders/blinn_phong.frag | 2 +- assets/shaders/blinn_phong.vert | 2 + examples/gltf_loading/ex_gltf_loading.c | 2 +- examples/obj_loading/ex_obj_loading.c | 2 +- src/animation.h | 2 +- src/maths/maths.h | 65 ++++++++++++++++----------------- src/renderer/render.c | 31 +--------------- src/renderer/render_types.h | 9 ++--- 8 files changed, 42 insertions(+), 73 deletions(-) (limited to 'src/renderer/render.c') diff --git a/assets/shaders/blinn_phong.frag b/assets/shaders/blinn_phong.frag index adcc53e..a0ba905 100644 --- a/assets/shaders/blinn_phong.frag +++ b/assets/shaders/blinn_phong.frag @@ -56,7 +56,7 @@ void main() { result += CalcPointLight(pointLights[i], norm, fs_in.FragPos, viewDir); } - // FragColor = vec4(result, 1.0); +// FragColor = vec4(result, 1.0); FragColor = fs_in.Color + 0.5; } diff --git a/assets/shaders/blinn_phong.vert b/assets/shaders/blinn_phong.vert index 6028178..06dc5e7 100644 --- a/assets/shaders/blinn_phong.vert +++ b/assets/shaders/blinn_phong.vert @@ -15,6 +15,7 @@ out VS_OUT { vec3 Normal; vec2 TexCoords; vec4 FragPosLightSpace; + vec4 Color; } vs_out; void main() { @@ -22,5 +23,6 @@ void main() { vs_out.Normal = inNormal; vs_out.TexCoords = inTexCoords; vs_out.FragPosLightSpace = lightSpaceMatrix * vec4(vs_out.FragPos, 1.0); + vs_out.Color = vec4(1.0); gl_Position = projection * view * model * vec4(inPos, 1.0); } \ No newline at end of file diff --git a/examples/gltf_loading/ex_gltf_loading.c b/examples/gltf_loading/ex_gltf_loading.c index b181426..900cf1b 100644 --- a/examples/gltf_loading/ex_gltf_loading.c +++ b/examples/gltf_loading/ex_gltf_loading.c @@ -56,7 +56,7 @@ int main() { scene our_scene = { .dir_light = dir_light, .n_point_lights = 4 }; memcpy(&our_scene.point_lights, &point_lights, sizeof(point_light[4])); - while (!glfwWindowShouldClose(core->renderer.window)) { + while (!should_exit(core)) { currentFrame = glfwGetTime(); deltaTime = currentFrame - lastFrame; lastFrame = currentFrame; diff --git a/examples/obj_loading/ex_obj_loading.c b/examples/obj_loading/ex_obj_loading.c index aaed2a0..906816b 100644 --- a/examples/obj_loading/ex_obj_loading.c +++ b/examples/obj_loading/ex_obj_loading.c @@ -55,7 +55,7 @@ int main() { memcpy(&our_scene.point_lights, &point_lights, sizeof(point_light[4])); // --- Enter Main loop - while (!glfwWindowShouldClose(core->renderer.window)) { + while (!should_exit(core)) { input_update(&core->input); threadpool_process_results(&core->threadpool, 1); diff --git a/src/animation.h b/src/animation.h index 9d5d03b..5462e65 100644 --- a/src/animation.h +++ b/src/animation.h @@ -29,7 +29,7 @@ typedef struct keyframes { } keyframes; typedef struct joint { - char* name; // optional + char* name; // optional transform transform_components; mat4 inverse_bind_matrix; mat4 local_transform; diff --git a/src/maths/maths.h b/src/maths/maths.h index 8e48435..e0d39d7 100644 --- a/src/maths/maths.h +++ b/src/maths/maths.h @@ -91,45 +91,42 @@ static inline quat quat_slerp(quat a, quat b, f32 percentage) { quat q1 = quat_normalise(b); // Compute the cosine of the angle between the two vectors. - f32 dot = quat_dot(q0, q1); - - // If the dot product is negative, slerp won't take - // the shorter path. Note that v1 and -v1 are equivalent when - // the negation is applied to all four components. Fix by - // reversing one quaternion. - if (dot < 0.0f) { - q1.x = -q1.x; - q1.y = -q1.y; - q1.z = -q1.z; - q1.w = -q1.w; - dot = -dot; - } + f32 dot = quat_dot(q0, q1); + + // If the dot product is negative, slerp won't take + // the shorter path. Note that v1 and -v1 are equivalent when + // the negation is applied to all four components. Fix by + // reversing one quaternion. + if (dot < 0.0f) { + q1.x = -q1.x; + q1.y = -q1.y; + q1.z = -q1.z; + q1.w = -q1.w; + dot = -dot; + } - const f32 DOT_THRESHOLD = 0.9995f; - if (dot > DOT_THRESHOLD) { - // If the inputs are too close for comfort, linearly interpolate - // and normalize the result. - out_quaternion = (quat){q0.x + ((q1.x - q0.x) * percentage), - q0.y + ((q1.y - q0.y) * percentage), - q0.z + ((q1.z - q0.z) * percentage), - q0.w + ((q1.w - q0.w) * percentage)}; + const f32 DOT_THRESHOLD = 0.9995f; + if (dot > DOT_THRESHOLD) { + // If the inputs are too close for comfort, linearly interpolate + // and normalize the result. + out_quaternion = + (quat){ q0.x + ((q1.x - q0.x) * percentage), q0.y + ((q1.y - q0.y) * percentage), + q0.z + ((q1.z - q0.z) * percentage), q0.w + ((q1.w - q0.w) * percentage) }; - return quat_normalise(out_quaternion); - } + return quat_normalise(out_quaternion); + } - // Since dot is in range [0, DOT_THRESHOLD], acos is safe - f32 theta_0 = cos(dot); // theta_0 = angle between input vectors - f32 theta = theta_0 * percentage; // theta = angle between v0 and result - f32 sin_theta = sin(theta); // compute this value only once - f32 sin_theta_0 = sin(theta_0); // compute this value only once + // Since dot is in range [0, DOT_THRESHOLD], acos is safe + f32 theta_0 = cos(dot); // theta_0 = angle between input vectors + f32 theta = theta_0 * percentage; // theta = angle between v0 and result + f32 sin_theta = sin(theta); // compute this value only once + f32 sin_theta_0 = sin(theta_0); // compute this value only once - f32 s0 = - cos(theta) - - dot * sin_theta / sin_theta_0; // == sin(theta_0 - theta) / sin(theta_0) - f32 s1 = sin_theta / sin_theta_0; + f32 s0 = cos(theta) - dot * sin_theta / sin_theta_0; // == sin(theta_0 - theta) / sin(theta_0) + f32 s1 = sin_theta / sin_theta_0; - return (quat){(q0.x * s0) + (q1.x * s1), (q0.y * s0) + (q1.y * s1), - (q0.z * s0) + (q1.z * s1), (q0.w * s0) + (q1.w * s1)}; + return (quat){ (q0.x * s0) + (q1.x * s1), (q0.y * s0) + (q1.y * s1), (q0.z * s0) + (q1.z * s1), + (q0.w * s0) + (q1.w * s1) }; } // --- Matrix Implementations diff --git a/src/renderer/render.c b/src/renderer/render.c index b688613..b1e2a46 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -176,7 +176,6 @@ void draw_mesh(renderer* ren, mesh* mesh, mat4* model_tf, material* mat, mat4* v bind_texture(lighting_shader, &mat->specular_texture, 1); // bind to slot 1 uniform_f32(lighting_shader.program_id, "material.shininess", 32.); - // upload model, view, and projection matrices uniform_mat4f(lighting_shader.program_id, "model", model_tf); uniform_mat4f(lighting_shader.program_id, "view", view); @@ -295,35 +294,6 @@ void model_upload_meshes(renderer* ren, model* model) { // TRACE("Uploading vertex array data: %d verts", num_vertices); total_verts += num_vertices; - // TODO: convert this garbage into a function - f32 verts[num_vertices * 8]; - // for each face - // for (int i = 0; i < (num_vertices / 3); i++) { - // // for each vert in face - // for (int j = 0; j < 3; j++) { - // size_t stride = (i * 24) + j * 8; - // // printf("i: %d, stride: %ld, loc %d\n", i, stride, i * 3 + j); - // vertex vert = model->meshes->data[mesh_i].vertices->data[i]; - // // printf("pos %f %f %f\n", vert.position.x, vert.position.y, vert.position.z); - // // printf("norm %f %f %f\n", vert.normal.x, vert.normal.y, vert.normal.z); - // // printf("tex %f %f\n", vert.uv.x, vert.uv.y); - // verts[stride + 0] = - // ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].position.x; - // verts[stride + 1] = - // ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].position.y; - // verts[stride + 2] = - // ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].position.z; - // verts[stride + 3] = - // ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].normal.x; - // verts[stride + 4] = - // ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].normal.y; - // verts[stride + 5] = - // ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].normal.z; - // verts[stride + 6] = ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + - // j].uv.x; verts[stride + 7] = ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 - // + j].uv.y; - // } - // } size_t static_vertex_size = 2 * sizeof(vec3) + sizeof(vec2); size_t skinned_vertex_size = 2 * sizeof(vec3) + sizeof(vec2) + 4 * sizeof(u32) + sizeof(vec4); size_t vertex_size = mesh.is_skinned ? skinned_vertex_size : static_vertex_size; @@ -335,6 +305,7 @@ void model_upload_meshes(renderer* ren, model* model) { assert(vertex_size == sizeof(vertex)); assert(vertex_size == 8 * sizeof(float)); } + size_t buffer_size = vertex_size * num_vertices; u8* bytes = malloc(buffer_size); diff --git a/src/renderer/render_types.h b/src/renderer/render_types.h index 6e69708..423b58f 100644 --- a/src/renderer/render_types.h +++ b/src/renderer/render_types.h @@ -127,17 +127,16 @@ typedef struct vertex_bone_data { #include "animation.h" #ifndef TYPED_VERTEX_ARRAY -KITC_DECL_TYPED_ARRAY(vertex) // creates "vertex_darray" +KITC_DECL_TYPED_ARRAY(vertex) // creates "vertex_darray" KITC_DECL_TYPED_ARRAY(vertex_bone_data) // creates "skinned_vertex_darray" KITC_DECL_TYPED_ARRAY(joint) #define TYPED_VERTEX_ARRAY #endif - typedef struct mesh { - vertex_darray* vertices; - vertex_bone_data_darray* vertex_bone_data; // only used if model needs it - joint_darray* bones; + vertex_darray *vertices; + vertex_bone_data_darray *vertex_bone_data; // only used if model needs it + joint_darray *bones; bool is_skinned; u32 vertex_size; /** size in bytes of each vertex including necessary padding */ bool has_indices; -- cgit v1.2.3-70-g09d2 From 69b1487e3e063cbecba96706c550d417b2f24e37 Mon Sep 17 00:00:00 2001 From: omnisci3nce Date: Sat, 27 Apr 2024 18:15:56 +1000 Subject: getting us compiling on windows --- examples/main_loop/ex_main_loop.c | 3 +- src/core.c | 8 +- src/core.h | 4 +- src/platform/path.c | 6 +- src/renderer/archive/render.c | 413 +++++++++++++++++ src/renderer/archive/render.h | 46 ++ src/renderer/archive/render_backend.h | 39 ++ src/renderer/archive/render_types.h | 210 +++++++++ src/renderer/backends/backend_dx11.c | 4 +- src/renderer/backends/backend_dx11.h | 27 ++ src/renderer/cleanroom/ral.h | 86 ---- src/renderer/cleanroom/types.h | 130 +----- src/renderer/ral.h | 87 ++++ src/renderer/ral_types.h | 93 ++++ src/renderer/render.c | 395 +---------------- src/renderer/render.h | 41 +- src/renderer/render_backend.h | 39 -- src/renderer/render_types.h | 277 ++++-------- src/resources/gltf.c | 810 +++++++++++++++++----------------- src/resources/obj.c | 595 +++++++++++++------------ xmake.lua | 139 +++--- 21 files changed, 1840 insertions(+), 1612 deletions(-) create mode 100644 src/renderer/archive/render.c create mode 100644 src/renderer/archive/render.h create mode 100644 src/renderer/archive/render_backend.h create mode 100644 src/renderer/archive/render_types.h create mode 100644 src/renderer/backends/backend_dx11.h delete mode 100644 src/renderer/cleanroom/ral.h create mode 100644 src/renderer/ral.h create mode 100644 src/renderer/ral_types.h delete mode 100644 src/renderer/render_backend.h (limited to 'src/renderer/render.c') diff --git a/examples/main_loop/ex_main_loop.c b/examples/main_loop/ex_main_loop.c index 8819f7c..4e1f6a9 100644 --- a/examples/main_loop/ex_main_loop.c +++ b/examples/main_loop/ex_main_loop.c @@ -4,7 +4,6 @@ #include "core.h" #include "maths.h" #include "render.h" -#include "render_backend.h" int main() { core* core = core_bringup(); @@ -14,7 +13,7 @@ int main() { // Main loop while (!glfwWindowShouldClose(core->renderer.window)) { input_update(&core->input); - threadpool_process_results(&core->threadpool, 1); + // threadpool_process_results(&core->threadpool, 1); render_frame_begin(&core->renderer); diff --git a/src/core.c b/src/core.c index 0db8962..714505f 100644 --- a/src/core.c +++ b/src/core.c @@ -8,7 +8,7 @@ #include "log.h" #include "render.h" #include "render_types.h" -#include "threadpool.h" +// #include "threadpool.h" #define SCR_WIDTH 1000 #define SCR_HEIGHT 1000 @@ -21,10 +21,10 @@ core* core_bringup() { .scr_height = SCR_HEIGHT, .clear_colour = (vec3){ .08, .08, .1 } }; c->renderer.config = conf; - c->renderer.backend_state = NULL; + c->renderer.backend_context = NULL; - threadpool_create(&c->threadpool, 6, 256); - threadpool_set_ctx(&c->threadpool, c); // Gives the threadpool access to the core + // threadpool_create(&c->threadpool, 6, 256); + // threadpool_set_ctx(&c->threadpool, c); // Gives the threadpool access to the core // initialise all subsystems if (!renderer_init(&c->renderer)) { diff --git a/src/core.h b/src/core.h index be88c53..a122448 100644 --- a/src/core.h +++ b/src/core.h @@ -6,13 +6,13 @@ #include "screenspace.h" #include "terrain.h" #include "text.h" -#include "threadpool.h" +// #include "threadpool.h" typedef struct core { const char* app_name; // foundations renderer renderer; - threadpool threadpool; + // threadpool threadpool; // systems input_state input; text_system_state text; diff --git a/src/platform/path.c b/src/platform/path.c index 9572941..bee4e9c 100644 --- a/src/platform/path.c +++ b/src/platform/path.c @@ -1,12 +1,12 @@ #include "path.h" -#include #include #include #include "mem.h" #include "str.h" #if defined(CEL_PLATFORM_LINUX) || defined(CEL_PLATFORM_MAC) +#include path_opt path_parent(arena* a, const char* path) { // Duplicate the string because dirname doesnt like const literals char* path_copy = arena_alloc(a, strlen(path) + 1); @@ -17,4 +17,6 @@ path_opt path_parent(arena* a, const char* path) { #endif #ifdef CEL_PLATFORM_WINDOWS // TODO: path_opt path_parent(const char* path) -#endif \ No newline at end of file +#endif + +path_opt path_parent(arena* a, const char* path) {} \ No newline at end of file diff --git a/src/renderer/archive/render.c b/src/renderer/archive/render.c new file mode 100644 index 0000000..b1e2a46 --- /dev/null +++ b/src/renderer/archive/render.c @@ -0,0 +1,413 @@ +#include +#include +#include +#include "animation.h" +#include "maths_types.h" +#include "mem.h" +#include "transform_hierarchy.h" +#define STB_IMAGE_IMPLEMENTATION +#include + +#define STB_TRUETYPE_IMPLEMENTATION +#include + +#include "render.h" +#include "render_types.h" + +#include +#include + +#include "defines.h" +#include "log.h" +#include "maths.h" +#include "render_backend.h" + +// FIXME: get rid of these and store dynamic screen realestate +// in renderer +#define SCR_WIDTH 1000 +#define SCR_HEIGHT 1000 + +material DEFAULT_MATERIAL = { 0 }; + +bool renderer_init(renderer* ren) { + INFO("Renderer init"); + + // NOTE: all platforms use GLFW at the moment but thats subject to change + glfwInit(); + +#if defined(CEL_REND_BACKEND_OPENGL) + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); +#elif defined(CEL_REND_BACKEND_VULKAN) + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); +#endif + + // glfw window creation + GLFWwindow* window = glfwCreateWindow(ren->config.scr_width, ren->config.scr_height, + ren->config.window_name, NULL, NULL); + if (window == NULL) { + ERROR("Failed to create GLFW window\n"); + glfwTerminate(); + return false; + } + ren->window = window; + + glfwMakeContextCurrent(ren->window); + + DEBUG("init graphics api backend"); + if (!gfx_backend_init(ren)) { + FATAL("Couldnt load graphics api backend"); + return false; + } + + ren->blinn_phong = + shader_create_separate("assets/shaders/blinn_phong.vert", "assets/shaders/blinn_phong.frag"); + + ren->skinned = + shader_create_separate("assets/shaders/skinned.vert", "assets/shaders/blinn_phong.frag"); + + default_material_init(); + + return true; +} + +void renderer_shutdown(renderer* ren) {} + +void render_frame_begin(renderer* ren) { + vec3 color = ren->config.clear_colour; + clear_screen(color); +} +void render_frame_end(renderer* ren) { + // present frame + glfwSwapBuffers(ren->window); + glfwPollEvents(); +} + +void default_material_init() { + INFO("Load default material") + DEFAULT_MATERIAL.ambient_colour = (vec3){ 0.5, 0.5, 0.5 }; + DEFAULT_MATERIAL.diffuse = (vec3){ 0.8, 0.8, 0.8 }; + DEFAULT_MATERIAL.specular = (vec3){ 1.0, 1.0, 1.0 }; + DEFAULT_MATERIAL.diffuse_texture = texture_data_load("assets/textures/white1x1.png", false); + DEFAULT_MATERIAL.specular_texture = texture_data_load("assets/textures/black1x1.png", false); + DEFAULT_MATERIAL.spec_exponent = 32.0; + strcpy(DEFAULT_MATERIAL.name, "Default"); + texture_data_upload(&DEFAULT_MATERIAL.diffuse_texture); + texture_data_upload(&DEFAULT_MATERIAL.specular_texture); +} + +void model_destroy(model* model) { + TRACE("Freeing all data for model %s", model->name); + arena_free_all(&model->animation_data_arena); + arena_free_storage(&model->animation_data_arena); + mesh_darray_free(model->meshes); + material_darray_free(model->materials); + if (model->is_uploaded) { + // Delete gpu buffer data + for (u32 i = 0; i < mesh_darray_len(model->meshes); i++) { + // FIXME: dont leak Opengl + glDeleteBuffers(1, &model->meshes->data[i].vbo); + glDeleteVertexArrays(1, &model->meshes->data[i].vao); + } + } +} + +typedef struct draw_ctx { + model_darray* models; + renderer* ren; + camera* cam; + scene* scene; +} draw_ctx; +bool draw_scene_node(transform_node* node, void* ctx_data) { + if (!node || !node->parent) return true; + draw_ctx* ctx = ctx_data; + model* m = &ctx->models->data[node->model.raw]; + draw_model(ctx->ren, ctx->cam, m, &node->world_matrix_tf, ctx->scene); + return true; +} + +void draw_scene(arena* frame, model_darray* models, renderer* ren, camera* camera, + transform_hierarchy* tfh, scene* scene) { + draw_ctx* ctx = arena_alloc(frame, sizeof(draw_ctx)); + ctx->models = models; + ctx->ren = ren; + ctx->cam = camera; + ctx->scene = scene; + transform_hierarchy_dfs(transform_hierarchy_root_node(tfh), draw_scene_node, true, ctx); +} + +void draw_model(renderer* ren, camera* camera, model* model, mat4* model_tf, scene* scene) { + // TRACE("Drawing model: %s", model->name); + mat4 view; + mat4 proj; + camera_view_projection(camera, SCR_HEIGHT, SCR_WIDTH, &view, &proj); + + set_shader(ren->blinn_phong); + + // set camera uniform + uniform_vec3f(ren->blinn_phong.program_id, "viewPos", &camera->position); + // set light uniforms + dir_light_upload_uniforms(ren->blinn_phong, &scene->dir_light); + for (int i = 0; i < scene->n_point_lights; i++) { + point_light_upload_uniforms(ren->blinn_phong, &scene->point_lights[i], '0' + i); + } + + for (size_t i = 0; i < mesh_darray_len(model->meshes); i++) { + mesh* m = &model->meshes->data[i]; + if (vertex_darray_len(m->vertices) == 0) { + continue; + } + // TRACE("Drawing mesh %d", i); + material* mat = &model->materials->data[m->material_index]; + draw_mesh(ren, m, model_tf, mat, &view, &proj); + } +} + +void draw_mesh(renderer* ren, mesh* mesh, mat4* model_tf, material* mat, mat4* view, mat4* proj) { + shader lighting_shader = ren->blinn_phong; + + // bind buffer + bind_mesh_vertex_buffer(ren->backend_state, mesh); + + // bind textures + bind_texture(lighting_shader, &mat->diffuse_texture, 0); // bind to slot 0 + bind_texture(lighting_shader, &mat->specular_texture, 1); // bind to slot 1 + uniform_f32(lighting_shader.program_id, "material.shininess", 32.); + + // upload model, view, and projection matrices + uniform_mat4f(lighting_shader.program_id, "model", model_tf); + uniform_mat4f(lighting_shader.program_id, "view", view); + uniform_mat4f(lighting_shader.program_id, "projection", proj); + + // draw triangles + u32 num_vertices = vertex_darray_len(mesh->vertices); + draw_primitives(CEL_PRIMITIVE_TOPOLOGY_TRIANGLE, 0, num_vertices); +} + +void draw_skinned_mesh(renderer* ren, mesh* mesh, transform tf, material* mat, mat4* view, + mat4* proj) { + shader lighting_shader = ren->skinned; + + // bind buffer + bind_mesh_vertex_buffer(ren->backend_state, mesh); + + // bind textures + bind_texture(lighting_shader, &mat->diffuse_texture, 0); // bind to slot 0 + bind_texture(lighting_shader, &mat->specular_texture, 1); // bind to slot 1 + + // Uniforms + uniform_f32(lighting_shader.program_id, "material.shininess", 32.); + mat4 trans = mat4_translation(tf.position); + mat4 rot = mat4_rotation(tf.rotation); + mat4 scale = mat4_scale(tf.scale); + mat4 model_tf = mat4_mult(trans, mat4_mult(rot, scale)); + uniform_mat4f(lighting_shader.program_id, "model", &model_tf); + uniform_mat4f(lighting_shader.program_id, "view", view); + uniform_mat4f(lighting_shader.program_id, "projection", proj); + + // bone transforms + size_t n_bones = mesh->bones->len; + + // for now assume correct ordering + mat4* bone_transforms = malloc(n_bones * sizeof(mat4)); + mat4 parent = mat4_ident(); + for (int bone_i = 0; bone_i < n_bones; bone_i++) { + joint j = mesh->bones->data[bone_i]; + transform tf = mesh->bones->data[bone_i].transform_components; + tf.position.y = -tf.position.y; + mat4 local = transform_to_mat(&tf); + mat4 inverse = j.inverse_bind_matrix; + inverse.data[13] = -inverse.data[13]; + mat4 intemediate = mat4_mult(local, inverse); + + bone_transforms[bone_i] = intemediate; + parent = bone_transforms[bone_i]; + } + + // premultiply the inverses + // for (int bone_i = 0; bone_i < n_bones; bone_i++) { + // joint j = mesh->bones->data[bone_i]; + // // bone_transforms[bone_i] = mat4_mult(bone_transforms[bone_i], j.inverse_bind_matrix); + // bone_transforms[bone_i] = mat4_mult(bone_transforms[bone_i], j.inverse_bind_matrix); + // } + + glUniformMatrix4fv(glGetUniformLocation(lighting_shader.program_id, "boneMatrices"), n_bones, + GL_FALSE, &bone_transforms->data[0]); + + free(bone_transforms); + + // draw triangles + u32 num_vertices = vertex_darray_len(mesh->vertices); + draw_primitives(CEL_PRIMITIVE_TOPOLOGY_TRIANGLE, 0, num_vertices); +} + +void draw_skinned_model(renderer* ren, camera* cam, model* model, transform tf, scene* scene) { + mat4 view; + mat4 proj; + camera_view_projection(cam, SCR_HEIGHT, SCR_WIDTH, &view, &proj); + + set_shader(ren->skinned); + + // set camera uniform + uniform_vec3f(ren->skinned.program_id, "viewPos", &cam->position); + // set light uniforms + dir_light_upload_uniforms(ren->skinned, &scene->dir_light); + for (int i = 0; i < scene->n_point_lights; i++) { + point_light_upload_uniforms(ren->skinned, &scene->point_lights[i], '0' + i); + } + + for (size_t i = 0; i < mesh_darray_len(model->meshes); i++) { + mesh* m = &model->meshes->data[i]; + if (vertex_darray_len(m->vertices) == 0) { + continue; + } + // material* mat = &model->materials->data[m->material_index]; + material* mat = &DEFAULT_MATERIAL; + draw_skinned_mesh(ren, m, tf, mat, &view, &proj); + } +} + +void model_upload_meshes(renderer* ren, model* model) { + INFO("Upload mesh vertex data to GPU for model %s", model->name); + + size_t num_meshes = mesh_darray_len(model->meshes); + u32 VBOs[num_meshes]; + u32 VAOs[num_meshes]; + glGenBuffers(num_meshes, VBOs); + glGenVertexArrays(num_meshes, VAOs); + + u64 total_verts = 0; + + TRACE("num meshes %d", num_meshes); + + // upload each mesh to the GPU + for (int mesh_i = 0; mesh_i < num_meshes; mesh_i++) { + mesh mesh = model->meshes->data[mesh_i]; + model->meshes->data[mesh_i].vao = VAOs[mesh_i]; + model->meshes->data[mesh_i].vbo = VBOs[mesh_i]; + // 3. bind buffers + glBindBuffer(GL_ARRAY_BUFFER, VBOs[mesh_i]); + + size_t num_vertices = vertex_darray_len(model->meshes->data[mesh_i].vertices); + // TRACE("Uploading vertex array data: %d verts", num_vertices); + total_verts += num_vertices; + + size_t static_vertex_size = 2 * sizeof(vec3) + sizeof(vec2); + size_t skinned_vertex_size = 2 * sizeof(vec3) + sizeof(vec2) + 4 * sizeof(u32) + sizeof(vec4); + size_t vertex_size = mesh.is_skinned ? skinned_vertex_size : static_vertex_size; + + // TRACE("sizeof(vertex) -> %ld, vertex_size -> %ld\n", sizeof(vertex), vertex_size); + if (mesh.is_skinned) { + assert(vertex_size == (12 + 12 + 8 + 16 + 16)); + } else { + assert(vertex_size == sizeof(vertex)); + assert(vertex_size == 8 * sizeof(float)); + } + + size_t buffer_size = vertex_size * num_vertices; + u8* bytes = malloc(buffer_size); + + for (int i = 0; i < num_vertices; i++) { + u8* p = bytes + vertex_size * i; + memcpy(p, &mesh.vertices->data[i], static_vertex_size); + if (mesh.is_skinned) { + u8* bone_data_offset = p + static_vertex_size; + memcpy(bone_data_offset, &mesh.vertex_bone_data->data[i], sizeof(vertex_bone_data)); + } + } + + // 4. upload data + glBufferData(GL_ARRAY_BUFFER, buffer_size, bytes, GL_STATIC_DRAW); + + // 5. cont. set mesh vertex layout + glBindVertexArray(model->meshes->data[mesh_i].vao); + // position attribute + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, vertex_size, (void*)0); + glEnableVertexAttribArray(0); + // normal vector attribute + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, vertex_size, (void*)(3 * sizeof(float))); + glEnableVertexAttribArray(1); + // tex coords + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, vertex_size, (void*)(6 * sizeof(float))); + glEnableVertexAttribArray(2); + + // skinning (optional) + if (mesh.is_skinned) { + glEnableVertexAttribArray(3); + glVertexAttribIPointer(3, 4, GL_INT, vertex_size, (void*)(8 * sizeof(float))); + + glEnableVertexAttribArray(4); + glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, vertex_size, (void*)(12 * sizeof(float))); + } + } + + INFO("Uploaded %d submeshes with a total of %d vertices\n", num_meshes, total_verts); + + // 6. reset buffer + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +texture texture_data_load(const char* path, bool invert_y) { + TRACE("Load texture %s", path); + + // load the file data + // texture loading + int width, height, num_channels; + stbi_set_flip_vertically_on_load(invert_y); + +#pragma GCC diagnostic ignored "-Wpointer-sign" + char* data = stbi_load(path, &width, &height, &num_channels, 0); // STBI_rgb_alpha); + if (data) { + DEBUG("loaded texture: %s", path); + } else { + WARN("failed to load texture"); + } + + unsigned int channel_type; + if (num_channels == 4) { + channel_type = GL_RGBA; + } else { + channel_type = GL_RGB; + } + + return (texture){ .texture_id = 0, + .width = width, + .height = height, + .channel_count = num_channels, + .channel_type = channel_type, + .name = "TODO: Texture names", + .image_data = data }; +} + +void dir_light_upload_uniforms(shader shader, directional_light* light) { + uniform_vec3f(shader.program_id, "dirLight.direction", &light->direction); + uniform_vec3f(shader.program_id, "dirLight.ambient", &light->ambient); + uniform_vec3f(shader.program_id, "dirLight.diffuse", &light->diffuse); + uniform_vec3f(shader.program_id, "dirLight.specular", &light->specular); +} + +void point_light_upload_uniforms(shader shader, point_light* light, char index) { + char position_str[] = "pointLights[x].position"; + position_str[12] = (char)index; + char ambient_str[] = "pointLights[x].ambient"; + ambient_str[12] = (char)index; + char diffuse_str[] = "pointLights[x].diffuse"; + diffuse_str[12] = (char)index; + char specular_str[] = "pointLights[x].specular"; + specular_str[12] = (char)index; + char constant_str[] = "pointLights[x].constant"; + constant_str[12] = (char)index; + char linear_str[] = "pointLights[x].linear"; + linear_str[12] = (char)index; + char quadratic_str[] = "pointLights[x].quadratic"; + quadratic_str[12] = (char)index; + uniform_vec3f(shader.program_id, position_str, &light->position); + uniform_vec3f(shader.program_id, ambient_str, &light->ambient); + uniform_vec3f(shader.program_id, diffuse_str, &light->diffuse); + uniform_vec3f(shader.program_id, specular_str, &light->specular); + uniform_f32(shader.program_id, constant_str, light->constant); + uniform_f32(shader.program_id, linear_str, light->linear); + uniform_f32(shader.program_id, quadratic_str, light->quadratic); +} \ No newline at end of file diff --git a/src/renderer/archive/render.h b/src/renderer/archive/render.h new file mode 100644 index 0000000..31cf3b0 --- /dev/null +++ b/src/renderer/archive/render.h @@ -0,0 +1,46 @@ +/** + * @file render.h + * @author your name (you@domain.com) + * @brief Renderer frontend + * @version 0.1 + * @date 2024-03-21 + * + * @copyright Copyright (c) 2024 + * + */ +#pragma once + +#include "camera.h" +#include "loaders.h" +#include "render_types.h" +#include "transform_hierarchy.h" + +// --- Lifecycle +/** @brief initialise the render system frontend */ +bool renderer_init(renderer* ren); +/** @brief shutdown the render system frontend */ +void renderer_shutdown(renderer* ren); + +void renderer_on_resize(renderer* ren); + +struct render_packet; + +// --- Frame + +void render_frame_begin(renderer* ren); +void render_frame_end(renderer* ren); +void render_frame_draw(renderer* ren); + +// --- models meshes +void model_upload_meshes(renderer* ren, model* model); +void draw_model(renderer* ren, camera* camera, model* model, mat4* tf, scene* scene); +void draw_mesh(renderer* ren, mesh* mesh, mat4* tf, material* mat, mat4* view, mat4* proj); +void draw_scene(arena* frame, model_darray* models, renderer* ren, camera* camera, + transform_hierarchy* tfh, scene* scene); + +void draw_skinned_model(renderer* ren, camera* cam, model* model, transform tf, scene* scene); + +void model_destroy(model* model); + +// --- +texture texture_data_load(const char* path, bool invert_y); // #frontend diff --git a/src/renderer/archive/render_backend.h b/src/renderer/archive/render_backend.h new file mode 100644 index 0000000..da30bcc --- /dev/null +++ b/src/renderer/archive/render_backend.h @@ -0,0 +1,39 @@ +/** + * @brief Renderer backend + */ +#pragma once + +#include "camera.h" +#include "maths_types.h" +#include "render_types.h" + +/// --- Lifecycle + +/** @brief Initialise the graphics API backend */ +bool gfx_backend_init(renderer* ren); +void gfx_backend_shutdown(renderer* ren); + +void gfx_backend_draw_frame(renderer* ren, camera* camera, mat4 model, texture* tex); +void gfx_backend_update_global_state(mat4 projection, mat4 view, vec3 view_pos, vec4 ambient_colour, + i32 mode); + +void clear_screen(vec3 colour); + +void texture_data_upload(texture* tex); +void bind_texture(shader s, texture* tex, u32 slot); +void bind_mesh_vertex_buffer(void* backend, mesh* mesh); +void draw_primitives(cel_primitive_topology primitive, u32 start_index, u32 count); + +shader shader_create_separate(const char* vert_shader, const char* frag_shader); +void set_shader(shader s); + +// --- Uniforms + +/** @brief upload a vec3 of f32 to a uniform */ +void uniform_vec3f(u32 program_id, const char* uniform_name, vec3* value); +/** @brief upload a single f32 to a uniform */ +void uniform_f32(u32 program_id, const char* uniform_name, f32 value); +/** @brief upload a integer to a uniform */ +void uniform_i32(u32 program_id, const char* uniform_name, i32 value); +/** @brief upload a mat4 of f32 to a uniform */ +void uniform_mat4f(u32 program_id, const char* uniform_name, mat4* value); \ No newline at end of file diff --git a/src/renderer/archive/render_types.h b/src/renderer/archive/render_types.h new file mode 100644 index 0000000..13a6651 --- /dev/null +++ b/src/renderer/archive/render_types.h @@ -0,0 +1,210 @@ +// /** +// * @file render_types.h +// * @author Omniscient +// * @brief Type definitions for the majority of data required by the renderer system +// * @date 2024-02-24 +// * +// */ +// #pragma once + +#include "animation.h" +#include "darray.h" +#include "maths.h" +#include "maths_types.h" +#include "str.h" + +struct GLFWwindow; + +#define MAX_MATERIAL_NAME_LEN 256 +#define MAX_TEXTURE_NAME_LEN 256 + +// #ifndef RESOURCE_HANDLE_DEFS +// // CORE_DEFINE_HANDLE(model_handle); +// #define ABSENT_MODEL_HANDLE 999999999 +// // CORE_DEFINE_HANDLE(texture_handle); +// #define RESOURCE_HANDLE_DEFS +// #endif + +/* @brief Opaque wrapper around a shader program */ +typedef struct shader { + u32 program_id; +} shader; + +/** @brief configuration passed to the renderer at init time */ +typedef struct renderer_config { + char window_name[256]; + u32 scr_width, scr_height; + vec3 clear_colour; /** colour that the screen gets cleared to every frame */ +} renderer_config; + +// typedef struct frame_stats { +// u64 last_time; +// } frame_stats; + +typedef struct renderer { + struct GLFWwindow *window; /** Currently all platforms use GLFW*/ + void *backend_state; /** Graphics API-specific state */ + renderer_config config; + // shaders + shader blinn_phong; + shader skinned; +} renderer; + +// // --- Lighting & Materials + +typedef struct texture { + u32 texture_id; + char name[MAX_TEXTURE_NAME_LEN]; + void *image_data; + void *backend_data; + u32 width; + u32 height; + u8 channel_count; + u32 channel_type; +} texture; + +typedef struct blinn_phong_material { + char name[MAX_MATERIAL_NAME_LEN]; + texture diffuse_texture; + char diffuse_tex_path[256]; + texture specular_texture; + char specular_tex_path[256]; + vec3 ambient_colour; + vec3 diffuse; + vec3 specular; + f32 spec_exponent; + bool is_loaded; + bool is_uploaded; +} blinn_phong_material; +typedef blinn_phong_material material; // when we start using PBR, this will no longer be the +// case + +// // the default blinn-phong material. MUST be initialised with the function below +// extern material DEFAULT_MATERIAL; +// void default_material_init(); + +#ifndef TYPED_MATERIAL_ARRAY +KITC_DECL_TYPED_ARRAY(material) // creates "material_darray" +#define TYPED_MATERIAL_ARRAY +#endif + +#ifndef TYPED_ANIMATION_CLIP_ARRAY +KITC_DECL_TYPED_ARRAY(animation_clip) // creates "material_darray" +#define TYPED_ANIMATION_CLIP_ARRAY +#endif + +// // lights +// typedef struct point_light { +// vec3 position; +// f32 constant, linear, quadratic; +// vec3 ambient; +// vec3 diffuse; +// vec3 specular; +// } point_light; + +// typedef struct directional_light { +// vec3 direction; +// vec3 ambient; +// vec3 diffuse; +// vec3 specular; +// } directional_light; + +// void point_light_upload_uniforms(shader shader, point_light *light, char index); +// void dir_light_upload_uniforms(shader shader, directional_light *light); + +// // --- Models & Meshes + +// /** @brief Vertex format for a static mesh */ +typedef struct vertex { + vec3 position; + vec3 normal; + vec2 uv; +} vertex; + +typedef struct vertex_bone_data { + vec4u joints; /** @brief 4 indices of joints that influence vectors position */ + vec4 weights; /** @brief weight (0,1) of each joint */ +} vertex_bone_data; + +#include "animation.h" +#ifndef TYPED_VERTEX_ARRAY +KITC_DECL_TYPED_ARRAY(vertex) // creates "vertex_darray" +KITC_DECL_TYPED_ARRAY(vertex_bone_data) // creates "skinned_vertex_darray" +KITC_DECL_TYPED_ARRAY(joint) +#define TYPED_VERTEX_ARRAY +#endif + +typedef struct mesh { + vertex_darray *vertices; + vertex_bone_data_darray *vertex_bone_data; // only used if model needs it + joint_darray *bones; + bool is_skinned; + u32 vertex_size; /** size in bytes of each vertex including necessary padding */ + bool has_indices; + u32 *indices; + u32 indices_len; + size_t material_index; + u32 vbo, vao; /** OpenGL data. TODO: dont leak OpenGL details */ +} mesh; + +// #ifndef TYPED_MESH_ARRAY +// KITC_DECL_TYPED_ARRAY(mesh) // creates "mesh_darray" +// #define TYPED_MESH_ARRAY +// #endif + +typedef struct model { + str8 name; + mesh_darray *meshes; + aabb_3d bbox; + material_darray *materials; + animation_clip_darray *animations; + arena animation_data_arena; + bool is_loaded; + bool is_uploaded; +} model; + +// #ifndef TYPED_MODEL_ARRAY +// KITC_DECL_TYPED_ARRAY(model) // creates "model_darray" +// #define TYPED_MODEL_ARRAY +// #endif + +// // --- Scene + +// // NOTE: This struct won't stay like this for a long time. It's somewhat temporary +// // in order to get a basic scene working without putting burden on the caller of +// // draw_model() +// typedef struct scene { +// directional_light dir_light; +// point_light point_lights[4]; +// size_t n_point_lights; +// } scene; + +// // --- Graphics API related + +// // typedef enum cel_primitive_topology { +// // CEL_PRIMITIVE_TOPOLOGY_POINT, +// // CEL_PRIMITIVE_TOPOLOGY_LINE, +// // CEL_PRIMITIVE_TOPOLOGY_LINE_STRIP, +// // CEL_PRIMITIVE_TOPOLOGY_TRIANGLE, +// // CEL_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, +// // CEL_PRIMITIVE_TOPOLOGY_COUNT +// // } cel_primitive_topology; + +// // typedef enum gpu_texture_type { +// // TEXTURE_TYPE_2D, +// // TEXTURE_TYPE_3D, +// // TEXTURE_TYPE_2D_ARRAY, +// // TEXTURE_TYPE_CUBE_MAP, +// // TEXTURE_TYPE_COUNT +// // } gpu_texture_type; + +// // typedef enum gpu_texture_format { +// // TEXTURE_FORMAT_8_8_8_8_RGBA_UNORM, +// // TEXTURE_FORMAT_DEPTH_DEFAULT, +// // TEXTURE_FORMAT_COUNT +// // } gpu_texture_format; + +// // typedef enum pipeline_kind { +// // GRAPHICS, +// // COMPUTE, +// // } pipeline_kind; \ No newline at end of file diff --git a/src/renderer/backends/backend_dx11.c b/src/renderer/backends/backend_dx11.c index d991f03..7e48853 100644 --- a/src/renderer/backends/backend_dx11.c +++ b/src/renderer/backends/backend_dx11.c @@ -1,3 +1,5 @@ -#if CEL_REND_BACKEND_DX11 +#if defined(CEL_REND_BACKEND_DX11) +#include +#include #endif \ No newline at end of file diff --git a/src/renderer/backends/backend_dx11.h b/src/renderer/backends/backend_dx11.h new file mode 100644 index 0000000..163ca97 --- /dev/null +++ b/src/renderer/backends/backend_dx11.h @@ -0,0 +1,27 @@ +#pragma once +#include "ral.h" + +#define GPU_SWAPCHAIN_IMG_COUNT 2 + +typedef struct gpu_swapchain { +} gpu_swapchain; +typedef struct gpu_device { + // VkPhysicalDevice physical_device; + // VkDevice logical_device; + // VkPhysicalDeviceProperties properties; + // VkPhysicalDeviceFeatures features; + // VkPhysicalDeviceMemoryProperties memory; + // VkCommandPool pool; +} gpu_device; +typedef struct gpu_pipeline { +} gpu_pipeline; + +typedef struct gpu_renderpass { + // VkRenderPass vk_handle; + // VkFramebuffer framebuffers[GPU_SWAPCHAIN_IMG_COUNT]; + // u32 +} gpu_renderpass; + +typedef struct gpu_cmd_encoder { + // VkCommandBuffer cmd_buffer; +} gpu_cmd_encoder; \ No newline at end of file diff --git a/src/renderer/cleanroom/ral.h b/src/renderer/cleanroom/ral.h deleted file mode 100644 index 15eb027..0000000 --- a/src/renderer/cleanroom/ral.h +++ /dev/null @@ -1,86 +0,0 @@ -/** - * @file ral.h - * @author your name (you@domain.com) - * @brief Render Abstraction Layer - * @details API that a graphics backend *must* implement - * @version 0.1 - * @date 2024-03-31 - * - * @copyright Copyright (c) 2024 - * - */ -#pragma once - -#include "cleanroom/types.h" -#include "defines.h" - -// Forward declare structs -typedef struct gpu_swapchain gpu_swapchain; -typedef struct gpu_device gpu_device; -typedef struct gpu_pipeline gpu_pipeline; -typedef struct gpu_renderpass gpu_renderpass; -typedef struct gpu_cmd_encoder gpu_cmd_encoder; // Recording -typedef struct gpu_cmd_buffer gpu_cmd_buffer; // Ready for submission - -enum pipeline_kind { - GRAPHICS, - COMPUTE, -} pipeline_kind; - -typedef struct shader_desc { - const char* debug_name; - str8 filepath; // where it came from - str8 glsl; // contents -} shader_desc; - -struct pipeline_desc { - shader_desc vs; /** @brief Vertex shader stage */ - shader_desc fs; /** @brief Fragment shader stage */ -}; - -// lifecycle functions -gpu_device* gpu_device_create(); -void gpu_device_destroy(); - -gpu_renderpass* gpu_renderpass_create(); -void gpu_renderpass_destroy(gpu_renderpass* pass); - -gpu_pipeline* gpu_pipeline_create(enum pipeline_kind kind, struct pipeline_desc description); -void gpu_pipeline_destroy(gpu_pipeline* pipeline); - -void gpu_cmd_encoder_begin(); -void gpu_cmd_encoder_begin_render(); -void gpu_cmd_encoder_begin_compute(); - -/* Actual commands that we can encode */ -void encode_buffer_copy(gpu_cmd_encoder* encoder, buffer_handle src, u64 src_offset, - buffer_handle dst, u64 dst_offset, u64 copy_size); -void encode_clear_buffer(gpu_cmd_encoder* encoder, buffer_handle buf); -void encode_set_pipeline(gpu_cmd_encoder* encoder, gpu_pipeline* pipeline); -// render pass -void encode_set_vertex_buffer(gpu_cmd_encoder* encoder, buffer_handle buf); -void encode_set_index_buffer(gpu_cmd_encoder* encoder, buffer_handle buf); -void encode_set_bind_group(); -void encode_draw(gpu_cmd_encoder* encoder); -void encode_draw_indexed(gpu_cmd_encoder* encoder, u64 index_count); - -// FUTURE: compute passes - -/** @brief Finish recording and return a command buffer that can be submitted to a queue */ -gpu_cmd_buffer gpu_cmd_encoder_finish(gpu_cmd_encoder* encoder); - -void gpu_queue_submit(gpu_cmd_buffer* buffer); - -// Buffers -void gpu_buffer_create(u64 size); -void gpu_buffer_destroy(buffer_handle buffer); -void gpu_buffer_upload(); -void gpu_buffer_bind(buffer_handle buffer); - -// Textures -void gpu_texture_create(); -void gpu_texture_destroy(); -void gpu_texture_upload(); - -// Samplers -void gpu_sampler_create(); \ No newline at end of file diff --git a/src/renderer/cleanroom/types.h b/src/renderer/cleanroom/types.h index 7a6cfbd..0a28b1c 100644 --- a/src/renderer/cleanroom/types.h +++ b/src/renderer/cleanroom/types.h @@ -3,22 +3,11 @@ #include "defines.h" #include "maths_types.h" #include "str.h" +#include "render_types.h" -CORE_DEFINE_HANDLE(buffer_handle); -CORE_DEFINE_HANDLE(texture_handle); -CORE_DEFINE_HANDLE(sampler_handle); -CORE_DEFINE_HANDLE(shader_handle); -CORE_DEFINE_HANDLE(model_handle); +// typedef struct transform_hierarchy { +// } transform_hierarchy; -typedef struct transform_hierarchy { -} transform_hierarchy; - -/** @brief Texture Description - used by texture creation functions */ -typedef struct texture_desc { - // gpu_texture_type tex_type; - // gpu_texture_format format; - // u32x2 extents; -} texture_desc; /* - render_types.h @@ -27,102 +16,10 @@ typedef struct texture_desc { - render.h ? */ -// gpu types -typedef enum gpu_primitive_topology { - CEL_PRIMITIVE_TOPOLOGY_POINT, - CEL_PRIMITIVE_TOPOLOGY_LINE, - CEL_PRIMITIVE_TOPOLOGY_LINE_STRIP, - CEL_PRIMITIVE_TOPOLOGY_TRIANGLE, - CEL_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, - CEL_PRIMITIVE_TOPOLOGY_COUNT -} cel_primitive_topology; - -typedef enum gpu_texture_type { - CEL_TEXTURE_TYPE_2D, - CEL_TEXTURE_TYPE_3D, - CEL_TEXTURE_TYPE_2D_ARRAY, - CEL_TEXTURE_TYPE_CUBE_MAP, - CEL_TEXTURE_TYPE_COUNT -} gpu_texture_type; - -typedef enum gpu_texture_format { - CEL_TEXTURE_FORMAT_8_8_8_8_RGBA_UNORM, - CEL_TEXTURE_FORMAT_DEPTH_DEFAULT, - CEL_TEXTURE_FORMAT_COUNT -} gpu_texture_format; - /* render_types */ typedef struct model pbr_material; typedef struct model bp_material; // blinn-phong -#include "maths_types.h" - -typedef enum vertex_format { - VERTEX_STATIC_3D, - VERTEX_SPRITE, - VERTEX_SKINNED, - VERTEX_COUNT -} vertex_format; - -typedef union vertex { - struct { - vec3 position; - vec4 colour; - vec2 tex_coords; - vec3 normal; - } static_3d; /** @brief standard vertex format for static geometry in 3D */ - - struct { - vec2 position; - vec4 colour; - vec2 tex_coords; - } sprite; /** @brief vertex format for 2D sprites or quads */ - - struct { - vec3 position; - vec4 colour; - vec2 tex_coords; - vec3 normal; - vec4i bone_ids; // Integer vector for bone IDs - vec4 bone_weights; // Weight of each bone's influence - } skinned_3d; /** @brief vertex format for skeletal (animated) geometry in 3D */ -} vertex; - -KITC_DECL_TYPED_ARRAY(vertex) -KITC_DECL_TYPED_ARRAY(u32) - -typedef struct geometry_data { - vertex_format format; - vertex_darray vertices; - bool has_indices; - u32_darray indices; - vec3 colour; /** Optional: set vertex colours */ -} geometry_data; - -void geo_set_vertex_colours(geometry_data* geo, vec4 colour); - -typedef struct mesh { - buffer_handle vertex_buffer; - buffer_handle index_buffer; - u32 index_count; - bool has_indices; - geometry_data* vertices; // NULL means it has been freed -} mesh; - -/* Hot reloading: -C side - reload_model(): - - load model from disk using existing loader - - remove from transform graph so it isnt tried to be drawn - - - -*/ - -// TODO: move to some sort of render layer (not inside the abstraction layer) -typedef struct model { - str8 debug_name; - mesh* meshes; - u32 mesh_count; -} model; // ? How to tie together materials and shaders @@ -149,27 +46,6 @@ typedef struct model { /* --- Renderer layer */ /* render.h */ -typedef struct renderer { - void* backend_context; -} renderer; - -bool renderer_init(renderer* ren); -void renderer_shutdown(renderer* ren); - -// frontend -- these can be called from say a loop in an example, or via FFI -texture_handle texture_create(const char* debug_name, texture_desc description, const u8* data); - -// Frontend Resources -void texture_data_upload(texture_handle texture); -buffer_handle buffer_create(const char* debug_name, u64 size); -bool buffer_destroy(buffer_handle buffer); -sampler_handle sampler_create(); - -void shader_hot_reload(const char* filepath); - -// models and meshes are implemented **in terms of the above** -mesh mesh_create(geometry_data* geometry); -model_handle model_load(const char* debug_name, const char* filepath); // Drawing diff --git a/src/renderer/ral.h b/src/renderer/ral.h new file mode 100644 index 0000000..fd83e76 --- /dev/null +++ b/src/renderer/ral.h @@ -0,0 +1,87 @@ +/** + * @file ral.h + * @author your name (you@domain.com) + * @brief Render Abstraction Layer + * @details API that a graphics backend *must* implement + * @version 0.1 + * @date 2024-03-31 + * + * @copyright Copyright (c) 2024 + * + */ +#pragma once + +#include "ral_types.h" +#include "defines.h" +#include "str.h" + +// Forward declare structs +typedef struct gpu_swapchain gpu_swapchain; +typedef struct gpu_device gpu_device; +typedef struct gpu_pipeline gpu_pipeline; +typedef struct gpu_renderpass gpu_renderpass; +typedef struct gpu_cmd_encoder gpu_cmd_encoder; // Recording +typedef struct gpu_cmd_buffer gpu_cmd_buffer; // Ready for submission + +enum pipeline_kind { + GRAPHICS, + COMPUTE, +}; + +typedef struct shader_desc { + const char* debug_name; + str8 filepath; // where it came from + str8 glsl; // contents +} shader_desc; + +struct pipeline_desc { + shader_desc vs; /** @brief Vertex shader stage */ + shader_desc fs; /** @brief Fragment shader stage */ +}; + +// lifecycle functions +gpu_device* gpu_device_create(); +void gpu_device_destroy(); + +gpu_renderpass* gpu_renderpass_create(); +void gpu_renderpass_destroy(gpu_renderpass* pass); + +gpu_pipeline* gpu_pipeline_create(enum pipeline_kind kind, struct pipeline_desc description); +void gpu_pipeline_destroy(gpu_pipeline* pipeline); + +void gpu_cmd_encoder_begin(); +void gpu_cmd_encoder_begin_render(); +void gpu_cmd_encoder_begin_compute(); + +/* Actual commands that we can encode */ +void encode_buffer_copy(gpu_cmd_encoder* encoder, buffer_handle src, u64 src_offset, + buffer_handle dst, u64 dst_offset, u64 copy_size); +void encode_clear_buffer(gpu_cmd_encoder* encoder, buffer_handle buf); +void encode_set_pipeline(gpu_cmd_encoder* encoder, gpu_pipeline* pipeline); +// render pass +void encode_set_vertex_buffer(gpu_cmd_encoder* encoder, buffer_handle buf); +void encode_set_index_buffer(gpu_cmd_encoder* encoder, buffer_handle buf); +void encode_set_bind_group(); +void encode_draw(gpu_cmd_encoder* encoder); +void encode_draw_indexed(gpu_cmd_encoder* encoder, u64 index_count); + +// FUTURE: compute passes + +/** @brief Finish recording and return a command buffer that can be submitted to a queue */ +gpu_cmd_buffer gpu_cmd_encoder_finish(gpu_cmd_encoder* encoder); + +void gpu_queue_submit(gpu_cmd_buffer* buffer); + +// Buffers +void gpu_buffer_create(u64 size); +void gpu_buffer_destroy(buffer_handle buffer); +void gpu_buffer_upload(); +void gpu_buffer_bind(buffer_handle buffer); + +// Textures +void gpu_texture_create(); +void gpu_texture_destroy(); +void gpu_texture_upload(); + +// Samplers +void gpu_sampler_create(); \ No newline at end of file diff --git a/src/renderer/ral_types.h b/src/renderer/ral_types.h new file mode 100644 index 0000000..9b1ef62 --- /dev/null +++ b/src/renderer/ral_types.h @@ -0,0 +1,93 @@ +/** + * @file ral_types.h + * @author your name (you@domain.com) + * @brief + * @version 0.1 + * @date 2024-04-27 + * + * @copyright Copyright (c) 2024 + * + */ +#pragma once + +#include "defines.h" +#include "maths_types.h" +#include "darray.h" + +#ifndef RENDERER_TYPED_HANDLES +CORE_DEFINE_HANDLE(buffer_handle); +CORE_DEFINE_HANDLE(texture_handle); +CORE_DEFINE_HANDLE(sampler_handle); +CORE_DEFINE_HANDLE(shader_handle); +CORE_DEFINE_HANDLE(model_handle); +#define ABSENT_MODEL_HANDLE 999999999 +#define RENDERER_TYPED_HANDLES +#endif + +// gpu types +typedef enum gpu_primitive_topology { + CEL_PRIMITIVE_TOPOLOGY_POINT, + CEL_PRIMITIVE_TOPOLOGY_LINE, + CEL_PRIMITIVE_TOPOLOGY_LINE_STRIP, + CEL_PRIMITIVE_TOPOLOGY_TRIANGLE, + CEL_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, + CEL_PRIMITIVE_TOPOLOGY_COUNT +} cel_primitive_topology; + +typedef enum gpu_texture_type { + CEL_TEXTURE_TYPE_2D, + CEL_TEXTURE_TYPE_3D, + CEL_TEXTURE_TYPE_2D_ARRAY, + CEL_TEXTURE_TYPE_CUBE_MAP, + CEL_TEXTURE_TYPE_COUNT +} gpu_texture_type; + +typedef enum gpu_texture_format { + CEL_TEXTURE_FORMAT_8_8_8_8_RGBA_UNORM, + CEL_TEXTURE_FORMAT_DEPTH_DEFAULT, + CEL_TEXTURE_FORMAT_COUNT +} gpu_texture_format; + +/** @brief Texture Description - used by texture creation functions */ +typedef struct texture_desc { + // gpu_texture_type tex_type; + // gpu_texture_format format; + // u32x2 extents; +} texture_desc; + +typedef enum vertex_format { + VERTEX_STATIC_3D, + VERTEX_SPRITE, + VERTEX_SKINNED, + VERTEX_COUNT +} vertex_format; + +typedef union vertex { + struct { + vec3 position; + vec4 colour; + vec2 tex_coords; + vec3 normal; + } static_3d; /** @brief standard vertex format for static geometry in 3D */ + + struct { + vec2 position; + vec4 colour; + vec2 tex_coords; + } sprite; /** @brief vertex format for 2D sprites or quads */ + + struct { + vec3 position; + vec4 colour; + vec2 tex_coords; + vec3 normal; + vec4i bone_ids; // Integer vector for bone IDs + vec4 bone_weights; // Weight of each bone's influence + } skinned_3d; /** @brief vertex format for skeletal (animated) geometry in 3D */ +} vertex; + +#ifndef TYPED_VERTEX_ARRAY +KITC_DECL_TYPED_ARRAY(vertex) +KITC_DECL_TYPED_ARRAY(u32) +#define TYPED_VERTEX_ARRAY +#endif \ No newline at end of file diff --git a/src/renderer/render.c b/src/renderer/render.c index b1e2a46..8cd7421 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -1,36 +1,9 @@ -#include -#include -#include -#include "animation.h" -#include "maths_types.h" -#include "mem.h" -#include "transform_hierarchy.h" -#define STB_IMAGE_IMPLEMENTATION -#include - -#define STB_TRUETYPE_IMPLEMENTATION -#include - -#include "render.h" -#include "render_types.h" - -#include #include - -#include "defines.h" -#include "log.h" -#include "maths.h" -#include "render_backend.h" - -// FIXME: get rid of these and store dynamic screen realestate -// in renderer -#define SCR_WIDTH 1000 -#define SCR_HEIGHT 1000 - -material DEFAULT_MATERIAL = { 0 }; +#include "render.h" +#include "camera.h" bool renderer_init(renderer* ren) { - INFO("Renderer init"); + // INFO("Renderer init"); // NOTE: all platforms use GLFW at the moment but thats subject to change glfwInit(); @@ -48,7 +21,7 @@ bool renderer_init(renderer* ren) { GLFWwindow* window = glfwCreateWindow(ren->config.scr_width, ren->config.scr_height, ren->config.window_name, NULL, NULL); if (window == NULL) { - ERROR("Failed to create GLFW window\n"); + // ERROR("Failed to create GLFW window\n"); glfwTerminate(); return false; } @@ -56,358 +29,26 @@ bool renderer_init(renderer* ren) { glfwMakeContextCurrent(ren->window); - DEBUG("init graphics api backend"); - if (!gfx_backend_init(ren)) { - FATAL("Couldnt load graphics api backend"); - return false; - } + // DEBUG("init graphics api backend"); + // if (!gfx_backend_init(ren)) { + // FATAL("Couldnt load graphics api backend"); + // return false; + // } - ren->blinn_phong = - shader_create_separate("assets/shaders/blinn_phong.vert", "assets/shaders/blinn_phong.frag"); + // ren->blinn_phong = + // shader_create_separate("assets/shaders/blinn_phong.vert", "assets/shaders/blinn_phong.frag"); - ren->skinned = - shader_create_separate("assets/shaders/skinned.vert", "assets/shaders/blinn_phong.frag"); + // ren->skinned = + // shader_create_separate("assets/shaders/skinned.vert", "assets/shaders/blinn_phong.frag"); - default_material_init(); + // default_material_init(); return true; } - void renderer_shutdown(renderer* ren) {} -void render_frame_begin(renderer* ren) { - vec3 color = ren->config.clear_colour; - clear_screen(color); -} -void render_frame_end(renderer* ren) { - // present frame - glfwSwapBuffers(ren->window); - glfwPollEvents(); -} - -void default_material_init() { - INFO("Load default material") - DEFAULT_MATERIAL.ambient_colour = (vec3){ 0.5, 0.5, 0.5 }; - DEFAULT_MATERIAL.diffuse = (vec3){ 0.8, 0.8, 0.8 }; - DEFAULT_MATERIAL.specular = (vec3){ 1.0, 1.0, 1.0 }; - DEFAULT_MATERIAL.diffuse_texture = texture_data_load("assets/textures/white1x1.png", false); - DEFAULT_MATERIAL.specular_texture = texture_data_load("assets/textures/black1x1.png", false); - DEFAULT_MATERIAL.spec_exponent = 32.0; - strcpy(DEFAULT_MATERIAL.name, "Default"); - texture_data_upload(&DEFAULT_MATERIAL.diffuse_texture); - texture_data_upload(&DEFAULT_MATERIAL.specular_texture); -} - -void model_destroy(model* model) { - TRACE("Freeing all data for model %s", model->name); - arena_free_all(&model->animation_data_arena); - arena_free_storage(&model->animation_data_arena); - mesh_darray_free(model->meshes); - material_darray_free(model->materials); - if (model->is_uploaded) { - // Delete gpu buffer data - for (u32 i = 0; i < mesh_darray_len(model->meshes); i++) { - // FIXME: dont leak Opengl - glDeleteBuffers(1, &model->meshes->data[i].vbo); - glDeleteVertexArrays(1, &model->meshes->data[i].vao); - } - } -} - -typedef struct draw_ctx { - model_darray* models; - renderer* ren; - camera* cam; - scene* scene; -} draw_ctx; -bool draw_scene_node(transform_node* node, void* ctx_data) { - if (!node || !node->parent) return true; - draw_ctx* ctx = ctx_data; - model* m = &ctx->models->data[node->model.raw]; - draw_model(ctx->ren, ctx->cam, m, &node->world_matrix_tf, ctx->scene); - return true; -} - -void draw_scene(arena* frame, model_darray* models, renderer* ren, camera* camera, - transform_hierarchy* tfh, scene* scene) { - draw_ctx* ctx = arena_alloc(frame, sizeof(draw_ctx)); - ctx->models = models; - ctx->ren = ren; - ctx->cam = camera; - ctx->scene = scene; - transform_hierarchy_dfs(transform_hierarchy_root_node(tfh), draw_scene_node, true, ctx); -} - -void draw_model(renderer* ren, camera* camera, model* model, mat4* model_tf, scene* scene) { - // TRACE("Drawing model: %s", model->name); - mat4 view; - mat4 proj; - camera_view_projection(camera, SCR_HEIGHT, SCR_WIDTH, &view, &proj); - - set_shader(ren->blinn_phong); - - // set camera uniform - uniform_vec3f(ren->blinn_phong.program_id, "viewPos", &camera->position); - // set light uniforms - dir_light_upload_uniforms(ren->blinn_phong, &scene->dir_light); - for (int i = 0; i < scene->n_point_lights; i++) { - point_light_upload_uniforms(ren->blinn_phong, &scene->point_lights[i], '0' + i); - } - - for (size_t i = 0; i < mesh_darray_len(model->meshes); i++) { - mesh* m = &model->meshes->data[i]; - if (vertex_darray_len(m->vertices) == 0) { - continue; - } - // TRACE("Drawing mesh %d", i); - material* mat = &model->materials->data[m->material_index]; - draw_mesh(ren, m, model_tf, mat, &view, &proj); - } -} - -void draw_mesh(renderer* ren, mesh* mesh, mat4* model_tf, material* mat, mat4* view, mat4* proj) { - shader lighting_shader = ren->blinn_phong; - - // bind buffer - bind_mesh_vertex_buffer(ren->backend_state, mesh); - - // bind textures - bind_texture(lighting_shader, &mat->diffuse_texture, 0); // bind to slot 0 - bind_texture(lighting_shader, &mat->specular_texture, 1); // bind to slot 1 - uniform_f32(lighting_shader.program_id, "material.shininess", 32.); - - // upload model, view, and projection matrices - uniform_mat4f(lighting_shader.program_id, "model", model_tf); - uniform_mat4f(lighting_shader.program_id, "view", view); - uniform_mat4f(lighting_shader.program_id, "projection", proj); - - // draw triangles - u32 num_vertices = vertex_darray_len(mesh->vertices); - draw_primitives(CEL_PRIMITIVE_TOPOLOGY_TRIANGLE, 0, num_vertices); -} - -void draw_skinned_mesh(renderer* ren, mesh* mesh, transform tf, material* mat, mat4* view, - mat4* proj) { - shader lighting_shader = ren->skinned; - - // bind buffer - bind_mesh_vertex_buffer(ren->backend_state, mesh); - - // bind textures - bind_texture(lighting_shader, &mat->diffuse_texture, 0); // bind to slot 0 - bind_texture(lighting_shader, &mat->specular_texture, 1); // bind to slot 1 - - // Uniforms - uniform_f32(lighting_shader.program_id, "material.shininess", 32.); - mat4 trans = mat4_translation(tf.position); - mat4 rot = mat4_rotation(tf.rotation); - mat4 scale = mat4_scale(tf.scale); - mat4 model_tf = mat4_mult(trans, mat4_mult(rot, scale)); - uniform_mat4f(lighting_shader.program_id, "model", &model_tf); - uniform_mat4f(lighting_shader.program_id, "view", view); - uniform_mat4f(lighting_shader.program_id, "projection", proj); - - // bone transforms - size_t n_bones = mesh->bones->len; - - // for now assume correct ordering - mat4* bone_transforms = malloc(n_bones * sizeof(mat4)); - mat4 parent = mat4_ident(); - for (int bone_i = 0; bone_i < n_bones; bone_i++) { - joint j = mesh->bones->data[bone_i]; - transform tf = mesh->bones->data[bone_i].transform_components; - tf.position.y = -tf.position.y; - mat4 local = transform_to_mat(&tf); - mat4 inverse = j.inverse_bind_matrix; - inverse.data[13] = -inverse.data[13]; - mat4 intemediate = mat4_mult(local, inverse); - - bone_transforms[bone_i] = intemediate; - parent = bone_transforms[bone_i]; - } - - // premultiply the inverses - // for (int bone_i = 0; bone_i < n_bones; bone_i++) { - // joint j = mesh->bones->data[bone_i]; - // // bone_transforms[bone_i] = mat4_mult(bone_transforms[bone_i], j.inverse_bind_matrix); - // bone_transforms[bone_i] = mat4_mult(bone_transforms[bone_i], j.inverse_bind_matrix); - // } - - glUniformMatrix4fv(glGetUniformLocation(lighting_shader.program_id, "boneMatrices"), n_bones, - GL_FALSE, &bone_transforms->data[0]); - - free(bone_transforms); - - // draw triangles - u32 num_vertices = vertex_darray_len(mesh->vertices); - draw_primitives(CEL_PRIMITIVE_TOPOLOGY_TRIANGLE, 0, num_vertices); -} - -void draw_skinned_model(renderer* ren, camera* cam, model* model, transform tf, scene* scene) { - mat4 view; - mat4 proj; - camera_view_projection(cam, SCR_HEIGHT, SCR_WIDTH, &view, &proj); - - set_shader(ren->skinned); - - // set camera uniform - uniform_vec3f(ren->skinned.program_id, "viewPos", &cam->position); - // set light uniforms - dir_light_upload_uniforms(ren->skinned, &scene->dir_light); - for (int i = 0; i < scene->n_point_lights; i++) { - point_light_upload_uniforms(ren->skinned, &scene->point_lights[i], '0' + i); - } - - for (size_t i = 0; i < mesh_darray_len(model->meshes); i++) { - mesh* m = &model->meshes->data[i]; - if (vertex_darray_len(m->vertices) == 0) { - continue; - } - // material* mat = &model->materials->data[m->material_index]; - material* mat = &DEFAULT_MATERIAL; - draw_skinned_mesh(ren, m, tf, mat, &view, &proj); - } -} - -void model_upload_meshes(renderer* ren, model* model) { - INFO("Upload mesh vertex data to GPU for model %s", model->name); - - size_t num_meshes = mesh_darray_len(model->meshes); - u32 VBOs[num_meshes]; - u32 VAOs[num_meshes]; - glGenBuffers(num_meshes, VBOs); - glGenVertexArrays(num_meshes, VAOs); - - u64 total_verts = 0; - - TRACE("num meshes %d", num_meshes); - - // upload each mesh to the GPU - for (int mesh_i = 0; mesh_i < num_meshes; mesh_i++) { - mesh mesh = model->meshes->data[mesh_i]; - model->meshes->data[mesh_i].vao = VAOs[mesh_i]; - model->meshes->data[mesh_i].vbo = VBOs[mesh_i]; - // 3. bind buffers - glBindBuffer(GL_ARRAY_BUFFER, VBOs[mesh_i]); - - size_t num_vertices = vertex_darray_len(model->meshes->data[mesh_i].vertices); - // TRACE("Uploading vertex array data: %d verts", num_vertices); - total_verts += num_vertices; - - size_t static_vertex_size = 2 * sizeof(vec3) + sizeof(vec2); - size_t skinned_vertex_size = 2 * sizeof(vec3) + sizeof(vec2) + 4 * sizeof(u32) + sizeof(vec4); - size_t vertex_size = mesh.is_skinned ? skinned_vertex_size : static_vertex_size; - - // TRACE("sizeof(vertex) -> %ld, vertex_size -> %ld\n", sizeof(vertex), vertex_size); - if (mesh.is_skinned) { - assert(vertex_size == (12 + 12 + 8 + 16 + 16)); - } else { - assert(vertex_size == sizeof(vertex)); - assert(vertex_size == 8 * sizeof(float)); - } - - size_t buffer_size = vertex_size * num_vertices; - u8* bytes = malloc(buffer_size); - - for (int i = 0; i < num_vertices; i++) { - u8* p = bytes + vertex_size * i; - memcpy(p, &mesh.vertices->data[i], static_vertex_size); - if (mesh.is_skinned) { - u8* bone_data_offset = p + static_vertex_size; - memcpy(bone_data_offset, &mesh.vertex_bone_data->data[i], sizeof(vertex_bone_data)); - } - } - - // 4. upload data - glBufferData(GL_ARRAY_BUFFER, buffer_size, bytes, GL_STATIC_DRAW); - - // 5. cont. set mesh vertex layout - glBindVertexArray(model->meshes->data[mesh_i].vao); - // position attribute - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, vertex_size, (void*)0); - glEnableVertexAttribArray(0); - // normal vector attribute - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, vertex_size, (void*)(3 * sizeof(float))); - glEnableVertexAttribArray(1); - // tex coords - glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, vertex_size, (void*)(6 * sizeof(float))); - glEnableVertexAttribArray(2); - - // skinning (optional) - if (mesh.is_skinned) { - glEnableVertexAttribArray(3); - glVertexAttribIPointer(3, 4, GL_INT, vertex_size, (void*)(8 * sizeof(float))); - - glEnableVertexAttribArray(4); - glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, vertex_size, (void*)(12 * sizeof(float))); - } - } - - INFO("Uploaded %d submeshes with a total of %d vertices\n", num_meshes, total_verts); - - // 6. reset buffer - glBindBuffer(GL_ARRAY_BUFFER, 0); -} - -texture texture_data_load(const char* path, bool invert_y) { - TRACE("Load texture %s", path); - - // load the file data - // texture loading - int width, height, num_channels; - stbi_set_flip_vertically_on_load(invert_y); - -#pragma GCC diagnostic ignored "-Wpointer-sign" - char* data = stbi_load(path, &width, &height, &num_channels, 0); // STBI_rgb_alpha); - if (data) { - DEBUG("loaded texture: %s", path); - } else { - WARN("failed to load texture"); - } - - unsigned int channel_type; - if (num_channels == 4) { - channel_type = GL_RGBA; - } else { - channel_type = GL_RGB; - } - - return (texture){ .texture_id = 0, - .width = width, - .height = height, - .channel_count = num_channels, - .channel_type = channel_type, - .name = "TODO: Texture names", - .image_data = data }; -} - -void dir_light_upload_uniforms(shader shader, directional_light* light) { - uniform_vec3f(shader.program_id, "dirLight.direction", &light->direction); - uniform_vec3f(shader.program_id, "dirLight.ambient", &light->ambient); - uniform_vec3f(shader.program_id, "dirLight.diffuse", &light->diffuse); - uniform_vec3f(shader.program_id, "dirLight.specular", &light->specular); -} +void render_frame_begin(renderer* ren) {} +void render_frame_end(renderer* ren) {} +void render_frame_draw(renderer* ren) {} -void point_light_upload_uniforms(shader shader, point_light* light, char index) { - char position_str[] = "pointLights[x].position"; - position_str[12] = (char)index; - char ambient_str[] = "pointLights[x].ambient"; - ambient_str[12] = (char)index; - char diffuse_str[] = "pointLights[x].diffuse"; - diffuse_str[12] = (char)index; - char specular_str[] = "pointLights[x].specular"; - specular_str[12] = (char)index; - char constant_str[] = "pointLights[x].constant"; - constant_str[12] = (char)index; - char linear_str[] = "pointLights[x].linear"; - linear_str[12] = (char)index; - char quadratic_str[] = "pointLights[x].quadratic"; - quadratic_str[12] = (char)index; - uniform_vec3f(shader.program_id, position_str, &light->position); - uniform_vec3f(shader.program_id, ambient_str, &light->ambient); - uniform_vec3f(shader.program_id, diffuse_str, &light->diffuse); - uniform_vec3f(shader.program_id, specular_str, &light->specular); - uniform_f32(shader.program_id, constant_str, light->constant); - uniform_f32(shader.program_id, linear_str, light->linear); - uniform_f32(shader.program_id, quadratic_str, light->quadratic); -} \ No newline at end of file +void gfx_backend_draw_frame(renderer* ren, camera* camera, mat4 model, texture* tex) {} \ No newline at end of file diff --git a/src/renderer/render.h b/src/renderer/render.h index 31cf3b0..0aeeac2 100644 --- a/src/renderer/render.h +++ b/src/renderer/render.h @@ -10,37 +10,34 @@ */ #pragma once -#include "camera.h" -#include "loaders.h" #include "render_types.h" -#include "transform_hierarchy.h" +#include "ral_types.h" -// --- Lifecycle -/** @brief initialise the render system frontend */ bool renderer_init(renderer* ren); -/** @brief shutdown the render system frontend */ void renderer_shutdown(renderer* ren); -void renderer_on_resize(renderer* ren); - -struct render_packet; - -// --- Frame - void render_frame_begin(renderer* ren); void render_frame_end(renderer* ren); void render_frame_draw(renderer* ren); -// --- models meshes -void model_upload_meshes(renderer* ren, model* model); -void draw_model(renderer* ren, camera* camera, model* model, mat4* tf, scene* scene); -void draw_mesh(renderer* ren, mesh* mesh, mat4* tf, material* mat, mat4* view, mat4* proj); -void draw_scene(arena* frame, model_darray* models, renderer* ren, camera* camera, - transform_hierarchy* tfh, scene* scene); +// ! TEMP +typedef struct camera camera; +void gfx_backend_draw_frame(renderer* ren, camera* camera, mat4 model, texture* tex); + +// frontend -- these can be called from say a loop in an example, or via FFI +texture_handle texture_create(const char* debug_name, texture_desc description, const u8* data); + +// Frontend Resources +// TODO: void texture_data_upload(texture_handle texture); +void texture_data_upload(texture* tex); +texture texture_data_load(const char* path, bool invert_y); +buffer_handle buffer_create(const char* debug_name, u64 size); +bool buffer_destroy(buffer_handle buffer); +sampler_handle sampler_create(); -void draw_skinned_model(renderer* ren, camera* cam, model* model, transform tf, scene* scene); +void shader_hot_reload(const char* filepath); -void model_destroy(model* model); +// models and meshes are implemented **in terms of the above** +mesh mesh_create(geometry_data* geometry); -// --- -texture texture_data_load(const char* path, bool invert_y); // #frontend +model_handle model_load(const char* debug_name, const char* filepath); \ No newline at end of file diff --git a/src/renderer/render_backend.h b/src/renderer/render_backend.h deleted file mode 100644 index da30bcc..0000000 --- a/src/renderer/render_backend.h +++ /dev/null @@ -1,39 +0,0 @@ -/** - * @brief Renderer backend - */ -#pragma once - -#include "camera.h" -#include "maths_types.h" -#include "render_types.h" - -/// --- Lifecycle - -/** @brief Initialise the graphics API backend */ -bool gfx_backend_init(renderer* ren); -void gfx_backend_shutdown(renderer* ren); - -void gfx_backend_draw_frame(renderer* ren, camera* camera, mat4 model, texture* tex); -void gfx_backend_update_global_state(mat4 projection, mat4 view, vec3 view_pos, vec4 ambient_colour, - i32 mode); - -void clear_screen(vec3 colour); - -void texture_data_upload(texture* tex); -void bind_texture(shader s, texture* tex, u32 slot); -void bind_mesh_vertex_buffer(void* backend, mesh* mesh); -void draw_primitives(cel_primitive_topology primitive, u32 start_index, u32 count); - -shader shader_create_separate(const char* vert_shader, const char* frag_shader); -void set_shader(shader s); - -// --- Uniforms - -/** @brief upload a vec3 of f32 to a uniform */ -void uniform_vec3f(u32 program_id, const char* uniform_name, vec3* value); -/** @brief upload a single f32 to a uniform */ -void uniform_f32(u32 program_id, const char* uniform_name, f32 value); -/** @brief upload a integer to a uniform */ -void uniform_i32(u32 program_id, const char* uniform_name, i32 value); -/** @brief upload a mat4 of f32 to a uniform */ -void uniform_mat4f(u32 program_id, const char* uniform_name, mat4* value); \ No newline at end of file diff --git a/src/renderer/render_types.h b/src/renderer/render_types.h index 3bce88f..bc9692f 100644 --- a/src/renderer/render_types.h +++ b/src/renderer/render_types.h @@ -1,60 +1,71 @@ -// /** -// * @file render_types.h -// * @author Omniscient -// * @brief Type definitions for the majority of data required by the renderer system -// * @date 2024-02-24 -// * -// */ -// #pragma once +/** + * @file render_types.h + * @author your name (you@domain.com) + * @brief + * @version 0.1 + * @date 2024-04-27 + * + * @copyright Copyright (c) 2024 + * + */ +#pragma once + +#include "ral_types.h" +#include "ral.h" +#if defined(CEL_PLATFORM_WINDOWS) +#include "backend_dx11.h" +#endif -#include "animation.h" -#include "darray.h" -#include "maths.h" -#include "maths_types.h" -#include "str.h" - -// struct GLFWwindow; - -// #define MAX_MATERIAL_NAME_LEN 256 -// #define MAX_TEXTURE_NAME_LEN 256 - -// #ifndef RESOURCE_HANDLE_DEFS -// // CORE_DEFINE_HANDLE(model_handle); -// #define ABSENT_MODEL_HANDLE 999999999 -// // CORE_DEFINE_HANDLE(texture_handle); -// #define RESOURCE_HANDLE_DEFS -// #endif - -// /* @brief Opaque wrapper around a shader program */ -// typedef struct shader { -// u32 program_id; -// } shader; - -// /** @brief configuration passed to the renderer at init time */ -// typedef struct renderer_config { -// char window_name[256]; -// u32 scr_width, scr_height; -// vec3 clear_colour; /** colour that the screen gets cleared to every frame */ -// } renderer_config; - -// typedef struct frame_stats { -// u64 last_time; -// } frame_stats; +struct GLFWwindow; + +/** @brief configuration passed to the renderer at init time */ +typedef struct renderer_config { + char window_name[256]; + u32 scr_width, scr_height; + vec3 clear_colour; /** colour that the screen gets cleared to every frame */ +} renderer_config; typedef struct renderer { - struct GLFWwindow *window; /** Currently all platforms use GLFW*/ - void *backend_state; /** Graphics API-specific state */ + struct GLFWwindow* window; + void* backend_context; renderer_config config; - // shaders - shader blinn_phong; - shader skinned; + gpu_device* device; + gpu_pipeline* static_opaque_pipeline; } renderer; -// // --- Lighting & Materials +typedef struct geometry_data { + vertex_format format; + vertex_darray* vertices; // TODO: make it not a pointe + bool has_indices; + u32_darray indices; + vec3 colour; /** Optional: set vertex colours */ +} geometry_data; + +void geo_set_vertex_colours(geometry_data* geo, vec4 colour); + +typedef struct mesh { + buffer_handle vertex_buffer; + buffer_handle index_buffer; + u32 index_count; + bool has_indices; + geometry_data* vertices; // NULL means it has been freed +} mesh; + +/* Hot reloading: +C side - reload_model(): + - load model from disk using existing loader + - remove from transform graph so it isnt tried to be drawn +*/ + +typedef struct model { + str8 name; + mesh* meshes; + u32 mesh_count; +} model; typedef struct texture { u32 texture_id; - char name[MAX_TEXTURE_NAME_LEN]; + char name[256]; void *image_data; void *backend_data; u32 width; @@ -63,148 +74,42 @@ typedef struct texture { u32 channel_type; } texture; -// typedef struct blinn_phong_material { -// char name[MAX_MATERIAL_NAME_LEN]; -// texture diffuse_texture; -// char diffuse_tex_path[256]; -// texture specular_texture; -// char specular_tex_path[256]; -// vec3 ambient_colour; -// vec3 diffuse; -// vec3 specular; -// f32 spec_exponent; -// bool is_loaded; -// bool is_uploaded; -// } blinn_phong_material; -// typedef blinn_phong_material material; // when we start using PBR, this will no longer be the -// case - -// // the default blinn-phong material. MUST be initialised with the function below -// extern material DEFAULT_MATERIAL; -// void default_material_init(); +typedef struct blinn_phong_material { + char name[256]; + texture diffuse_texture; + char diffuse_tex_path[256]; + texture specular_texture; + char specular_tex_path[256]; + vec3 ambient_colour; + vec3 diffuse; + vec3 specular; + f32 spec_exponent; + bool is_loaded; + bool is_uploaded; +} blinn_phong_material; +typedef blinn_phong_material material; -#ifndef TYPED_MATERIAL_ARRAY -KITC_DECL_TYPED_ARRAY(material) // creates "material_darray" -#define TYPED_MATERIAL_ARRAY -#endif +// the default blinn-phong material. MUST be initialised with the function below +extern material DEFAULT_MATERIAL; +void default_material_init(); -#ifndef TYPED_ANIMATION_CLIP_ARRAY -KITC_DECL_TYPED_ARRAY(animation_clip) // creates "material_darray" -#define TYPED_ANIMATION_CLIP_ARRAY +#ifndef TYPED_MESH_ARRAY +KITC_DECL_TYPED_ARRAY(mesh) +#define TYPED_MESH_ARRAY #endif -// // lights -// typedef struct point_light { -// vec3 position; -// f32 constant, linear, quadratic; -// vec3 ambient; -// vec3 diffuse; -// vec3 specular; -// } point_light; - -// typedef struct directional_light { -// vec3 direction; -// vec3 ambient; -// vec3 diffuse; -// vec3 specular; -// } directional_light; - -// void point_light_upload_uniforms(shader shader, point_light *light, char index); -// void dir_light_upload_uniforms(shader shader, directional_light *light); - -// // --- Models & Meshes - -// /** @brief Vertex format for a static mesh */ -// typedef struct vertex { -// vec3 position; -// vec3 normal; -// vec2 uv; -// } vertex; - -typedef struct vertex_bone_data { - vec4u joints; /** @brief 4 indices of joints that influence vectors position */ - vec4 weights; /** @brief weight (0,1) of each joint */ -} vertex_bone_data; - -#include "animation.h" -#ifndef TYPED_VERTEX_ARRAY -KITC_DECL_TYPED_ARRAY(vertex) // creates "vertex_darray" -KITC_DECL_TYPED_ARRAY(vertex_bone_data) // creates "skinned_vertex_darray" -KITC_DECL_TYPED_ARRAY(joint) -#define TYPED_VERTEX_ARRAY +#ifndef TYPED_MODEL_ARRAY +KITC_DECL_TYPED_ARRAY(model) +#define TYPED_MODEL_ARRAY #endif -typedef struct mesh { - vertex_darray *vertices; - vertex_bone_data_darray *vertex_bone_data; // only used if model needs it - joint_darray *bones; - bool is_skinned; - u32 vertex_size; /** size in bytes of each vertex including necessary padding */ - bool has_indices; - u32 *indices; - u32 indices_len; - size_t material_index; - u32 vbo, vao; /** OpenGL data. TODO: dont leak OpenGL details */ -} mesh; - -// #ifndef TYPED_MESH_ARRAY -// KITC_DECL_TYPED_ARRAY(mesh) // creates "mesh_darray" -// #define TYPED_MESH_ARRAY -// #endif - -typedef struct model { - str8 name; - mesh_darray *meshes; - aabb_3d bbox; - material_darray *materials; - animation_clip_darray *animations; - arena animation_data_arena; - bool is_loaded; - bool is_uploaded; -} model; +#ifndef TYPED_MATERIAL_ARRAY +KITC_DECL_TYPED_ARRAY(material) +#define TYPED_MATERIAL_ARRAY +#endif -// #ifndef TYPED_MODEL_ARRAY -// KITC_DECL_TYPED_ARRAY(model) // creates "model_darray" -// #define TYPED_MODEL_ARRAY -// #endif - -// // --- Scene - -// // NOTE: This struct won't stay like this for a long time. It's somewhat temporary -// // in order to get a basic scene working without putting burden on the caller of -// // draw_model() -// typedef struct scene { -// directional_light dir_light; -// point_light point_lights[4]; -// size_t n_point_lights; -// } scene; - -// // --- Graphics API related - -// // typedef enum cel_primitive_topology { -// // CEL_PRIMITIVE_TOPOLOGY_POINT, -// // CEL_PRIMITIVE_TOPOLOGY_LINE, -// // CEL_PRIMITIVE_TOPOLOGY_LINE_STRIP, -// // CEL_PRIMITIVE_TOPOLOGY_TRIANGLE, -// // CEL_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, -// // CEL_PRIMITIVE_TOPOLOGY_COUNT -// // } cel_primitive_topology; - -// // typedef enum gpu_texture_type { -// // TEXTURE_TYPE_2D, -// // TEXTURE_TYPE_3D, -// // TEXTURE_TYPE_2D_ARRAY, -// // TEXTURE_TYPE_CUBE_MAP, -// // TEXTURE_TYPE_COUNT -// // } gpu_texture_type; - -// // typedef enum gpu_texture_format { -// // TEXTURE_FORMAT_8_8_8_8_RGBA_UNORM, -// // TEXTURE_FORMAT_DEPTH_DEFAULT, -// // TEXTURE_FORMAT_COUNT -// // } gpu_texture_format; - -// // typedef enum pipeline_kind { -// // GRAPHICS, -// // COMPUTE, -// // } pipeline_kind; \ No newline at end of file +#ifndef TYPED_ANIMATION_CLIP_ARRAY +#include "animation.h" +KITC_DECL_TYPED_ARRAY(animation_clip) +#define TYPED_ANIMATION_CLIP_ARRAY +#endif \ No newline at end of file diff --git a/src/resources/gltf.c b/src/resources/gltf.c index 81992d1..022bf95 100644 --- a/src/resources/gltf.c +++ b/src/resources/gltf.c @@ -12,7 +12,7 @@ #include "mem.h" #include "path.h" #include "render.h" -#include "render_backend.h" +// #include "render_backend.h" #include "render_types.h" #include "str.h" @@ -26,7 +26,6 @@ typedef struct face face; KITC_DECL_TYPED_ARRAY(vec3) KITC_DECL_TYPED_ARRAY(vec2) -KITC_DECL_TYPED_ARRAY(u32) KITC_DECL_TYPED_ARRAY(vec4u) KITC_DECL_TYPED_ARRAY(vec4) KITC_DECL_TYPED_ARRAY(face) @@ -49,7 +48,7 @@ model_handle model_load_gltf(struct core *core, const char *path, bool invert_te model model = { 0 }; model.name = str8_cstr_view(path); model.meshes = mesh_darray_new(1); - model.materials = material_darray_new(1); + // model.materials = material_darray_new(1); bool success = model_load_gltf_str(file_string, path, relative_path.path, &model, invert_texture_y); @@ -79,403 +78,414 @@ void assert_path_type_matches_component_type(cgltf_animation_path_type target_pa bool model_load_gltf_str(const char *file_string, const char *filepath, str8 relative_path, model *out_model, bool invert_textures_y) { - TRACE("Load GLTF from string"); - - // Setup temps - vec3_darray *tmp_positions = vec3_darray_new(1000); - vec3_darray *tmp_normals = vec3_darray_new(1000); - vec2_darray *tmp_uvs = vec2_darray_new(1000); - vec4u_darray *tmp_joint_indices = vec4u_darray_new(1000); - vec4_darray *tmp_weights = vec4_darray_new(1000); - joint_darray *tmp_joints = joint_darray_new(256); - vertex_bone_data_darray *tmp_vertex_bone_data = vertex_bone_data_darray_new(1000); - - cgltf_options options = { 0 }; - cgltf_data *data = NULL; - cgltf_result result = cgltf_parse_file(&options, filepath, &data); - if (result != cgltf_result_success) { - WARN("gltf load failed"); - // TODO: cleanup arrays(allocate all from arena ?) - return false; - } - - cgltf_load_buffers(&options, data, filepath); - DEBUG("loaded buffers"); - - // --- Skin - size_t num_skins = data->skins_count; - bool is_skinned = false; - if (num_skins == 1) { - is_skinned = true; - } else if (num_skins > 1) { - WARN("GLTF files with more than 1 skin are not supported"); - return false; - } - - if (is_skinned) { - cgltf_skin *gltf_skin = data->skins; - DEBUG("loading skin %s", gltf_skin->name); - size_t num_joints = gltf_skin->joints_count; - DEBUG("# Joints %d", num_joints); - - cgltf_accessor *gltf_inverse_bind_matrices = gltf_skin->inverse_bind_matrices; - - // for each one we'll spit out a joint - for (size_t i = 0; i < num_joints; i++) { - cgltf_node *joint_node = gltf_skin->joints[i]; - - joint joint_i = { .name = "testjoint" }; - if (joint_node->children_count > 0 && !joint_node->has_translation && - !joint_node->has_rotation) { - WARN("joint Node with index %d is the root node", i); - joint_i.transform_components = TRANSFORM_DEFAULT; - } else { - TRACE("Storing joint transform"); - joint_i.transform_components = TRANSFORM_DEFAULT; - if (joint_node->has_translation) { - memcpy(&joint_i.transform_components.position, &joint_node->translation, 3 * sizeof(f32)); - } - if (joint_node->has_rotation) { - memcpy(&joint_i.transform_components.rotation, &joint_node->rotation, 4 * sizeof(f32)); - } - // TODO: support scaling as vec instead of float - } - joint_i.local_transform = transform_to_mat(&joint_i.transform_components); - cgltf_accessor_read_float(gltf_inverse_bind_matrices, i, &joint_i.inverse_bind_matrix.data[0], - 16); - joint_darray_push(tmp_joints, joint_i); - } - } - - // --- Materials - TRACE("Num materials %d", data->materials_count); - size_t num_materials = data->materials_count; - for (size_t m = 0; m < num_materials; m++) { - cgltf_material gltf_material = data->materials[m]; - material our_material = DEFAULT_MATERIAL; - - strcpy(our_material.name, gltf_material.name); - - cgltf_pbr_metallic_roughness pbr = gltf_material.pbr_metallic_roughness; - if (gltf_material.has_pbr_metallic_roughness) { - // we will use base color texture like blinn phong - cgltf_texture_view diff_tex_view = pbr.base_color_texture; - - char diffuse_map_path[1024]; - snprintf(diffuse_map_path, sizeof(diffuse_map_path), "%s/%s", relative_path.buf, - diff_tex_view.texture->image->uri); - - strcpy(our_material.diffuse_tex_path, diffuse_map_path); - texture diffuse_texture = texture_data_load(our_material.diffuse_tex_path, false); - texture_data_upload(&diffuse_texture); - our_material.diffuse_texture = diffuse_texture; - - cgltf_texture_view specular_tex_view = pbr.metallic_roughness_texture; - - char specular_map_path[1024]; - snprintf(specular_map_path, sizeof(specular_map_path), "%s/%s", relative_path.buf, - specular_tex_view.texture->image->uri); - - strcpy(our_material.specular_tex_path, specular_map_path); - texture specular_texture = texture_data_load(our_material.specular_tex_path, false); - texture_data_upload(&specular_texture); - our_material.specular_texture = specular_texture; - } - - material_darray_push(out_model->materials, our_material); - } - - // --- Meshes - TRACE("Num meshes %d", data->meshes_count); - size_t num_meshes = data->meshes_count; - for (size_t m = 0; m < num_meshes; m++) { - cgltf_primitive primitive = data->meshes[m].primitives[0]; - DEBUG("Found %d attributes", primitive.attributes_count); - // DEBUG("Number of this primitive %d", primitive.) - - for (int a = 0; a < data->meshes[m].primitives[0].attributes_count; a++) { - cgltf_attribute attribute = data->meshes[m].primitives[0].attributes[a]; - if (attribute.type == cgltf_attribute_type_position) { - TRACE("Load positions from accessor"); - - cgltf_accessor *accessor = attribute.data; - assert(accessor->component_type == cgltf_component_type_r_32f); - // CASSERT_MSG(accessor->type == cgltf_type_vec3, "Vertex positions should be a vec3"); - - TRACE("Loading %d vec3 components", accessor->count); - - for (cgltf_size v = 0; v < accessor->count; ++v) { - vec3 pos; - cgltf_accessor_read_float(accessor, v, &pos.x, 3); - vec3_darray_push(tmp_positions, pos); - } - - } else if (attribute.type == cgltf_attribute_type_normal) { - TRACE("Load normals from accessor"); - - cgltf_accessor *accessor = attribute.data; - assert(accessor->component_type == cgltf_component_type_r_32f); - // CASSERT_MSG(accessor->type == cgltf_type_vec3, "Normal vectors should be a vec3"); - - for (cgltf_size v = 0; v < accessor->count; ++v) { - vec3 pos; - cgltf_accessor_read_float(accessor, v, &pos.x, 3); - vec3_darray_push(tmp_normals, pos); - } - - } else if (attribute.type == cgltf_attribute_type_texcoord) { - TRACE("Load texture coordinates from accessor"); - cgltf_accessor *accessor = attribute.data; - assert(accessor->component_type == cgltf_component_type_r_32f); - // CASSERT_MSG(accessor->type == cgltf_type_vec2, "Texture coordinates should be a vec2"); - - for (cgltf_size v = 0; v < accessor->count; ++v) { - vec2 tex; - bool success = cgltf_accessor_read_float(accessor, v, &tex.x, 2); - if (!success) { - ERROR("Error loading tex coord"); - } - vec2_darray_push(tmp_uvs, tex); - } - } else if (attribute.type == cgltf_attribute_type_joints) { - TRACE("Load joint indices from accessor"); - cgltf_accessor *accessor = attribute.data; - assert(accessor->component_type == cgltf_component_type_r_16u); - assert(accessor->type == cgltf_type_vec4); - vec4u joint_indices; - vec4 joints_as_floats; - for (cgltf_size v = 0; v < accessor->count; ++v) { - cgltf_accessor_read_float(accessor, v, &joints_as_floats.x, 4); - joint_indices.x = (u32)joints_as_floats.x; - joint_indices.y = (u32)joints_as_floats.y; - joint_indices.z = (u32)joints_as_floats.z; - joint_indices.w = (u32)joints_as_floats.w; - printf("Joints affecting vertex %d : %d %d %d %d\n", v, joint_indices.x, joint_indices.y, - joint_indices.z, joint_indices.w); - vec4u_darray_push(tmp_joint_indices, joint_indices); - } - - } else if (attribute.type == cgltf_attribute_type_weights) { - TRACE("Load joint weights from accessor"); - cgltf_accessor *accessor = attribute.data; - assert(accessor->component_type == cgltf_component_type_r_32f); - assert(accessor->type == cgltf_type_vec4); - - for (cgltf_size v = 0; v < accessor->count; ++v) { - vec4 weights; - cgltf_accessor_read_float(accessor, v, &weights.x, 4); - printf("Weights affecting vertex %d : %f %f %f %f\n", v, weights.x, weights.y, weights.z, - weights.w); - vec4_darray_push(tmp_weights, weights); - } - } else { - WARN("Unhandled cgltf_attribute_type: %s. skipping..", attribute.name); - } - } - - mesh mesh = { 0 }; - mesh.vertices = vertex_darray_new(10); - mesh.vertex_bone_data = vertex_bone_data_darray_new(1); - - if (primitive.material != NULL) { - for (int i = 0; i < material_darray_len(out_model->materials); i++) { - printf("%s vs %s \n", primitive.material->name, out_model->materials->data[i].name); - if (strcmp(primitive.material->name, out_model->materials->data[i].name) == 0) { - TRACE("Found material"); - mesh.material_index = i; - break; - } - } - } - - if (is_skinned) { - mesh.is_skinned = true; - // mesh.vertex_bone_data = vertex_bone_data_darray_new(tmp_joint_indices->len); - mesh.bones = joint_darray_new(tmp_joints->len); - for (int i = 0; i < tmp_joint_indices->len; i++) { - vertex_bone_data data; - data.joints = tmp_joint_indices->data[i]; - data.weights = tmp_weights->data[i]; - vertex_bone_data_darray_push(tmp_vertex_bone_data, - data); // Push the temp data that aligns with raw vertices - } - for (int i = 0; i < tmp_joints->len; i++) { - joint data = tmp_joints->data[i]; - joint_darray_push(mesh.bones, data); - } - } - - cgltf_accessor *indices = primitive.indices; - if (primitive.indices > 0) { - WARN("indices!"); - mesh.has_indices = true; - - mesh.indices = malloc(indices->count * sizeof(u32)); - mesh.indices_len = indices->count; - - // store indices - for (cgltf_size i = 0; i < indices->count; ++i) { - cgltf_uint ei; - cgltf_accessor_read_uint(indices, i, &ei, 1); - mesh.indices[i] = ei; - } - - // fetch and store vertices for each index - for (cgltf_size i = 0; i < indices->count; ++i) { - vertex vert; - cgltf_uint index = mesh.indices[i]; - vert.position = tmp_positions->data[index]; - vert.normal = tmp_normals->data[index]; - vert.uv = tmp_uvs->data[index]; - vertex_darray_push(mesh.vertices, vert); - - if (is_skinned) { - vertex_bone_data vbd = tmp_vertex_bone_data->data[index]; // create a copy - vertex_bone_data_darray_push(mesh.vertex_bone_data, vbd); - } - // for each vertex do the bone data - } - } else { - mesh.has_indices = false; - return false; // TODO - } - - mesh_darray_push(out_model->meshes, mesh); - - // clear data for each mesh - vec3_darray_clear(tmp_positions); - vec3_darray_clear(tmp_normals); - vec2_darray_free(tmp_uvs); - vec4u_darray_clear(tmp_joint_indices); - vec4_darray_clear(tmp_weights); - joint_darray_clear(tmp_joints); - } - - for (int i = 0; i < out_model->meshes->len; i++) { - u32 mat_idx = out_model->meshes->data[i].material_index; - printf("Mesh %d Mat index %d Mat name %s\n", i, mat_idx, - out_model->materials->data[mat_idx].name); - } - - // Animations - TRACE("Num animations %d", data->animations_count); - size_t num_animations = data->animations_count; - if (num_animations > 0) { -// Create an arena for all animation related data -#define ANIMATION_STORAGE_ARENA_SIZE (1024 * 1024 * 1024) - char *animation_backing_storage = malloc(ANIMATION_STORAGE_ARENA_SIZE); - // We'll store data on this arena so we can easily free it all at once later - out_model->animation_data_arena = - arena_create(animation_backing_storage, ANIMATION_STORAGE_ARENA_SIZE); - arena *arena = &out_model->animation_data_arena; - - if (!out_model->animations) { - out_model->animations = animation_clip_darray_new(num_animations); - } - - for (int anim_idx = 0; anim_idx < data->animations_count; anim_idx++) { - cgltf_animation animation = data->animations[anim_idx]; - animation_clip clip = { 0 }; - - for (size_t c = 0; c < animation.channels_count; c++) { - cgltf_animation_channel channel = animation.channels[c]; - - animation_sampler *sampler = arena_alloc(arena, sizeof(animation_sampler)); - - animation_sampler **target_property; - keyframe_kind data_type; - - switch (channel.target_path) { - case cgltf_animation_path_type_rotation: - target_property = &clip.rotation; - data_type = KEYFRAME_ROTATION; - break; - case cgltf_animation_path_type_translation: - target_property = &clip.translation; - data_type = KEYFRAME_TRANSLATION; - break; - case cgltf_animation_path_type_scale: - target_property = &clip.scale; - data_type = KEYFRAME_SCALE; - break; - case cgltf_animation_path_type_weights: - target_property = &clip.weights; - data_type = KEYFRAME_WEIGHTS; - WARN("Morph target weights arent supported yet"); - return false; - default: - WARN("unsupported animation type"); - return false; - } - *target_property = sampler; - - sampler->current_index = 0; - printf("1 %d index\n", sampler->current_index); - sampler->animation.interpolation = INTERPOLATION_LINEAR; - - // keyframe times - size_t n_frames = channel.sampler->input->count; - assert(channel.sampler->input->component_type == cgltf_component_type_r_32f); - // FIXME: CASSERT_MSG function "Expected animation sampler input component to be type f32 - // (keyframe times)"); - f32 *times = arena_alloc(arena, n_frames * sizeof(f32)); - sampler->animation.n_timestamps = n_frames; - sampler->animation.timestamps = times; - cgltf_accessor_unpack_floats(channel.sampler->input, times, n_frames); - - assert_path_type_matches_component_type(channel.target_path, channel.sampler->output); - - // keyframe values - size_t n_values = channel.sampler->output->count; - assert(n_frames == n_values); - - keyframes keyframes = { 0 }; - keyframes.kind = KEYFRAME_ROTATION; - keyframes.count = n_values; - keyframes.values = arena_alloc(arena, n_values * sizeof(keyframe)); - for (cgltf_size v = 0; v < channel.sampler->output->count; ++v) { - switch (data_type) { - case KEYFRAME_ROTATION: { - quat rot; - cgltf_accessor_read_float(channel.sampler->output, v, &rot.x, 4); - // printf("Quat %f %f %f %f\n", rot.x, rot.y, rot.z, rot.w); - keyframes.values[v].rotation = rot; - break; - } - case KEYFRAME_TRANSLATION: { - vec3 trans; - cgltf_accessor_read_float(channel.sampler->output, v, &trans.x, 3); - keyframes.values[v].translation = trans; - break; - } - case KEYFRAME_SCALE: { - vec3 scale; - cgltf_accessor_read_float(channel.sampler->output, v, &scale.x, 3); - keyframes.values[v].scale = scale; - break; - } - case KEYFRAME_WEIGHTS: { - // TODO - break; - } - } - } - sampler->animation.values = keyframes; - - sampler->min = channel.sampler->input->min[0]; - sampler->max = channel.sampler->input->max[0]; - - // clip.rotation = sampler; - // printf("%d timestamps\n", sampler->animation.n_timestamps); - // printf("%d index\n", sampler->current_index); - } - - WARN("stuff %ld", clip.rotation->animation.n_timestamps); - animation_clip_darray_push(out_model->animations, clip); - } - } - - return true; + return false; + // TRACE("Load GLTF from string"); + + // // Setup temps + // vec3_darray *tmp_positions = vec3_darray_new(1000); + // vec3_darray *tmp_normals = vec3_darray_new(1000); + // vec2_darray *tmp_uvs = vec2_darray_new(1000); + // vec4u_darray *tmp_joint_indices = vec4u_darray_new(1000); + // vec4_darray *tmp_weights = vec4_darray_new(1000); + // // FIXME + // // joint_darray *tmp_joints = joint_darray_new(256); + // // vertex_bone_data_darray *tmp_vertex_bone_data = vertex_bone_data_darray_new(1000); + + // cgltf_options options = { 0 }; + // cgltf_data *data = NULL; + // cgltf_result result = cgltf_parse_file(&options, filepath, &data); + // if (result != cgltf_result_success) { + // WARN("gltf load failed"); + // // TODO: cleanup arrays(allocate all from arena ?) + // return false; + // } + + // cgltf_load_buffers(&options, data, filepath); + // DEBUG("loaded buffers"); + + // // --- Skin + // size_t num_skins = data->skins_count; + // bool is_skinned = false; + // if (num_skins == 1) { + // is_skinned = true; + // } else if (num_skins > 1) { + // WARN("GLTF files with more than 1 skin are not supported"); + // return false; + // } + + // if (is_skinned) { + // cgltf_skin *gltf_skin = data->skins; + // DEBUG("loading skin %s", gltf_skin->name); + // size_t num_joints = gltf_skin->joints_count; + // DEBUG("# Joints %d", num_joints); + + // cgltf_accessor *gltf_inverse_bind_matrices = gltf_skin->inverse_bind_matrices; + + // // for each one we'll spit out a joint + // for (size_t i = 0; i < num_joints; i++) { + // cgltf_node *joint_node = gltf_skin->joints[i]; + + // joint joint_i = { .name = "testjoint" }; + // if (joint_node->children_count > 0 && !joint_node->has_translation && + // !joint_node->has_rotation) { + // WARN("joint Node with index %d is the root node", i); + // joint_i.transform_components = TRANSFORM_DEFAULT; + // } else { + // TRACE("Storing joint transform"); + // joint_i.transform_components = TRANSFORM_DEFAULT; + // if (joint_node->has_translation) { + // memcpy(&joint_i.transform_components.position, &joint_node->translation, 3 * + // sizeof(f32)); + // } + // if (joint_node->has_rotation) { + // memcpy(&joint_i.transform_components.rotation, &joint_node->rotation, 4 * + // sizeof(f32)); + // } + // // TODO: support scaling as vec instead of float + // } + // joint_i.local_transform = transform_to_mat(&joint_i.transform_components); + // cgltf_accessor_read_float(gltf_inverse_bind_matrices, i, + // &joint_i.inverse_bind_matrix.data[0], + // 16); + // // joint_darray_push(tmp_joints, joint_i); + // } + // } + + // // --- Materials + // TRACE("Num materials %d", data->materials_count); + // size_t num_materials = data->materials_count; + // for (size_t m = 0; m < num_materials; m++) { + // cgltf_material gltf_material = data->materials[m]; + // material our_material = DEFAULT_MATERIAL; + + // strcpy(our_material.name, gltf_material.name); + + // cgltf_pbr_metallic_roughness pbr = gltf_material.pbr_metallic_roughness; + // if (gltf_material.has_pbr_metallic_roughness) { + // // we will use base color texture like blinn phong + // cgltf_texture_view diff_tex_view = pbr.base_color_texture; + + // char diffuse_map_path[1024]; + // snprintf(diffuse_map_path, sizeof(diffuse_map_path), "%s/%s", relative_path.buf, + // diff_tex_view.texture->image->uri); + + // strcpy(our_material.diffuse_tex_path, diffuse_map_path); + // texture diffuse_texture = texture_data_load(our_material.diffuse_tex_path, false); + // texture_data_upload(&diffuse_texture); + // our_material.diffuse_texture = diffuse_texture; + + // cgltf_texture_view specular_tex_view = pbr.metallic_roughness_texture; + + // char specular_map_path[1024]; + // snprintf(specular_map_path, sizeof(specular_map_path), "%s/%s", relative_path.buf, + // specular_tex_view.texture->image->uri); + + // strcpy(our_material.specular_tex_path, specular_map_path); + // texture specular_texture = texture_data_load(our_material.specular_tex_path, false); + // texture_data_upload(&specular_texture); + // our_material.specular_texture = specular_texture; + // } + + // // material_darray_push(out_model->materials, our_material); + // } + + // // --- Meshes + // TRACE("Num meshes %d", data->meshes_count); + // size_t num_meshes = data->meshes_count; + // for (size_t m = 0; m < num_meshes; m++) { + // cgltf_primitive primitive = data->meshes[m].primitives[0]; + // DEBUG("Found %d attributes", primitive.attributes_count); + // // DEBUG("Number of this primitive %d", primitive.) + + // for (int a = 0; a < data->meshes[m].primitives[0].attributes_count; a++) { + // cgltf_attribute attribute = data->meshes[m].primitives[0].attributes[a]; + // if (attribute.type == cgltf_attribute_type_position) { + // TRACE("Load positions from accessor"); + + // cgltf_accessor *accessor = attribute.data; + // assert(accessor->component_type == cgltf_component_type_r_32f); + // // CASSERT_MSG(accessor->type == cgltf_type_vec3, "Vertex positions should be a vec3"); + + // TRACE("Loading %d vec3 components", accessor->count); + + // for (cgltf_size v = 0; v < accessor->count; ++v) { + // vec3 pos; + // cgltf_accessor_read_float(accessor, v, &pos.x, 3); + // vec3_darray_push(tmp_positions, pos); + // } + + // } else if (attribute.type == cgltf_attribute_type_normal) { + // TRACE("Load normals from accessor"); + + // cgltf_accessor *accessor = attribute.data; + // assert(accessor->component_type == cgltf_component_type_r_32f); + // // CASSERT_MSG(accessor->type == cgltf_type_vec3, "Normal vectors should be a vec3"); + + // for (cgltf_size v = 0; v < accessor->count; ++v) { + // vec3 pos; + // cgltf_accessor_read_float(accessor, v, &pos.x, 3); + // vec3_darray_push(tmp_normals, pos); + // } + + // } else if (attribute.type == cgltf_attribute_type_texcoord) { + // TRACE("Load texture coordinates from accessor"); + // cgltf_accessor *accessor = attribute.data; + // assert(accessor->component_type == cgltf_component_type_r_32f); + // // CASSERT_MSG(accessor->type == cgltf_type_vec2, "Texture coordinates should be a + // vec2"); + + // for (cgltf_size v = 0; v < accessor->count; ++v) { + // vec2 tex; + // bool success = cgltf_accessor_read_float(accessor, v, &tex.x, 2); + // if (!success) { + // ERROR("Error loading tex coord"); + // } + // vec2_darray_push(tmp_uvs, tex); + // } + // } else if (attribute.type == cgltf_attribute_type_joints) { + // TRACE("Load joint indices from accessor"); + // cgltf_accessor *accessor = attribute.data; + // assert(accessor->component_type == cgltf_component_type_r_16u); + // assert(accessor->type == cgltf_type_vec4); + // vec4u joint_indices; + // vec4 joints_as_floats; + // for (cgltf_size v = 0; v < accessor->count; ++v) { + // cgltf_accessor_read_float(accessor, v, &joints_as_floats.x, 4); + // joint_indices.x = (u32)joints_as_floats.x; + // joint_indices.y = (u32)joints_as_floats.y; + // joint_indices.z = (u32)joints_as_floats.z; + // joint_indices.w = (u32)joints_as_floats.w; + // printf("Joints affecting vertex %d : %d %d %d %d\n", v, joint_indices.x, + // joint_indices.y, + // joint_indices.z, joint_indices.w); + // vec4u_darray_push(tmp_joint_indices, joint_indices); + // } + + // } else if (attribute.type == cgltf_attribute_type_weights) { + // TRACE("Load joint weights from accessor"); + // cgltf_accessor *accessor = attribute.data; + // assert(accessor->component_type == cgltf_component_type_r_32f); + // assert(accessor->type == cgltf_type_vec4); + + // for (cgltf_size v = 0; v < accessor->count; ++v) { + // vec4 weights; + // cgltf_accessor_read_float(accessor, v, &weights.x, 4); + // printf("Weights affecting vertex %d : %f %f %f %f\n", v, weights.x, weights.y, + // weights.z, + // weights.w); + // vec4_darray_push(tmp_weights, weights); + // } + // } else { + // WARN("Unhandled cgltf_attribute_type: %s. skipping..", attribute.name); + // } + // } + + // mesh mesh = { 0 }; + // mesh.vertices = vertex_darray_new(10); + // // mesh.vertex_bone_data = vertex_bone_data_darray_new(1); + + // if (primitive.material != NULL) { + // // for (int i = 0; i < material_darray_len(out_model->materials); i++) { + // // printf("%s vs %s \n", primitive.material->name, out_model->materials->data[i].name); + // // if (strcmp(primitive.material->name, out_model->materials->data[i].name) == 0) { + // // TRACE("Found material"); + // // mesh.material_index = i; + // // break; + // // } + // // } + // } + + // // FIXME + // // if (is_skinned) { + // // mesh.is_skinned = true; + // // // mesh.vertex_bone_data = vertex_bone_data_darray_new(tmp_joint_indices->len); + // // mesh.bones = joint_darray_new(tmp_joints->len); + // // for (int i = 0; i < tmp_joint_indices->len; i++) { + // // vertex_bone_data data; + // // data.joints = tmp_joint_indices->data[i]; + // // data.weights = tmp_weights->data[i]; + // // vertex_bone_data_darray_push(tmp_vertex_bone_data, + // // data); // Push the temp data that aligns with raw + // vertices + // // } + // // for (int i = 0; i < tmp_joints->len; i++) { + // // joint data = tmp_joints->data[i]; + // // joint_darray_push(mesh.bones, data); + // // } + // // } + + // cgltf_accessor *indices = primitive.indices; + // if (primitive.indices > 0) { + // WARN("indices!"); + // mesh.has_indices = true; + + // mesh.indices = malloc(indices->count * sizeof(u32)); + // mesh.indices_len = indices->count; + + // // store indices + // for (cgltf_size i = 0; i < indices->count; ++i) { + // cgltf_uint ei; + // cgltf_accessor_read_uint(indices, i, &ei, 1); + // mesh.indices[i] = ei; + // } + + // // fetch and store vertices for each index + // for (cgltf_size i = 0; i < indices->count; ++i) { + // vertex vert; + // cgltf_uint index = mesh.indices[i]; + // vert.position = tmp_positions->data[index]; + // vert.normal = tmp_normals->data[index]; + // vert.uv = tmp_uvs->data[index]; + // vertex_darray_push(mesh.vertices, vert); + + // if (is_skinned) { + // vertex_bone_data vbd = tmp_vertex_bone_data->data[index]; // create a copy + // vertex_bone_data_darray_push(mesh.vertex_bone_data, vbd); + // } + // // for each vertex do the bone data + // } + // } else { + // mesh.has_indices = false; + // return false; // TODO + // } + + // mesh_darray_push(out_model->meshes, mesh); + + // // clear data for each mesh + // vec3_darray_clear(tmp_positions); + // vec3_darray_clear(tmp_normals); + // vec2_darray_free(tmp_uvs); + // vec4u_darray_clear(tmp_joint_indices); + // vec4_darray_clear(tmp_weights); + // joint_darray_clear(tmp_joints); + // } + + // for (int i = 0; i < out_model->meshes->len; i++) { + // u32 mat_idx = out_model->meshes->data[i].material_index; + // printf("Mesh %d Mat index %d Mat name %s\n", i, mat_idx, + // out_model->materials->data[mat_idx].name); + // } + + // // Animations + // TRACE("Num animations %d", data->animations_count); + // size_t num_animations = data->animations_count; + // if (num_animations > 0) { + // // Create an arena for all animation related data + // #define ANIMATION_STORAGE_ARENA_SIZE (1024 * 1024 * 1024) + // char *animation_backing_storage = malloc(ANIMATION_STORAGE_ARENA_SIZE); + // // We'll store data on this arena so we can easily free it all at once later + // out_model->animation_data_arena = + // arena_create(animation_backing_storage, ANIMATION_STORAGE_ARENA_SIZE); + // arena *arena = &out_model->animation_data_arena; + + // if (!out_model->animations) { + // out_model->animations = animation_clip_darray_new(num_animations); + // } + + // for (int anim_idx = 0; anim_idx < data->animations_count; anim_idx++) { + // cgltf_animation animation = data->animations[anim_idx]; + // animation_clip clip = { 0 }; + + // for (size_t c = 0; c < animation.channels_count; c++) { + // cgltf_animation_channel channel = animation.channels[c]; + + // animation_sampler *sampler = arena_alloc(arena, sizeof(animation_sampler)); + + // animation_sampler **target_property; + // keyframe_kind data_type; + + // switch (channel.target_path) { + // case cgltf_animation_path_type_rotation: + // target_property = &clip.rotation; + // data_type = KEYFRAME_ROTATION; + // break; + // case cgltf_animation_path_type_translation: + // target_property = &clip.translation; + // data_type = KEYFRAME_TRANSLATION; + // break; + // case cgltf_animation_path_type_scale: + // target_property = &clip.scale; + // data_type = KEYFRAME_SCALE; + // break; + // case cgltf_animation_path_type_weights: + // target_property = &clip.weights; + // data_type = KEYFRAME_WEIGHTS; + // WARN("Morph target weights arent supported yet"); + // return false; + // default: + // WARN("unsupported animation type"); + // return false; + // } + // *target_property = sampler; + + // sampler->current_index = 0; + // printf("1 %d index\n", sampler->current_index); + // sampler->animation.interpolation = INTERPOLATION_LINEAR; + + // // keyframe times + // size_t n_frames = channel.sampler->input->count; + // assert(channel.sampler->input->component_type == cgltf_component_type_r_32f); + // // FIXME: CASSERT_MSG function "Expected animation sampler input component to be type + // f32 + // // (keyframe times)"); + // f32 *times = arena_alloc(arena, n_frames * sizeof(f32)); + // sampler->animation.n_timestamps = n_frames; + // sampler->animation.timestamps = times; + // cgltf_accessor_unpack_floats(channel.sampler->input, times, n_frames); + + // assert_path_type_matches_component_type(channel.target_path, channel.sampler->output); + + // // keyframe values + // size_t n_values = channel.sampler->output->count; + // assert(n_frames == n_values); + + // keyframes keyframes = { 0 }; + // keyframes.kind = KEYFRAME_ROTATION; + // keyframes.count = n_values; + // keyframes.values = arena_alloc(arena, n_values * sizeof(keyframe)); + // for (cgltf_size v = 0; v < channel.sampler->output->count; ++v) { + // switch (data_type) { + // case KEYFRAME_ROTATION: { + // quat rot; + // cgltf_accessor_read_float(channel.sampler->output, v, &rot.x, 4); + // // printf("Quat %f %f %f %f\n", rot.x, rot.y, rot.z, rot.w); + // keyframes.values[v].rotation = rot; + // break; + // } + // case KEYFRAME_TRANSLATION: { + // vec3 trans; + // cgltf_accessor_read_float(channel.sampler->output, v, &trans.x, 3); + // keyframes.values[v].translation = trans; + // break; + // } + // case KEYFRAME_SCALE: { + // vec3 scale; + // cgltf_accessor_read_float(channel.sampler->output, v, &scale.x, 3); + // keyframes.values[v].scale = scale; + // break; + // } + // case KEYFRAME_WEIGHTS: { + // // TODO + // break; + // } + // } + // } + // sampler->animation.values = keyframes; + + // sampler->min = channel.sampler->input->min[0]; + // sampler->max = channel.sampler->input->max[0]; + + // // clip.rotation = sampler; + // // printf("%d timestamps\n", sampler->animation.n_timestamps); + // // printf("%d index\n", sampler->current_index); + // } + + // WARN("stuff %ld", clip.rotation->animation.n_timestamps); + // animation_clip_darray_push(out_model->animations, clip); + // } + // } + + // return true; } /* diff --git a/src/resources/obj.c b/src/resources/obj.c index ea73ffa..888e16e 100644 --- a/src/resources/obj.c +++ b/src/resources/obj.c @@ -18,7 +18,7 @@ #include "mem.h" #include "path.h" #include "render.h" -#include "render_backend.h" +// #include "render_backend.h" #include "render_types.h" #include "str.h" @@ -55,7 +55,7 @@ model_handle model_load_obj(core *core, const char *path, bool invert_textures_y model model = { 0 }; model.name = str8_cstr_view(path); model.meshes = mesh_darray_new(1); - model.materials = material_darray_new(1); + // model.materials = material_darray_new(1); bool success = model_load_obj_str(file_string, relative_path.path, &model, invert_textures_y); @@ -76,151 +76,157 @@ bool model_load_obj_str(const char *file_string, str8 relative_path, model *out_ bool invert_textures_y) { TRACE("Load OBJ from string"); - // Setup temps - vec3_darray *tmp_positions = vec3_darray_new(1000); - vec3_darray *tmp_normals = vec3_darray_new(1000); - vec2_darray *tmp_uvs = vec2_darray_new(1000); - face_darray *tmp_faces = face_darray_new(1000); - // TODO: In the future I'd like these temporary arrays to be allocated from an arena provided - // by the function one level up, model_load_obj. That way we can just `return false;` anywhere in - // this code to indicate an error, and be sure that all that memory will be cleaned up without - // having to call vec3_darray_free in every single error case before returning. - - // Other state - bool object_set = false; - bool material_loaded = false; - char current_material_name[64]; - - char *pch; - char *rest = file_string; - pch = strtok_r((char *)file_string, "\n", &rest); - - int line_num = 0; - char last_char_type = 'a'; - - while (pch != NULL) { - line_num++; - char line_header[128]; - int offset = 0; - - // skip whitespace - char *p = pch; - - skip_space(pch); - - if (*p == '\0') { - /* the string is empty */ - } else { - // read the first word of the line - int res = sscanf(pch, "%s %n", line_header, &offset); - /* printf("header: %s, offset : %d res: %d\n",line_header, offset, res); */ - if (res != 1) { - break; - } - - if (strcmp(line_header, "o") == 0 || strcmp(line_header, "g") == 0) { - // if we're currently parsing one - if (!object_set) { - object_set = true; - } else { - create_submesh(out_model->meshes, tmp_positions, tmp_normals, tmp_uvs, tmp_faces, - out_model->materials, material_loaded, current_material_name); - object_set = false; - } - } else if (strcmp(line_header, "v") == 0) { - // special logic: if we went from faces back to vertices trigger a mesh output. - // PS: I hate OBJ - if (last_char_type == 'f') { - create_submesh(out_model->meshes, tmp_positions, tmp_normals, tmp_uvs, tmp_faces, - out_model->materials, material_loaded, current_material_name); - object_set = false; - } - - last_char_type = 'v'; - vec3 vertex; - sscanf(pch + offset, "%f %f %f", &vertex.x, &vertex.y, &vertex.z); - - vec3_darray_push(tmp_positions, vertex); - } else if (strcmp(line_header, "vt") == 0) { - last_char_type = 't'; - vec2 uv; - char copy[1024]; - memcpy(copy, pch + offset, strlen(pch + offset) + 1); - char *p = pch + offset; - while (isspace((unsigned char)*p)) ++p; - - // I can't remember what is going on here - memset(copy, 0, 1024); - memcpy(copy, pch + offset, strlen(pch + offset) + 1); - int res = sscanf(copy, "%f %f", &uv.x, &uv.y); - memset(copy, 0, 1024); - memcpy(copy, pch + offset, strlen(pch + offset) + 1); - if (res != 1) { - // da frick? some .obj files have 3 uvs instead of 2 - f32 dummy; - int res2 = sscanf(copy, "%f %f %f", &uv.x, &uv.y, &dummy); - } - - if (invert_textures_y) { - uv.y = -uv.y; // flip Y axis to be consistent with how other PNGs are being handled - // `texture_load` will flip it again - } - vec2_darray_push(tmp_uvs, uv); - } else if (strcmp(line_header, "vn") == 0) { - last_char_type = 'n'; - vec3 normal; - sscanf(pch + offset, "%f %f %f", &normal.x, &normal.y, &normal.z); - vec3_darray_push(tmp_normals, normal); - } else if (strcmp(line_header, "f") == 0) { - last_char_type = 'f'; - struct face f; - sscanf(pch + offset, "%d/%d/%d %d/%d/%d %d/%d/%d", &f.vertex_indices[0], &f.uv_indices[0], - &f.normal_indices[0], &f.vertex_indices[1], &f.uv_indices[1], &f.normal_indices[1], - &f.vertex_indices[2], &f.uv_indices[2], &f.normal_indices[2]); - // printf("f %d/%d/%d %d/%d/%d %d/%d/%d\n", f.vertex_indices[0], f.uv_indices[0], - // f.normal_indices[0], - // f.vertex_indices[1], f.uv_indices[1], f.normal_indices[1], - // f.vertex_indices[2], f.uv_indices[2], f.normal_indices[2]); - face_darray_push(tmp_faces, f); - } else if (strcmp(line_header, "mtllib") == 0) { - char filename[1024]; - sscanf(pch + offset, "%s", filename); - char mtllib_path[1024]; - snprintf(mtllib_path, sizeof(mtllib_path), "%s/%s", relative_path.buf, filename); - if (!load_material_lib(mtllib_path, relative_path, out_model->materials)) { - ERROR("couldnt load material lib"); - return false; - } - } else if (strcmp(line_header, "usemtl") == 0) { - material_loaded = true; - sscanf(pch + offset, "%s", current_material_name); - } - } - - pch = strtok_r(NULL, "\n", &rest); - } - - // last mesh or if one wasnt created with 'o' directive - if (face_darray_len(tmp_faces) > 0) { - TRACE("Last leftover mesh"); - create_submesh(out_model->meshes, tmp_positions, tmp_normals, tmp_uvs, tmp_faces, - out_model->materials, material_loaded, current_material_name); - } - - // Free data - free((char *)file_string); - vec3_darray_free(tmp_positions); - vec3_darray_free(tmp_normals); - vec2_darray_free(tmp_uvs); - face_darray_free(tmp_faces); - TRACE("Freed temporary OBJ loading data"); - - if (mesh_darray_len(out_model->meshes) > 256) { - printf("num meshes: %ld\n", mesh_darray_len(out_model->meshes)); - } - - // TODO: bounding box calculation for each mesh - // TODO: bounding box calculation for model + // // Setup temps + // vec3_darray *tmp_positions = vec3_darray_new(1000); + // vec3_darray *tmp_normals = vec3_darray_new(1000); + // vec2_darray *tmp_uvs = vec2_darray_new(1000); + // face_darray *tmp_faces = face_darray_new(1000); + // // TODO: In the future I'd like these temporary arrays to be allocated from an arena provided + // // by the function one level up, model_load_obj. That way we can just `return false;` anywhere + // in + // // this code to indicate an error, and be sure that all that memory will be cleaned up without + // // having to call vec3_darray_free in every single error case before returning. + + // // Other state + // bool object_set = false; + // bool material_loaded = false; + // char current_material_name[64]; + + // char *pch; + // char *rest = file_string; + // pch = strtok_r((char *)file_string, "\n", &rest); + + // int line_num = 0; + // char last_char_type = 'a'; + + // while (pch != NULL) { + // line_num++; + // char line_header[128]; + // int offset = 0; + + // // skip whitespace + // char *p = pch; + + // skip_space(pch); + + // if (*p == '\0') { + // /* the string is empty */ + // } else { + // // read the first word of the line + // int res = sscanf(pch, "%s %n", line_header, &offset); + // /* printf("header: %s, offset : %d res: %d\n",line_header, offset, res); */ + // if (res != 1) { + // break; + // } + + // if (strcmp(line_header, "o") == 0 || strcmp(line_header, "g") == 0) { + // // if we're currently parsing one + // if (!object_set) { + // object_set = true; + // } else { + // create_submesh(out_model->meshes, tmp_positions, tmp_normals, tmp_uvs, tmp_faces, + // NULL, // out_model->materials, + // material_loaded, current_material_name); + // object_set = false; + // } + // } else if (strcmp(line_header, "v") == 0) { + // // special logic: if we went from faces back to vertices trigger a mesh output. + // // PS: I hate OBJ + // if (last_char_type == 'f') { + // create_submesh(out_model->meshes, tmp_positions, tmp_normals, tmp_uvs, tmp_faces, + // NULL, // FIXME: out_model->materials, + // material_loaded, current_material_name); + // object_set = false; + // } + + // last_char_type = 'v'; + // vec3 vertex; + // sscanf(pch + offset, "%f %f %f", &vertex.x, &vertex.y, &vertex.z); + + // vec3_darray_push(tmp_positions, vertex); + // } else if (strcmp(line_header, "vt") == 0) { + // last_char_type = 't'; + // vec2 uv; + // char copy[1024]; + // memcpy(copy, pch + offset, strlen(pch + offset) + 1); + // char *p = pch + offset; + // while (isspace((unsigned char)*p)) ++p; + + // // I can't remember what is going on here + // memset(copy, 0, 1024); + // memcpy(copy, pch + offset, strlen(pch + offset) + 1); + // int res = sscanf(copy, "%f %f", &uv.x, &uv.y); + // memset(copy, 0, 1024); + // memcpy(copy, pch + offset, strlen(pch + offset) + 1); + // if (res != 1) { + // // da frick? some .obj files have 3 uvs instead of 2 + // f32 dummy; + // int res2 = sscanf(copy, "%f %f %f", &uv.x, &uv.y, &dummy); + // } + + // if (invert_textures_y) { + // uv.y = -uv.y; // flip Y axis to be consistent with how other PNGs are being handled + // // `texture_load` will flip it again + // } + // vec2_darray_push(tmp_uvs, uv); + // } else if (strcmp(line_header, "vn") == 0) { + // last_char_type = 'n'; + // vec3 normal; + // sscanf(pch + offset, "%f %f %f", &normal.x, &normal.y, &normal.z); + // vec3_darray_push(tmp_normals, normal); + // } else if (strcmp(line_header, "f") == 0) { + // last_char_type = 'f'; + // struct face f; + // sscanf(pch + offset, "%d/%d/%d %d/%d/%d %d/%d/%d", &f.vertex_indices[0], + // &f.uv_indices[0], + // &f.normal_indices[0], &f.vertex_indices[1], &f.uv_indices[1], + // &f.normal_indices[1], &f.vertex_indices[2], &f.uv_indices[2], + // &f.normal_indices[2]); + // // printf("f %d/%d/%d %d/%d/%d %d/%d/%d\n", f.vertex_indices[0], f.uv_indices[0], + // // f.normal_indices[0], + // // f.vertex_indices[1], f.uv_indices[1], f.normal_indices[1], + // // f.vertex_indices[2], f.uv_indices[2], f.normal_indices[2]); + // face_darray_push(tmp_faces, f); + // } else if (strcmp(line_header, "mtllib") == 0) { + // char filename[1024]; + // sscanf(pch + offset, "%s", filename); + // char mtllib_path[1024]; + // snprintf(mtllib_path, sizeof(mtllib_path), "%s/%s", relative_path.buf, filename); + // if (!load_material_lib(mtllib_path, relative_path, out_model->materials)) { + // ERROR("couldnt load material lib"); + // return false; + // } + // } else if (strcmp(line_header, "usemtl") == 0) { + // material_loaded = true; + // sscanf(pch + offset, "%s", current_material_name); + // } + // } + + // pch = strtok_r(NULL, "\n", &rest); + // } + + // // last mesh or if one wasnt created with 'o' directive + // if (face_darray_len(tmp_faces) > 0) { + // TRACE("Last leftover mesh"); + // create_submesh(out_model->meshes, tmp_positions, tmp_normals, tmp_uvs, tmp_faces, + // NULL, // TODO: out_model->materials, + // material_loaded, current_material_name); + // } + + // // Free data + // free((char *)file_string); + // vec3_darray_free(tmp_positions); + // vec3_darray_free(tmp_normals); + // vec2_darray_free(tmp_uvs); + // face_darray_free(tmp_faces); + // TRACE("Freed temporary OBJ loading data"); + + // if (mesh_darray_len(out_model->meshes) > 256) { + // printf("num meshes: %ld\n", mesh_darray_len(out_model->meshes)); + // } + + // // TODO: bounding box calculation for each mesh + // // TODO: bounding box calculation for model return true; } @@ -232,158 +238,159 @@ bool model_load_obj_str(const char *file_string, str8 relative_path, model *out_ void create_submesh(mesh_darray *meshes, vec3_darray *tmp_positions, vec3_darray *tmp_normals, vec2_darray *tmp_uvs, face_darray *tmp_faces, material_darray *materials, bool material_loaded, char current_material_name[256]) { - size_t num_verts = face_darray_len(tmp_faces) * 3; - vertex_darray *out_vertices = vertex_darray_new(num_verts); - - face_darray_iter face_iter = face_darray_iter_new(tmp_faces); - struct face *f; - - while ((f = face_darray_iter_next(&face_iter))) { - for (int j = 0; j < 3; j++) { - vertex vert = { 0 }; - vert.position = tmp_positions->data[f->vertex_indices[j] - 1]; - if (vec3_darray_len(tmp_normals) == 0) { - vert.normal = vec3_create(0.0, 0.0, 0.0); - } else { - vert.normal = tmp_normals->data[f->normal_indices[j] - 1]; - } - vert.uv = tmp_uvs->data[f->uv_indices[j] - 1]; - vertex_darray_push(out_vertices, vert); - } - } - - DEBUG("Loaded submesh\n vertices: %zu\n uvs: %zu\n normals: %zu\n faces: %zu", - vec3_darray_len(tmp_positions), vec2_darray_len(tmp_uvs), vec3_darray_len(tmp_normals), - face_darray_len(tmp_faces)); - - // Clear current object faces - face_darray_clear(tmp_faces); - - mesh m = { .vertices = out_vertices }; - if (material_loaded) { - // linear scan to find material - bool found = false; - DEBUG("Num of materials : %ld", material_darray_len(materials)); - material_darray_iter mat_iter = material_darray_iter_new(materials); - blinn_phong_material *cur_material; - while ((cur_material = material_darray_iter_next(&mat_iter))) { - if (strcmp(cur_material->name, current_material_name) == 0) { - DEBUG("Found match"); - m.material_index = mat_iter.current_idx - 1; - found = true; - break; - } - } - - if (!found) { - // TODO: default material - m.material_index = 0; - DEBUG("Set default material"); - } - } - mesh_darray_push(meshes, m); + // size_t num_verts = face_darray_len(tmp_faces) * 3; + // vertex_darray *out_vertices = vertex_darray_new(num_verts); + + // face_darray_iter face_iter = face_darray_iter_new(tmp_faces); + // struct face *f; + + // while ((f = face_darray_iter_next(&face_iter))) { + // for (int j = 0; j < 3; j++) { + // vertex vert = { 0 }; + // vert.position = tmp_positions->data[f->vertex_indices[j] - 1]; + // if (vec3_darray_len(tmp_normals) == 0) { + // vert.normal = vec3_create(0.0, 0.0, 0.0); + // } else { + // vert.normal = tmp_normals->data[f->normal_indices[j] - 1]; + // } + // vert.uv = tmp_uvs->data[f->uv_indices[j] - 1]; + // vertex_darray_push(out_vertices, vert); + // } + // } + + // DEBUG("Loaded submesh\n vertices: %zu\n uvs: %zu\n normals: %zu\n faces: %zu", + // vec3_darray_len(tmp_positions), vec2_darray_len(tmp_uvs), vec3_darray_len(tmp_normals), + // face_darray_len(tmp_faces)); + + // // Clear current object faces + // face_darray_clear(tmp_faces); + + // mesh m = { .vertices = out_vertices }; + // if (material_loaded) { + // // linear scan to find material + // bool found = false; + // DEBUG("Num of materials : %ld", material_darray_len(materials)); + // material_darray_iter mat_iter = material_darray_iter_new(materials); + // blinn_phong_material *cur_material; + // while ((cur_material = material_darray_iter_next(&mat_iter))) { + // if (strcmp(cur_material->name, current_material_name) == 0) { + // DEBUG("Found match"); + // m.material_index = mat_iter.current_idx - 1; + // found = true; + // break; + // } + // } + + // if (!found) { + // // TODO: default material + // m.material_index = 0; + // DEBUG("Set default material"); + // } + // } + // mesh_darray_push(meshes, m); } bool load_material_lib(const char *path, str8 relative_path, material_darray *materials) { TRACE("BEGIN load material lib at %s", path); - const char *file_string = string_from_file(path); - if (file_string == NULL) { - ERROR("couldnt load %s", path); - return false; - } - - char *pch; - char *saveptr; - pch = strtok_r((char *)file_string, "\n", &saveptr); - - material current_material = DEFAULT_MATERIAL; - - bool material_set = false; - - while (pch != NULL) { - char line_header[128]; - int offset = 0; - // read the first word of the line - int res = sscanf(pch, "%s %n", line_header, &offset); - if (res != 1) { - break; - } - - // When we see "newmtl", start a new material, or flush the previous one - if (strcmp(line_header, "newmtl") == 0) { - if (material_set) { - // a material was being parsed, so flush that one and start a new one - material_darray_push(materials, current_material); - DEBUG("pushed material with name %s", current_material.name); - WARN("Reset current material"); - current_material = DEFAULT_MATERIAL; - } else { - material_set = true; - } - // scan the new material name - char material_name[64]; - sscanf(pch + offset, "%s", current_material.name); - DEBUG("material name %s\n", current_material.name); - // current_material.name = material_name; - } else if (strcmp(line_header, "Ka") == 0) { - // ambient - sscanf(pch + offset, "%f %f %f", ¤t_material.ambient_colour.x, - ¤t_material.ambient_colour.y, ¤t_material.ambient_colour.z); - } else if (strcmp(line_header, "Kd") == 0) { - // diffuse - sscanf(pch + offset, "%f %f %f", ¤t_material.diffuse.x, ¤t_material.diffuse.y, - ¤t_material.diffuse.z); - } else if (strcmp(line_header, "Ks") == 0) { - // specular - sscanf(pch + offset, "%f %f %f", ¤t_material.specular.x, ¤t_material.specular.y, - ¤t_material.specular.z); - } else if (strcmp(line_header, "Ns") == 0) { - // specular exponent - sscanf(pch + offset, "%f", ¤t_material.spec_exponent); - } else if (strcmp(line_header, "map_Kd") == 0) { - char diffuse_map_filename[1024]; - sscanf(pch + offset, "%s", diffuse_map_filename); - char diffuse_map_path[1024]; - snprintf(diffuse_map_path, sizeof(diffuse_map_path), "%s/%s", relative_path.buf, - diffuse_map_filename); - printf("load from %s\n", diffuse_map_path); - - // -------------- - texture diffuse_texture = texture_data_load(diffuse_map_path, true); - current_material.diffuse_texture = diffuse_texture; - strcpy(current_material.diffuse_tex_path, diffuse_map_path); - texture_data_upload(¤t_material.diffuse_texture); - // -------------- - } else if (strcmp(line_header, "map_Ks") == 0) { - // char specular_map_path[1024] = "assets/"; - // sscanf(pch + offset, "%s", specular_map_path + 7); - char specular_map_filename[1024]; - sscanf(pch + offset, "%s", specular_map_filename); - char specular_map_path[1024]; - snprintf(specular_map_path, sizeof(specular_map_path), "%s/%s", relative_path.buf, - specular_map_filename); - printf("load from %s\n", specular_map_path); - // -------------- - texture specular_texture = texture_data_load(specular_map_path, true); - current_material.specular_texture = specular_texture; - strcpy(current_material.specular_tex_path, specular_map_path); - texture_data_upload(¤t_material.specular_texture); - // -------------- - } else if (strcmp(line_header, "map_Bump") == 0) { - // TODO - } - - pch = strtok_r(NULL, "\n", &saveptr); - } - - TRACE("end load material lib"); - - // last mesh or if one wasnt created with 'o' directive - // TRACE("Last leftover material"); - material_darray_push(materials, current_material); - - INFO("Loaded %ld materials", material_darray_len(materials)); + // const char *file_string = string_from_file(path); + // if (file_string == NULL) { + // ERROR("couldnt load %s", path); + // return false; + // } + + // char *pch; + // char *saveptr; + // pch = strtok_r((char *)file_string, "\n", &saveptr); + + // material current_material = DEFAULT_MATERIAL; + + // bool material_set = false; + + // while (pch != NULL) { + // char line_header[128]; + // int offset = 0; + // // read the first word of the line + // int res = sscanf(pch, "%s %n", line_header, &offset); + // if (res != 1) { + // break; + // } + + // // When we see "newmtl", start a new material, or flush the previous one + // if (strcmp(line_header, "newmtl") == 0) { + // if (material_set) { + // // a material was being parsed, so flush that one and start a new one + // material_darray_push(materials, current_material); + // DEBUG("pushed material with name %s", current_material.name); + // WARN("Reset current material"); + // current_material = DEFAULT_MATERIAL; + // } else { + // material_set = true; + // } + // // scan the new material name + // char material_name[64]; + // sscanf(pch + offset, "%s", current_material.name); + // DEBUG("material name %s\n", current_material.name); + // // current_material.name = material_name; + // } else if (strcmp(line_header, "Ka") == 0) { + // // ambient + // sscanf(pch + offset, "%f %f %f", ¤t_material.ambient_colour.x, + // ¤t_material.ambient_colour.y, ¤t_material.ambient_colour.z); + // } else if (strcmp(line_header, "Kd") == 0) { + // // diffuse + // sscanf(pch + offset, "%f %f %f", ¤t_material.diffuse.x, ¤t_material.diffuse.y, + // ¤t_material.diffuse.z); + // } else if (strcmp(line_header, "Ks") == 0) { + // // specular + // sscanf(pch + offset, "%f %f %f", ¤t_material.specular.x, + // ¤t_material.specular.y, + // ¤t_material.specular.z); + // } else if (strcmp(line_header, "Ns") == 0) { + // // specular exponent + // sscanf(pch + offset, "%f", ¤t_material.spec_exponent); + // } else if (strcmp(line_header, "map_Kd") == 0) { + // char diffuse_map_filename[1024]; + // sscanf(pch + offset, "%s", diffuse_map_filename); + // char diffuse_map_path[1024]; + // snprintf(diffuse_map_path, sizeof(diffuse_map_path), "%s/%s", relative_path.buf, + // diffuse_map_filename); + // printf("load from %s\n", diffuse_map_path); + + // // -------------- + // texture diffuse_texture = texture_data_load(diffuse_map_path, true); + // current_material.diffuse_texture = diffuse_texture; + // strcpy(current_material.diffuse_tex_path, diffuse_map_path); + // texture_data_upload(¤t_material.diffuse_texture); + // // -------------- + // } else if (strcmp(line_header, "map_Ks") == 0) { + // // char specular_map_path[1024] = "assets/"; + // // sscanf(pch + offset, "%s", specular_map_path + 7); + // char specular_map_filename[1024]; + // sscanf(pch + offset, "%s", specular_map_filename); + // char specular_map_path[1024]; + // snprintf(specular_map_path, sizeof(specular_map_path), "%s/%s", relative_path.buf, + // specular_map_filename); + // printf("load from %s\n", specular_map_path); + // // -------------- + // texture specular_texture = texture_data_load(specular_map_path, true); + // current_material.specular_texture = specular_texture; + // strcpy(current_material.specular_tex_path, specular_map_path); + // texture_data_upload(¤t_material.specular_texture); + // // -------------- + // } else if (strcmp(line_header, "map_Bump") == 0) { + // // TODO + // } + + // pch = strtok_r(NULL, "\n", &saveptr); + // } + + // TRACE("end load material lib"); + + // // last mesh or if one wasnt created with 'o' directive + // // TRACE("Last leftover material"); + // material_darray_push(materials, current_material); + + // INFO("Loaded %ld materials", material_darray_len(materials)); TRACE("END load material lib"); return true; } diff --git a/xmake.lua b/xmake.lua index a5e6239..0fc042c 100644 --- a/xmake.lua +++ b/xmake.lua @@ -1,6 +1,6 @@ set_project("celeritas") set_version("0.1.0") -set_config("cc", "gcc") +set_config("cc", "clang") add_rules("mode.debug", "mode.release") -- we have two modes: debug & release @@ -25,7 +25,7 @@ if is_plat("linux") then elseif is_plat("windows") then add_defines("CEL_PLATFORM_WINDOWS") add_syslinks("user32", "gdi32", "kernel32", "shell32") - add_links("pthreadVC2-w64") + -- add_links("pthreadVC2-w64") elseif is_plat("macosx") then add_defines("CEL_PLATFORM_MAC") add_frameworks("Cocoa", "IOKit", "CoreVideo", "OpenGL") @@ -54,11 +54,10 @@ add_requires("local_glfw") local core_sources = { "deps/glad/src/glad.c", "src/*.c", - "src/logos/*.c", + -- "src/logos/*.c", "src/platform/*.c", "src/renderer/*.c", "src/renderer/backends/*.c", - "src/renderer/cleanroom/*.c", "src/resources/*.c", "src/std/*.c", "src/std/containers/*.c", @@ -96,12 +95,12 @@ target("core_config") add_includedirs("deps/stb_image_write", {public = true}) add_includedirs("deps/stb_truetype", {public = true}) add_includedirs("src/", {public = true}) - add_includedirs("src/logos/", {public = true}) + -- add_includedirs("src/logos/", {public = true}) add_includedirs("src/maths/", {public = true}) add_includedirs("src/platform/", {public = true}) add_includedirs("src/renderer/", {public = true}) add_includedirs("src/renderer/backends/", {public = true}) - add_includedirs("src/renderer/cleanroom/", {public = true}) + -- add_includedirs("src/renderer/cleanroom/", {public = true}) add_includedirs("src/resources/", {public = true}) add_includedirs("src/std/", {public = true}) add_includedirs("src/std/containers", {public = true}) @@ -133,75 +132,75 @@ target("core_shared") if is_plat("windows") then add_links("msvcrt", "legacy_stdio_definitions") -- for release builds add_links("msvcrtd", "legacy_stdio_definitions") -- for debug builds - add_links("pthreadVC2-w64") + -- add_links("pthreadVC2-w64") end target("main_loop") - set_kind("binary") - set_group("examples") - add_deps("core_shared") - add_files("examples/main_loop/ex_main_loop.c") - set_rundir("$(projectdir)") - -target("std") - set_kind("binary") - set_group("examples") - add_deps("core_static") - add_files("examples/standard_lib/ex_std.c") - set_rundir("$(projectdir)") - -target("obj") - set_kind("binary") - set_group("examples") - add_deps("core_static") - add_files("examples/obj_loading/ex_obj_loading.c") - set_rundir("$(projectdir)") - -target("input") - set_kind("binary") - set_group("examples") - add_deps("core_static") - add_files("examples/input/ex_input.c") - set_rundir("$(projectdir)") - -target("gltf") - set_kind("binary") - set_group("examples") - add_deps("core_static") - add_files("examples/gltf_loading/ex_gltf_loading.c") - set_rundir("$(projectdir)") - -target("transforms") - set_kind("binary") - set_group("examples") - add_deps("core_shared") - add_files("examples/transforms/ex_transforms.c") - set_rundir("$(projectdir)") - -target("animation") - set_kind("binary") - set_group("examples") - add_deps("core_shared") - add_files("examples/property_animation/ex_property_animation.c") - set_rundir("$(projectdir)") - -target("skinned") - set_kind("binary") - set_group("examples") - add_deps("core_shared") - add_files("examples/skinned_animation/ex_skinned_animation.c") - set_rundir("$(projectdir)") - -target("input") set_kind("binary") set_group("examples") add_deps("core_static") - add_files("examples/input/ex_input.c") + add_files("examples/main_loop/ex_main_loop.c") set_rundir("$(projectdir)") -target("demo") - set_kind("binary") - set_group("examples") - add_deps("core_static") - add_files("examples/demo/demo.c") - set_rundir("$(projectdir)") +-- target("std") +-- set_kind("binary") +-- set_group("examples") +-- add_deps("core_static") +-- add_files("examples/standard_lib/ex_std.c") +-- set_rundir("$(projectdir)") + +-- target("obj") +-- set_kind("binary") +-- set_group("examples") +-- add_deps("core_static") +-- add_files("examples/obj_loading/ex_obj_loading.c") +-- set_rundir("$(projectdir)") + +-- target("input") +-- set_kind("binary") +-- set_group("examples") +-- add_deps("core_static") +-- add_files("examples/input/ex_input.c") +-- set_rundir("$(projectdir)") + +-- target("gltf") +-- set_kind("binary") +-- set_group("examples") +-- add_deps("core_static") +-- add_files("examples/gltf_loading/ex_gltf_loading.c") +-- set_rundir("$(projectdir)") + +-- target("transforms") +-- set_kind("binary") +-- set_group("examples") +-- add_deps("core_shared") +-- add_files("examples/transforms/ex_transforms.c") +-- set_rundir("$(projectdir)") + +-- target("animation") +-- set_kind("binary") +-- set_group("examples") +-- add_deps("core_shared") +-- add_files("examples/property_animation/ex_property_animation.c") +-- set_rundir("$(projectdir)") + +-- target("skinned") +-- set_kind("binary") +-- set_group("examples") +-- add_deps("core_shared") +-- add_files("examples/skinned_animation/ex_skinned_animation.c") +-- set_rundir("$(projectdir)") + +-- target("input") +-- set_kind("binary") +-- set_group("examples") +-- add_deps("core_static") +-- add_files("examples/input/ex_input.c") +-- set_rundir("$(projectdir)") + +-- target("demo") +-- set_kind("binary") +-- set_group("examples") +-- add_deps("core_static") +-- add_files("examples/demo/demo.c") +-- set_rundir("$(projectdir)") -- cgit v1.2.3-70-g09d2 From 7be34d14afa3a091228bab033f7d604111dcb402 Mon Sep 17 00:00:00 2001 From: omnisci3nce Date: Sat, 27 Apr 2024 18:16:09 +1000 Subject: chore: fmt --- src/renderer/render.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/renderer/render.c') diff --git a/src/renderer/render.c b/src/renderer/render.c index 8cd7421..d6b77c0 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -1,5 +1,5 @@ -#include #include "render.h" +#include #include "camera.h" bool renderer_init(renderer* ren) { @@ -31,12 +31,13 @@ bool renderer_init(renderer* ren) { // DEBUG("init graphics api backend"); // if (!gfx_backend_init(ren)) { - // FATAL("Couldnt load graphics api backend"); - // return false; + // FATAL("Couldnt load graphics api backend"); + // return false; // } // ren->blinn_phong = - // shader_create_separate("assets/shaders/blinn_phong.vert", "assets/shaders/blinn_phong.frag"); + // shader_create_separate("assets/shaders/blinn_phong.vert", + // "assets/shaders/blinn_phong.frag"); // ren->skinned = // shader_create_separate("assets/shaders/skinned.vert", "assets/shaders/blinn_phong.frag"); -- cgit v1.2.3-70-g09d2 From 411520b240446f878a27c5d89812000774cc3c15 Mon Sep 17 00:00:00 2001 From: omnisci3nce Date: Sun, 28 Apr 2024 09:14:22 +1000 Subject: getting vulkan working on windows --- examples/main_loop/ex_main_loop.c | 2 + src/defines.h | 3 +- src/maths/maths.h | 1 + src/maths/primitives.c | 261 ++-- src/renderer/archive/old_backend_vulkan.c | 1990 +++++++++++++++++++++++++++++ src/renderer/backends/backend_dx11.h | 4 + src/renderer/backends/backend_vulkan.c | 1990 +---------------------------- src/renderer/backends/backend_vulkan.h | 27 + src/renderer/cleanroom/backend_vulkan.c | 65 - src/renderer/cleanroom/backend_vulkan.h | 28 - src/renderer/cleanroom/immediate.c | 18 - src/renderer/cleanroom/immediate.h | 20 - src/renderer/cleanroom/types.h | 60 - src/renderer/immediate.c | 18 + src/renderer/immediate.h | 20 + src/renderer/ral.h | 10 +- src/renderer/ral_types.h | 46 +- src/renderer/render.c | 4 +- src/renderer/render.h | 4 +- src/renderer/render_types.h | 2 - src/systems/screenspace.h | 2 +- xmake.lua | 14 +- 22 files changed, 2309 insertions(+), 2280 deletions(-) create mode 100644 src/renderer/archive/old_backend_vulkan.c create mode 100644 src/renderer/backends/backend_vulkan.h delete mode 100644 src/renderer/cleanroom/backend_vulkan.c delete mode 100644 src/renderer/cleanroom/backend_vulkan.h delete mode 100644 src/renderer/cleanroom/immediate.c delete mode 100644 src/renderer/cleanroom/immediate.h create mode 100644 src/renderer/immediate.c create mode 100644 src/renderer/immediate.h (limited to 'src/renderer/render.c') diff --git a/examples/main_loop/ex_main_loop.c b/examples/main_loop/ex_main_loop.c index 4e1f6a9..4e31313 100644 --- a/examples/main_loop/ex_main_loop.c +++ b/examples/main_loop/ex_main_loop.c @@ -26,6 +26,8 @@ int main() { // insert work here render_frame_end(&core->renderer); + glfwSwapBuffers(core->renderer.window); + glfwPollEvents(); } return 0; diff --git a/src/defines.h b/src/defines.h index 54f2dd5..ec526e0 100644 --- a/src/defines.h +++ b/src/defines.h @@ -71,7 +71,8 @@ Renderer backend defines: #endif #if defined(CEL_PLATFORM_WINDOWS) -#define CEL_REND_BACKEND_DX11 1 +// #define CEL_REND_BACKEND_DX11 1 +#define CEL_REND_BACKEND_VULKAN 1 #endif #if defined(CEL_PLATFORM_MAC) diff --git a/src/maths/maths.h b/src/maths/maths.h index ad33981..88a5215 100644 --- a/src/maths/maths.h +++ b/src/maths/maths.h @@ -53,6 +53,7 @@ static inline void print_vec3(vec3 v) { printf("{ x: %f, y: %f, z: %f )\n", v.x, // TODO: Dimension 2 static inline vec2 vec2_create(f32 x, f32 y) { return (vec2){ x, y }; } +static inline vec2 vec2_div(vec2 a, f32 s) { return (vec2){ a.x / s, a.y / s }; } // TODO: Dimension 4 static inline vec4 vec4_create(f32 x, f32 y, f32 z, f32 w) { return (vec4){ x, y, z, w }; } diff --git a/src/maths/primitives.c b/src/maths/primitives.c index 55ff5fc..310fc98 100644 --- a/src/maths/primitives.c +++ b/src/maths/primitives.c @@ -1,4 +1,5 @@ #include "primitives.h" +#include "maths.h" // vertices f32 plane_vertex_positions[] = { @@ -13,9 +14,9 @@ geometry_data geo_create_plane(f32x2 extents) { vertex_format format = VERTEX_STATIC_3D; vertex_darray* vertices = vertex_darray_new(4); - vertex_darray_push(vertices, (vertex){ .static_3d = { .position = } }); + // vertex_darray_push(vertices, (vertex){ .static_3d = { .position = } }); - return (geometry_data) { .format = format, .vertices =.has_indices = true, } + // return (geometry_data) { .format = format, .vertices =.has_indices = true, } } // OLD @@ -33,130 +34,132 @@ static mesh prim_cube_mesh_create() { mesh cube = { 0 }; cube.vertices = vertex_darray_new(36); - // back faces - vertex_darray_push( - cube.vertices, - (vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_Z, .uv = (vec2){ 0, 1 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = BACK_TOP_LEFT, .normal = VEC3_NEG_Z, .uv = (vec2){ 0, 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_NEG_Z, .uv = (vec2){ 1, 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_NEG_Z, .uv = (vec2){ 1, 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_NEG_Z, .uv = (vec2){ 1, 1 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_Z, .uv = (vec2){ 0, 1 } }); - - // front faces - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_BOT_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0, 1 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 1, 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_TOP_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0, 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_BOT_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0, 1 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 1, 1 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 1, 0 } }); - - // top faces - vertex_darray_push(cube.vertices, - (vertex){ .position = BACK_TOP_LEFT, .normal = VEC3_Y, .uv = (vec2){ 0, 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_TOP_LEFT, .normal = VEC3_Y, .uv = (vec2){ 0, 1 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 1, 1 } }); - vertex_darray_push(cube.vertices, - (vertex){ .position = BACK_TOP_LEFT, .normal = VEC3_Y, .uv = (vec2){ 0, 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 1, 1 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 1, 0 } }); - - // bottom faces - vertex_darray_push( - cube.vertices, - (vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_Y, .uv = (vec2){ 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_NEG_Y, .uv = (vec2){ 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_BOT_LEFT, .normal = VEC3_NEG_Y, .uv = (vec2){ 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_Y, .uv = (vec2){ 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_NEG_Y, .uv = (vec2){ 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_NEG_Y, .uv = (vec2){ 0 } }); - - // right faces - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0, 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 1, 1 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 1, 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 1, 1 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0, 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0, 1 } }); - - // left faces - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_TOP_LEFT, .normal = VEC3_NEG_X, .uv = (vec2){ 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = BACK_TOP_LEFT, .normal = VEC3_NEG_X, .uv = (vec2){ 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_X, .uv = (vec2){ 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_X, .uv = (vec2){ 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_BOT_LEFT, .normal = VEC3_NEG_X, .uv = (vec2){ 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_TOP_LEFT, .normal = VEC3_NEG_X, .uv = (vec2){ 0 } }); - - cube.indices_len = cube.vertices->len; - cube.indices = malloc(sizeof(u32) * cube.indices_len); - - for (u32 i = 0; i < cube.indices_len; i++) { - cube.indices[i] = i; - } + // // back faces + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_Z, .uv = (vec2){ 0, 1 } }); + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = BACK_TOP_LEFT, .normal = VEC3_NEG_Z, .uv = (vec2){ 0, 0 } }); + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_NEG_Z, .uv = (vec2){ 1, 0 } }); + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_NEG_Z, .uv = (vec2){ 1, 0 } }); + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_NEG_Z, .uv = (vec2){ 1, 1 } }); + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_Z, .uv = (vec2){ 0, 1 } }); + + // // front faces + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = FRONT_BOT_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0, 1 } }); + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 1, 0 } }); + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = FRONT_TOP_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0, 0 } }); + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = FRONT_BOT_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0, 1 } }); + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 1, 1 } }); + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 1, 0 } }); + + // // top faces + // vertex_darray_push(cube.vertices, + // (vertex){ .position = BACK_TOP_LEFT, .normal = VEC3_Y, .uv = (vec2){ 0, 0 + // } }); + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = FRONT_TOP_LEFT, .normal = VEC3_Y, .uv = (vec2){ 0, 1 } }); + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 1, 1 } }); + // vertex_darray_push(cube.vertices, + // (vertex){ .position = BACK_TOP_LEFT, .normal = VEC3_Y, .uv = (vec2){ 0, 0 + // } }); + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 1, 1 } }); + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 1, 0 } }); + + // // bottom faces + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_Y, .uv = (vec2){ 0 } }); + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_NEG_Y, .uv = (vec2){ 0 } }); + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = FRONT_BOT_LEFT, .normal = VEC3_NEG_Y, .uv = (vec2){ 0 } }); + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_Y, .uv = (vec2){ 0 } }); + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_NEG_Y, .uv = (vec2){ 0 } }); + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_NEG_Y, .uv = (vec2){ 0 } }); + + // // right faces + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0, 0 } }); + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 1, 1 } }); + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 1, 0 } }); + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 1, 1 } }); + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0, 0 } }); + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0, 1 } }); + + // // left faces + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = FRONT_TOP_LEFT, .normal = VEC3_NEG_X, .uv = (vec2){ 0 } }); + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = BACK_TOP_LEFT, .normal = VEC3_NEG_X, .uv = (vec2){ 0 } }); + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_X, .uv = (vec2){ 0 } }); + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_X, .uv = (vec2){ 0 } }); + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = FRONT_BOT_LEFT, .normal = VEC3_NEG_X, .uv = (vec2){ 0 } }); + // vertex_darray_push( + // cube.vertices, + // (vertex){ .position = FRONT_TOP_LEFT, .normal = VEC3_NEG_X, .uv = (vec2){ 0 } }); + + // cube.indices_len = cube.vertices->len; + // cube.indices = malloc(sizeof(u32) * cube.indices_len); + + // for (u32 i = 0; i < cube.indices_len; i++) { + // cube.indices[i] = i; + // } cube.has_indices = true; @@ -174,4 +177,10 @@ static model_handle prim_cube_new(core* core) { u32 index = (u32)model_darray_len(core->models); model_darray_push_copy(core->models, &model); return (model_handle){ .raw = index }; +} + +// --- Spheres + +geometry_data geo_create_uvsphere(f32 radius, f32 north_south_lines, f32 east_west_lines) { + // TODO } \ No newline at end of file diff --git a/src/renderer/archive/old_backend_vulkan.c b/src/renderer/archive/old_backend_vulkan.c new file mode 100644 index 0000000..a18ca70 --- /dev/null +++ b/src/renderer/archive/old_backend_vulkan.c @@ -0,0 +1,1990 @@ +#include "camera.h" +#include "primitives.h" +#define CDEBUG +#define CEL_REND_BACKEND_VULKAN 1 +#if CEL_REND_BACKEND_VULKAN +// ^ Temporary + +#include +#include +#include +#include +#include +#include +#include +#include "colours.h" +#include "str.h" + +#include "darray.h" +#include "defines.h" +#include "file.h" +#include "log.h" +#include "maths.h" +#include "maths_types.h" +#include "render_backend.h" +#include "render_types.h" +// #include "vulkan_helpers.h" + +#include + +#define SCR_WIDTH 1000 +#define SCR_HEIGHT 1000 + +#include + +#include + +KITC_DECL_TYPED_ARRAY(VkLayerProperties) + +typedef struct vulkan_device { + VkPhysicalDevice physical_device; + VkDevice logical_device; + vulkan_swapchain_support_info swapchain_support; + i32 graphics_queue_index; + i32 present_queue_index; + i32 compute_queue_index; + i32 transfer_queue_index; + VkQueue graphics_queue; + VkQueue present_queue; + VkQueue compute_queue; + VkQueue transfer_queue; + VkCommandPool gfx_command_pool; + VkPhysicalDeviceProperties properties; + VkPhysicalDeviceFeatures features; + VkPhysicalDeviceMemoryProperties memory; + VkFormat depth_format; +} vulkan_device; + +typedef struct vulkan_image { + VkImage handle; + VkDeviceMemory memory; + VkImageView view; + u32 width; + u32 height; +} vulkan_image; + +typedef struct vulkan_texture_data { + vulkan_image image; + VkSampler sampler; +} vulkan_texture_data; + +typedef enum vulkan_renderpass_state { + READY, + RECORDING, + IN_RENDER_PASS, + RECORDING_ENDING, + SUBMITTED, + NOT_ALLOCATED +} vulkan_renderpass_state; + +typedef struct vulkan_renderpass { + VkRenderPass handle; + vec4 render_area; + vec4 clear_colour; + f32 depth; + u32 stencil; + vulkan_renderpass_state state; +} vulkan_renderpass; + +typedef struct vulkan_framebuffer { + VkFramebuffer handle; + u32 attachment_count; + VkImageView* attachments; + vulkan_renderpass* renderpass; +} vulkan_framebuffer; + +KITC_DECL_TYPED_ARRAY(vulkan_framebuffer) + +typedef struct vulkan_swapchain { + VkSurfaceFormatKHR image_format; + u8 max_frames_in_flight; + VkSwapchainKHR handle; + u32 image_count; + VkImage* images; + VkImageView* views; + vulkan_image depth_attachment; + vulkan_framebuffer_darray* framebuffers; +} vulkan_swapchain; + +// overengineered +typedef enum vulkan_command_buffer_state { + COMMAND_BUFFER_STATE_READY, + COMMAND_BUFFER_STATE_IN_RENDER_PASS, + COMMAND_BUFFER_STATE_RECORDING, + COMMAND_BUFFER_STATE_RECORDING_ENDED, + COMMAND_BUFFER_STATE_SUBMITTED, + COMMAND_BUFFER_STATE_NOT_ALLOCATED, +} vulkan_command_buffer_state; + +typedef struct vulkan_command_buffer { + VkCommandBuffer handle; + vulkan_command_buffer_state state; +} vulkan_command_buffer; + +KITC_DECL_TYPED_ARRAY(vulkan_command_buffer) + +typedef struct vulkan_fence { + VkFence handle; + bool is_signaled; +} vulkan_fence; + +typedef struct vulkan_shader_stage { + VkShaderModuleCreateInfo create_info; + VkShaderModule handle; + VkPipelineShaderStageCreateInfo stage_create_info; +} vulkan_shader_stage; + +typedef struct vulkan_pipeline { + VkPipeline handle; + VkPipelineLayout layout; +} vulkan_pipeline; + +typedef struct global_object_uniform { + mat4 projection; // 64 bytes + mat4 view; // 64 bytes + f32 padding[32]; +} global_object_uniform; + +typedef struct object_uniform { + vec4 diffuse_colour; + vec4 v_reserved0; + vec4 v_reserved1; + vec4 v_reserved2; +} object_uniform; + +#define MAX_OBJECT_COUNT 1024 +#define VULKAN_OBJECT_SHADER_DESCRIPTOR_COUNT 1 + +typedef struct geometry_render_data { + u32 id; + mat4 model; + texture* textures[16]; +} geometry_render_data; + +typedef struct vulkan_buffer { + u64 total_size; + VkBuffer handle; + VkBufferUsageFlagBits usage; + bool is_locked; + VkDeviceMemory memory; + i32 memory_index; + u32 memory_property_flags; +} vulkan_buffer; + +#define SHADER_STAGE_COUNT 2 + +typedef struct vulkan_shader { + // vertex, fragment + vulkan_shader_stage stages[SHADER_STAGE_COUNT]; + vulkan_pipeline pipeline; + + // descriptors + VkDescriptorPool descriptor_pool; + VkDescriptorSetLayout descriptor_set_layout; + VkDescriptorSet descriptor_sets[3]; // one for each in-flight frame + + vulkan_buffer global_uniforms_buffer; + + // Data that's global for all objects drawn + global_object_uniform global_ubo; + object_uniform object_ubo; + vulkan_texture_data* texture_data; +} vulkan_shader; + +typedef struct vulkan_context { + VkInstance instance; + VkAllocationCallbacks* allocator; + VkSurfaceKHR surface; + vulkan_device device; + u32 framebuffer_width; + u32 framebuffer_height; + vulkan_swapchain swapchain; + vulkan_renderpass main_renderpass; + vulkan_buffer object_vertex_buffer; + vulkan_buffer object_index_buffer; + u64 geometry_vertex_offset; + u64 geometry_index_offset; + + vulkan_command_buffer_darray* gfx_command_buffers; + + VkSemaphore* image_available_semaphores; + VkSemaphore* queue_complete_semaphores; + u32 in_flight_fence_count; + vulkan_fence* in_flight_fences; + vulkan_fence** images_in_flight; + + u32 image_index; + u32 current_frame; + + vulkan_shader object_shader; + + // TODO: swapchain recreation + +#if defined(DEBUG) + VkDebugUtilsMessengerEXT vk_debugger; +#endif +} vulkan_context; + +static vulkan_context context; + +static i32 find_memory_index(vulkan_context* context, u32 type_filter, u32 property_flags) { + VkPhysicalDeviceMemoryProperties memory_properties; + vkGetPhysicalDeviceMemoryProperties(context->device.physical_device, &memory_properties); + + for (u32 i = 0; i < memory_properties.memoryTypeCount; ++i) { + // Check each memory type to see if its bit is set to 1. + if (type_filter & (1 << i) && + (memory_properties.memoryTypes[i].propertyFlags & property_flags) == property_flags) { + return i; + } + } + + WARN("Unable to find suitable memory type!"); + return -1; +} + +/** @brief Internal backend state */ +typedef struct vulkan_state { +} vulkan_state; + +typedef struct vertex_pos { + vec3 pos; + vec3 normal; +} vertex_pos; + +// pipeline stuff +bool vulkan_graphics_pipeline_create(vulkan_context* context, vulkan_renderpass* renderpass, + u32 attribute_count, + VkVertexInputAttributeDescription* attributes, + u32 descriptor_set_layout_count, + VkDescriptorSetLayout* descriptor_set_layouts, u32 stage_count, + VkPipelineShaderStageCreateInfo* stages, VkViewport viewport, + VkRect2D scissor, bool is_wireframe, + vulkan_pipeline* out_pipeline) { + VkPipelineViewportStateCreateInfo viewport_state = { + VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO + }; + viewport_state.viewportCount = 1; + viewport_state.pViewports = &viewport; + viewport_state.scissorCount = 1; + viewport_state.pScissors = &scissor; + + // Rasterizer + VkPipelineRasterizationStateCreateInfo rasterizer_create_info = { + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO + }; + rasterizer_create_info.depthClampEnable = VK_FALSE; + rasterizer_create_info.rasterizerDiscardEnable = VK_FALSE; + rasterizer_create_info.polygonMode = is_wireframe ? VK_POLYGON_MODE_LINE : VK_POLYGON_MODE_FILL; + rasterizer_create_info.lineWidth = 1.0f; + rasterizer_create_info.cullMode = VK_CULL_MODE_BACK_BIT; + rasterizer_create_info.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterizer_create_info.depthBiasEnable = VK_FALSE; + rasterizer_create_info.depthBiasConstantFactor = 0.0; + rasterizer_create_info.depthBiasClamp = 0.0; + rasterizer_create_info.depthBiasSlopeFactor = 0.0; + + // Multisampling + VkPipelineMultisampleStateCreateInfo ms_create_info = { + VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO + }; + ms_create_info.sampleShadingEnable = VK_FALSE; + ms_create_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + ms_create_info.minSampleShading = 1.0; + ms_create_info.pSampleMask = 0; + ms_create_info.alphaToCoverageEnable = VK_FALSE; + ms_create_info.alphaToOneEnable = VK_FALSE; + + // Depth and stencil testing + VkPipelineDepthStencilStateCreateInfo depth_stencil = { + VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO + }; + depth_stencil.depthTestEnable = VK_TRUE; + depth_stencil.depthWriteEnable = VK_TRUE; + depth_stencil.depthCompareOp = VK_COMPARE_OP_LESS; + depth_stencil.depthBoundsTestEnable = VK_FALSE; + depth_stencil.stencilTestEnable = VK_FALSE; + depth_stencil.pNext = 0; + + VkPipelineColorBlendAttachmentState color_blend_attachment_state; + color_blend_attachment_state.blendEnable = VK_TRUE; + color_blend_attachment_state.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + color_blend_attachment_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + color_blend_attachment_state.colorBlendOp = VK_BLEND_OP_ADD; + color_blend_attachment_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + color_blend_attachment_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + color_blend_attachment_state.alphaBlendOp = VK_BLEND_OP_ADD; + color_blend_attachment_state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | + VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + + VkPipelineColorBlendStateCreateInfo color_blend = { + VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO + }; + color_blend.logicOpEnable = VK_FALSE; + color_blend.logicOp = VK_LOGIC_OP_COPY; + color_blend.attachmentCount = 1; + color_blend.pAttachments = &color_blend_attachment_state; + + const u32 dynamic_state_count = 3; + VkDynamicState dynamic_states[3] = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR, + VK_DYNAMIC_STATE_LINE_WIDTH, + }; + + VkPipelineDynamicStateCreateInfo dynamic_state = { + VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO + }; + dynamic_state.dynamicStateCount = dynamic_state_count; + dynamic_state.pDynamicStates = dynamic_states; + + // Vertex input + VkVertexInputBindingDescription binding_desc; + binding_desc.binding = 0; + binding_desc.stride = sizeof(vertex); + binding_desc.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + VkPipelineVertexInputStateCreateInfo vertex_input_info = { + VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO + }; + vertex_input_info.vertexBindingDescriptionCount = 1; + vertex_input_info.pVertexBindingDescriptions = &binding_desc; + vertex_input_info.vertexAttributeDescriptionCount = attribute_count; + vertex_input_info.pVertexAttributeDescriptions = attributes; + + VkPipelineInputAssemblyStateCreateInfo input_assembly = { + VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO + }; + input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + input_assembly.primitiveRestartEnable = VK_FALSE; + + VkPipelineLayoutCreateInfo pipeline_layout_create_info = { + VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO + }; + + // Pushconstants + VkPushConstantRange push_constant; + push_constant.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + push_constant.offset = sizeof(mat4) * 0; + push_constant.size = sizeof(mat4) * 2; + + pipeline_layout_create_info.pushConstantRangeCount = 1; + pipeline_layout_create_info.pPushConstantRanges = &push_constant; + + pipeline_layout_create_info.setLayoutCount = descriptor_set_layout_count; + pipeline_layout_create_info.pSetLayouts = descriptor_set_layouts; + + vkCreatePipelineLayout(context->device.logical_device, &pipeline_layout_create_info, + context->allocator, &out_pipeline->layout); + + VkGraphicsPipelineCreateInfo pipeline_create_info = { + VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO + }; + pipeline_create_info.stageCount = stage_count; + pipeline_create_info.pStages = stages; + pipeline_create_info.pVertexInputState = &vertex_input_info; + pipeline_create_info.pInputAssemblyState = &input_assembly; + + pipeline_create_info.pViewportState = &viewport_state; + pipeline_create_info.pRasterizationState = &rasterizer_create_info; + pipeline_create_info.pMultisampleState = &ms_create_info; + pipeline_create_info.pDepthStencilState = &depth_stencil; + pipeline_create_info.pColorBlendState = &color_blend; + pipeline_create_info.pDynamicState = &dynamic_state; + pipeline_create_info.pTessellationState = 0; + + pipeline_create_info.layout = out_pipeline->layout; + + pipeline_create_info.renderPass = renderpass->handle; + pipeline_create_info.subpass = 0; + pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE; + pipeline_create_info.basePipelineIndex = -1; + + VkResult result = + vkCreateGraphicsPipelines(context->device.logical_device, VK_NULL_HANDLE, 1, + &pipeline_create_info, context->allocator, &out_pipeline->handle); + if (result != VK_SUCCESS) { + FATAL("graphics pipeline creation failed. its fked mate"); + ERROR_EXIT("Doomed"); + } + + return true; +} + +void vulkan_pipeline_bind(vulkan_command_buffer* command_buffer, VkPipelineBindPoint bind_point, + vulkan_pipeline* pipeline) { + vkCmdBindPipeline(command_buffer->handle, bind_point, pipeline->handle); +} + +void vulkan_buffer_bind(vulkan_context* context, vulkan_buffer* buffer, u64 offset) { + vkBindBufferMemory(context->device.logical_device, buffer->handle, buffer->memory, offset); +} + +bool vulkan_buffer_create(vulkan_context* context, u64 size, VkBufferUsageFlagBits usage, + u32 memory_property_flags, bool bind_on_create, + vulkan_buffer* out_buffer) { + memset(out_buffer, 0, sizeof(vulkan_buffer)); + out_buffer->total_size = size; + out_buffer->usage = usage; + out_buffer->memory_property_flags = memory_property_flags; + + VkBufferCreateInfo buffer_info = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; + buffer_info.size = size; + buffer_info.usage = usage; + buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + vkCreateBuffer(context->device.logical_device, &buffer_info, context->allocator, + &out_buffer->handle); + + VkMemoryRequirements requirements; + vkGetBufferMemoryRequirements(context->device.logical_device, out_buffer->handle, &requirements); + out_buffer->memory_index = + find_memory_index(context, requirements.memoryTypeBits, out_buffer->memory_property_flags); + + // Allocate + VkMemoryAllocateInfo allocate_info = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + allocate_info.allocationSize = requirements.size; + allocate_info.memoryTypeIndex = (u32)out_buffer->memory_index; + + vkAllocateMemory(context->device.logical_device, &allocate_info, context->allocator, + &out_buffer->memory); + + if (bind_on_create) { + vulkan_buffer_bind(context, out_buffer, 0); + } + + DEBUG("Created buffer."); + + return true; +} + +// lock and unlock? + +void* vulkan_buffer_lock_memory(vulkan_context* context, vulkan_buffer* buffer, u64 offset, + u64 size, u32 flags) { + void* data; + vkMapMemory(context->device.logical_device, buffer->memory, offset, size, flags, &data); + return data; +} +void* vulkan_buffer_unlock_memory(vulkan_context* context, vulkan_buffer* buffer) { + vkUnmapMemory(context->device.logical_device, buffer->memory); +} + +void vulkan_buffer_load_data(vulkan_context* context, vulkan_buffer* buffer, u64 offset, u64 size, + u32 flags, const void* data) { + void* data_ptr = 0; + VK_CHECK( + vkMapMemory(context->device.logical_device, buffer->memory, offset, size, flags, &data_ptr)); + memcpy(data_ptr, data, size); + vkUnmapMemory(context->device.logical_device, buffer->memory); +} + +// TODO: destroy + +bool create_shader_module(vulkan_context* context, const char* filename, const char* type_str, + VkShaderStageFlagBits flag, u32 stage_index, + vulkan_shader_stage* shader_stages) { + memset(&shader_stages[stage_index].create_info, 0, sizeof(VkShaderModuleCreateInfo)); + memset(&shader_stages[stage_index].stage_create_info, 0, sizeof(VkPipelineShaderStageCreateInfo)); + + shader_stages[stage_index].create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + + // todo: file input + FileData file_contents = load_spv_file(filename); + + shader_stages[stage_index].create_info.codeSize = file_contents.size; + shader_stages[stage_index].create_info.pCode = (u32*)file_contents.data; + + vkCreateShaderModule(context->device.logical_device, &shader_stages[stage_index].create_info, + context->allocator, &shader_stages[stage_index].handle); + + shader_stages[stage_index].stage_create_info.sType = + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shader_stages[stage_index].stage_create_info.stage = flag; + shader_stages[stage_index].stage_create_info.module = shader_stages[stage_index].handle; + shader_stages[stage_index].stage_create_info.pName = "main"; + + free(file_contents.data); + + // TODO: Descriptors + + return true; +} + +bool vulkan_object_shader_create(vulkan_context* context, vulkan_shader* out_shader) { + char stage_type_strs[SHADER_STAGE_COUNT][5] = { "vert", "frag" }; + char stage_filenames[SHADER_STAGE_COUNT][256] = { "build/linux/x86_64/debug/object.vert.spv", + "build/linux/x86_64/debug/object.frag.spv" }; + VkShaderStageFlagBits stage_types[SHADER_STAGE_COUNT] = { VK_SHADER_STAGE_VERTEX_BIT, + VK_SHADER_STAGE_FRAGMENT_BIT }; + for (u8 i = 0; i < SHADER_STAGE_COUNT; i++) { + DEBUG("Loading %s", stage_filenames[i]); + create_shader_module(context, stage_filenames[i], stage_type_strs[i], stage_types[i], i, + out_shader->stages); + } + + // descriptors + VkDescriptorSetLayoutBinding global_ubo_layout_binding; + global_ubo_layout_binding.binding = 0; + global_ubo_layout_binding.descriptorCount = 1; + global_ubo_layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + global_ubo_layout_binding.pImmutableSamplers = 0; + global_ubo_layout_binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + + VkDescriptorSetLayoutBinding sampler_layout_binding; + sampler_layout_binding.binding = 1; + sampler_layout_binding.descriptorCount = 1; + sampler_layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + sampler_layout_binding.pImmutableSamplers = 0; + sampler_layout_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + + VkDescriptorSetLayoutCreateInfo global_layout_info = { + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO + }; + + VkDescriptorSetLayoutBinding bindings[2] = { global_ubo_layout_binding, sampler_layout_binding }; + + global_layout_info.bindingCount = 2; + global_layout_info.pBindings = bindings; + + VK_CHECK(vkCreateDescriptorSetLayout(context->device.logical_device, &global_layout_info, + context->allocator, &out_shader->descriptor_set_layout)); + + VkDescriptorPoolSize global_pool_size; + global_pool_size.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + global_pool_size.descriptorCount = 3; + + VkDescriptorPoolSize sampler_pool_size; + sampler_pool_size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + sampler_pool_size.descriptorCount = 3; + + VkDescriptorPoolSize pool_sizes[2] = { global_pool_size, sampler_pool_size }; + + VkDescriptorPoolCreateInfo pool_info = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; + pool_info.poolSizeCount = 2; + pool_info.pPoolSizes = pool_sizes; + pool_info.maxSets = 3; + + VK_CHECK(vkCreateDescriptorPool(context->device.logical_device, &pool_info, context->allocator, + &out_shader->descriptor_pool)); + + // Pipeline creation + VkViewport viewport; + viewport.x = 0; + viewport.y = 0; + viewport.width = (f32)context->framebuffer_width; + viewport.height = (f32)context->framebuffer_height; + viewport.minDepth = 0.0; + viewport.maxDepth = 1.0; + + VkRect2D scissor; + scissor.offset.x = scissor.offset.y = 0; + scissor.extent.width = context->framebuffer_width; + scissor.extent.height = context->framebuffer_height; + + // Attributes + u32 offset = 0; + const i32 attribute_count = 3; + VkVertexInputAttributeDescription attribute_descs[3]; + // Position + VkFormat formats[3] = { VK_FORMAT_R32G32B32_SFLOAT, VK_FORMAT_R32G32B32_SFLOAT, + VK_FORMAT_R32G32_SFLOAT }; + + u64 sizes[3] = { sizeof(vec3), sizeof(vec3), sizeof(vec2) }; + + for (u32 i = 0; i < attribute_count; i++) { + attribute_descs[i].binding = 0; + attribute_descs[i].location = i; + attribute_descs[i].format = formats[i]; + attribute_descs[i].offset = offset; + offset += sizes[i]; + } + + // Descriptor set layouts + VkDescriptorSetLayout layouts[1] = { out_shader->descriptor_set_layout }; + + // Stages + VkPipelineShaderStageCreateInfo stage_create_infos[SHADER_STAGE_COUNT]; + memset(stage_create_infos, 0, sizeof(stage_create_infos)); + for (u32 i = 0; i < SHADER_STAGE_COUNT; i++) { + stage_create_infos[i].sType = out_shader->stages[i].stage_create_info.sType; + stage_create_infos[i] = out_shader->stages[i].stage_create_info; + } + + vulkan_graphics_pipeline_create( + context, &context->main_renderpass, attribute_count, attribute_descs, 1, layouts, + SHADER_STAGE_COUNT, stage_create_infos, viewport, scissor, false, &out_shader->pipeline); + INFO("Graphics pipeline created!"); + + // Uniform buffer + if (!vulkan_buffer_create(context, sizeof(global_object_uniform), + VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + true, &out_shader->global_uniforms_buffer)) { + ERROR("Couldnt create uniforms buffer"); + return false; + } + + VkDescriptorSetLayout global_layouts[3] = { + out_shader->descriptor_set_layout, + out_shader->descriptor_set_layout, + out_shader->descriptor_set_layout, + }; + + VkDescriptorSetAllocateInfo alloc_info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; + alloc_info.descriptorPool = out_shader->descriptor_pool; + alloc_info.descriptorSetCount = 3; + alloc_info.pSetLayouts = global_layouts; + VK_CHECK(vkAllocateDescriptorSets(context->device.logical_device, &alloc_info, + out_shader->descriptor_sets)); + + return true; +} +void vulkan_object_shader_destroy(vulkan_context* context, vulkan_shader* shader) {} +void vulkan_object_shader_use(vulkan_context* context, vulkan_shader* shader) { + u32 image_index = context->image_index; + vulkan_pipeline_bind(&context->gfx_command_buffers->data[image_index], + VK_PIPELINE_BIND_POINT_GRAPHICS, &shader->pipeline); +} +void vulkan_object_shader_update_global_state(vulkan_context* context, vulkan_shader* shader) { + u32 image_index = context->image_index; + VkCommandBuffer cmd_buffer = context->gfx_command_buffers->data[image_index].handle; + VkDescriptorSet global_descriptors = shader->descriptor_sets[image_index]; + + u32 range = sizeof(global_object_uniform); + u64 offset = 0; + + // copy data to buffer + vulkan_buffer_load_data(context, &shader->global_uniforms_buffer, offset, range, 0, + &shader->global_ubo); + + VkDescriptorBufferInfo buffer_info; + buffer_info.buffer = shader->global_uniforms_buffer.handle; + buffer_info.offset = offset; + buffer_info.range = range; + + VkDescriptorImageInfo image_info; + image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + image_info.imageView = shader->texture_data->image.view; + image_info.sampler = shader->texture_data->sampler; + + VkWriteDescriptorSet uniform_write = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; + uniform_write.dstSet = shader->descriptor_sets[image_index]; + uniform_write.dstBinding = 0; + uniform_write.dstArrayElement = 0; + uniform_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + uniform_write.descriptorCount = 1; + uniform_write.pBufferInfo = &buffer_info; + + VkWriteDescriptorSet texture_write = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; + texture_write.dstSet = shader->descriptor_sets[image_index]; + texture_write.dstBinding = 1; + texture_write.dstArrayElement = 0; + texture_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + texture_write.descriptorCount = 1; + texture_write.pImageInfo = &image_info; + + VkWriteDescriptorSet writes[2] = { uniform_write, texture_write }; + + vkUpdateDescriptorSets(context->device.logical_device, 2, writes, 0, 0); + + vkCmdBindDescriptorSets(cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, shader->pipeline.layout, 0, + 1, &global_descriptors, 0, 0); +} + +void vulkan_object_shader_update_object(vulkan_context* context, vulkan_shader* shader, + mat4 model) { + u32 image_index = context->image_index; + VkCommandBuffer cmd_buffer = context->gfx_command_buffers->data[image_index].handle; + // vulkan_command_buffer* cmd_buffer = &context->gfx_command_buffers->data[context.image_index]; + + vkCmdPushConstants(cmd_buffer, shader->pipeline.layout, VK_SHADER_STAGE_VERTEX_BIT, 0, + sizeof(mat4), &model); + + // vulkan_object_shader_use(context, &context->object_shader); + VkDeviceSize offsets[1] = { 0 }; + vkCmdBindVertexBuffers(cmd_buffer, 0, 1, &context->object_vertex_buffer.handle, + (VkDeviceSize*)offsets); + + vkCmdBindIndexBuffer(cmd_buffer, context->object_index_buffer.handle, 0, VK_INDEX_TYPE_UINT32); + + vkCmdDrawIndexed(cmd_buffer, 36, 1, 0, 0, 0); + // vkCmdDraw(cmd_buffer, 36, 1, 0, 0); +} + +bool select_physical_device(vulkan_context* ctx) { + u32 physical_device_count = 0; + VK_CHECK(vkEnumeratePhysicalDevices(ctx->instance, &physical_device_count, 0)); + if (physical_device_count == 0) { + FATAL("No devices that support vulkan were found"); + return false; + } + TRACE("Number of devices found %d", physical_device_count); + + VkPhysicalDevice physical_devices[physical_device_count]; + VK_CHECK(vkEnumeratePhysicalDevices(ctx->instance, &physical_device_count, physical_devices)); + + for (u32 i = 0; i < physical_device_count; i++) { + VkPhysicalDeviceProperties properties; + vkGetPhysicalDeviceProperties(physical_devices[i], &properties); + + VkPhysicalDeviceFeatures features; + vkGetPhysicalDeviceFeatures(physical_devices[i], &features); + + VkPhysicalDeviceMemoryProperties memory; + vkGetPhysicalDeviceMemoryProperties(physical_devices[i], &memory); + + vulkan_physical_device_requirements requirements = {}; + requirements.graphics = true; + requirements.present = true; + requirements.compute = true; + requirements.transfer = true; + + requirements.sampler_anistropy = true; + requirements.discrete_gpu = true; + requirements.device_ext_names[0] = str8lit(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + requirements.device_ext_name_count = 1; + + vulkan_physical_device_queue_family_info queue_info = {}; + + bool result = physical_device_meets_requirements(physical_devices[i], ctx->surface, &properties, + &features, &requirements, &queue_info, + &ctx->device.swapchain_support); + + if (result) { + INFO("GPU Driver version: %d.%d.%d", VK_VERSION_MAJOR(properties.driverVersion), + VK_VERSION_MINOR(properties.driverVersion), VK_VERSION_PATCH(properties.driverVersion)); + + INFO("Vulkan API version: %d.%d.%d", VK_VERSION_MAJOR(properties.apiVersion), + VK_VERSION_MINOR(properties.apiVersion), VK_VERSION_PATCH(properties.apiVersion)); + + // TODO: print gpu memory information - + // https://youtu.be/6Kj3O2Ov1RU?si=pXfP5NvXXcXjJsrG&t=2439 + + ctx->device.physical_device = physical_devices[i]; + ctx->device.graphics_queue_index = queue_info.graphics_family_index; + ctx->device.present_queue_index = queue_info.present_family_index; + ctx->device.compute_queue_index = queue_info.compute_family_index; + ctx->device.transfer_queue_index = queue_info.transfer_family_index; + ctx->device.properties = properties; + ctx->device.features = features; + ctx->device.memory = memory; + break; + } + } + + if (!ctx->device.physical_device) { + ERROR("No suitable physical devices were found :("); + return false; + } + + INFO("Physical device selected: %s\n", ctx->device.properties.deviceName); + return true; +} + +bool vulkan_device_create(vulkan_context* context) { + // Physical device - NOTE: mutates the context directly + if (!select_physical_device(context)) { + return false; + } + +// Logical device - NOTE: mutates the context directly + +// queues +#define VULKAN_QUEUES_COUNT 2 + const char* queue_names[VULKAN_QUEUES_COUNT] = { + "GRAPHICS", + "TRANSFER", + }; + i32 indices[VULKAN_QUEUES_COUNT] = { + context->device.graphics_queue_index, + context->device.transfer_queue_index, + }; + f32 prio_one = 1.0; + VkDeviceQueueCreateInfo queue_create_info[2]; // HACK: make 2 queues, graphics and transfer + // graphics + queue_create_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_create_info[0].queueFamilyIndex = 0; + queue_create_info[0].queueCount = 1; + queue_create_info[0].flags = 0; + queue_create_info[0].pNext = 0; + queue_create_info[0].pQueuePriorities = &prio_one; + // transfer + queue_create_info[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_create_info[1].queueFamilyIndex = 1; + queue_create_info[1].queueCount = 1; + queue_create_info[1].flags = 0; + queue_create_info[1].pNext = 0; + queue_create_info[1].pQueuePriorities = &prio_one; + + // for (int i = 0; i < 2; i++) { + // TRACE("Configure %s queue", queue_names[i]); + // queue_create_info[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + // queue_create_info[i].queueFamilyIndex = indices[i]; + // queue_create_info[i].queueCount = 1; // make just one of them + // queue_create_info[i].flags = 0; + // queue_create_info[i].pNext = 0; + // f32 priority = 1.0; + // queue_create_info[i].pQueuePriorities = &priority; + // } + + // features + VkPhysicalDeviceFeatures device_features = {}; + device_features.samplerAnisotropy = VK_TRUE; // request anistrophy + + // device itself + VkDeviceCreateInfo device_create_info = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO }; + device_create_info.queueCreateInfoCount = VULKAN_QUEUES_COUNT; + device_create_info.pQueueCreateInfos = queue_create_info; + device_create_info.pEnabledFeatures = &device_features; + device_create_info.enabledExtensionCount = 1; + const char* extension_names = VK_KHR_SWAPCHAIN_EXTENSION_NAME; + device_create_info.ppEnabledExtensionNames = &extension_names; + + // deprecated + device_create_info.enabledLayerCount = 0; + device_create_info.ppEnabledLayerNames = 0; + + VkResult result = vkCreateDevice(context->device.physical_device, &device_create_info, + context->allocator, &context->device.logical_device); + if (result != VK_SUCCESS) { + printf("error creating logical device with status %u\n", result); + ERROR_EXIT("Bye bye"); + } + INFO("Logical device created"); + + // get queues + vkGetDeviceQueue(context->device.logical_device, context->device.graphics_queue_index, 0, + &context->device.graphics_queue); + // vkGetDeviceQueue(context->device.logical_device, context->device.present_queue_index, 0, + // &context->device.present_queue); + // vkGetDeviceQueue(context->device.logical_device, context->device.compute_queue_index, 0, + // &context->device.compute_queue); + vkGetDeviceQueue(context->device.logical_device, context->device.transfer_queue_index, 0, + &context->device.transfer_queue); + + // create command pool for graphics queue + VkCommandPoolCreateInfo pool_create_info = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO }; + pool_create_info.queueFamilyIndex = context->device.graphics_queue_index; + pool_create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + vkCreateCommandPool(context->device.logical_device, &pool_create_info, context->allocator, + &context->device.gfx_command_pool); + INFO("Created Command Pool") + + return true; +} +void vulkan_device_destroy(vulkan_context* context) { + context->device.physical_device = 0; // release + // TODO: reset other memory +} + +bool vulkan_device_detect_depth_format(vulkan_device* device) { + const size_t n_candidates = 3; + VkFormat candidates[3] = { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, + VK_FORMAT_D24_UNORM_S8_UINT }; + u32 flags = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT; + for (u64 i = 0; i < n_candidates; i++) { + VkFormatProperties properties; + vkGetPhysicalDeviceFormatProperties(device->physical_device, candidates[i], &properties); + + if ((properties.linearTilingFeatures & flags) == flags) { + device->depth_format = candidates[i]; + return true; + } + if ((properties.optimalTilingFeatures & flags) == flags) { + device->depth_format = candidates[i]; + return true; + } + } + return false; +} + +void vulkan_image_view_create(vulkan_context* context, VkFormat format, vulkan_image* image, + VkImageAspectFlags aspect_flags) { + VkImageViewCreateInfo view_create_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; + view_create_info.image = image->handle; + view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + view_create_info.format = format; + view_create_info.subresourceRange.aspectMask = aspect_flags; + + view_create_info.subresourceRange.baseMipLevel = 0; + view_create_info.subresourceRange.levelCount = 1; + view_create_info.subresourceRange.baseArrayLayer = 0; + view_create_info.subresourceRange.layerCount = 1; + + vkCreateImageView(context->device.logical_device, &view_create_info, context->allocator, + &image->view); +} + +void vulkan_image_transition_layout(vulkan_context* context, vulkan_command_buffer* command_buffer, + vulkan_image* image, VkFormat format, VkImageLayout old_layout, + VkImageLayout new_layout) { + VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; + void vulkan_image_transition_layout( + vulkan_context * context, vulkan_command_buffer * command_buffer, vulkan_image * image, + VkFormat format, VkImageLayout old_layout, VkImageLayout new_layout) { + VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; + barrier.oldLayout = old_layout; + barrier.newLayout = new_layout; + barrier.srcQueueFamilyIndex = context->device.graphics_queue_index; + barrier.dstQueueFamilyIndex = context->device.graphics_queue_index; + barrier.image = image->handle; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + barrier.srcAccessMask = 0; + barrier.dstAccessMask = 0; + + VkPipelineStageFlags source_stage; + VkPipelineStageFlags dest_stage; + + if (old_layout == VK_IMAGE_LAYOUT_UNDEFINED && + new_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + source_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + dest_stage = VK_PIPELINE_STAGE_TRANSFER_BIT; + + } else if (old_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && + new_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + source_stage = VK_PIPELINE_STAGE_TRANSFER_BIT; + dest_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + } else { + FATAL("Unsupported image layout transition"); + return; + } + + vkCmdPipelineBarrier(command_buffer->handle, source_stage, dest_stage, 0, 0, 0, 0, 0, 1, + &barrier); + } + + void vulkan_image_copy_from_buffer(vulkan_image * image, VkBuffer buffer, + vulkan_command_buffer * command_buffer) { + VkBufferImageCopy region; + region.bufferOffset = 0; + region.bufferRowLength = 0; + region.bufferImageHeight = 0; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.mipLevel = 0; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = 1; + printf("Image details width: %d height %d\n", image->width, image->height); + region.imageOffset.x = 0; + region.imageOffset.y = 0; + region.imageOffset.z = 0; + region.imageExtent.width = image->width; + region.imageExtent.height = image->height; + region.imageExtent.depth = 1; + + vkCmdCopyBufferToImage(command_buffer->handle, buffer, image->handle, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + } + + void vulkan_image_create(vulkan_context * context, VkImageType image_type, u32 width, u32 height, + VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, + VkMemoryPropertyFlags memory_flags, bool create_view, + VkImageAspectFlags aspect_flags, vulkan_image* out_image) { + // copy params + out_image->width = width; + out_image->height = height; + + // create info + VkImageCreateInfo image_create_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; + image_create_info.imageType = image_type; + image_create_info.extent.width = width; + image_create_info.extent.height = height; + image_create_info.extent.depth = 1; + image_create_info.mipLevels = 1; + image_create_info.arrayLayers = 1; + image_create_info.format = format; + image_create_info.tiling = tiling; + image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + image_create_info.usage = usage; + image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; + image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + VK_CHECK(vkCreateImage(context->device.logical_device, &image_create_info, context->allocator, + &out_image->handle)); + + VkMemoryRequirements memory_reqs; + vkGetImageMemoryRequirements(context->device.logical_device, out_image->handle, &memory_reqs); + + i32 memory_type = -1; + VkPhysicalDeviceMemoryProperties memory_properties; + vkGetPhysicalDeviceMemoryProperties(context->device.physical_device, &memory_properties); + + for (u32 i = 0; i < memory_properties.memoryTypeCount; i++) { + // typefilter = memoryTypeBits , prop filter = memory_flags + if (memory_reqs.memoryTypeBits & (1 << i) && + (memory_properties.memoryTypes[i].propertyFlags & memory_flags)) { + memory_type = i; + break; + } + } + + if (memory_type < 0) { + ERROR_EXIT("couldnt find a suitable memory type for the image"); + } + + // allocate memory + VkMemoryAllocateInfo memory_allocate_info = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + memory_allocate_info.allocationSize = memory_reqs.size; + memory_allocate_info.memoryTypeIndex = memory_type; + vkAllocateMemory(context->device.logical_device, &memory_allocate_info, context->allocator, + &out_image->memory); + + // bind memory + // TODO: maybe bind context->device.logical_device to device at the top of the functions? + vkBindImageMemory(context->device.logical_device, out_image->handle, out_image->memory, 0); + + if (create_view) { + out_image->view = 0; + vulkan_image_view_create(context, format, out_image, aspect_flags); + } + } + + // TODO: vulkan_image_destroy + + void vulkan_framebuffer_create(vulkan_context * context, vulkan_renderpass * renderpass, + u32 width, u32 height, u32 attachment_count, + VkImageView * attachments, vulkan_framebuffer * out_framebuffer) { + out_framebuffer->attachments = malloc(sizeof(VkImageView) * attachment_count); + for (u32 i = 0; i < attachment_count; i++) { + out_framebuffer->attachments[i] = attachments[i]; + } + out_framebuffer->attachment_count = attachment_count; + out_framebuffer->renderpass = renderpass; + + VkFramebufferCreateInfo framebuffer_create_info = { + VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO + }; // TODO + + framebuffer_create_info.renderPass = renderpass->handle; + framebuffer_create_info.attachmentCount = attachment_count; + framebuffer_create_info.pAttachments = out_framebuffer->attachments; + framebuffer_create_info.width = width; + framebuffer_create_info.height = height; + framebuffer_create_info.layers = 1; + + vkCreateFramebuffer(context->device.logical_device, &framebuffer_create_info, + context->allocator, &out_framebuffer->handle); + } + + // TODO: vulkan_framebuffer_destroy + + void vulkan_command_buffer_allocate(vulkan_context * context, VkCommandPool pool, bool is_primary, + vulkan_command_buffer* out_command_buffer) { + VkCommandBufferAllocateInfo allocate_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; + allocate_info.commandPool = pool; + allocate_info.level = + is_primary ? VK_COMMAND_BUFFER_LEVEL_PRIMARY : VK_COMMAND_BUFFER_LEVEL_SECONDARY; + allocate_info.commandBufferCount = 1; + allocate_info.pNext = 0; + + out_command_buffer->state = COMMAND_BUFFER_STATE_NOT_ALLOCATED; + vkAllocateCommandBuffers(context->device.logical_device, &allocate_info, + &out_command_buffer->handle); + out_command_buffer->state = COMMAND_BUFFER_STATE_READY; + } + + void vulkan_command_buffer_free(vulkan_context * context, VkCommandPool pool, + vulkan_command_buffer * out_command_buffer) { + // TODO: implement freeing + } + + void vulkan_command_buffer_begin(vulkan_command_buffer * command_buffer, bool is_single_use, + bool is_renderpass_continue, bool is_simultaneous_use) { + VkCommandBufferBeginInfo begin_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; + begin_info.flags = 0; + if (is_single_use) { + begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + } + // TODO: RENDER_PASS_CONTINUE_BIT & SIMULTANEOUS_USE_BIT + + begin_info.pNext = 0; + begin_info.pInheritanceInfo = 0; + vkBeginCommandBuffer(command_buffer->handle, &begin_info); + + command_buffer->state = COMMAND_BUFFER_STATE_RECORDING; + } + + void vulkan_command_buffer_end(vulkan_command_buffer * command_buffer) { + VK_CHECK(vkEndCommandBuffer(command_buffer->handle)); + command_buffer->state = COMMAND_BUFFER_STATE_RECORDING_ENDED; + } + void vulkan_command_buffer_update_submitted(vulkan_command_buffer * command_buffer) { + command_buffer->state = COMMAND_BUFFER_STATE_SUBMITTED; + } + void vulkan_command_buffer_reset(vulkan_command_buffer * command_buffer) { + command_buffer->state = COMMAND_BUFFER_STATE_READY; + } + + void vulkan_command_buffer_allocate_and_begin_oneshot( + vulkan_context * context, VkCommandPool pool, vulkan_command_buffer * out_command_buffer) { + vulkan_command_buffer_allocate(context, pool, true, out_command_buffer); + vulkan_command_buffer_begin(out_command_buffer, true, false, false); + } + + void vulkan_command_buffer_end_oneshot(vulkan_context * context, VkCommandPool pool, + vulkan_command_buffer * command_buffer, VkQueue queue) { + vulkan_command_buffer_end(command_buffer); + + // submit to queue + VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &command_buffer->handle; + VK_CHECK(vkQueueSubmit(queue, 1, &submit_info, 0)); + // wait for it to finish + VK_CHECK(vkQueueWaitIdle(queue)); + + vulkan_command_buffer_free(context, pool, command_buffer); + } + + void vulkan_buffer_copy_to(vulkan_context * context, VkCommandPool pool, VkFence fence, + VkQueue queue, VkBuffer source, u64 source_offset, VkBuffer dest, + u64 dest_offset, u64 size) { + vkQueueWaitIdle(queue); + + vulkan_command_buffer temp_cmd_buf; + vulkan_command_buffer_allocate_and_begin_oneshot(context, pool, &temp_cmd_buf); + + VkBufferCopy copy_region; + copy_region.srcOffset = source_offset; + copy_region.dstOffset = dest_offset; + copy_region.size = size; + + vkCmdCopyBuffer(temp_cmd_buf.handle, source, dest, 1, ©_region); + + vulkan_command_buffer_end_oneshot(context, pool, &temp_cmd_buf, queue); + } + + void vulkan_swapchain_create(vulkan_context * context, u32 width, u32 height, + vulkan_swapchain * out_swapchain) { + VkExtent2D swapchain_extent = { width, height }; + out_swapchain->max_frames_in_flight = 2; // support triple buffering + + // find a format + bool found; + for (u32 i = 0; i < context->device.swapchain_support.format_count; i++) { + VkSurfaceFormatKHR format = context->device.swapchain_support.formats[i]; + if (format.format == VK_FORMAT_B8G8R8A8_UNORM && + format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { + out_swapchain->image_format = format; + found = true; + break; + } + } + if (!found) { + out_swapchain->image_format = context->device.swapchain_support.formats[0]; + } + + VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR; // guaranteed to be implemented + // TODO: look for mailbox - https://youtu.be/jWKVb_QdSNM?si=bHcd3sEf-M0x3QwH&t=1687 + + // TODO: requery swapchain support + + u32 image_count = context->device.swapchain_support.capabilities.minImageCount; + + VkSwapchainCreateInfoKHR swapchain_create_info = { + VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR + }; + swapchain_create_info.surface = context->surface; + swapchain_create_info.minImageCount = image_count; + swapchain_create_info.imageFormat = out_swapchain->image_format.format; + swapchain_create_info.imageColorSpace = out_swapchain->image_format.colorSpace; + DEBUG("Image extent %d %d\n", swapchain_extent.width, swapchain_extent.height); + swapchain_create_info.imageExtent = swapchain_extent; + swapchain_create_info.imageArrayLayers = 1; + swapchain_create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + swapchain_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swapchain_create_info.queueFamilyIndexCount = 0; + swapchain_create_info.pQueueFamilyIndices = 0; + + swapchain_create_info.preTransform = + context->device.swapchain_support.capabilities.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; + + TRACE("Create swapchain"); + VK_CHECK(vkCreateSwapchainKHR(context->device.logical_device, &swapchain_create_info, + context->allocator, &out_swapchain->handle)); + + context->current_frame = 0; + + // images + out_swapchain->image_count = 0; + vkGetSwapchainImagesKHR(context->device.logical_device, out_swapchain->handle, + &out_swapchain->image_count, 0); + + if (!out_swapchain->images) { + out_swapchain->images = (VkImage*)malloc(sizeof(VkImage) * out_swapchain->image_count); + } + if (!out_swapchain->views) { + out_swapchain->views = (VkImageView*)malloc(sizeof(VkImage) * out_swapchain->image_count); + } + VK_CHECK(vkGetSwapchainImagesKHR(context->device.logical_device, out_swapchain->handle, + &out_swapchain->image_count, out_swapchain->images)); + + // views + for (int i = 0; i < out_swapchain->image_count; i++) { + VkImageViewCreateInfo view_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; + view_info.image = out_swapchain->images[i]; + view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + view_info.format = out_swapchain->image_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; + + VK_CHECK(vkCreateImageView(context->device.logical_device, &view_info, context->allocator, + &out_swapchain->views[i])); + } + + // depth attachment + if (!vulkan_device_detect_depth_format(&context->device)) { + ERROR_EXIT("Failed to find a supported depth format"); + } + vulkan_image_create(context, VK_IMAGE_TYPE_2D, swapchain_extent.width, swapchain_extent.height, + context->device.depth_format, VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, true, VK_IMAGE_ASPECT_DEPTH_BIT, + &out_swapchain->depth_attachment); + INFO("Depth attachment created"); + + INFO("Swapchain created successfully"); + } + + // TODO: swapchain destroy + void vulkan_swapchain_recreate(vulkan_context * context, u32 width, u32 height, + vulkan_swapchain * swapchain) { + // TODO + } + bool vulkan_swapchain_acquire_next_image_index( + vulkan_context * context, vulkan_swapchain * swapchain, u64 timeout_ns, + VkSemaphore image_available_semaphore, VkFence fence, u32 * out_image_index) { + VkResult result = + vkAcquireNextImageKHR(context->device.logical_device, swapchain->handle, timeout_ns, + image_available_semaphore, fence, out_image_index); + + if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { + FATAL("Failed to acquire swapchain image"); + return false; + } + + return true; + } + + void vulkan_swapchain_present(vulkan_context * context, vulkan_swapchain * swapchain, + VkQueue graphics_queue, VkQueue present_queue, + VkSemaphore render_complete_semaphore, u32 present_image_index) { + // return image to swapchain for presentation + VkPresentInfoKHR present_info = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; + present_info.waitSemaphoreCount = 1; + present_info.pWaitSemaphores = &render_complete_semaphore; + present_info.swapchainCount = 1; + present_info.pSwapchains = &swapchain->handle; + present_info.pImageIndices = &present_image_index; + present_info.pResults = 0; + + VkResult result = vkQueuePresentKHR(present_queue, &present_info); + if (result != VK_SUCCESS) { + if (result == VK_SUBOPTIMAL_KHR) { + // WARN("Swapchain suboptimal - maybe resize needed?"); + } else { + FATAL("Failed to present swapchain iamge"); + } + } + + // advance the current frame + context->current_frame = (context->current_frame + 1) % swapchain->max_frames_in_flight; + } + + void vulkan_renderpass_create(vulkan_context * context, vulkan_renderpass * out_renderpass, + vec4 render_area, vec4 clear_colour, f32 depth, u32 stencil) { + out_renderpass->render_area = render_area; + out_renderpass->clear_colour = clear_colour; + out_renderpass->depth = depth; + out_renderpass->stencil = stencil; + + // main subpass + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + + // attachments + u32 attachment_desc_count = 2; + VkAttachmentDescription attachment_descriptions[2]; + + // Colour attachment + VkAttachmentDescription color_attachment; + color_attachment.format = context->swapchain.image_format.format; + color_attachment.samples = VK_SAMPLE_COUNT_1_BIT; + color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + color_attachment.flags = 0; + + attachment_descriptions[0] = color_attachment; + + VkAttachmentReference color_attachment_reference; + color_attachment_reference.attachment = 0; + color_attachment_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &color_attachment_reference; + + // Depth attachment + VkAttachmentDescription depth_attachment; + depth_attachment.format = context->device.depth_format; + depth_attachment.samples = VK_SAMPLE_COUNT_1_BIT; + depth_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + depth_attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + depth_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + depth_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + depth_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + depth_attachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + depth_attachment.flags = 0; + + attachment_descriptions[1] = depth_attachment; + + VkAttachmentReference depth_attachment_reference; + depth_attachment_reference.attachment = 1; + depth_attachment_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + subpass.pDepthStencilAttachment = &depth_attachment_reference; + + // TODO: other attachment styles + + subpass.inputAttachmentCount = 0; + subpass.pInputAttachments = 0; + subpass.pResolveAttachments = 0; + subpass.preserveAttachmentCount = 0; + subpass.preserveAttachmentCount = 0; + + // renderpass dependencies + VkSubpassDependency dependency; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = 0; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstAccessMask = + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependency.dependencyFlags = 0; + + VkRenderPassCreateInfo render_pass_create_info = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; + render_pass_create_info.attachmentCount = attachment_desc_count; + render_pass_create_info.pAttachments = attachment_descriptions; + render_pass_create_info.subpassCount = 1; + render_pass_create_info.pSubpasses = &subpass; + render_pass_create_info.dependencyCount = 1; + render_pass_create_info.pDependencies = &dependency; + render_pass_create_info.pNext = 0; + render_pass_create_info.flags = 0; + + VK_CHECK(vkCreateRenderPass(context->device.logical_device, &render_pass_create_info, + context->allocator, &out_renderpass->handle)); + } + + // TODO: renderpass destroy + + void vulkan_renderpass_begin(vulkan_command_buffer * command_buffer, + vulkan_renderpass * renderpass, VkFramebuffer framebuffer) { + VkRenderPassBeginInfo begin_info = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; + begin_info.renderPass = renderpass->handle; + begin_info.framebuffer = framebuffer; + begin_info.renderArea.offset.x = renderpass->render_area.x; + begin_info.renderArea.offset.y = renderpass->render_area.y; + begin_info.renderArea.extent.width = renderpass->render_area.z; + begin_info.renderArea.extent.height = renderpass->render_area.w; + + VkClearValue clear_values[2]; + memset(&clear_values, 0, sizeof(VkClearValue) * 2); + clear_values[0].color.float32[0] = renderpass->clear_colour.x; + clear_values[0].color.float32[1] = renderpass->clear_colour.y; + clear_values[0].color.float32[2] = renderpass->clear_colour.z; + clear_values[0].color.float32[3] = renderpass->clear_colour.w; + clear_values[1].depthStencil.depth = renderpass->depth; + clear_values[1].depthStencil.stencil = renderpass->stencil; + + begin_info.clearValueCount = 2; + begin_info.pClearValues = clear_values; + + vkCmdBeginRenderPass(command_buffer->handle, &begin_info, VK_SUBPASS_CONTENTS_INLINE); + command_buffer->state = COMMAND_BUFFER_STATE_IN_RENDER_PASS; + } + + void vulkan_renderpass_end(vulkan_command_buffer * command_buffer, + vulkan_renderpass * renderpass) { + vkCmdEndRenderPass(command_buffer->handle); + command_buffer->state = COMMAND_BUFFER_STATE_RECORDING; + } + + bool create_buffers(vulkan_context * context) { + VkMemoryPropertyFlagBits mem_prop_flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + + const u64 vertex_buffer_size = sizeof(vertex_pos) * 1024 * 1024; + if (!vulkan_buffer_create(context, vertex_buffer_size, + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT, + mem_prop_flags, true, &context->object_vertex_buffer)) { + ERROR("couldnt create vertex buffer"); + return false; + } + + context->geometry_vertex_offset = 0; + + const u64 index1_buffer_size = sizeof(u32) * 1024 * 1024; + if (!vulkan_buffer_create(context, index1_buffer_size, + VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT, + mem_prop_flags, true, &context->object_index_buffer)) { + ERROR("couldnt create vertex buffer"); + return false; + } + context->geometry_index_offset = 0; + + return true; + } + + void create_command_buffers(renderer * ren) { + if (!context.gfx_command_buffers) { + context.gfx_command_buffers = vulkan_command_buffer_darray_new(context.swapchain.image_count); + } + + for (u32 i = 0; i < context.swapchain.image_count; i++) { + vulkan_command_buffer_allocate(&context, context.device.gfx_command_pool, true, + &context.gfx_command_buffers->data[i]); + } + } + + void upload_data_range(vulkan_context * context, VkCommandPool pool, VkFence fence, VkQueue queue, + vulkan_buffer * buffer, u64 offset, u64 size, void* data) { + VkBufferUsageFlags flags = + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + vulkan_buffer staging; + vulkan_buffer_create(context, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, flags, true, &staging); + // load data into staging buffer + printf("Size: %ld\n", size); + vulkan_buffer_load_data(context, &staging, 0, size, 0, data); + + // copy + vulkan_buffer_copy_to(context, pool, fence, queue, staging.handle, 0, buffer->handle, offset, + size); + + vkDestroyBuffer(context->device.logical_device, staging.handle, context->allocator); + } + + void regenerate_framebuffers(renderer * ren, vulkan_swapchain * swapchain, + vulkan_renderpass * renderpass) { + for (u32 i = 0; i < swapchain->image_count; i++) { + u32 attachment_count = 2; // one for depth, one for colour + + VkImageView attachments[2] = { swapchain->views[i], swapchain->depth_attachment.view }; + + vulkan_framebuffer_create(&context, renderpass, context.framebuffer_width, + context.framebuffer_height, 2, attachments, + &swapchain->framebuffers->data[i]); + } + } + + void vulkan_fence_create(vulkan_context * context, bool create_signaled, + vulkan_fence* out_fence) { + out_fence->is_signaled = create_signaled; + VkFenceCreateInfo fence_create_info = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO }; + if (out_fence->is_signaled) { + fence_create_info.flags = VK_FENCE_CREATE_SIGNALED_BIT; + } + + vkCreateFence(context->device.logical_device, &fence_create_info, context->allocator, + &out_fence->handle); + } + + // TODO: vulkan_fence_destroy + + bool vulkan_fence_wait(vulkan_context * context, vulkan_fence * fence, u64 timeout_ns) { + if (!fence->is_signaled) { + VkResult result = + vkWaitForFences(context->device.logical_device, 1, &fence->handle, true, timeout_ns); + switch (result) { + case VK_SUCCESS: + fence->is_signaled = true; + return true; + case VK_TIMEOUT: + WARN("vk_fence_wait - Timed out"); + break; + default: + ERROR("vk_fence_wait - Unhanlded error type"); + break; + } + } else { + return true; + } + + return false; + } + void vulkan_fence_reset(vulkan_context * context, vulkan_fence * fence) { + if (fence->is_signaled) { + vkResetFences(context->device.logical_device, 1, &fence->handle); + fence->is_signaled = false; + } + } + + bool gfx_backend_init(renderer * ren) { + INFO("loading Vulkan backend"); + + vulkan_state* internal = malloc(sizeof(vulkan_state)); + ren->backend_state = (void*)internal; + + context.allocator = 0; // TODO: custom allocator + + context.framebuffer_width = SCR_WIDTH; + context.framebuffer_height = SCR_HEIGHT; + + // Setup Vulkan instance + VkApplicationInfo app_info = { VK_STRUCTURE_TYPE_APPLICATION_INFO }; + app_info.apiVersion = VK_API_VERSION_1_3; + app_info.pApplicationName = ren->config.window_name; + app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0); + app_info.pEngineName = "Celeritas Engine"; + app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0); + + VkInstanceCreateInfo create_info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO }; + create_info.pApplicationInfo = &app_info; + + cstr_darray* required_extensions = cstr_darray_new(2); + cstr_darray_push(required_extensions, VK_KHR_SURFACE_EXTENSION_NAME); + + plat_get_required_extension_names(required_extensions); + +#if defined(CDEBUG) + cstr_darray_push(required_extensions, VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + + DEBUG("Required extensions:"); + for (u32 i = 0; i < cstr_darray_len(required_extensions); i++) { + DEBUG(" %s", required_extensions->data[i]); + } +#endif + + create_info.enabledExtensionCount = cstr_darray_len(required_extensions); + create_info.ppEnabledExtensionNames = required_extensions->data; + + // Validation layers + create_info.enabledLayerCount = 0; + create_info.ppEnabledLayerNames = 0; +#if defined(CDEBUG) + INFO("Validation layers enabled"); + cstr_darray* desired_validation_layers = cstr_darray_new(1); + cstr_darray_push(desired_validation_layers, "VK_LAYER_KHRONOS_validation"); + + u32 n_available_layers = 0; + VK_CHECK(vkEnumerateInstanceLayerProperties(&n_available_layers, 0)); + TRACE("%d available layers", n_available_layers); + VkLayerProperties_darray* available_layers = VkLayerProperties_darray_new(n_available_layers); + VK_CHECK(vkEnumerateInstanceLayerProperties(&n_available_layers, available_layers->data)); + + for (int i = 0; i < cstr_darray_len(desired_validation_layers); i++) { + // look through layers to make sure we can find the ones we want + bool found = false; + for (int j = 0; j < n_available_layers; j++) { + if (str8_equals(str8_cstr_view(desired_validation_layers->data[i]), + str8_cstr_view(available_layers->data[j].layerName))) { + found = true; + TRACE("Found layer %s", desired_validation_layers->data[i]); + break; + } + } + + if (!found) { + FATAL("Required validation is missing %s", desired_validation_layers->data[i]); + return false; + } + } + INFO("All validation layers are present"); + create_info.enabledLayerCount = cstr_darray_len(desired_validation_layers); + create_info.ppEnabledLayerNames = desired_validation_layers->data; +#endif + + VkResult result = vkCreateInstance(&create_info, NULL, &context.instance); + if (result != VK_SUCCESS) { + ERROR("vkCreateInstance failed with result: %u", result); + return false; + } + + // Debugger +#if defined(CDEBUG) + DEBUG("Creating Vulkan debugger") + u32 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 = vk_debug_callback; + + PFN_vkCreateDebugUtilsMessengerEXT func = + (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(context.instance, + "vkCreateDebugUtilsMessengerEXT"); + assert(func); + VK_CHECK(func(context.instance, &debug_create_info, context.allocator, &context.vk_debugger)); + DEBUG("Vulkan debugger created"); + +#endif + + // Surface creation + DEBUG("Create SurfaceKHR") + VkSurfaceKHR surface; + VK_CHECK(glfwCreateWindowSurface(context.instance, ren->window, NULL, &surface)); + context.surface = surface; + DEBUG("Vulkan surface created") + + // Device creation + if (!vulkan_device_create(&context)) { + FATAL("device creation failed"); + return false; + } + + // Swapchain creation + vulkan_swapchain_create(&context, SCR_WIDTH, SCR_HEIGHT, &context.swapchain); + + // Renderpass creation + vulkan_renderpass_create(&context, &context.main_renderpass, + vec4(0, 0, context.framebuffer_width, context.framebuffer_height), + rgba_to_vec4(COLOUR_SEA_GREEN), 1.0, 0); + + // Framebiffers creation + context.swapchain.framebuffers = vulkan_framebuffer_darray_new(context.swapchain.image_count); + regenerate_framebuffers(ren, &context.swapchain, &context.main_renderpass); + INFO("Framebuffers created"); + + // Command buffers creation + create_command_buffers(ren); + INFO("Command buffers created"); + + // Sync objects + context.image_available_semaphores = + calloc(context.swapchain.max_frames_in_flight, sizeof(VkSemaphore)); + context.queue_complete_semaphores = + calloc(context.swapchain.max_frames_in_flight, sizeof(VkSemaphore)); + context.in_flight_fences = calloc(context.swapchain.max_frames_in_flight, sizeof(vulkan_fence)); + + for (u8 i = 0; i < context.swapchain.max_frames_in_flight; i++) { + VkSemaphoreCreateInfo semaphore_create_info = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; + vkCreateSemaphore(context.device.logical_device, &semaphore_create_info, context.allocator, + &context.image_available_semaphores[i]); + vkCreateSemaphore(context.device.logical_device, &semaphore_create_info, context.allocator, + &context.queue_complete_semaphores[i]); + + // create the fence in a signaled state + vulkan_fence_create(&context, true, &context.in_flight_fences[i]); + } + + context.images_in_flight = + malloc(sizeof(vulkan_fence*) * context.swapchain.max_frames_in_flight); + for (u8 i = 0; i < context.swapchain.max_frames_in_flight; i++) { + context.images_in_flight[i] = 0; + } + INFO("Sync objects created"); + + // Shader modules + vulkan_object_shader_create(&context, &context.object_shader); + INFO("Compiled shader modules") + + create_buffers(&context); + INFO("Created buffers"); + + // TODO: temporary test code + + mesh cube = prim_cube_mesh_create(); + + vertex* verts = malloc(sizeof(vertex) * cube.vertices->len); + + f32 scale = 3.0; + for (size_t i = 0; i < cube.vertices->len; i++) { + verts[i].position = vec3_mult(cube.vertices->data[i].position, scale); + verts[i].normal = cube.vertices->data[i].normal; + verts[i].uv = cube.vertices->data[i].uv; + } + + // const f32 s = 1.0; + // const u32 vert_count = 4; + // vertex_pos verts[4] = { 0 }; + + // verts[0].pos.x = -0.5 * s; + // verts[0].pos.y = -0.5 * s; + + // verts[1].pos.x = 0.5 * s; + // verts[1].pos.y = 0.5 * s; + + // verts[2].pos.x = -0.5 * s; + // verts[2].pos.y = 0.5 * s; + + // verts[3].pos.x = 0.5 * s; + // verts[3].pos.y = -0.5 * s; + + // const u32 index_count = 6; + // u32 indices[6] = { 0, 1, 2, 0, 3, 1 }; + + upload_data_range(&context, context.device.gfx_command_pool, 0, context.device.graphics_queue, + &context.object_vertex_buffer, 0, sizeof(vertex) * cube.vertices->len, verts); + TRACE("Uploaded vertex data"); + upload_data_range(&context, context.device.gfx_command_pool, 0, context.device.graphics_queue, + &context.object_index_buffer, 0, sizeof(u32) * cube.indices_len, + cube.indices); + TRACE("Uploaded index data"); + vertex_darray_free(cube.vertices); + free(cube.indices); + + // upload texture + + // --- End test code + + INFO("Vulkan renderer initialisation succeeded"); + return true; + } + + void gfx_backend_shutdown(renderer * ren) { + DEBUG("Destroying Vulkan debugger"); + if (context.vk_debugger) { + PFN_vkDestroyDebugUtilsMessengerEXT func = + (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr( + context.instance, "vkDestroyDebugUtilsMessengerEXT"); + func(context.instance, context.vk_debugger, context.allocator); + } + + DEBUG("Destroying Vulkan instance..."); + vkDestroyInstance(context.instance, context.allocator); + } + + void backend_begin_frame(renderer * ren, f32 delta_time) { + vulkan_device* device = &context.device; + + // TODO: resize gubbins + + if (!vulkan_fence_wait(&context, &context.in_flight_fences[context.current_frame], + UINT64_MAX)) { + WARN("In-flight fence wait failure"); + } + + if (!vulkan_swapchain_acquire_next_image_index( + &context, &context.swapchain, UINT64_MAX, + context.image_available_semaphores[context.current_frame], 0, &context.image_index)) { + WARN("couldnt acquire swapchain next image"); + } + + vulkan_command_buffer* command_buffer = &context.gfx_command_buffers->data[context.image_index]; + vulkan_command_buffer_reset(command_buffer); + vulkan_command_buffer_begin(command_buffer, false, false, false); + + VkViewport viewport; + viewport.x = 0.0; + viewport.y = 0; + viewport.width = (f32)context.framebuffer_width; + viewport.height = (f32)context.framebuffer_height; + viewport.minDepth = 0.0; + viewport.maxDepth = 1.0; + + VkRect2D scissor; + scissor.offset.x = scissor.offset.y = 0; + scissor.extent.width = context.framebuffer_width; + scissor.extent.height = context.framebuffer_height; + + vkCmdSetViewport(command_buffer->handle, 0, 1, &viewport); + vkCmdSetScissor(command_buffer->handle, 0, 1, &scissor); + + context.main_renderpass.render_area.z = context.framebuffer_width; + context.main_renderpass.render_area.w = context.framebuffer_height; + + vulkan_renderpass_begin(command_buffer, &context.main_renderpass, + context.swapchain.framebuffers->data[context.image_index].handle); + } + + void texture_data_upload(texture * tex) { + printf("Texture name %s\n", tex->name); + tex->backend_data = malloc(sizeof(vulkan_texture_data)); + vulkan_texture_data* data = (vulkan_texture_data*)tex->backend_data; + printf("Texture (%s) details: \n width %d\n height %d\n channel count %d\n", tex->name, + tex->width, tex->height, tex->channel_count); + VkDeviceSize image_size = tex->width * tex->height * max(tex->channel_count, 4); + + TRACE("Creating buffer of size %ld", image_size); + + VkFormat image_format = VK_FORMAT_R8G8B8A8_SRGB; + + VkBufferUsageFlags usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + VkMemoryPropertyFlags memory_prop_flags = + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + vulkan_buffer staging; + vulkan_buffer_create(&context, image_size, usage, memory_prop_flags, true, &staging); + DEBUG("Uploading image data"); + vulkan_buffer_load_data(&context, &staging, 0, image_size, 0, tex->image_data); + INFO("Loaded iamge data!"); + + vulkan_image_create( + &context, VK_IMAGE_TYPE_2D, tex->width, tex->height, image_format, VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | + VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, true, VK_IMAGE_ASPECT_COLOR_BIT, &data->image); + + vulkan_command_buffer temp_buffer; + vulkan_command_buffer_allocate_and_begin_oneshot(&context, context.device.gfx_command_pool, + &temp_buffer); + + vulkan_image_transition_layout(&context, &temp_buffer, &data->image, image_format, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + vulkan_image_copy_from_buffer(&data->image, staging.handle, &temp_buffer); + + vulkan_image_transition_layout(&context, &temp_buffer, &data->image, image_format, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + vulkan_command_buffer_end_oneshot(&context, context.device.gfx_command_pool, &temp_buffer, + context.device.graphics_queue); + + VkSamplerCreateInfo sampler_info = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; + sampler_info.magFilter = VK_FILTER_LINEAR; + sampler_info.minFilter = VK_FILTER_LINEAR; + sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler_info.anisotropyEnable = VK_TRUE; + sampler_info.maxAnisotropy = 16; + sampler_info.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; + sampler_info.unnormalizedCoordinates = VK_FALSE; + sampler_info.compareEnable = VK_FALSE; + sampler_info.compareOp = VK_COMPARE_OP_ALWAYS; + sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + sampler_info.mipLodBias = 0.0; + sampler_info.minLod = 0.0; + sampler_info.maxLod = 0.0; + + VkResult res = vkCreateSampler(context.device.logical_device, &sampler_info, context.allocator, + &data->sampler); + if (res != VK_SUCCESS) { + ERROR("Error creating texture sampler for image %s", tex->name); + return; + } + + tex->image_data = (void*)data; + } + + // TODO: destroy texture + + void backend_end_frame(renderer * ren, f32 delta_time) { + vulkan_command_buffer* command_buffer = &context.gfx_command_buffers->data[context.image_index]; + + vulkan_renderpass_end(command_buffer, &context.main_renderpass); + + vulkan_command_buffer_end(command_buffer); + + // TODO: wait on fence - https://youtu.be/hRL71D1f3pU?si=nLJx-ZsemDBeQiQ1&t=1037 + + context.images_in_flight[context.image_index] = + &context.in_flight_fences[context.current_frame]; + + vulkan_fence_reset(&context, &context.in_flight_fences[context.current_frame]); + + VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &command_buffer->handle; + submit_info.signalSemaphoreCount = 1; + submit_info.pSignalSemaphores = &context.queue_complete_semaphores[context.current_frame]; + submit_info.waitSemaphoreCount = 1; + submit_info.pWaitSemaphores = &context.image_available_semaphores[context.current_frame]; + + VkPipelineStageFlags flags[1] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; + submit_info.pWaitDstStageMask = flags; + + VkResult result = vkQueueSubmit(context.device.graphics_queue, 1, &submit_info, + context.in_flight_fences[context.current_frame].handle); + + if (result != VK_SUCCESS) { + ERROR("queue submission failed. fark."); + } + + vulkan_command_buffer_update_submitted(command_buffer); + + vulkan_swapchain_present( + &context, &context.swapchain, context.device.graphics_queue, context.device.graphics_queue, + context.queue_complete_semaphores[context.current_frame], context.image_index); + } + + void gfx_backend_draw_frame(renderer * ren, camera * cam, mat4 model, texture * tex) { + backend_begin_frame(ren, 16.0); + + mat4 proj; + mat4 view; + + camera_view_projection(cam, SCR_HEIGHT, SCR_WIDTH, &view, &proj); + + context.object_shader.texture_data = (vulkan_texture_data*)tex->image_data; + gfx_backend_update_global_state(proj, view, cam->position, vec4(1.0, 1.0, 1.0, 1.0), 0); + + vulkan_object_shader_update_object(&context, &context.object_shader, model); + + backend_end_frame(ren, 16.0); + } + + void gfx_backend_update_global_state(mat4 projection, mat4 view, vec3 view_pos, + vec4 ambient_colour, i32 mode) { + vulkan_object_shader_use(&context, &context.object_shader); + + vulkan_object_shader_update_global_state(&context, &context.object_shader); + context.object_shader.global_ubo.projection = projection; + context.object_shader.global_ubo.view = view; + // TODO: other UBO properties + } + + void clear_screen(vec3 colour) {} + + void bind_texture(shader s, texture * tex, u32 slot) {} + void bind_mesh_vertex_buffer(void* backend, mesh* mesh) {} + void draw_primitives(cel_primitive_topology primitive, u32 start_index, u32 count) {} + + shader shader_create_separate(const char* vert_shader, const char* frag_shader) {} + void set_shader(shader s) {} + + void uniform_vec3f(u32 program_id, const char* uniform_name, vec3* value) {} + void uniform_f32(u32 program_id, const char* uniform_name, f32 value) {} + void uniform_i32(u32 program_id, const char* uniform_name, i32 value) {} + void uniform_mat4f(u32 program_id, const char* uniform_name, mat4* value) {} + + VKAPI_ATTR VkBool32 VKAPI_CALL vk_debug_callback( + VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT flags, + const VkDebugUtilsMessengerCallbackDataEXT* callback_data, void* user_data) { + switch (severity) { + default: + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: + ERROR("%s", callback_data->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: + WARN("%s", callback_data->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: + INFO("%s", callback_data->pMessage); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: + TRACE("%s", callback_data->pMessage); + break; + } + return VK_FALSE; + } + +#endif \ No newline at end of file diff --git a/src/renderer/backends/backend_dx11.h b/src/renderer/backends/backend_dx11.h index 163ca97..e076659 100644 --- a/src/renderer/backends/backend_dx11.h +++ b/src/renderer/backends/backend_dx11.h @@ -1,4 +1,7 @@ #pragma once +#include +#include + #include "ral.h" #define GPU_SWAPCHAIN_IMG_COUNT 2 @@ -6,6 +9,7 @@ typedef struct gpu_swapchain { } gpu_swapchain; typedef struct gpu_device { + // VkPhysicalDevice physical_device; // VkDevice logical_device; // VkPhysicalDeviceProperties properties; diff --git a/src/renderer/backends/backend_vulkan.c b/src/renderer/backends/backend_vulkan.c index 75b0439..b66aeca 100644 --- a/src/renderer/backends/backend_vulkan.c +++ b/src/renderer/backends/backend_vulkan.c @@ -1,841 +1,44 @@ -#include "camera.h" -#include "primitives.h" -#define CDEBUG -// #define CEL_PLATFORM_LINUX -#if CEL_REND_BACKEND_VULKAN -// ^ Temporary - -#include -#include -#include -#include #include #include #include -#include "colours.h" -#include "str.h" -#include "darray.h" +#include "backend_vulkan.h" + #include "defines.h" -#include "file.h" #include "log.h" -#include "maths.h" -#include "maths_types.h" -#include "render_backend.h" -#include "render_types.h" -#include "vulkan_helpers.h" - -#include - -#define SCR_WIDTH 1000 -#define SCR_HEIGHT 1000 - -#include - -#include - -KITC_DECL_TYPED_ARRAY(VkLayerProperties) - -typedef struct vulkan_device { - VkPhysicalDevice physical_device; - VkDevice logical_device; - vulkan_swapchain_support_info swapchain_support; - i32 graphics_queue_index; - i32 present_queue_index; - i32 compute_queue_index; - i32 transfer_queue_index; - VkQueue graphics_queue; - VkQueue present_queue; - VkQueue compute_queue; - VkQueue transfer_queue; - VkCommandPool gfx_command_pool; - VkPhysicalDeviceProperties properties; - VkPhysicalDeviceFeatures features; - VkPhysicalDeviceMemoryProperties memory; - VkFormat depth_format; -} vulkan_device; - -typedef struct vulkan_image { - VkImage handle; - VkDeviceMemory memory; - VkImageView view; - u32 width; - u32 height; -} vulkan_image; - -typedef struct vulkan_texture_data { - vulkan_image image; - VkSampler sampler; -} vulkan_texture_data; - -typedef enum vulkan_renderpass_state { - READY, - RECORDING, - IN_RENDER_PASS, - RECORDING_ENDING, - SUBMITTED, - NOT_ALLOCATED -} vulkan_renderpass_state; - -typedef struct vulkan_renderpass { - VkRenderPass handle; - vec4 render_area; - vec4 clear_colour; - f32 depth; - u32 stencil; - vulkan_renderpass_state state; -} vulkan_renderpass; - -typedef struct vulkan_framebuffer { - VkFramebuffer handle; - u32 attachment_count; - VkImageView* attachments; - vulkan_renderpass* renderpass; -} vulkan_framebuffer; - -KITC_DECL_TYPED_ARRAY(vulkan_framebuffer) - -typedef struct vulkan_swapchain { - VkSurfaceFormatKHR image_format; - u8 max_frames_in_flight; - VkSwapchainKHR handle; - u32 image_count; - VkImage* images; - VkImageView* views; - vulkan_image depth_attachment; - vulkan_framebuffer_darray* framebuffers; -} vulkan_swapchain; - -// overengineered -typedef enum vulkan_command_buffer_state { - COMMAND_BUFFER_STATE_READY, - COMMAND_BUFFER_STATE_IN_RENDER_PASS, - COMMAND_BUFFER_STATE_RECORDING, - COMMAND_BUFFER_STATE_RECORDING_ENDED, - COMMAND_BUFFER_STATE_SUBMITTED, - COMMAND_BUFFER_STATE_NOT_ALLOCATED, -} vulkan_command_buffer_state; - -typedef struct vulkan_command_buffer { - VkCommandBuffer handle; - vulkan_command_buffer_state state; -} vulkan_command_buffer; - -KITC_DECL_TYPED_ARRAY(vulkan_command_buffer) - -typedef struct vulkan_fence { - VkFence handle; - bool is_signaled; -} vulkan_fence; - -typedef struct vulkan_shader_stage { - VkShaderModuleCreateInfo create_info; - VkShaderModule handle; - VkPipelineShaderStageCreateInfo stage_create_info; -} vulkan_shader_stage; - -typedef struct vulkan_pipeline { - VkPipeline handle; - VkPipelineLayout layout; -} vulkan_pipeline; - -typedef struct global_object_uniform { - mat4 projection; // 64 bytes - mat4 view; // 64 bytes - f32 padding[32]; -} global_object_uniform; - -typedef struct object_uniform { - vec4 diffuse_colour; - vec4 v_reserved0; - vec4 v_reserved1; - vec4 v_reserved2; -} object_uniform; - -#define MAX_OBJECT_COUNT 1024 -#define VULKAN_OBJECT_SHADER_DESCRIPTOR_COUNT 1 - -typedef struct geometry_render_data { - u32 id; - mat4 model; - texture* textures[16]; -} geometry_render_data; - -typedef struct vulkan_buffer { - u64 total_size; - VkBuffer handle; - VkBufferUsageFlagBits usage; - bool is_locked; - VkDeviceMemory memory; - i32 memory_index; - u32 memory_property_flags; -} vulkan_buffer; +#include "ral.h" +#include "ral_types.h" -#define SHADER_STAGE_COUNT 2 - -typedef struct vulkan_shader { - // vertex, fragment - vulkan_shader_stage stages[SHADER_STAGE_COUNT]; - vulkan_pipeline pipeline; - - // descriptors - VkDescriptorPool descriptor_pool; - VkDescriptorSetLayout descriptor_set_layout; - VkDescriptorSet descriptor_sets[3]; // one for each in-flight frame - - vulkan_buffer global_uniforms_buffer; - - // Data that's global for all objects drawn - global_object_uniform global_ubo; - object_uniform object_ubo; - vulkan_texture_data* texture_data; -} vulkan_shader; +#define VULKAN_QUEUES_COUNT 2 +const char* queue_names[VULKAN_QUEUES_COUNT] = { "GRAPHICS", "TRANSFER" }; typedef struct vulkan_context { - VkInstance instance; + gpu_device device; VkAllocationCallbacks* allocator; - VkSurfaceKHR surface; - vulkan_device device; - u32 framebuffer_width; - u32 framebuffer_height; - vulkan_swapchain swapchain; - vulkan_renderpass main_renderpass; - vulkan_buffer object_vertex_buffer; - vulkan_buffer object_index_buffer; - u64 geometry_vertex_offset; - u64 geometry_index_offset; - - vulkan_command_buffer_darray* gfx_command_buffers; - VkSemaphore* image_available_semaphores; - VkSemaphore* queue_complete_semaphores; - u32 in_flight_fence_count; - vulkan_fence* in_flight_fences; - vulkan_fence** images_in_flight; - - u32 image_index; - u32 current_frame; - - vulkan_shader object_shader; - - // TODO: swapchain recreation + VkInstance instance; -#if defined(DEBUG) - VkDebugUtilsMessengerEXT vk_debugger; -#endif } vulkan_context; static vulkan_context context; -static i32 find_memory_index(vulkan_context* context, u32 type_filter, u32 property_flags) { - VkPhysicalDeviceMemoryProperties memory_properties; - vkGetPhysicalDeviceMemoryProperties(context->device.physical_device, &memory_properties); - - for (u32 i = 0; i < memory_properties.memoryTypeCount; ++i) { - // Check each memory type to see if its bit is set to 1. - if (type_filter & (1 << i) && - (memory_properties.memoryTypes[i].propertyFlags & property_flags) == property_flags) { - return i; - } - } - - WARN("Unable to find suitable memory type!"); - return -1; -} - -/** @brief Internal backend state */ -typedef struct vulkan_state { -} vulkan_state; - -typedef struct vertex_pos { - vec3 pos; - vec3 normal; -} vertex_pos; - -// pipeline stuff -bool vulkan_graphics_pipeline_create(vulkan_context* context, vulkan_renderpass* renderpass, - u32 attribute_count, - VkVertexInputAttributeDescription* attributes, - u32 descriptor_set_layout_count, - VkDescriptorSetLayout* descriptor_set_layouts, u32 stage_count, - VkPipelineShaderStageCreateInfo* stages, VkViewport viewport, - VkRect2D scissor, bool is_wireframe, - vulkan_pipeline* out_pipeline) { - VkPipelineViewportStateCreateInfo viewport_state = { - VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO - }; - viewport_state.viewportCount = 1; - viewport_state.pViewports = &viewport; - viewport_state.scissorCount = 1; - viewport_state.pScissors = &scissor; - - // Rasterizer - VkPipelineRasterizationStateCreateInfo rasterizer_create_info = { - VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO - }; - rasterizer_create_info.depthClampEnable = VK_FALSE; - rasterizer_create_info.rasterizerDiscardEnable = VK_FALSE; - rasterizer_create_info.polygonMode = is_wireframe ? VK_POLYGON_MODE_LINE : VK_POLYGON_MODE_FILL; - rasterizer_create_info.lineWidth = 1.0f; - rasterizer_create_info.cullMode = VK_CULL_MODE_BACK_BIT; - rasterizer_create_info.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; - rasterizer_create_info.depthBiasEnable = VK_FALSE; - rasterizer_create_info.depthBiasConstantFactor = 0.0; - rasterizer_create_info.depthBiasClamp = 0.0; - rasterizer_create_info.depthBiasSlopeFactor = 0.0; - - // Multisampling - VkPipelineMultisampleStateCreateInfo ms_create_info = { - VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO - }; - ms_create_info.sampleShadingEnable = VK_FALSE; - ms_create_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - ms_create_info.minSampleShading = 1.0; - ms_create_info.pSampleMask = 0; - ms_create_info.alphaToCoverageEnable = VK_FALSE; - ms_create_info.alphaToOneEnable = VK_FALSE; - - // Depth and stencil testing - VkPipelineDepthStencilStateCreateInfo depth_stencil = { - VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO - }; - depth_stencil.depthTestEnable = VK_TRUE; - depth_stencil.depthWriteEnable = VK_TRUE; - depth_stencil.depthCompareOp = VK_COMPARE_OP_LESS; - depth_stencil.depthBoundsTestEnable = VK_FALSE; - depth_stencil.stencilTestEnable = VK_FALSE; - depth_stencil.pNext = 0; - - VkPipelineColorBlendAttachmentState color_blend_attachment_state; - color_blend_attachment_state.blendEnable = VK_TRUE; - color_blend_attachment_state.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; - color_blend_attachment_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - color_blend_attachment_state.colorBlendOp = VK_BLEND_OP_ADD; - color_blend_attachment_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; - color_blend_attachment_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - color_blend_attachment_state.alphaBlendOp = VK_BLEND_OP_ADD; - color_blend_attachment_state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | - VK_COLOR_COMPONENT_G_BIT | - VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; - - VkPipelineColorBlendStateCreateInfo color_blend = { - VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO - }; - color_blend.logicOpEnable = VK_FALSE; - color_blend.logicOp = VK_LOGIC_OP_COPY; - color_blend.attachmentCount = 1; - color_blend.pAttachments = &color_blend_attachment_state; - - const u32 dynamic_state_count = 3; - VkDynamicState dynamic_states[3] = { - VK_DYNAMIC_STATE_VIEWPORT, - VK_DYNAMIC_STATE_SCISSOR, - VK_DYNAMIC_STATE_LINE_WIDTH, - }; - - VkPipelineDynamicStateCreateInfo dynamic_state = { - VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO - }; - dynamic_state.dynamicStateCount = dynamic_state_count; - dynamic_state.pDynamicStates = dynamic_states; - - // Vertex input - VkVertexInputBindingDescription binding_desc; - binding_desc.binding = 0; - binding_desc.stride = sizeof(vertex); - binding_desc.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - - VkPipelineVertexInputStateCreateInfo vertex_input_info = { - VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO - }; - vertex_input_info.vertexBindingDescriptionCount = 1; - vertex_input_info.pVertexBindingDescriptions = &binding_desc; - vertex_input_info.vertexAttributeDescriptionCount = attribute_count; - vertex_input_info.pVertexAttributeDescriptions = attributes; - - VkPipelineInputAssemblyStateCreateInfo input_assembly = { - VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO - }; - input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - input_assembly.primitiveRestartEnable = VK_FALSE; - - VkPipelineLayoutCreateInfo pipeline_layout_create_info = { - VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO - }; - - // Pushconstants - VkPushConstantRange push_constant; - push_constant.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; - push_constant.offset = sizeof(mat4) * 0; - push_constant.size = sizeof(mat4) * 2; - - pipeline_layout_create_info.pushConstantRangeCount = 1; - pipeline_layout_create_info.pPushConstantRanges = &push_constant; - - pipeline_layout_create_info.setLayoutCount = descriptor_set_layout_count; - pipeline_layout_create_info.pSetLayouts = descriptor_set_layouts; - - vkCreatePipelineLayout(context->device.logical_device, &pipeline_layout_create_info, - context->allocator, &out_pipeline->layout); - - VkGraphicsPipelineCreateInfo pipeline_create_info = { - VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO - }; - pipeline_create_info.stageCount = stage_count; - pipeline_create_info.pStages = stages; - pipeline_create_info.pVertexInputState = &vertex_input_info; - pipeline_create_info.pInputAssemblyState = &input_assembly; - - pipeline_create_info.pViewportState = &viewport_state; - pipeline_create_info.pRasterizationState = &rasterizer_create_info; - pipeline_create_info.pMultisampleState = &ms_create_info; - pipeline_create_info.pDepthStencilState = &depth_stencil; - pipeline_create_info.pColorBlendState = &color_blend; - pipeline_create_info.pDynamicState = &dynamic_state; - pipeline_create_info.pTessellationState = 0; - - pipeline_create_info.layout = out_pipeline->layout; - - pipeline_create_info.renderPass = renderpass->handle; - pipeline_create_info.subpass = 0; - pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE; - pipeline_create_info.basePipelineIndex = -1; - - VkResult result = - vkCreateGraphicsPipelines(context->device.logical_device, VK_NULL_HANDLE, 1, - &pipeline_create_info, context->allocator, &out_pipeline->handle); - if (result != VK_SUCCESS) { - FATAL("graphics pipeline creation failed. its fked mate"); - ERROR_EXIT("Doomed"); - } - - return true; -} - -void vulkan_pipeline_bind(vulkan_command_buffer* command_buffer, VkPipelineBindPoint bind_point, - vulkan_pipeline* pipeline) { - vkCmdBindPipeline(command_buffer->handle, bind_point, pipeline->handle); -} - -void vulkan_buffer_bind(vulkan_context* context, vulkan_buffer* buffer, u64 offset) { - vkBindBufferMemory(context->device.logical_device, buffer->handle, buffer->memory, offset); -} - -bool vulkan_buffer_create(vulkan_context* context, u64 size, VkBufferUsageFlagBits usage, - u32 memory_property_flags, bool bind_on_create, - vulkan_buffer* out_buffer) { - memset(out_buffer, 0, sizeof(vulkan_buffer)); - out_buffer->total_size = size; - out_buffer->usage = usage; - out_buffer->memory_property_flags = memory_property_flags; - - VkBufferCreateInfo buffer_info = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; - buffer_info.size = size; - buffer_info.usage = usage; - buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - - vkCreateBuffer(context->device.logical_device, &buffer_info, context->allocator, - &out_buffer->handle); - - VkMemoryRequirements requirements; - vkGetBufferMemoryRequirements(context->device.logical_device, out_buffer->handle, &requirements); - out_buffer->memory_index = - find_memory_index(context, requirements.memoryTypeBits, out_buffer->memory_property_flags); - - // Allocate - VkMemoryAllocateInfo allocate_info = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; - allocate_info.allocationSize = requirements.size; - allocate_info.memoryTypeIndex = (u32)out_buffer->memory_index; - - vkAllocateMemory(context->device.logical_device, &allocate_info, context->allocator, - &out_buffer->memory); - - if (bind_on_create) { - vulkan_buffer_bind(context, out_buffer, 0); - } - - DEBUG("Created buffer."); - - return true; -} - -// lock and unlock? - -void* vulkan_buffer_lock_memory(vulkan_context* context, vulkan_buffer* buffer, u64 offset, - u64 size, u32 flags) { - void* data; - vkMapMemory(context->device.logical_device, buffer->memory, offset, size, flags, &data); - return data; -} -void* vulkan_buffer_unlock_memory(vulkan_context* context, vulkan_buffer* buffer) { - vkUnmapMemory(context->device.logical_device, buffer->memory); -} - -void vulkan_buffer_load_data(vulkan_context* context, vulkan_buffer* buffer, u64 offset, u64 size, - u32 flags, const void* data) { - void* data_ptr = 0; - VK_CHECK( - vkMapMemory(context->device.logical_device, buffer->memory, offset, size, flags, &data_ptr)); - memcpy(data_ptr, data, size); - vkUnmapMemory(context->device.logical_device, buffer->memory); -} - -// TODO: destroy - -bool create_shader_module(vulkan_context* context, const char* filename, const char* type_str, - VkShaderStageFlagBits flag, u32 stage_index, - vulkan_shader_stage* shader_stages) { - memset(&shader_stages[stage_index].create_info, 0, sizeof(VkShaderModuleCreateInfo)); - memset(&shader_stages[stage_index].stage_create_info, 0, sizeof(VkPipelineShaderStageCreateInfo)); - - shader_stages[stage_index].create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - - // todo: file input - FileData file_contents = load_spv_file(filename); - - shader_stages[stage_index].create_info.codeSize = file_contents.size; - shader_stages[stage_index].create_info.pCode = (u32*)file_contents.data; - - vkCreateShaderModule(context->device.logical_device, &shader_stages[stage_index].create_info, - context->allocator, &shader_stages[stage_index].handle); - - shader_stages[stage_index].stage_create_info.sType = - VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - shader_stages[stage_index].stage_create_info.stage = flag; - shader_stages[stage_index].stage_create_info.module = shader_stages[stage_index].handle; - shader_stages[stage_index].stage_create_info.pName = "main"; - - free(file_contents.data); - - // TODO: Descriptors - - return true; -} - -bool vulkan_object_shader_create(vulkan_context* context, vulkan_shader* out_shader) { - char stage_type_strs[SHADER_STAGE_COUNT][5] = { "vert", "frag" }; - char stage_filenames[SHADER_STAGE_COUNT][256] = { "build/linux/x86_64/debug/object.vert.spv", - "build/linux/x86_64/debug/object.frag.spv" }; - VkShaderStageFlagBits stage_types[SHADER_STAGE_COUNT] = { VK_SHADER_STAGE_VERTEX_BIT, - VK_SHADER_STAGE_FRAGMENT_BIT }; - for (u8 i = 0; i < SHADER_STAGE_COUNT; i++) { - DEBUG("Loading %s", stage_filenames[i]); - create_shader_module(context, stage_filenames[i], stage_type_strs[i], stage_types[i], i, - out_shader->stages); - } - - // descriptors - VkDescriptorSetLayoutBinding global_ubo_layout_binding; - global_ubo_layout_binding.binding = 0; - global_ubo_layout_binding.descriptorCount = 1; - global_ubo_layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - global_ubo_layout_binding.pImmutableSamplers = 0; - global_ubo_layout_binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; +static bool select_physical_device(gpu_device* out_device) {} - VkDescriptorSetLayoutBinding sampler_layout_binding; - sampler_layout_binding.binding = 1; - sampler_layout_binding.descriptorCount = 1; - sampler_layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - sampler_layout_binding.pImmutableSamplers = 0; - sampler_layout_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - - VkDescriptorSetLayoutCreateInfo global_layout_info = { - VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO - }; - - VkDescriptorSetLayoutBinding bindings[2] = { global_ubo_layout_binding, sampler_layout_binding }; - - global_layout_info.bindingCount = 2; - global_layout_info.pBindings = bindings; - - VK_CHECK(vkCreateDescriptorSetLayout(context->device.logical_device, &global_layout_info, - context->allocator, &out_shader->descriptor_set_layout)); - - VkDescriptorPoolSize global_pool_size; - global_pool_size.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - global_pool_size.descriptorCount = 3; - - VkDescriptorPoolSize sampler_pool_size; - sampler_pool_size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - sampler_pool_size.descriptorCount = 3; - - VkDescriptorPoolSize pool_sizes[2] = { global_pool_size, sampler_pool_size }; - - VkDescriptorPoolCreateInfo pool_info = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; - pool_info.poolSizeCount = 2; - pool_info.pPoolSizes = pool_sizes; - pool_info.maxSets = 3; - - VK_CHECK(vkCreateDescriptorPool(context->device.logical_device, &pool_info, context->allocator, - &out_shader->descriptor_pool)); - - // Pipeline creation - VkViewport viewport; - viewport.x = 0; - viewport.y = 0; - viewport.width = (f32)context->framebuffer_width; - viewport.height = (f32)context->framebuffer_height; - viewport.minDepth = 0.0; - viewport.maxDepth = 1.0; - - VkRect2D scissor; - scissor.offset.x = scissor.offset.y = 0; - scissor.extent.width = context->framebuffer_width; - scissor.extent.height = context->framebuffer_height; - - // Attributes - u32 offset = 0; - const i32 attribute_count = 3; - VkVertexInputAttributeDescription attribute_descs[3]; - // Position - VkFormat formats[3] = { VK_FORMAT_R32G32B32_SFLOAT, VK_FORMAT_R32G32B32_SFLOAT, - VK_FORMAT_R32G32_SFLOAT }; - - u64 sizes[3] = { sizeof(vec3), sizeof(vec3), sizeof(vec2) }; - - for (u32 i = 0; i < attribute_count; i++) { - attribute_descs[i].binding = 0; - attribute_descs[i].location = i; - attribute_descs[i].format = formats[i]; - attribute_descs[i].offset = offset; - offset += sizes[i]; - } - - // Descriptor set layouts - VkDescriptorSetLayout layouts[1] = { out_shader->descriptor_set_layout }; - - // Stages - VkPipelineShaderStageCreateInfo stage_create_infos[SHADER_STAGE_COUNT]; - memset(stage_create_infos, 0, sizeof(stage_create_infos)); - for (u32 i = 0; i < SHADER_STAGE_COUNT; i++) { - stage_create_infos[i].sType = out_shader->stages[i].stage_create_info.sType; - stage_create_infos[i] = out_shader->stages[i].stage_create_info; - } - - vulkan_graphics_pipeline_create( - context, &context->main_renderpass, attribute_count, attribute_descs, 1, layouts, - SHADER_STAGE_COUNT, stage_create_infos, viewport, scissor, false, &out_shader->pipeline); - INFO("Graphics pipeline created!"); - - // Uniform buffer - if (!vulkan_buffer_create(context, sizeof(global_object_uniform), - VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | - VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - true, &out_shader->global_uniforms_buffer)) { - ERROR("Couldnt create uniforms buffer"); - return false; - } - - VkDescriptorSetLayout global_layouts[3] = { - out_shader->descriptor_set_layout, - out_shader->descriptor_set_layout, - out_shader->descriptor_set_layout, - }; - - VkDescriptorSetAllocateInfo alloc_info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; - alloc_info.descriptorPool = out_shader->descriptor_pool; - alloc_info.descriptorSetCount = 3; - alloc_info.pSetLayouts = global_layouts; - VK_CHECK(vkAllocateDescriptorSets(context->device.logical_device, &alloc_info, - out_shader->descriptor_sets)); - - return true; -} -void vulkan_object_shader_destroy(vulkan_context* context, vulkan_shader* shader) {} -void vulkan_object_shader_use(vulkan_context* context, vulkan_shader* shader) { - u32 image_index = context->image_index; - vulkan_pipeline_bind(&context->gfx_command_buffers->data[image_index], - VK_PIPELINE_BIND_POINT_GRAPHICS, &shader->pipeline); -} -void vulkan_object_shader_update_global_state(vulkan_context* context, vulkan_shader* shader) { - u32 image_index = context->image_index; - VkCommandBuffer cmd_buffer = context->gfx_command_buffers->data[image_index].handle; - VkDescriptorSet global_descriptors = shader->descriptor_sets[image_index]; - - u32 range = sizeof(global_object_uniform); - u64 offset = 0; - - // copy data to buffer - vulkan_buffer_load_data(context, &shader->global_uniforms_buffer, offset, range, 0, - &shader->global_ubo); - - VkDescriptorBufferInfo buffer_info; - buffer_info.buffer = shader->global_uniforms_buffer.handle; - buffer_info.offset = offset; - buffer_info.range = range; - - VkDescriptorImageInfo image_info; - image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - image_info.imageView = shader->texture_data->image.view; - image_info.sampler = shader->texture_data->sampler; - - VkWriteDescriptorSet uniform_write = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; - uniform_write.dstSet = shader->descriptor_sets[image_index]; - uniform_write.dstBinding = 0; - uniform_write.dstArrayElement = 0; - uniform_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - uniform_write.descriptorCount = 1; - uniform_write.pBufferInfo = &buffer_info; - - VkWriteDescriptorSet texture_write = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; - texture_write.dstSet = shader->descriptor_sets[image_index]; - texture_write.dstBinding = 1; - texture_write.dstArrayElement = 0; - texture_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - texture_write.descriptorCount = 1; - texture_write.pImageInfo = &image_info; - - VkWriteDescriptorSet writes[2] = { uniform_write, texture_write }; - - vkUpdateDescriptorSets(context->device.logical_device, 2, writes, 0, 0); - - vkCmdBindDescriptorSets(cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, shader->pipeline.layout, 0, - 1, &global_descriptors, 0, 0); -} - -void vulkan_object_shader_update_object(vulkan_context* context, vulkan_shader* shader, - mat4 model) { - u32 image_index = context->image_index; - VkCommandBuffer cmd_buffer = context->gfx_command_buffers->data[image_index].handle; - // vulkan_command_buffer* cmd_buffer = &context->gfx_command_buffers->data[context.image_index]; - - vkCmdPushConstants(cmd_buffer, shader->pipeline.layout, VK_SHADER_STAGE_VERTEX_BIT, 0, - sizeof(mat4), &model); - - // vulkan_object_shader_use(context, &context->object_shader); - VkDeviceSize offsets[1] = { 0 }; - vkCmdBindVertexBuffers(cmd_buffer, 0, 1, &context->object_vertex_buffer.handle, - (VkDeviceSize*)offsets); - - vkCmdBindIndexBuffer(cmd_buffer, context->object_index_buffer.handle, 0, VK_INDEX_TYPE_UINT32); - - vkCmdDrawIndexed(cmd_buffer, 36, 1, 0, 0, 0); - // vkCmdDraw(cmd_buffer, 36, 1, 0, 0); -} - -bool select_physical_device(vulkan_context* ctx) { - u32 physical_device_count = 0; - VK_CHECK(vkEnumeratePhysicalDevices(ctx->instance, &physical_device_count, 0)); - if (physical_device_count == 0) { - FATAL("No devices that support vulkan were found"); - return false; - } - TRACE("Number of devices found %d", physical_device_count); - - VkPhysicalDevice physical_devices[physical_device_count]; - VK_CHECK(vkEnumeratePhysicalDevices(ctx->instance, &physical_device_count, physical_devices)); - - for (u32 i = 0; i < physical_device_count; i++) { - VkPhysicalDeviceProperties properties; - vkGetPhysicalDeviceProperties(physical_devices[i], &properties); - - VkPhysicalDeviceFeatures features; - vkGetPhysicalDeviceFeatures(physical_devices[i], &features); - - VkPhysicalDeviceMemoryProperties memory; - vkGetPhysicalDeviceMemoryProperties(physical_devices[i], &memory); - - vulkan_physical_device_requirements requirements = {}; - requirements.graphics = true; - requirements.present = true; - requirements.compute = true; - requirements.transfer = true; - - requirements.sampler_anistropy = true; - requirements.discrete_gpu = true; - requirements.device_ext_names[0] = str8lit(VK_KHR_SWAPCHAIN_EXTENSION_NAME); - requirements.device_ext_name_count = 1; - - vulkan_physical_device_queue_family_info queue_info = {}; - - bool result = physical_device_meets_requirements(physical_devices[i], ctx->surface, &properties, - &features, &requirements, &queue_info, - &ctx->device.swapchain_support); - - if (result) { - INFO("GPU Driver version: %d.%d.%d", VK_VERSION_MAJOR(properties.driverVersion), - VK_VERSION_MINOR(properties.driverVersion), VK_VERSION_PATCH(properties.driverVersion)); - - INFO("Vulkan API version: %d.%d.%d", VK_VERSION_MAJOR(properties.apiVersion), - VK_VERSION_MINOR(properties.apiVersion), VK_VERSION_PATCH(properties.apiVersion)); - - // TODO: print gpu memory information - - // https://youtu.be/6Kj3O2Ov1RU?si=pXfP5NvXXcXjJsrG&t=2439 - - ctx->device.physical_device = physical_devices[i]; - ctx->device.graphics_queue_index = queue_info.graphics_family_index; - ctx->device.present_queue_index = queue_info.present_family_index; - ctx->device.compute_queue_index = queue_info.compute_family_index; - ctx->device.transfer_queue_index = queue_info.transfer_family_index; - ctx->device.properties = properties; - ctx->device.features = features; - ctx->device.memory = memory; - break; - } - } - - if (!ctx->device.physical_device) { - ERROR("No suitable physical devices were found :("); - return false; - } - - INFO("Physical device selected: %s\n", ctx->device.properties.deviceName); - return true; -} - -bool vulkan_device_create(vulkan_context* context) { - // Physical device - NOTE: mutates the context directly - if (!select_physical_device(context)) { - return false; - } - -// Logical device - NOTE: mutates the context directly - -// queues -#define VULKAN_QUEUES_COUNT 2 - const char* queue_names[VULKAN_QUEUES_COUNT] = { - "GRAPHICS", - "TRANSFER", - }; - i32 indices[VULKAN_QUEUES_COUNT] = { - context->device.graphics_queue_index, - context->device.transfer_queue_index, - }; - f32 prio_one = 1.0; - VkDeviceQueueCreateInfo queue_create_info[2]; // HACK: make 2 queues, graphics and transfer - // graphics - queue_create_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queue_create_info[0].queueFamilyIndex = 0; - queue_create_info[0].queueCount = 1; - queue_create_info[0].flags = 0; - queue_create_info[0].pNext = 0; - queue_create_info[0].pQueuePriorities = &prio_one; - // transfer - queue_create_info[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queue_create_info[1].queueFamilyIndex = 1; - queue_create_info[1].queueCount = 1; - queue_create_info[1].flags = 0; - queue_create_info[1].pNext = 0; - queue_create_info[1].pQueuePriorities = &prio_one; - - // for (int i = 0; i < 2; i++) { - // TRACE("Configure %s queue", queue_names[i]); - // queue_create_info[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - // queue_create_info[i].queueFamilyIndex = indices[i]; - // queue_create_info[i].queueCount = 1; // make just one of them - // queue_create_info[i].flags = 0; - // queue_create_info[i].pNext = 0; - // f32 priority = 1.0; - // queue_create_info[i].pQueuePriorities = &priority; +gpu_device gpu_device_create() { + gpu_device device = { 0 }; + // Physical device + // if (!select_physical_device()) { + // return false; // } + INFO("Physical device selected"); - // features + // Features VkPhysicalDeviceFeatures device_features = {}; device_features.samplerAnisotropy = VK_TRUE; // request anistrophy - // device itself + // Logical device + VkDeviceQueueCreateInfo queue_create_info[2]; + //.. VkDeviceCreateInfo device_create_info = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO }; device_create_info.queueCreateInfoCount = VULKAN_QUEUES_COUNT; device_create_info.pQueueCreateInfos = queue_create_info; @@ -844,1147 +47,38 @@ bool vulkan_device_create(vulkan_context* context) { const char* extension_names = VK_KHR_SWAPCHAIN_EXTENSION_NAME; device_create_info.ppEnabledExtensionNames = &extension_names; - // deprecated - device_create_info.enabledLayerCount = 0; - device_create_info.ppEnabledLayerNames = 0; - - VkResult result = vkCreateDevice(context->device.physical_device, &device_create_info, - context->allocator, &context->device.logical_device); + VkResult result = vkCreateDevice(device.physical_device, &device_create_info, context.allocator, + &device.logical_device); if (result != VK_SUCCESS) { - printf("error creating logical device with status %u\n", result); - ERROR_EXIT("Bye bye"); + FATAL("Error creating logical device with status %u\n", result); + exit(1); } INFO("Logical device created"); - // get queues - vkGetDeviceQueue(context->device.logical_device, context->device.graphics_queue_index, 0, - &context->device.graphics_queue); - // vkGetDeviceQueue(context->device.logical_device, context->device.present_queue_index, 0, - // &context->device.present_queue); - // vkGetDeviceQueue(context->device.logical_device, context->device.compute_queue_index, 0, - // &context->device.compute_queue); - vkGetDeviceQueue(context->device.logical_device, context->device.transfer_queue_index, 0, - &context->device.transfer_queue); + // Queues - // create command pool for graphics queue - VkCommandPoolCreateInfo pool_create_info = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO }; - pool_create_info.queueFamilyIndex = context->device.graphics_queue_index; - pool_create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - vkCreateCommandPool(context->device.logical_device, &pool_create_info, context->allocator, - &context->device.gfx_command_pool); - INFO("Created Command Pool") + // Create the command pool - return true; + context.device = device; + return device; } -void vulkan_device_destroy(vulkan_context* context) { - context->device.physical_device = 0; // release - // TODO: reset other memory -} - -bool vulkan_device_detect_depth_format(vulkan_device* device) { - const size_t n_candidates = 3; - VkFormat candidates[3] = { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, - VK_FORMAT_D24_UNORM_S8_UINT }; - u32 flags = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT; - for (u64 i = 0; i < n_candidates; i++) { - VkFormatProperties properties; - vkGetPhysicalDeviceFormatProperties(device->physical_device, candidates[i], &properties); - if ((properties.linearTilingFeatures & flags) == flags) { - device->depth_format = candidates[i]; - return true; - } - if ((properties.optimalTilingFeatures & flags) == flags) { - device->depth_format = candidates[i]; - return true; - } - } - return false; +gpu_renderpass* gpu_renderpass_create() { + // Allocate it + // sets everything up + // return pointer to it } -void vulkan_image_view_create(vulkan_context* context, VkFormat format, vulkan_image* image, - VkImageAspectFlags aspect_flags) { - VkImageViewCreateInfo view_create_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; - view_create_info.image = image->handle; - view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; - view_create_info.format = format; - view_create_info.subresourceRange.aspectMask = aspect_flags; - - view_create_info.subresourceRange.baseMipLevel = 0; - view_create_info.subresourceRange.levelCount = 1; - view_create_info.subresourceRange.baseArrayLayer = 0; - view_create_info.subresourceRange.layerCount = 1; - - vkCreateImageView(context->device.logical_device, &view_create_info, context->allocator, - &image->view); +void encode_set_pipeline(gpu_cmd_encoder* encoder, gpu_pipeline* pipeline) { + // VK_PIPELINE_BIND_POINT_GRAPHICS, &shader->pipeline); + // if (kind == PIPELINE_GRAPHICS) { + // // ... + // } else { + // // ... + // } } -void vulkan_image_transition_layout(vulkan_context* context, vulkan_command_buffer* command_buffer, - vulkan_image* image, VkFormat format, VkImageLayout old_layout, - VkImageLayout new_layout) { - VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; - void vulkan_image_transition_layout( - vulkan_context * context, vulkan_command_buffer * command_buffer, vulkan_image * image, - VkFormat format, VkImageLayout old_layout, VkImageLayout new_layout) { - VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; - barrier.oldLayout = old_layout; - barrier.newLayout = new_layout; - barrier.srcQueueFamilyIndex = context->device.graphics_queue_index; - barrier.dstQueueFamilyIndex = context->device.graphics_queue_index; - barrier.image = image->handle; - barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - barrier.subresourceRange.baseMipLevel = 0; - barrier.subresourceRange.levelCount = 1; - barrier.subresourceRange.baseArrayLayer = 0; - barrier.subresourceRange.layerCount = 1; - barrier.srcAccessMask = 0; - barrier.dstAccessMask = 0; - - VkPipelineStageFlags source_stage; - VkPipelineStageFlags dest_stage; - - if (old_layout == VK_IMAGE_LAYOUT_UNDEFINED && - new_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - barrier.srcAccessMask = 0; - barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - - source_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - dest_stage = VK_PIPELINE_STAGE_TRANSFER_BIT; - - } else if (old_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && - new_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - source_stage = VK_PIPELINE_STAGE_TRANSFER_BIT; - dest_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - } else { - FATAL("Unsupported image layout transition"); - return; - } - - vkCmdPipelineBarrier(command_buffer->handle, source_stage, dest_stage, 0, 0, 0, 0, 0, 1, - &barrier); - } - - void vulkan_image_copy_from_buffer(vulkan_image * image, VkBuffer buffer, - vulkan_command_buffer * command_buffer) { - VkBufferImageCopy region; - region.bufferOffset = 0; - region.bufferRowLength = 0; - region.bufferImageHeight = 0; - region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - region.imageSubresource.mipLevel = 0; - region.imageSubresource.baseArrayLayer = 0; - region.imageSubresource.layerCount = 1; - printf("Image details width: %d height %d\n", image->width, image->height); - region.imageOffset.x = 0; - region.imageOffset.y = 0; - region.imageOffset.z = 0; - region.imageExtent.width = image->width; - region.imageExtent.height = image->height; - region.imageExtent.depth = 1; - - vkCmdCopyBufferToImage(command_buffer->handle, buffer, image->handle, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); - } - - void vulkan_image_create(vulkan_context * context, VkImageType image_type, u32 width, u32 height, - VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, - VkMemoryPropertyFlags memory_flags, bool create_view, - VkImageAspectFlags aspect_flags, vulkan_image* out_image) { - // copy params - out_image->width = width; - out_image->height = height; - - // create info - VkImageCreateInfo image_create_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; - image_create_info.imageType = image_type; - image_create_info.extent.width = width; - image_create_info.extent.height = height; - image_create_info.extent.depth = 1; - image_create_info.mipLevels = 1; - image_create_info.arrayLayers = 1; - image_create_info.format = format; - image_create_info.tiling = tiling; - image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - image_create_info.usage = usage; - image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; - image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - - VK_CHECK(vkCreateImage(context->device.logical_device, &image_create_info, context->allocator, - &out_image->handle)); - - VkMemoryRequirements memory_reqs; - vkGetImageMemoryRequirements(context->device.logical_device, out_image->handle, &memory_reqs); - - i32 memory_type = -1; - VkPhysicalDeviceMemoryProperties memory_properties; - vkGetPhysicalDeviceMemoryProperties(context->device.physical_device, &memory_properties); - - for (u32 i = 0; i < memory_properties.memoryTypeCount; i++) { - // typefilter = memoryTypeBits , prop filter = memory_flags - if (memory_reqs.memoryTypeBits & (1 << i) && - (memory_properties.memoryTypes[i].propertyFlags & memory_flags)) { - memory_type = i; - break; - } - } - - if (memory_type < 0) { - ERROR_EXIT("couldnt find a suitable memory type for the image"); - } - - // allocate memory - VkMemoryAllocateInfo memory_allocate_info = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; - memory_allocate_info.allocationSize = memory_reqs.size; - memory_allocate_info.memoryTypeIndex = memory_type; - vkAllocateMemory(context->device.logical_device, &memory_allocate_info, context->allocator, - &out_image->memory); - - // bind memory - // TODO: maybe bind context->device.logical_device to device at the top of the functions? - vkBindImageMemory(context->device.logical_device, out_image->handle, out_image->memory, 0); - - if (create_view) { - out_image->view = 0; - vulkan_image_view_create(context, format, out_image, aspect_flags); - } - } - - // TODO: vulkan_image_destroy - - void vulkan_framebuffer_create(vulkan_context * context, vulkan_renderpass * renderpass, - u32 width, u32 height, u32 attachment_count, - VkImageView * attachments, vulkan_framebuffer * out_framebuffer) { - out_framebuffer->attachments = malloc(sizeof(VkImageView) * attachment_count); - for (u32 i = 0; i < attachment_count; i++) { - out_framebuffer->attachments[i] = attachments[i]; - } - out_framebuffer->attachment_count = attachment_count; - out_framebuffer->renderpass = renderpass; - - VkFramebufferCreateInfo framebuffer_create_info = { - VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO - }; // TODO - - framebuffer_create_info.renderPass = renderpass->handle; - framebuffer_create_info.attachmentCount = attachment_count; - framebuffer_create_info.pAttachments = out_framebuffer->attachments; - framebuffer_create_info.width = width; - framebuffer_create_info.height = height; - framebuffer_create_info.layers = 1; - - vkCreateFramebuffer(context->device.logical_device, &framebuffer_create_info, - context->allocator, &out_framebuffer->handle); - } - - // TODO: vulkan_framebuffer_destroy - - void vulkan_command_buffer_allocate(vulkan_context * context, VkCommandPool pool, bool is_primary, - vulkan_command_buffer* out_command_buffer) { - VkCommandBufferAllocateInfo allocate_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; - allocate_info.commandPool = pool; - allocate_info.level = - is_primary ? VK_COMMAND_BUFFER_LEVEL_PRIMARY : VK_COMMAND_BUFFER_LEVEL_SECONDARY; - allocate_info.commandBufferCount = 1; - allocate_info.pNext = 0; - - out_command_buffer->state = COMMAND_BUFFER_STATE_NOT_ALLOCATED; - vkAllocateCommandBuffers(context->device.logical_device, &allocate_info, - &out_command_buffer->handle); - out_command_buffer->state = COMMAND_BUFFER_STATE_READY; - } - - void vulkan_command_buffer_free(vulkan_context * context, VkCommandPool pool, - vulkan_command_buffer * out_command_buffer) { - // TODO: implement freeing - } - - void vulkan_command_buffer_begin(vulkan_command_buffer * command_buffer, bool is_single_use, - bool is_renderpass_continue, bool is_simultaneous_use) { - VkCommandBufferBeginInfo begin_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; - begin_info.flags = 0; - if (is_single_use) { - begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - } - // TODO: RENDER_PASS_CONTINUE_BIT & SIMULTANEOUS_USE_BIT - - begin_info.pNext = 0; - begin_info.pInheritanceInfo = 0; - vkBeginCommandBuffer(command_buffer->handle, &begin_info); - - command_buffer->state = COMMAND_BUFFER_STATE_RECORDING; - } - - void vulkan_command_buffer_end(vulkan_command_buffer * command_buffer) { - VK_CHECK(vkEndCommandBuffer(command_buffer->handle)); - command_buffer->state = COMMAND_BUFFER_STATE_RECORDING_ENDED; - } - void vulkan_command_buffer_update_submitted(vulkan_command_buffer * command_buffer) { - command_buffer->state = COMMAND_BUFFER_STATE_SUBMITTED; - } - void vulkan_command_buffer_reset(vulkan_command_buffer * command_buffer) { - command_buffer->state = COMMAND_BUFFER_STATE_READY; - } - - void vulkan_command_buffer_allocate_and_begin_oneshot( - vulkan_context * context, VkCommandPool pool, vulkan_command_buffer * out_command_buffer) { - vulkan_command_buffer_allocate(context, pool, true, out_command_buffer); - vulkan_command_buffer_begin(out_command_buffer, true, false, false); - } - - void vulkan_command_buffer_end_oneshot(vulkan_context * context, VkCommandPool pool, - vulkan_command_buffer * command_buffer, VkQueue queue) { - vulkan_command_buffer_end(command_buffer); - - // submit to queue - VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; - submit_info.commandBufferCount = 1; - submit_info.pCommandBuffers = &command_buffer->handle; - VK_CHECK(vkQueueSubmit(queue, 1, &submit_info, 0)); - // wait for it to finish - VK_CHECK(vkQueueWaitIdle(queue)); - - vulkan_command_buffer_free(context, pool, command_buffer); - } - - void vulkan_buffer_copy_to(vulkan_context * context, VkCommandPool pool, VkFence fence, - VkQueue queue, VkBuffer source, u64 source_offset, VkBuffer dest, - u64 dest_offset, u64 size) { - vkQueueWaitIdle(queue); - - vulkan_command_buffer temp_cmd_buf; - vulkan_command_buffer_allocate_and_begin_oneshot(context, pool, &temp_cmd_buf); - - VkBufferCopy copy_region; - copy_region.srcOffset = source_offset; - copy_region.dstOffset = dest_offset; - copy_region.size = size; - - vkCmdCopyBuffer(temp_cmd_buf.handle, source, dest, 1, ©_region); - - vulkan_command_buffer_end_oneshot(context, pool, &temp_cmd_buf, queue); - } - - void vulkan_swapchain_create(vulkan_context * context, u32 width, u32 height, - vulkan_swapchain * out_swapchain) { - VkExtent2D swapchain_extent = { width, height }; - out_swapchain->max_frames_in_flight = 2; // support triple buffering - - // find a format - bool found; - for (u32 i = 0; i < context->device.swapchain_support.format_count; i++) { - VkSurfaceFormatKHR format = context->device.swapchain_support.formats[i]; - if (format.format == VK_FORMAT_B8G8R8A8_UNORM && - format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { - out_swapchain->image_format = format; - found = true; - break; - } - } - if (!found) { - out_swapchain->image_format = context->device.swapchain_support.formats[0]; - } - - VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR; // guaranteed to be implemented - // TODO: look for mailbox - https://youtu.be/jWKVb_QdSNM?si=bHcd3sEf-M0x3QwH&t=1687 - - // TODO: requery swapchain support - - u32 image_count = context->device.swapchain_support.capabilities.minImageCount; - - VkSwapchainCreateInfoKHR swapchain_create_info = { - VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR - }; - swapchain_create_info.surface = context->surface; - swapchain_create_info.minImageCount = image_count; - swapchain_create_info.imageFormat = out_swapchain->image_format.format; - swapchain_create_info.imageColorSpace = out_swapchain->image_format.colorSpace; - DEBUG("Image extent %d %d\n", swapchain_extent.width, swapchain_extent.height); - swapchain_create_info.imageExtent = swapchain_extent; - swapchain_create_info.imageArrayLayers = 1; - swapchain_create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - swapchain_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - swapchain_create_info.queueFamilyIndexCount = 0; - swapchain_create_info.pQueueFamilyIndices = 0; - - swapchain_create_info.preTransform = - context->device.swapchain_support.capabilities.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; - - TRACE("Create swapchain"); - VK_CHECK(vkCreateSwapchainKHR(context->device.logical_device, &swapchain_create_info, - context->allocator, &out_swapchain->handle)); - - context->current_frame = 0; - - // images - out_swapchain->image_count = 0; - vkGetSwapchainImagesKHR(context->device.logical_device, out_swapchain->handle, - &out_swapchain->image_count, 0); - - if (!out_swapchain->images) { - out_swapchain->images = (VkImage*)malloc(sizeof(VkImage) * out_swapchain->image_count); - } - if (!out_swapchain->views) { - out_swapchain->views = (VkImageView*)malloc(sizeof(VkImage) * out_swapchain->image_count); - } - VK_CHECK(vkGetSwapchainImagesKHR(context->device.logical_device, out_swapchain->handle, - &out_swapchain->image_count, out_swapchain->images)); - - // views - for (int i = 0; i < out_swapchain->image_count; i++) { - VkImageViewCreateInfo view_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; - view_info.image = out_swapchain->images[i]; - view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; - view_info.format = out_swapchain->image_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; - - VK_CHECK(vkCreateImageView(context->device.logical_device, &view_info, context->allocator, - &out_swapchain->views[i])); - } - - // depth attachment - if (!vulkan_device_detect_depth_format(&context->device)) { - ERROR_EXIT("Failed to find a supported depth format"); - } - vulkan_image_create(context, VK_IMAGE_TYPE_2D, swapchain_extent.width, swapchain_extent.height, - context->device.depth_format, VK_IMAGE_TILING_OPTIMAL, - VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, true, VK_IMAGE_ASPECT_DEPTH_BIT, - &out_swapchain->depth_attachment); - INFO("Depth attachment created"); - - INFO("Swapchain created successfully"); - } - - // TODO: swapchain destroy - void vulkan_swapchain_recreate(vulkan_context * context, u32 width, u32 height, - vulkan_swapchain * swapchain) { - // TODO - } - bool vulkan_swapchain_acquire_next_image_index( - vulkan_context * context, vulkan_swapchain * swapchain, u64 timeout_ns, - VkSemaphore image_available_semaphore, VkFence fence, u32 * out_image_index) { - VkResult result = - vkAcquireNextImageKHR(context->device.logical_device, swapchain->handle, timeout_ns, - image_available_semaphore, fence, out_image_index); - - if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { - FATAL("Failed to acquire swapchain image"); - return false; - } - - return true; - } - - void vulkan_swapchain_present(vulkan_context * context, vulkan_swapchain * swapchain, - VkQueue graphics_queue, VkQueue present_queue, - VkSemaphore render_complete_semaphore, u32 present_image_index) { - // return image to swapchain for presentation - VkPresentInfoKHR present_info = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; - present_info.waitSemaphoreCount = 1; - present_info.pWaitSemaphores = &render_complete_semaphore; - present_info.swapchainCount = 1; - present_info.pSwapchains = &swapchain->handle; - present_info.pImageIndices = &present_image_index; - present_info.pResults = 0; - - VkResult result = vkQueuePresentKHR(present_queue, &present_info); - if (result != VK_SUCCESS) { - if (result == VK_SUBOPTIMAL_KHR) { - // WARN("Swapchain suboptimal - maybe resize needed?"); - } else { - FATAL("Failed to present swapchain iamge"); - } - } - - // advance the current frame - context->current_frame = (context->current_frame + 1) % swapchain->max_frames_in_flight; - } - - void vulkan_renderpass_create(vulkan_context * context, vulkan_renderpass * out_renderpass, - vec4 render_area, vec4 clear_colour, f32 depth, u32 stencil) { - out_renderpass->render_area = render_area; - out_renderpass->clear_colour = clear_colour; - out_renderpass->depth = depth; - out_renderpass->stencil = stencil; - - // main subpass - VkSubpassDescription subpass = {}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - - // attachments - u32 attachment_desc_count = 2; - VkAttachmentDescription attachment_descriptions[2]; - - // Colour attachment - VkAttachmentDescription color_attachment; - color_attachment.format = context->swapchain.image_format.format; - color_attachment.samples = VK_SAMPLE_COUNT_1_BIT; - color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - color_attachment.flags = 0; - - attachment_descriptions[0] = color_attachment; - - VkAttachmentReference color_attachment_reference; - color_attachment_reference.attachment = 0; - color_attachment_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &color_attachment_reference; - - // Depth attachment - VkAttachmentDescription depth_attachment; - depth_attachment.format = context->device.depth_format; - depth_attachment.samples = VK_SAMPLE_COUNT_1_BIT; - depth_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - depth_attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - depth_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - depth_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - depth_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - depth_attachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - depth_attachment.flags = 0; - - attachment_descriptions[1] = depth_attachment; - - VkAttachmentReference depth_attachment_reference; - depth_attachment_reference.attachment = 1; - depth_attachment_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - subpass.pDepthStencilAttachment = &depth_attachment_reference; - - // TODO: other attachment styles - - subpass.inputAttachmentCount = 0; - subpass.pInputAttachments = 0; - subpass.pResolveAttachments = 0; - subpass.preserveAttachmentCount = 0; - subpass.preserveAttachmentCount = 0; - - // renderpass dependencies - VkSubpassDependency dependency; - dependency.srcSubpass = VK_SUBPASS_EXTERNAL; - dependency.dstSubpass = 0; - dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.srcAccessMask = 0; - dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.dstAccessMask = - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - dependency.dependencyFlags = 0; - - VkRenderPassCreateInfo render_pass_create_info = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO }; - render_pass_create_info.attachmentCount = attachment_desc_count; - render_pass_create_info.pAttachments = attachment_descriptions; - render_pass_create_info.subpassCount = 1; - render_pass_create_info.pSubpasses = &subpass; - render_pass_create_info.dependencyCount = 1; - render_pass_create_info.pDependencies = &dependency; - render_pass_create_info.pNext = 0; - render_pass_create_info.flags = 0; - - VK_CHECK(vkCreateRenderPass(context->device.logical_device, &render_pass_create_info, - context->allocator, &out_renderpass->handle)); - } - - // TODO: renderpass destroy - - void vulkan_renderpass_begin(vulkan_command_buffer * command_buffer, - vulkan_renderpass * renderpass, VkFramebuffer framebuffer) { - VkRenderPassBeginInfo begin_info = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; - begin_info.renderPass = renderpass->handle; - begin_info.framebuffer = framebuffer; - begin_info.renderArea.offset.x = renderpass->render_area.x; - begin_info.renderArea.offset.y = renderpass->render_area.y; - begin_info.renderArea.extent.width = renderpass->render_area.z; - begin_info.renderArea.extent.height = renderpass->render_area.w; - - VkClearValue clear_values[2]; - memset(&clear_values, 0, sizeof(VkClearValue) * 2); - clear_values[0].color.float32[0] = renderpass->clear_colour.x; - clear_values[0].color.float32[1] = renderpass->clear_colour.y; - clear_values[0].color.float32[2] = renderpass->clear_colour.z; - clear_values[0].color.float32[3] = renderpass->clear_colour.w; - clear_values[1].depthStencil.depth = renderpass->depth; - clear_values[1].depthStencil.stencil = renderpass->stencil; - - begin_info.clearValueCount = 2; - begin_info.pClearValues = clear_values; - - vkCmdBeginRenderPass(command_buffer->handle, &begin_info, VK_SUBPASS_CONTENTS_INLINE); - command_buffer->state = COMMAND_BUFFER_STATE_IN_RENDER_PASS; - } - - void vulkan_renderpass_end(vulkan_command_buffer * command_buffer, - vulkan_renderpass * renderpass) { - vkCmdEndRenderPass(command_buffer->handle); - command_buffer->state = COMMAND_BUFFER_STATE_RECORDING; - } - - bool create_buffers(vulkan_context * context) { - VkMemoryPropertyFlagBits mem_prop_flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; - - const u64 vertex_buffer_size = sizeof(vertex_pos) * 1024 * 1024; - if (!vulkan_buffer_create(context, vertex_buffer_size, - VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | - VK_BUFFER_USAGE_TRANSFER_DST_BIT, - mem_prop_flags, true, &context->object_vertex_buffer)) { - ERROR("couldnt create vertex buffer"); - return false; - } - - context->geometry_vertex_offset = 0; - - const u64 index1_buffer_size = sizeof(u32) * 1024 * 1024; - if (!vulkan_buffer_create(context, index1_buffer_size, - VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | - VK_BUFFER_USAGE_TRANSFER_DST_BIT, - mem_prop_flags, true, &context->object_index_buffer)) { - ERROR("couldnt create vertex buffer"); - return false; - } - context->geometry_index_offset = 0; - - return true; - } - - void create_command_buffers(renderer * ren) { - if (!context.gfx_command_buffers) { - context.gfx_command_buffers = vulkan_command_buffer_darray_new(context.swapchain.image_count); - } - - for (u32 i = 0; i < context.swapchain.image_count; i++) { - vulkan_command_buffer_allocate(&context, context.device.gfx_command_pool, true, - &context.gfx_command_buffers->data[i]); - } - } - - void upload_data_range(vulkan_context * context, VkCommandPool pool, VkFence fence, VkQueue queue, - vulkan_buffer * buffer, u64 offset, u64 size, void* data) { - VkBufferUsageFlags flags = - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; - vulkan_buffer staging; - vulkan_buffer_create(context, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, flags, true, &staging); - // load data into staging buffer - printf("Size: %ld\n", size); - vulkan_buffer_load_data(context, &staging, 0, size, 0, data); - - // copy - vulkan_buffer_copy_to(context, pool, fence, queue, staging.handle, 0, buffer->handle, offset, - size); - - vkDestroyBuffer(context->device.logical_device, staging.handle, context->allocator); - } - - void regenerate_framebuffers(renderer * ren, vulkan_swapchain * swapchain, - vulkan_renderpass * renderpass) { - for (u32 i = 0; i < swapchain->image_count; i++) { - u32 attachment_count = 2; // one for depth, one for colour - - VkImageView attachments[2] = { swapchain->views[i], swapchain->depth_attachment.view }; - - vulkan_framebuffer_create(&context, renderpass, context.framebuffer_width, - context.framebuffer_height, 2, attachments, - &swapchain->framebuffers->data[i]); - } - } - - void vulkan_fence_create(vulkan_context * context, bool create_signaled, - vulkan_fence* out_fence) { - out_fence->is_signaled = create_signaled; - VkFenceCreateInfo fence_create_info = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO }; - if (out_fence->is_signaled) { - fence_create_info.flags = VK_FENCE_CREATE_SIGNALED_BIT; - } - - vkCreateFence(context->device.logical_device, &fence_create_info, context->allocator, - &out_fence->handle); - } - - // TODO: vulkan_fence_destroy - - bool vulkan_fence_wait(vulkan_context * context, vulkan_fence * fence, u64 timeout_ns) { - if (!fence->is_signaled) { - VkResult result = - vkWaitForFences(context->device.logical_device, 1, &fence->handle, true, timeout_ns); - switch (result) { - case VK_SUCCESS: - fence->is_signaled = true; - return true; - case VK_TIMEOUT: - WARN("vk_fence_wait - Timed out"); - break; - default: - ERROR("vk_fence_wait - Unhanlded error type"); - break; - } - } else { - return true; - } - - return false; - } - void vulkan_fence_reset(vulkan_context * context, vulkan_fence * fence) { - if (fence->is_signaled) { - vkResetFences(context->device.logical_device, 1, &fence->handle); - fence->is_signaled = false; - } - } - - bool gfx_backend_init(renderer * ren) { - INFO("loading Vulkan backend"); - - vulkan_state* internal = malloc(sizeof(vulkan_state)); - ren->backend_state = (void*)internal; - - context.allocator = 0; // TODO: custom allocator - - context.framebuffer_width = SCR_WIDTH; - context.framebuffer_height = SCR_HEIGHT; - - // Setup Vulkan instance - VkApplicationInfo app_info = { VK_STRUCTURE_TYPE_APPLICATION_INFO }; - app_info.apiVersion = VK_API_VERSION_1_3; - app_info.pApplicationName = ren->config.window_name; - app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0); - app_info.pEngineName = "Celeritas Engine"; - app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0); - - VkInstanceCreateInfo create_info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO }; - create_info.pApplicationInfo = &app_info; - - cstr_darray* required_extensions = cstr_darray_new(2); - cstr_darray_push(required_extensions, VK_KHR_SURFACE_EXTENSION_NAME); - - plat_get_required_extension_names(required_extensions); - -#if defined(CDEBUG) - cstr_darray_push(required_extensions, VK_EXT_DEBUG_UTILS_EXTENSION_NAME); - - DEBUG("Required extensions:"); - for (u32 i = 0; i < cstr_darray_len(required_extensions); i++) { - DEBUG(" %s", required_extensions->data[i]); - } -#endif - - create_info.enabledExtensionCount = cstr_darray_len(required_extensions); - create_info.ppEnabledExtensionNames = required_extensions->data; - - // Validation layers - create_info.enabledLayerCount = 0; - create_info.ppEnabledLayerNames = 0; -#if defined(CDEBUG) - INFO("Validation layers enabled"); - cstr_darray* desired_validation_layers = cstr_darray_new(1); - cstr_darray_push(desired_validation_layers, "VK_LAYER_KHRONOS_validation"); - - u32 n_available_layers = 0; - VK_CHECK(vkEnumerateInstanceLayerProperties(&n_available_layers, 0)); - TRACE("%d available layers", n_available_layers); - VkLayerProperties_darray* available_layers = VkLayerProperties_darray_new(n_available_layers); - VK_CHECK(vkEnumerateInstanceLayerProperties(&n_available_layers, available_layers->data)); - - for (int i = 0; i < cstr_darray_len(desired_validation_layers); i++) { - // look through layers to make sure we can find the ones we want - bool found = false; - for (int j = 0; j < n_available_layers; j++) { - if (str8_equals(str8_cstr_view(desired_validation_layers->data[i]), - str8_cstr_view(available_layers->data[j].layerName))) { - found = true; - TRACE("Found layer %s", desired_validation_layers->data[i]); - break; - } - } - - if (!found) { - FATAL("Required validation is missing %s", desired_validation_layers->data[i]); - return false; - } - } - INFO("All validation layers are present"); - create_info.enabledLayerCount = cstr_darray_len(desired_validation_layers); - create_info.ppEnabledLayerNames = desired_validation_layers->data; -#endif - - VkResult result = vkCreateInstance(&create_info, NULL, &context.instance); - if (result != VK_SUCCESS) { - ERROR("vkCreateInstance failed with result: %u", result); - return false; - } - - // Debugger -#if defined(CDEBUG) - DEBUG("Creating Vulkan debugger") - u32 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 = vk_debug_callback; - - PFN_vkCreateDebugUtilsMessengerEXT func = - (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(context.instance, - "vkCreateDebugUtilsMessengerEXT"); - assert(func); - VK_CHECK(func(context.instance, &debug_create_info, context.allocator, &context.vk_debugger)); - DEBUG("Vulkan debugger created"); - -#endif - - // Surface creation - DEBUG("Create SurfaceKHR") - VkSurfaceKHR surface; - VK_CHECK(glfwCreateWindowSurface(context.instance, ren->window, NULL, &surface)); - context.surface = surface; - DEBUG("Vulkan surface created") - - // Device creation - if (!vulkan_device_create(&context)) { - FATAL("device creation failed"); - return false; - } - - // Swapchain creation - vulkan_swapchain_create(&context, SCR_WIDTH, SCR_HEIGHT, &context.swapchain); - - // Renderpass creation - vulkan_renderpass_create(&context, &context.main_renderpass, - vec4(0, 0, context.framebuffer_width, context.framebuffer_height), - rgba_to_vec4(COLOUR_SEA_GREEN), 1.0, 0); - - // Framebiffers creation - context.swapchain.framebuffers = vulkan_framebuffer_darray_new(context.swapchain.image_count); - regenerate_framebuffers(ren, &context.swapchain, &context.main_renderpass); - INFO("Framebuffers created"); - - // Command buffers creation - create_command_buffers(ren); - INFO("Command buffers created"); - - // Sync objects - context.image_available_semaphores = - calloc(context.swapchain.max_frames_in_flight, sizeof(VkSemaphore)); - context.queue_complete_semaphores = - calloc(context.swapchain.max_frames_in_flight, sizeof(VkSemaphore)); - context.in_flight_fences = calloc(context.swapchain.max_frames_in_flight, sizeof(vulkan_fence)); - - for (u8 i = 0; i < context.swapchain.max_frames_in_flight; i++) { - VkSemaphoreCreateInfo semaphore_create_info = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; - vkCreateSemaphore(context.device.logical_device, &semaphore_create_info, context.allocator, - &context.image_available_semaphores[i]); - vkCreateSemaphore(context.device.logical_device, &semaphore_create_info, context.allocator, - &context.queue_complete_semaphores[i]); - - // create the fence in a signaled state - vulkan_fence_create(&context, true, &context.in_flight_fences[i]); - } - - context.images_in_flight = - malloc(sizeof(vulkan_fence*) * context.swapchain.max_frames_in_flight); - for (u8 i = 0; i < context.swapchain.max_frames_in_flight; i++) { - context.images_in_flight[i] = 0; - } - INFO("Sync objects created"); - - // Shader modules - vulkan_object_shader_create(&context, &context.object_shader); - INFO("Compiled shader modules") - - create_buffers(&context); - INFO("Created buffers"); - - // TODO: temporary test code - - mesh cube = prim_cube_mesh_create(); - - vertex* verts = malloc(sizeof(vertex) * cube.vertices->len); - - f32 scale = 3.0; - for (size_t i = 0; i < cube.vertices->len; i++) { - verts[i].position = vec3_mult(cube.vertices->data[i].position, scale); - verts[i].normal = cube.vertices->data[i].normal; - verts[i].uv = cube.vertices->data[i].uv; - } - - // const f32 s = 1.0; - // const u32 vert_count = 4; - // vertex_pos verts[4] = { 0 }; - - // verts[0].pos.x = -0.5 * s; - // verts[0].pos.y = -0.5 * s; - - // verts[1].pos.x = 0.5 * s; - // verts[1].pos.y = 0.5 * s; - - // verts[2].pos.x = -0.5 * s; - // verts[2].pos.y = 0.5 * s; - - // verts[3].pos.x = 0.5 * s; - // verts[3].pos.y = -0.5 * s; - - // const u32 index_count = 6; - // u32 indices[6] = { 0, 1, 2, 0, 3, 1 }; - - upload_data_range(&context, context.device.gfx_command_pool, 0, context.device.graphics_queue, - &context.object_vertex_buffer, 0, sizeof(vertex) * cube.vertices->len, verts); - TRACE("Uploaded vertex data"); - upload_data_range(&context, context.device.gfx_command_pool, 0, context.device.graphics_queue, - &context.object_index_buffer, 0, sizeof(u32) * cube.indices_len, - cube.indices); - TRACE("Uploaded index data"); - vertex_darray_free(cube.vertices); - free(cube.indices); - - // upload texture - - // --- End test code - - INFO("Vulkan renderer initialisation succeeded"); - return true; - } - - void gfx_backend_shutdown(renderer * ren) { - DEBUG("Destroying Vulkan debugger"); - if (context.vk_debugger) { - PFN_vkDestroyDebugUtilsMessengerEXT func = - (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr( - context.instance, "vkDestroyDebugUtilsMessengerEXT"); - func(context.instance, context.vk_debugger, context.allocator); - } - - DEBUG("Destroying Vulkan instance..."); - vkDestroyInstance(context.instance, context.allocator); - } - - void backend_begin_frame(renderer * ren, f32 delta_time) { - vulkan_device* device = &context.device; - - // TODO: resize gubbins - - if (!vulkan_fence_wait(&context, &context.in_flight_fences[context.current_frame], - UINT64_MAX)) { - WARN("In-flight fence wait failure"); - } - - if (!vulkan_swapchain_acquire_next_image_index( - &context, &context.swapchain, UINT64_MAX, - context.image_available_semaphores[context.current_frame], 0, &context.image_index)) { - WARN("couldnt acquire swapchain next image"); - } - - vulkan_command_buffer* command_buffer = &context.gfx_command_buffers->data[context.image_index]; - vulkan_command_buffer_reset(command_buffer); - vulkan_command_buffer_begin(command_buffer, false, false, false); - - VkViewport viewport; - viewport.x = 0.0; - viewport.y = 0; - viewport.width = (f32)context.framebuffer_width; - viewport.height = (f32)context.framebuffer_height; - viewport.minDepth = 0.0; - viewport.maxDepth = 1.0; - - VkRect2D scissor; - scissor.offset.x = scissor.offset.y = 0; - scissor.extent.width = context.framebuffer_width; - scissor.extent.height = context.framebuffer_height; - - vkCmdSetViewport(command_buffer->handle, 0, 1, &viewport); - vkCmdSetScissor(command_buffer->handle, 0, 1, &scissor); - - context.main_renderpass.render_area.z = context.framebuffer_width; - context.main_renderpass.render_area.w = context.framebuffer_height; - - vulkan_renderpass_begin(command_buffer, &context.main_renderpass, - context.swapchain.framebuffers->data[context.image_index].handle); - } - - void texture_data_upload(texture * tex) { - printf("Texture name %s\n", tex->name); - tex->backend_data = malloc(sizeof(vulkan_texture_data)); - vulkan_texture_data* data = (vulkan_texture_data*)tex->backend_data; - printf("Texture (%s) details: \n width %d\n height %d\n channel count %d\n", tex->name, - tex->width, tex->height, tex->channel_count); - VkDeviceSize image_size = tex->width * tex->height * max(tex->channel_count, 4); - - TRACE("Creating buffer of size %ld", image_size); - - VkFormat image_format = VK_FORMAT_R8G8B8A8_SRGB; - - VkBufferUsageFlags usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - VkMemoryPropertyFlags memory_prop_flags = - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; - vulkan_buffer staging; - vulkan_buffer_create(&context, image_size, usage, memory_prop_flags, true, &staging); - DEBUG("Uploading image data"); - vulkan_buffer_load_data(&context, &staging, 0, image_size, 0, tex->image_data); - INFO("Loaded iamge data!"); - - vulkan_image_create( - &context, VK_IMAGE_TYPE_2D, tex->width, tex->height, image_format, VK_IMAGE_TILING_OPTIMAL, - VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | - VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, true, VK_IMAGE_ASPECT_COLOR_BIT, &data->image); - - vulkan_command_buffer temp_buffer; - vulkan_command_buffer_allocate_and_begin_oneshot(&context, context.device.gfx_command_pool, - &temp_buffer); - - vulkan_image_transition_layout(&context, &temp_buffer, &data->image, image_format, - VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - - vulkan_image_copy_from_buffer(&data->image, staging.handle, &temp_buffer); - - vulkan_image_transition_layout(&context, &temp_buffer, &data->image, image_format, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - - vulkan_command_buffer_end_oneshot(&context, context.device.gfx_command_pool, &temp_buffer, - context.device.graphics_queue); - - VkSamplerCreateInfo sampler_info = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; - sampler_info.magFilter = VK_FILTER_LINEAR; - sampler_info.minFilter = VK_FILTER_LINEAR; - sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; - sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; - sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; - sampler_info.anisotropyEnable = VK_TRUE; - sampler_info.maxAnisotropy = 16; - sampler_info.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; - sampler_info.unnormalizedCoordinates = VK_FALSE; - sampler_info.compareEnable = VK_FALSE; - sampler_info.compareOp = VK_COMPARE_OP_ALWAYS; - sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - sampler_info.mipLodBias = 0.0; - sampler_info.minLod = 0.0; - sampler_info.maxLod = 0.0; - - VkResult res = vkCreateSampler(context.device.logical_device, &sampler_info, context.allocator, - &data->sampler); - if (res != VK_SUCCESS) { - ERROR("Error creating texture sampler for image %s", tex->name); - return; - } - - tex->image_data = (void*)data; - } - - // TODO: destroy texture - - void backend_end_frame(renderer * ren, f32 delta_time) { - vulkan_command_buffer* command_buffer = &context.gfx_command_buffers->data[context.image_index]; - - vulkan_renderpass_end(command_buffer, &context.main_renderpass); - - vulkan_command_buffer_end(command_buffer); - - // TODO: wait on fence - https://youtu.be/hRL71D1f3pU?si=nLJx-ZsemDBeQiQ1&t=1037 - - context.images_in_flight[context.image_index] = - &context.in_flight_fences[context.current_frame]; - - vulkan_fence_reset(&context, &context.in_flight_fences[context.current_frame]); - - VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; - submit_info.commandBufferCount = 1; - submit_info.pCommandBuffers = &command_buffer->handle; - submit_info.signalSemaphoreCount = 1; - submit_info.pSignalSemaphores = &context.queue_complete_semaphores[context.current_frame]; - submit_info.waitSemaphoreCount = 1; - submit_info.pWaitSemaphores = &context.image_available_semaphores[context.current_frame]; - - VkPipelineStageFlags flags[1] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; - submit_info.pWaitDstStageMask = flags; - - VkResult result = vkQueueSubmit(context.device.graphics_queue, 1, &submit_info, - context.in_flight_fences[context.current_frame].handle); - - if (result != VK_SUCCESS) { - ERROR("queue submission failed. fark."); - } - - vulkan_command_buffer_update_submitted(command_buffer); - - vulkan_swapchain_present( - &context, &context.swapchain, context.device.graphics_queue, context.device.graphics_queue, - context.queue_complete_semaphores[context.current_frame], context.image_index); - } - - void gfx_backend_draw_frame(renderer * ren, camera * cam, mat4 model, texture * tex) { - backend_begin_frame(ren, 16.0); - - mat4 proj; - mat4 view; - - camera_view_projection(cam, SCR_HEIGHT, SCR_WIDTH, &view, &proj); - - context.object_shader.texture_data = (vulkan_texture_data*)tex->image_data; - gfx_backend_update_global_state(proj, view, cam->position, vec4(1.0, 1.0, 1.0, 1.0), 0); - - vulkan_object_shader_update_object(&context, &context.object_shader, model); - - backend_end_frame(ren, 16.0); - } - - void gfx_backend_update_global_state(mat4 projection, mat4 view, vec3 view_pos, - vec4 ambient_colour, i32 mode) { - vulkan_object_shader_use(&context, &context.object_shader); - - vulkan_object_shader_update_global_state(&context, &context.object_shader); - context.object_shader.global_ubo.projection = projection; - context.object_shader.global_ubo.view = view; - // TODO: other UBO properties - } - - void clear_screen(vec3 colour) {} - - void bind_texture(shader s, texture * tex, u32 slot) {} - void bind_mesh_vertex_buffer(void* backend, mesh* mesh) {} - void draw_primitives(cel_primitive_topology primitive, u32 start_index, u32 count) {} - - shader shader_create_separate(const char* vert_shader, const char* frag_shader) {} - void set_shader(shader s) {} - - void uniform_vec3f(u32 program_id, const char* uniform_name, vec3* value) {} - void uniform_f32(u32 program_id, const char* uniform_name, f32 value) {} - void uniform_i32(u32 program_id, const char* uniform_name, i32 value) {} - void uniform_mat4f(u32 program_id, const char* uniform_name, mat4* value) {} - - VKAPI_ATTR VkBool32 VKAPI_CALL vk_debug_callback( - VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT flags, - const VkDebugUtilsMessengerCallbackDataEXT* callback_data, void* user_data) { - switch (severity) { - default: - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: - ERROR("%s", callback_data->pMessage); - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: - WARN("%s", callback_data->pMessage); - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: - INFO("%s", callback_data->pMessage); - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: - TRACE("%s", callback_data->pMessage); - break; - } - return VK_FALSE; - } - -#endif \ No newline at end of file +// --- Drawing +inline void encode_draw_indexed(gpu_cmd_encoder* encoder, u64 index_count) { + vkCmdDrawIndexed(encoder->cmd_buffer, index_count, 1, 0, 0, 0); +} \ No newline at end of file diff --git a/src/renderer/backends/backend_vulkan.h b/src/renderer/backends/backend_vulkan.h new file mode 100644 index 0000000..05f043e --- /dev/null +++ b/src/renderer/backends/backend_vulkan.h @@ -0,0 +1,27 @@ +#pragma once +#include "defines.h" + +#define GPU_SWAPCHAIN_IMG_COUNT 2 + +typedef struct gpu_swapchain { +} gpu_swapchain; +typedef struct gpu_device { + // In Vulkan we store both physical and logical device here + VkPhysicalDevice physical_device; + VkDevice logical_device; + VkPhysicalDeviceProperties properties; + VkPhysicalDeviceFeatures features; + VkPhysicalDeviceMemoryProperties memory; + VkCommandPool pool; +} gpu_device; +typedef struct gpu_pipeline { +} gpu_pipeline; + +typedef struct gpu_renderpass { + VkRenderPass vk_handle; + VkFramebuffer framebuffers[GPU_SWAPCHAIN_IMG_COUNT]; +} gpu_renderpass; + +typedef struct gpu_cmd_encoder { + VkCommandBuffer cmd_buffer; +} gpu_cmd_encoder; \ No newline at end of file diff --git a/src/renderer/cleanroom/backend_vulkan.c b/src/renderer/cleanroom/backend_vulkan.c deleted file mode 100644 index 71a09f3..0000000 --- a/src/renderer/cleanroom/backend_vulkan.c +++ /dev/null @@ -1,65 +0,0 @@ -#include -#include "ral.h" -#include "types.h" -// #include "render_types.h" - -#define VULKAN_QUEUES_COUNT 2 -const char* queue_names[VULKAN_QUEUES_COUNT] = { "GRAPHICS", "TRANSFER" }; - -typedef struct gpu_device { -} gpu_device; - -typedef struct vulkan_context { - gpu_device device; - - VkInstance instance; - -} vulkan_context; - -static vulkan_context context; - -static bool select_physical_device(gpu_device* out_device) {} - -bool gpu_device_create(gpu_device* out_device) { - // Physical device - if (!select_physical_device(out_device)) { - return false; - } - INFO("Physical device selected"); - - // Logical device - VkDeviceQueueCreateInfo queue_create_info[2]; - //.. - VkDeviceCreateInfo device_create_info = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO }; - - VkResult result = vkCreateDevice(); - if (result != VK_SUCCESS) { - FATAL("Error creating logical device with status %u\n", result); - exit(1); - } - INFO("Logical device created"); - - // Queues - - // Create the command pool -} - -gpu_renderpass* gpu_renderpass_create() { - // Allocate it - // sets everything up - // return pointer to it -} - -void encode_set_pipeline(gpu_cmd_encoder* encoder, pipeline_type kind, gpu_pipeline* pipeline) { - // VK_PIPELINE_BIND_POINT_GRAPHICS, &shader->pipeline); - if (kind == PIPELINE_GRAPHICS) { - // ... - } else { - // ... - } -} - -// --- Drawing -inline void encode_draw_indexed(gpu_cmd_encoder* encoder, u64 index_count) { - vkCmdDrawIndexed(encoder->cmd_buffer, index_count, 1, 0, 0, 0); -} \ No newline at end of file diff --git a/src/renderer/cleanroom/backend_vulkan.h b/src/renderer/cleanroom/backend_vulkan.h deleted file mode 100644 index c8d5777..0000000 --- a/src/renderer/cleanroom/backend_vulkan.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once -#include "cleanroom/ral.h" - -#define GPU_SWAPCHAIN_IMG_COUNT 2 - -typedef struct gpu_swapchain { -} gpu_swapchain; -typedef struct gpu_device { - // In Vulkan we store both physical and logical device here - VkPhysicalDevice physical_device; - VkDevice logical_device; - VkPhysicalDeviceProperties properties; - VkPhysicalDeviceFeatures features; - VkPhysicalDeviceMemoryProperties memory; - VkCommandPool pool; -} gpu_device; -typedef struct gpu_pipeline { -} gpu_pipeline; - -typedef struct gpu_renderpass { - VkRenderPass vk_handle; - VkFramebuffer framebuffers[GPU_SWAPCHAIN_IMG_COUNT]; - u32 -} gpu_renderpass; - -typedef struct gpu_cmd_encoder { - VkCommandBuffer cmd_buffer; -} gpu_cmd_encoder; \ No newline at end of file diff --git a/src/renderer/cleanroom/immediate.c b/src/renderer/cleanroom/immediate.c deleted file mode 100644 index 8e4bf7e..0000000 --- a/src/renderer/cleanroom/immediate.c +++ /dev/null @@ -1,18 +0,0 @@ -#include "immediate.h" -#include "maths.h" -#include "primitives.h" -#include "render.h" -#include "types.h" - -void imm_draw_sphere(vec3 pos, f32 radius, vec4 colour) { - // Create the vertices - geometry_data geometry = geo_create_uvsphere(radius, 16, 16); - geo_set_vertex_colours(&geometry, colour); - - // Upload to GPU - mat4 model = mat4_translation(pos); - - // Set pipeline - - // Draw -} \ No newline at end of file diff --git a/src/renderer/cleanroom/immediate.h b/src/renderer/cleanroom/immediate.h deleted file mode 100644 index 6d93c53..0000000 --- a/src/renderer/cleanroom/immediate.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "geometry.h" -#include "maths_types.h" - -// 3. SIMA (simplified immediate mode api) / render.h -// - dont need to worry about uploading mesh data -// - very useful for debugging -void imm_draw_cuboid(vec3 pos, quat rotation, f32x3 extents, vec4 colour); -void imm_draw_sphere(vec3 pos, f32 radius, vec4 colour); -void imm_draw_camera_frustum(); - -// static void imm_draw_model( -// const char* model_filepath); // tracks internally whether the model is loaded - -// static void imm_draw_model(const char* model_filepath) { - // check that model is loaded - // if not loaded, load model and upload to gpu - LRU cache for models - // else submit draw call -// } \ No newline at end of file diff --git a/src/renderer/cleanroom/types.h b/src/renderer/cleanroom/types.h index 0a28b1c..7360ebe 100644 --- a/src/renderer/cleanroom/types.h +++ b/src/renderer/cleanroom/types.h @@ -4,63 +4,3 @@ #include "maths_types.h" #include "str.h" #include "render_types.h" - -// typedef struct transform_hierarchy { -// } transform_hierarchy; - - -/* - - render_types.h - - ral_types.h - - ral.h - - render.h ? -*/ - -/* render_types */ -typedef struct model pbr_material; -typedef struct model bp_material; // blinn-phong - - -// ? How to tie together materials and shaders - -// Three registers -// 1. low level graphics api calls "ral" -// 2. higher level render calls -// 3. simplified immediate mode API - -// 3 - you don't need to know how the renderer works at all -// 2 - you need to know how the overall renderer is designed -// 1 - you need to understand graphics API specifics - -/* ral.h */ - -// command buffer gubbins - -/* --- Backends */ - -// struct vulkan_backend { -// gpu_pipeline static_opaque_pipeline; -// gpu_pipeline skinned_opaque_pipeline; -// }; - -/* --- Renderer layer */ -/* render.h */ - - -// Drawing - -// void draw_mesh(gpu_cmd_encoder* encoder, mesh* mesh) { -// encode_set_vertex_buffer(encoder, mesh->vertex_buffer); -// encode_set_index_buffer(encoder, mesh->index_buffer); -// encode_draw_indexed(encoder, mesh->index_count) -// // vkCmdDrawIndexed -// } - -// void draw_scene(arena* frame, model_darray* models, renderer* ren, camera* camera, -// transform_hierarchy* tfh, scene* scene) { -// // set the pipeline first -// encode_set_pipeline() -// // in open this sets the shader -// // in vulkan it sets the whole pipeline - -// } \ No newline at end of file diff --git a/src/renderer/immediate.c b/src/renderer/immediate.c new file mode 100644 index 0000000..e76d102 --- /dev/null +++ b/src/renderer/immediate.c @@ -0,0 +1,18 @@ +#include "immediate.h" +#include "maths.h" +#include "primitives.h" +#include "ral_types.h" +#include "render.h" + +void imm_draw_sphere(vec3 pos, f32 radius, vec4 colour) { + // Create the vertices + geometry_data geometry = geo_create_uvsphere(radius, 16, 16); + geo_set_vertex_colours(&geometry, colour); + + // Upload to GPU + mat4 model = mat4_translation(pos); + + // Set pipeline + + // Draw +} \ No newline at end of file diff --git a/src/renderer/immediate.h b/src/renderer/immediate.h new file mode 100644 index 0000000..6d93c53 --- /dev/null +++ b/src/renderer/immediate.h @@ -0,0 +1,20 @@ +#pragma once + +#include "geometry.h" +#include "maths_types.h" + +// 3. SIMA (simplified immediate mode api) / render.h +// - dont need to worry about uploading mesh data +// - very useful for debugging +void imm_draw_cuboid(vec3 pos, quat rotation, f32x3 extents, vec4 colour); +void imm_draw_sphere(vec3 pos, f32 radius, vec4 colour); +void imm_draw_camera_frustum(); + +// static void imm_draw_model( +// const char* model_filepath); // tracks internally whether the model is loaded + +// static void imm_draw_model(const char* model_filepath) { + // check that model is loaded + // if not loaded, load model and upload to gpu - LRU cache for models + // else submit draw call +// } \ No newline at end of file diff --git a/src/renderer/ral.h b/src/renderer/ral.h index fd83e76..fb77f0a 100644 --- a/src/renderer/ral.h +++ b/src/renderer/ral.h @@ -23,10 +23,10 @@ typedef struct gpu_renderpass gpu_renderpass; typedef struct gpu_cmd_encoder gpu_cmd_encoder; // Recording typedef struct gpu_cmd_buffer gpu_cmd_buffer; // Ready for submission -enum pipeline_kind { - GRAPHICS, - COMPUTE, -}; +typedef enum pipeline_kind { + PIPELINE_GRAPHICS, + PIPELINE_COMPUTE, +} pipeline_kind; typedef struct shader_desc { const char* debug_name; @@ -40,7 +40,7 @@ struct pipeline_desc { }; // lifecycle functions -gpu_device* gpu_device_create(); +gpu_device gpu_device_create(); void gpu_device_destroy(); gpu_renderpass* gpu_renderpass_create(); diff --git a/src/renderer/ral_types.h b/src/renderer/ral_types.h index 9b1ef62..a20e600 100644 --- a/src/renderer/ral_types.h +++ b/src/renderer/ral_types.h @@ -90,4 +90,48 @@ typedef union vertex { KITC_DECL_TYPED_ARRAY(vertex) KITC_DECL_TYPED_ARRAY(u32) #define TYPED_VERTEX_ARRAY -#endif \ No newline at end of file +#endif + +// ? How to tie together materials and shaders + +// Three registers +// 1. low level graphics api calls "ral" +// 2. higher level render calls +// 3. simplified immediate mode API + +// 3 - you don't need to know how the renderer works at all +// 2 - you need to know how the overall renderer is designed +// 1 - you need to understand graphics API specifics + +/* ral.h */ + +// command buffer gubbins + +/* --- Backends */ + +// struct vulkan_backend { +// gpu_pipeline static_opaque_pipeline; +// gpu_pipeline skinned_opaque_pipeline; +// }; + +/* --- Renderer layer */ +/* render.h */ + + +// Drawing + +// void draw_mesh(gpu_cmd_encoder* encoder, mesh* mesh) { +// encode_set_vertex_buffer(encoder, mesh->vertex_buffer); +// encode_set_index_buffer(encoder, mesh->index_buffer); +// encode_draw_indexed(encoder, mesh->index_count) +// // vkCmdDrawIndexed +// } + +// void draw_scene(arena* frame, model_darray* models, renderer* ren, camera* camera, +// transform_hierarchy* tfh, scene* scene) { +// // set the pipeline first +// encode_set_pipeline() +// // in open this sets the shader +// // in vulkan it sets the whole pipeline + +// } \ No newline at end of file diff --git a/src/renderer/render.c b/src/renderer/render.c index d6b77c0..034585a 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -52,4 +52,6 @@ void render_frame_begin(renderer* ren) {} void render_frame_end(renderer* ren) {} void render_frame_draw(renderer* ren) {} -void gfx_backend_draw_frame(renderer* ren, camera* camera, mat4 model, texture* tex) {} \ No newline at end of file +void gfx_backend_draw_frame(renderer* ren, camera* camera, mat4 model, texture* tex) {} + +void geo_set_vertex_colours(geometry_data* geo, vec4 colour) {} \ No newline at end of file diff --git a/src/renderer/render.h b/src/renderer/render.h index 0aeeac2..a5a5928 100644 --- a/src/renderer/render.h +++ b/src/renderer/render.h @@ -40,4 +40,6 @@ void shader_hot_reload(const char* filepath); // models and meshes are implemented **in terms of the above** mesh mesh_create(geometry_data* geometry); -model_handle model_load(const char* debug_name, const char* filepath); \ No newline at end of file +model_handle model_load(const char* debug_name, const char* filepath); + +void geo_set_vertex_colours(geometry_data* geo, vec4 colour); \ No newline at end of file diff --git a/src/renderer/render_types.h b/src/renderer/render_types.h index bc9692f..5faefad 100644 --- a/src/renderer/render_types.h +++ b/src/renderer/render_types.h @@ -41,8 +41,6 @@ typedef struct geometry_data { vec3 colour; /** Optional: set vertex colours */ } geometry_data; -void geo_set_vertex_colours(geometry_data* geo, vec4 colour); - typedef struct mesh { buffer_handle vertex_buffer; buffer_handle index_buffer; diff --git a/src/systems/screenspace.h b/src/systems/screenspace.h index f513148..5f0c579 100644 --- a/src/systems/screenspace.h +++ b/src/systems/screenspace.h @@ -25,7 +25,7 @@ struct draw_circle { /** @brief Tagged union that represents a UI shape to be drawn. */ typedef struct draw_cmd { - enum { RECT, CIRCLE } draw_cmd_type; + enum { DRAW_RECT, CIRCLE } draw_cmd_type; union { struct draw_rect rect; struct draw_circle circle; diff --git a/xmake.lua b/xmake.lua index 0fc042c..6fee04a 100644 --- a/xmake.lua +++ b/xmake.lua @@ -1,6 +1,6 @@ set_project("celeritas") set_version("0.1.0") -set_config("cc", "clang") +set_config("cc", "clang-cl") add_rules("mode.debug", "mode.release") -- we have two modes: debug & release @@ -25,6 +25,8 @@ if is_plat("linux") then elseif is_plat("windows") then add_defines("CEL_PLATFORM_WINDOWS") add_syslinks("user32", "gdi32", "kernel32", "shell32") + add_requires("vulkansdk", {system=true}) + -- add_links("pthreadVC2-w64") elseif is_plat("macosx") then add_defines("CEL_PLATFORM_MAC") @@ -55,6 +57,7 @@ local core_sources = { "deps/glad/src/glad.c", "src/*.c", -- "src/logos/*.c", + "src/maths/*.c", "src/platform/*.c", "src/renderer/*.c", "src/renderer/backends/*.c", @@ -84,6 +87,8 @@ rule("compile_glsl_frag_shaders") end) -- TODO: Metal shaders compilation +-- + -- common configuration for both static and shared libraries target("core_config") set_kind("static") -- kind is required but you can ignore it since it's just for common settings @@ -100,7 +105,6 @@ target("core_config") add_includedirs("src/platform/", {public = true}) add_includedirs("src/renderer/", {public = true}) add_includedirs("src/renderer/backends/", {public = true}) - -- add_includedirs("src/renderer/cleanroom/", {public = true}) add_includedirs("src/resources/", {public = true}) add_includedirs("src/std/", {public = true}) add_includedirs("src/std/containers", {public = true}) @@ -111,6 +115,11 @@ target("core_config") -- add_files("assets/shaders/object.vert") -- add_files("assets/shaders/object.frag") -- add_files("assets/shaders/*.frag") + if is_plat("windows") then + add_includedirs("$(env VULKAN_SDK)/Include", {public = true}) + add_linkdirs("$(env VULKAN_SDK)/Lib", {public = true}) + add_links("vulkan-1") + end set_default(false) -- prevents standalone building of this target target("core_static") @@ -132,7 +141,6 @@ target("core_shared") if is_plat("windows") then add_links("msvcrt", "legacy_stdio_definitions") -- for release builds add_links("msvcrtd", "legacy_stdio_definitions") -- for debug builds - -- add_links("pthreadVC2-w64") end target("main_loop") -- cgit v1.2.3-70-g09d2 From 5e382c2095bc4891e2952ba87609f2796f2248ad Mon Sep 17 00:00:00 2001 From: omnisci3nce Date: Sun, 28 Apr 2024 11:02:21 +1000 Subject: start porting vulkan code to new RAL --- examples/triangle/ex_triangle.c | 34 ++++++++ src/log.h | 12 +-- src/renderer/backends/backend_vulkan.c | 143 +++++++++++++++++++++++++++++---- src/renderer/backends/backend_vulkan.h | 11 +++ src/renderer/ral.h | 31 +++++-- src/renderer/ral_types.h | 14 +++- src/renderer/render.c | 25 +++++- src/renderer/render_types.h | 5 +- src/std/buf.h | 17 ++++ xmake.lua | 11 ++- 10 files changed, 267 insertions(+), 36 deletions(-) create mode 100644 examples/triangle/ex_triangle.c create mode 100644 src/std/buf.h (limited to 'src/renderer/render.c') diff --git a/examples/triangle/ex_triangle.c b/examples/triangle/ex_triangle.c new file mode 100644 index 0000000..4e31313 --- /dev/null +++ b/examples/triangle/ex_triangle.c @@ -0,0 +1,34 @@ +#include + +#include "camera.h" +#include "core.h" +#include "maths.h" +#include "render.h" + +int main() { + core* core = core_bringup(); + + camera camera = camera_create(vec3_create(0, 0, 20), VEC3_NEG_Z, VEC3_Y, deg_to_rad(45.0)); + + // Main loop + while (!glfwWindowShouldClose(core->renderer.window)) { + input_update(&core->input); + // threadpool_process_results(&core->threadpool, 1); + + render_frame_begin(&core->renderer); + + static f32 x = 0.0; + x += 0.01; + mat4 model = mat4_translation(vec3(x, 0, 0)); + + gfx_backend_draw_frame(&core->renderer, &camera, model, NULL); + + // insert work here + + render_frame_end(&core->renderer); + glfwSwapBuffers(core->renderer.window); + glfwPollEvents(); + } + + return 0; +} diff --git a/src/log.h b/src/log.h index b0c355b..d954684 100644 --- a/src/log.h +++ b/src/log.h @@ -38,19 +38,19 @@ void logger_shutdown(); void log_output(log_level level, const char* message, ...); -#define FATAL(message, ...) log_output(LOG_LEVEL_FATAL, message, ##__VA_ARGS__); -#define ERROR(message, ...) log_output(LOG_LEVEL_ERROR, message, ##__VA_ARGS__); -#define WARN(message, ...) log_output(LOG_LEVEL_WARN, message, ##__VA_ARGS__); -#define INFO(message, ...) log_output(LOG_LEVEL_INFO, message, ##__VA_ARGS__); +#define FATAL(message, ...) log_output(LOG_LEVEL_FATAL, message, ##__VA_ARGS__) +#define ERROR(message, ...) log_output(LOG_LEVEL_ERROR, message, ##__VA_ARGS__) +#define WARN(message, ...) log_output(LOG_LEVEL_WARN, message, ##__VA_ARGS__) +#define INFO(message, ...) log_output(LOG_LEVEL_INFO, message, ##__VA_ARGS__) #if LOG_DEBUG_ENABLED == 1 -#define DEBUG(message, ...) log_output(LOG_LEVEL_DEBUG, message, ##__VA_ARGS__); +#define DEBUG(message, ...) log_output(LOG_LEVEL_DEBUG, message, ##__VA_ARGS__) #else #define DEBUG(message, ...) #endif #if LOG_TRACE_ENABLED == 1 -#define TRACE(message, ...) log_output(LOG_LEVEL_TRACE, message, ##__VA_ARGS__); +#define TRACE(message, ...) log_output(LOG_LEVEL_TRACE, message, ##__VA_ARGS__) #else #define TRACE(message, ...) #endif \ No newline at end of file diff --git a/src/renderer/backends/backend_vulkan.c b/src/renderer/backends/backend_vulkan.c index b66aeca..2502dd2 100644 --- a/src/renderer/backends/backend_vulkan.c +++ b/src/renderer/backends/backend_vulkan.c @@ -1,39 +1,109 @@ +#include +#include #include #include #include #include "backend_vulkan.h" +#include "mem.h" +#include "vulkan_helpers.h" #include "defines.h" #include "log.h" #include "ral.h" #include "ral_types.h" +// TEMP +#define SCREEN_WIDTH 1000 +#define SCREEN_HEIGHT 1000 + #define VULKAN_QUEUES_COUNT 2 const char* queue_names[VULKAN_QUEUES_COUNT] = { "GRAPHICS", "TRANSFER" }; typedef struct vulkan_context { - gpu_device device; + VkInstance instance; VkAllocationCallbacks* allocator; + VkSurfaceKHR surface; - VkInstance instance; + arena temp_arena; + gpu_device* device; + gpu_swapchain* swapchain; + u32 screen_width; + u32 screen_height; } vulkan_context; static vulkan_context context; -static bool select_physical_device(gpu_device* out_device) {} +// --- Function forward declarations + +/** @brief Enumerates and selects the most appropriate graphics device */ +bool select_physical_device(gpu_device* out_device); +/** @brief Helper function for creating array of all extensions we want */ +cstr_darray* get_all_extensions(); + +bool gpu_backend_init(const char* window_name, GLFWwindow* window) { + context.allocator = 0; // TODO: use an allocator + context.screen_width = SCREEN_WIDTH; + context.screen_height = SCREEN_HEIGHT; + + // Create an allocator + size_t temp_arena_size = 1024 * 1024; + arena_create(malloc(temp_arena_size), temp_arena_size); + + // Setup Vulkan instance + VkApplicationInfo app_info = { VK_STRUCTURE_TYPE_APPLICATION_INFO }; + app_info.apiVersion = VK_API_VERSION_1_3; + app_info.pApplicationName = window_name; + app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0); + app_info.pEngineName = "Celeritas Engine"; + app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0); + + VkInstanceCreateInfo create_info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO }; + create_info.pApplicationInfo = &app_info; + + // Extensions + // FIXME: Use my own extension choices + // cstr_darray* required_extensions = cstr_darray_new(2); + // cstr_darray_push(required_extensions, VK_KHR_SURFACE_EXTENSION_NAME); + // create_info.enabledExtensionCount = cstr_darray_len(required_extensions); + // create_info.ppEnabledExtensionNames = required_extensions->data; + uint32_t count; + const char** extensions = glfwGetRequiredInstanceExtensions(&count); + create_info.enabledExtensionCount = count; + create_info.ppEnabledExtensionNames = extensions; + + // TODO: Validation layers + create_info.enabledLayerCount = 0; + create_info.ppEnabledLayerNames = NULL; + + VkResult result = vkCreateInstance(&create_info, NULL, &context.instance); + if (result != VK_SUCCESS) { + ERROR("vkCreateInstance failed with result: %u", result); + return false; + } + TRACE("Vulkan Instance created"); + + // Surface creation + VkSurfaceKHR surface; + VK_CHECK(glfwCreateWindowSurface(context.instance, window, NULL, &surface)); + context.surface = surface; + TRACE("Vulkan Surface created"); -gpu_device gpu_device_create() { - gpu_device device = { 0 }; + return true; +} + +void gpu_backend_shutdown() { arena_free_storage(&context.temp_arena); } + +bool gpu_device_create(gpu_device* out_device) { // Physical device - // if (!select_physical_device()) { - // return false; - // } - INFO("Physical device selected"); + if (!select_physical_device(out_device)) { + return false; + } + TRACE("Physical device selected"); // Features - VkPhysicalDeviceFeatures device_features = {}; + VkPhysicalDeviceFeatures device_features = { 0 }; device_features.samplerAnisotropy = VK_TRUE; // request anistrophy // Logical device @@ -47,20 +117,59 @@ gpu_device gpu_device_create() { const char* extension_names = VK_KHR_SWAPCHAIN_EXTENSION_NAME; device_create_info.ppEnabledExtensionNames = &extension_names; - VkResult result = vkCreateDevice(device.physical_device, &device_create_info, context.allocator, - &device.logical_device); + VkResult result = vkCreateDevice(out_device->physical_device, &device_create_info, + context.allocator, &out_device->logical_device); if (result != VK_SUCCESS) { FATAL("Error creating logical device with status %u\n", result); exit(1); } - INFO("Logical device created"); + TRACE("Logical device created"); // Queues // Create the command pool - context.device = device; - return device; + return true; +} + +bool gpu_swapchain_create(gpu_swapchain* out_swapchain) { + VkExtent2D swapchain_extent = { context.screen_width, context.screen_height }; + + // find a format + + VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR; // guaranteed to be implemented + + VkSwapchainCreateInfoKHR swapchain_create_info = { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR }; + + // swapchain_create_info.minImageCount = + + VK_CHECK(vkCreateSwapchainKHR(context.device->logical_device, &swapchain_create_info, + context.allocator, &out_swapchain->handle)); + TRACE("Vulkan Swapchain created"); +} + +gpu_pipeline* gpu_graphics_pipeline_create(struct graphics_pipeline_desc description) { + VkViewport viewport = { .x = 0, + .y = 0, + .width = (f32)context.screen_width, + .height = (f32)context.screen_height, + .minDepth = 0.0, + .maxDepth = 1.0 }; + VkRect2D scissor = { .offset = { .x = 0, .y = 0 }, + .extent = { .width = context.screen_width, + .height = context.screen_height } }; + + // TODO: Attributes + + // TODO: layouts + + VkPipelineViewportStateCreateInfo viewport_state = { + VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO + }; + viewport_state.viewportCount = 1; + viewport_state.pViewports = &viewport; + viewport_state.scissorCount = 1; + viewport_state.pScissors = &scissor; } gpu_renderpass* gpu_renderpass_create() { @@ -81,4 +190,6 @@ void encode_set_pipeline(gpu_cmd_encoder* encoder, gpu_pipeline* pipeline) { // --- Drawing inline void encode_draw_indexed(gpu_cmd_encoder* encoder, u64 index_count) { vkCmdDrawIndexed(encoder->cmd_buffer, index_count, 1, 0, 0, 0); -} \ No newline at end of file +} + +bool select_physical_device(gpu_device* out_device) {} \ No newline at end of file diff --git a/src/renderer/backends/backend_vulkan.h b/src/renderer/backends/backend_vulkan.h index 05f043e..dfe6a0f 100644 --- a/src/renderer/backends/backend_vulkan.h +++ b/src/renderer/backends/backend_vulkan.h @@ -1,9 +1,20 @@ #pragma once +#include +#include +#include + #include "defines.h" #define GPU_SWAPCHAIN_IMG_COUNT 2 +/* +Conventions: + - Place the 'handle' as the first field of a struct + - Vulkan specific data goes at the top, followed by our internal data +*/ + typedef struct gpu_swapchain { + VkSwapchainKHR handle; } gpu_swapchain; typedef struct gpu_device { // In Vulkan we store both physical and logical device here diff --git a/src/renderer/ral.h b/src/renderer/ral.h index fb77f0a..014c31e 100644 --- a/src/renderer/ral.h +++ b/src/renderer/ral.h @@ -14,6 +14,11 @@ #include "ral_types.h" #include "defines.h" #include "str.h" +#include "buf.h" + +// Unrelated forward declares +typedef struct arena arena; +struct GLFWwindow; // Forward declare structs typedef struct gpu_swapchain gpu_swapchain; @@ -34,21 +39,28 @@ typedef struct shader_desc { str8 glsl; // contents } shader_desc; -struct pipeline_desc { +struct graphics_pipeline_desc { shader_desc vs; /** @brief Vertex shader stage */ shader_desc fs; /** @brief Fragment shader stage */ }; -// lifecycle functions -gpu_device gpu_device_create(); +// --- Lifecycle functions + +bool gpu_backend_init(const char* window_name, struct GLFWwindow* window); +void gpu_backend_shutdown(); + +bool gpu_device_create(gpu_device* out_device); void gpu_device_destroy(); gpu_renderpass* gpu_renderpass_create(); void gpu_renderpass_destroy(gpu_renderpass* pass); -gpu_pipeline* gpu_pipeline_create(enum pipeline_kind kind, struct pipeline_desc description); +gpu_pipeline* gpu_graphics_pipeline_create(struct graphics_pipeline_desc description); void gpu_pipeline_destroy(gpu_pipeline* pipeline); +bool gpu_swapchain_create(gpu_swapchain* out_swapchain); +void gpu_swapchain_destroy(); + void gpu_cmd_encoder_begin(); void gpu_cmd_encoder_begin_render(); void gpu_cmd_encoder_begin_compute(); @@ -58,6 +70,10 @@ void encode_buffer_copy(gpu_cmd_encoder* encoder, buffer_handle src, u64 src_off buffer_handle dst, u64 dst_offset, u64 copy_size); void encode_clear_buffer(gpu_cmd_encoder* encoder, buffer_handle buf); void encode_set_pipeline(gpu_cmd_encoder* encoder, gpu_pipeline* pipeline); + +/** @brief Upload CPU-side data as array of bytes to a GPU buffer */ +void buffer_upload_bytes(buffer_handle gpu_buf, bytebuffer cpu_buf, u64 offset, u64 size); + // render pass void encode_set_vertex_buffer(gpu_cmd_encoder* encoder, buffer_handle buf); void encode_set_index_buffer(gpu_cmd_encoder* encoder, buffer_handle buf); @@ -84,4 +100,9 @@ void gpu_texture_destroy(); void gpu_texture_upload(); // Samplers -void gpu_sampler_create(); \ No newline at end of file +void gpu_sampler_create(); + +// --- Vertex formats +bytebuffer vertices_as_bytebuffer(arena* a, vertex_format format, vertex_darray* vertices); + +// TODO: Bindgroup texture samplers / shader resources \ No newline at end of file diff --git a/src/renderer/ral_types.h b/src/renderer/ral_types.h index a20e600..73b41f1 100644 --- a/src/renderer/ral_types.h +++ b/src/renderer/ral_types.h @@ -50,9 +50,9 @@ typedef enum gpu_texture_format { /** @brief Texture Description - used by texture creation functions */ typedef struct texture_desc { - // gpu_texture_type tex_type; - // gpu_texture_format format; - // u32x2 extents; + gpu_texture_type tex_type; + gpu_texture_format format; + u32x2 extents; } texture_desc; typedef enum vertex_format { @@ -65,7 +65,6 @@ typedef enum vertex_format { typedef union vertex { struct { vec3 position; - vec4 colour; vec2 tex_coords; vec3 normal; } static_3d; /** @brief standard vertex format for static geometry in 3D */ @@ -84,6 +83,13 @@ typedef union vertex { vec4i bone_ids; // Integer vector for bone IDs vec4 bone_weights; // Weight of each bone's influence } skinned_3d; /** @brief vertex format for skeletal (animated) geometry in 3D */ + + struct { + vec3 position; + vec2 tex_coords; + vec3 normal; + vec4 colour; + } coloured_static_3d; /** @brief vertex format used for debugging */ } vertex; #ifndef TYPED_VERTEX_ARRAY diff --git a/src/renderer/render.c b/src/renderer/render.c index 034585a..799cba7 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -1,6 +1,12 @@ #include "render.h" #include #include "camera.h" +#include "log.h" +#include "ral.h" + +/** @brief Creates the pipelines built into Celeritas such as rendering static opaque geometry, + debug visualisations, immediate mode UI, etc */ +void default_pipelines_init(renderer* ren); bool renderer_init(renderer* ren) { // INFO("Renderer init"); @@ -29,12 +35,20 @@ bool renderer_init(renderer* ren) { glfwMakeContextCurrent(ren->window); + DEBUG("Start backend init"); + + gpu_backend_init("Celeritas Engine - Vulkan", window); + gpu_device_create(&ren->device); // TODO: handle errors + gpu_swapchain_create(&ren->swapchain); + // DEBUG("init graphics api backend"); // if (!gfx_backend_init(ren)) { // FATAL("Couldnt load graphics api backend"); // return false; // } + default_pipelines_init(ren); + // ren->blinn_phong = // shader_create_separate("assets/shaders/blinn_phong.vert", // "assets/shaders/blinn_phong.frag"); @@ -46,7 +60,16 @@ bool renderer_init(renderer* ren) { return true; } -void renderer_shutdown(renderer* ren) {} +void renderer_shutdown(renderer* ren) { + // gpu_device_destroy(ren->device); +} + +void default_pipelines_init(renderer* ren) { + // Static opaque geometry + // graphics_pipeline_desc gfx = { + // }; + // ren->static_opaque_pipeline = gpu_graphics_pipeline_create(); +} void render_frame_begin(renderer* ren) {} void render_frame_end(renderer* ren) {} diff --git a/src/renderer/render_types.h b/src/renderer/render_types.h index 5faefad..6ef2461 100644 --- a/src/renderer/render_types.h +++ b/src/renderer/render_types.h @@ -29,8 +29,9 @@ typedef struct renderer { struct GLFWwindow* window; void* backend_context; renderer_config config; - gpu_device* device; - gpu_pipeline* static_opaque_pipeline; + gpu_device device; + gpu_swapchain swapchain; + gpu_pipeline static_opaque_pipeline; } renderer; typedef struct geometry_data { diff --git a/src/std/buf.h b/src/std/buf.h new file mode 100644 index 0000000..b0f8b85 --- /dev/null +++ b/src/std/buf.h @@ -0,0 +1,17 @@ +/** + * @file buf.h + * @author your name (you@domain.com) + * @brief + * @version 0.1 + * @date 2024-04-28 + * + * @copyright Copyright (c) 2024 + * + */ +#pragma once +#include "defines.h" + +typedef struct bytebuffer { + u8* buf; + size_t size; +} bytebuffer; diff --git a/xmake.lua b/xmake.lua index 6fee04a..ab6a7a6 100644 --- a/xmake.lua +++ b/xmake.lua @@ -143,11 +143,18 @@ target("core_shared") add_links("msvcrtd", "legacy_stdio_definitions") -- for debug builds end -target("main_loop") +-- target("main_loop") +-- set_kind("binary") +-- set_group("examples") +-- add_deps("core_static") +-- add_files("examples/main_loop/ex_main_loop.c") +-- set_rundir("$(projectdir)") + +target("tri") set_kind("binary") set_group("examples") add_deps("core_static") - add_files("examples/main_loop/ex_main_loop.c") + add_files("examples/triangle/ex_triangle.c") set_rundir("$(projectdir)") -- target("std") -- cgit v1.2.3-70-g09d2 From 5e0f5662ae65d268db1bf1bc53c773ef9cba33ea Mon Sep 17 00:00:00 2001 From: Omniscient <17525998+omnisci3nce@users.noreply.github.com> Date: Thu, 9 May 2024 10:55:13 +1000 Subject: frames in flight. recreate swapchain still has a few validation errors --- examples/triangle/ex_triangle.c | 2 +- src/renderer/backends/backend_vulkan.c | 178 ++++++++++++++++++++++----------- src/renderer/render.c | 6 +- 3 files changed, 126 insertions(+), 60 deletions(-) (limited to 'src/renderer/render.c') diff --git a/examples/triangle/ex_triangle.c b/examples/triangle/ex_triangle.c index 4aa9e96..1f7ef5e 100644 --- a/examples/triangle/ex_triangle.c +++ b/examples/triangle/ex_triangle.c @@ -76,7 +76,7 @@ int main() { // glfwSwapBuffers(core->renderer.window); } - gpu_backend_shutdown(); + renderer_shutdown(&core->renderer); return 0; } diff --git a/src/renderer/backends/backend_vulkan.c b/src/renderer/backends/backend_vulkan.c index e693da3..50f9662 100644 --- a/src/renderer/backends/backend_vulkan.c +++ b/src/renderer/backends/backend_vulkan.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -21,7 +22,7 @@ // TEMP #define SCREEN_WIDTH 1000 #define SCREEN_HEIGHT 1000 - +#define MAX_FRAMES_IN_FLIGHT 2 #define VULKAN_QUEUES_COUNT 2 const char* queue_names[VULKAN_QUEUES_COUNT] = { "GRAPHICS", "TRANSFER" }; @@ -38,11 +39,15 @@ typedef struct vulkan_context { VkFramebuffer* swapchain_framebuffers; // TODO: Move this data into the swapchain as its own struct - gpu_cmd_encoder main_cmd_buf; u32 current_img_index; - VkSemaphore image_available_semaphore; - VkSemaphore render_finished_semaphore; - VkFence in_flight_fence; + u32 current_frame; // super important + gpu_cmd_encoder main_cmd_bufs[MAX_FRAMES_IN_FLIGHT]; + VkSemaphore image_available_semaphores[MAX_FRAMES_IN_FLIGHT]; + VkSemaphore render_finished_semaphores[MAX_FRAMES_IN_FLIGHT]; + VkFence in_flight_fences[MAX_FRAMES_IN_FLIGHT]; + + // HACK + VkRenderPass main_renderpass; u32 screen_width; u32 screen_height; @@ -62,7 +67,7 @@ bool is_physical_device_suitable(VkPhysicalDevice device); queue_family_indices find_queue_families(VkPhysicalDevice device); bool create_logical_device(gpu_device* out_device); - +void create_swapchain_framebuffers(); void create_sync_objects(); VkShaderModule create_shader_module(str8 spirv); @@ -71,10 +76,12 @@ VkShaderModule create_shader_module(str8 spirv); cstr_darray* get_all_extensions(); bool gpu_backend_init(const char* window_name, GLFWwindow* window) { + memset(&context, 0, sizeof(vulkan_context)); context.allocator = 0; // TODO: use an allocator context.screen_width = SCREEN_WIDTH; context.screen_height = SCREEN_HEIGHT; context.current_img_index = 0; + context.current_frame = 0; // Create an allocator size_t temp_arena_size = 1024 * 1024; @@ -224,6 +231,9 @@ bool gpu_swapchain_create(gpu_swapchain* out_swapchain) { context.swapchain = out_swapchain; out_swapchain->swapchain_arena = arena_create(malloc(1024), 1024); + + vulkan_device_query_swapchain_support(context.device->physical_device, context.surface, + &context.swapchain_support); vulkan_swapchain_support_info swapchain_support = context.swapchain_support; // TODO: custom swapchain extents VkExtent2D swapchain_extent = { width, height }; @@ -293,6 +303,11 @@ bool gpu_swapchain_create(gpu_swapchain* out_swapchain) { } void gpu_swapchain_destroy(gpu_swapchain* swapchain) { + // Destroy Framebuffers + for (u32 i = 0; i < swapchain->image_count; i++) { + vkDestroyFramebuffer(context.device->logical_device, context.swapchain_framebuffers[i], + context.allocator); + } for (u32 i = 0; i < swapchain->image_count; i++) { vkDestroyImageView(context.device->logical_device, swapchain->image_views[i], context.allocator); @@ -301,6 +316,15 @@ void gpu_swapchain_destroy(gpu_swapchain* swapchain) { vkDestroySwapchainKHR(context.device->logical_device, swapchain->handle, context.allocator); } +static void recreate_swapchain(gpu_swapchain* swapchain) { + DEBUG("Recreating swapchain..."); + vkDeviceWaitIdle(context.device->logical_device); + + gpu_swapchain_destroy(swapchain); + gpu_swapchain_create(swapchain); + create_swapchain_framebuffers(); +} + gpu_pipeline* gpu_graphics_pipeline_create(struct graphics_pipeline_desc description) { // Allocate gpu_pipeline_layout* layout = malloc(sizeof(gpu_pipeline_layout)); @@ -406,12 +430,12 @@ gpu_pipeline* gpu_graphics_pipeline_create(struct graphics_pipeline_desc descrip // Blending VkPipelineColorBlendAttachmentState color_blend_attachment_state; color_blend_attachment_state.blendEnable = VK_FALSE; - // color_blend_attachment_state.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; - // color_blend_attachment_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - // color_blend_attachment_state.colorBlendOp = VK_BLEND_OP_ADD; - // color_blend_attachment_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; - // color_blend_attachment_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - // color_blend_attachment_state.alphaBlendOp = VK_BLEND_OP_ADD; + color_blend_attachment_state.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + color_blend_attachment_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + color_blend_attachment_state.colorBlendOp = VK_BLEND_OP_ADD; + color_blend_attachment_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + color_blend_attachment_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + color_blend_attachment_state.alphaBlendOp = VK_BLEND_OP_ADD; color_blend_attachment_state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; @@ -486,33 +510,27 @@ gpu_pipeline* gpu_graphics_pipeline_create(struct graphics_pipeline_desc descrip vkDestroyShaderModule(context.device->logical_device, fragment_shader, context.allocator); // Framebuffers - u32 image_count = context.swapchain->image_count; - context.swapchain_framebuffers = - arena_alloc(&context.swapchain->swapchain_arena, image_count * sizeof(VkFramebuffer)); - for (u32 i = 0; i < image_count; i++) { - VkImageView attachments[1] = { context.swapchain->image_views[i] }; - - VkFramebufferCreateInfo framebuffer_create_info = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; - framebuffer_create_info.attachmentCount = 1; - framebuffer_create_info.pAttachments = attachments; - - framebuffer_create_info.renderPass = description.renderpass->handle; - framebuffer_create_info.width = context.swapchain->extent.width; - framebuffer_create_info.height = context.swapchain->extent.height; - framebuffer_create_info.layers = 1; - - VK_CHECK(vkCreateFramebuffer(context.device->logical_device, &framebuffer_create_info, - context.allocator, &context.swapchain_framebuffers[i])); - } + create_swapchain_framebuffers(); TRACE("Swapchain Framebuffers created"); - context.main_cmd_buf = gpu_cmd_encoder_create(); + for (u32 frame_i = 0; frame_i < MAX_FRAMES_IN_FLIGHT; frame_i++) { + context.main_cmd_bufs[frame_i] = gpu_cmd_encoder_create(); + } TRACE("main Command Buffer created"); TRACE("Graphics pipeline created"); return pipeline; } -gpu_cmd_encoder* gpu_get_default_cmd_encoder() { return &context.main_cmd_buf; } + +void gpu_pipeline_destroy(gpu_pipeline* pipeline) { + vkDestroyPipeline(context.device->logical_device, pipeline->handle, context.allocator); + vkDestroyPipelineLayout(context.device->logical_device, pipeline->layout_handle, + context.allocator); +} + +gpu_cmd_encoder* gpu_get_default_cmd_encoder() { + return &context.main_cmd_bufs[context.current_frame]; +} gpu_renderpass* gpu_renderpass_create(const gpu_renderpass_desc* description) { // TEMP: allocate with malloc. in the future we will have a pool allocator on the context @@ -573,6 +591,9 @@ gpu_renderpass* gpu_renderpass_create(const gpu_renderpass_desc* description) { VK_CHECK(vkCreateRenderPass(context.device->logical_device, &render_pass_create_info, context.allocator, &renderpass->handle)); + // HACK + context.main_renderpass = renderpass->handle; + return renderpass; } @@ -609,13 +630,14 @@ void gpu_cmd_encoder_begin(gpu_cmd_encoder encoder) { void gpu_cmd_encoder_begin_render(gpu_cmd_encoder* encoder, gpu_renderpass* renderpass) { VkRenderPassBeginInfo begin_info = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; begin_info.renderPass = renderpass->handle; - printf("Current img: %d\n", context.current_img_index); + /* printf("Current img: %d Current frame %d\n", context.current_img_index, context.current_frame); + */ begin_info.framebuffer = context.swapchain_framebuffers[context.current_img_index]; begin_info.renderArea.offset = (VkOffset2D){ 0, 0 }; begin_info.renderArea.extent = context.swapchain->extent; // VkClearValue clear_values[2]; - VkClearValue clear_color = { { { 0.2f, 0.2f, 0.2f, 1.0f } } }; + VkClearValue clear_color = { { { 0.02f, 0.02f, 0.02f, 1.0f } } }; // clear_values[1].depthStencil.depth = renderpass->depth; // clear_values[1].depthStencil.stencil = renderpass->stencil; @@ -661,37 +683,53 @@ void encode_set_default_settings(gpu_cmd_encoder* encoder) { // --- Drawing void gpu_backend_begin_frame() { + u32 current_frame = context.current_frame; // TRACE("gpu_backend_begin_frame"); - vkWaitForFences(context.device->logical_device, 1, &context.in_flight_fence, VK_TRUE, UINT64_MAX); - vkResetFences(context.device->logical_device, 1, &context.in_flight_fence); + vkWaitForFences(context.device->logical_device, 1, &context.in_flight_fences[current_frame], + VK_TRUE, UINT64_MAX); + vkResetFences(context.device->logical_device, 1, &context.in_flight_fences[current_frame]); u32 image_index; - VK_CHECK(vkAcquireNextImageKHR(context.device->logical_device, context.swapchain->handle, - UINT64_MAX, context.image_available_semaphore, VK_NULL_HANDLE, - &image_index)); + VkResult result = vkAcquireNextImageKHR( + context.device->logical_device, context.swapchain->handle, UINT64_MAX, + context.image_available_semaphores[current_frame], VK_NULL_HANDLE, &image_index); + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { + recreate_swapchain(context.swapchain); + return; + } else if (result != VK_SUCCESS) { + ERROR_EXIT("failed to acquire swapchain image"); + } + context.current_img_index = image_index; - printf("Current img: %d\n", context.current_img_index); - VK_CHECK(vkResetCommandBuffer(context.main_cmd_buf.cmd_buffer, 0)); + /* printf("Current img: %d\n", context.current_img_index); */ + VK_CHECK(vkResetCommandBuffer(context.main_cmd_bufs[current_frame].cmd_buffer, 0)); } void gpu_temp_draw() { - gpu_cmd_encoder* encoder = &context.main_cmd_buf; - TRACE("Draw call"); + gpu_cmd_encoder* encoder = gpu_get_default_cmd_encoder(); // &context.main_cmd_buf; vkCmdDraw(encoder->cmd_buffer, 3, 1, 0, 0); } void gpu_backend_end_frame() { VkPresentInfoKHR present_info = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; present_info.waitSemaphoreCount = 1; - present_info.pWaitSemaphores = &context.render_finished_semaphore; + present_info.pWaitSemaphores = &context.render_finished_semaphores[context.current_frame]; VkSwapchainKHR swapchains[] = { context.swapchain->handle }; present_info.swapchainCount = 1; present_info.pSwapchains = swapchains; present_info.pImageIndices = &context.current_img_index; - vkQueuePresentKHR(context.device->present_queue, &present_info); - vkDeviceWaitIdle(context.device->logical_device); + VkResult result = vkQueuePresentKHR(context.device->present_queue, &present_info); + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { + recreate_swapchain(context.swapchain); + return; + } else if (result != VK_SUCCESS) { + ERROR_EXIT("failed to present swapchain image"); + } + context.current_frame = (context.current_frame + 1) % MAX_FRAMES_IN_FLIGHT; + + /* vkDeviceWaitIdle(context.device->logical_device); */ } // TODO: Move into better order in file @@ -699,7 +737,7 @@ void gpu_queue_submit(gpu_cmd_buffer* buffer) { VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; // Specify semaphore to wait on - VkSemaphore wait_semaphores[] = { context.image_available_semaphore }; + VkSemaphore wait_semaphores[] = { context.image_available_semaphores[context.current_frame] }; VkPipelineStageFlags wait_stages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; submit_info.waitSemaphoreCount = 1; @@ -707,14 +745,15 @@ void gpu_queue_submit(gpu_cmd_buffer* buffer) { submit_info.pWaitDstStageMask = wait_stages; // Specify semaphore to signal when finished executing buffer - VkSemaphore signal_semaphores[] = { context.render_finished_semaphore }; + VkSemaphore signal_semaphores[] = { context.render_finished_semaphores[context.current_frame] }; submit_info.signalSemaphoreCount = 1; submit_info.pSignalSemaphores = signal_semaphores; submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &buffer->cmd_buffer; - VK_CHECK(vkQueueSubmit(context.device->graphics_queue, 1, &submit_info, context.in_flight_fence)); + VK_CHECK(vkQueueSubmit(context.device->graphics_queue, 1, &submit_info, + context.in_flight_fences[context.current_frame])); } inline void encode_draw_indexed(gpu_cmd_encoder* encoder, u64 index_count) { @@ -878,15 +917,40 @@ VkShaderModule create_shader_module(str8 spirv) { return shader_module; } +void create_swapchain_framebuffers() { + u32 image_count = context.swapchain->image_count; + context.swapchain_framebuffers = + arena_alloc(&context.swapchain->swapchain_arena, image_count * sizeof(VkFramebuffer)); + for (u32 i = 0; i < image_count; i++) { + VkImageView attachments[1] = { context.swapchain->image_views[i] }; + + VkFramebufferCreateInfo framebuffer_create_info = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; + framebuffer_create_info.attachmentCount = 1; + framebuffer_create_info.pAttachments = attachments; + + framebuffer_create_info.renderPass = + context.main_renderpass; // TODO: description.renderpass->handle; + framebuffer_create_info.width = context.swapchain->extent.width; + framebuffer_create_info.height = context.swapchain->extent.height; + framebuffer_create_info.layers = 1; + + VK_CHECK(vkCreateFramebuffer(context.device->logical_device, &framebuffer_create_info, + context.allocator, &context.swapchain_framebuffers[i])); + } +} + void create_sync_objects() { VkSemaphoreCreateInfo semaphore_info = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; - VK_CHECK(vkCreateSemaphore(context.device->logical_device, &semaphore_info, context.allocator, - &context.image_available_semaphore);); - VK_CHECK(vkCreateSemaphore(context.device->logical_device, &semaphore_info, context.allocator, - &context.render_finished_semaphore);); - VkFenceCreateInfo fence_info = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO }; fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT; - VK_CHECK(vkCreateFence(context.device->logical_device, &fence_info, context.allocator, - &context.in_flight_fence)); -} \ No newline at end of file + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + VK_CHECK(vkCreateSemaphore(context.device->logical_device, &semaphore_info, context.allocator, + &context.image_available_semaphores[i]);); + VK_CHECK(vkCreateSemaphore(context.device->logical_device, &semaphore_info, context.allocator, + &context.render_finished_semaphores[i]);); + + VK_CHECK(vkCreateFence(context.device->logical_device, &fence_info, context.allocator, + &context.in_flight_fences[i])); + } +} diff --git a/src/renderer/render.c b/src/renderer/render.c index 799cba7..f73578e 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -61,7 +61,9 @@ bool renderer_init(renderer* ren) { return true; } void renderer_shutdown(renderer* ren) { - // gpu_device_destroy(ren->device); + gpu_swapchain_destroy(&ren->swapchain); + gpu_pipeline_destroy(&ren->static_opaque_pipeline); + gpu_backend_shutdown(); } void default_pipelines_init(renderer* ren) { @@ -77,4 +79,4 @@ void render_frame_draw(renderer* ren) {} void gfx_backend_draw_frame(renderer* ren, camera* camera, mat4 model, texture* tex) {} -void geo_set_vertex_colours(geometry_data* geo, vec4 colour) {} \ No newline at end of file +void geo_set_vertex_colours(geometry_data* geo, vec4 colour) {} -- cgit v1.2.3-70-g09d2 From 53fe85a463c9c68ec75fa4efb496d1ff864a929e Mon Sep 17 00:00:00 2001 From: Omniscient <17525998+omnisci3nce@users.noreply.github.com> Date: Fri, 10 May 2024 15:44:28 +1000 Subject: add bindings friendly functions --- include/celeritas.h | 8 +++-- src/core.c | 6 +++- src/core.h | 4 +++ src/renderer/render.c | 76 ++++++++++++++++++++++++++++++++++++++------- src/renderer/render_types.h | 2 ++ src/systems/input.c | 1 + 6 files changed, 81 insertions(+), 16 deletions(-) (limited to 'src/renderer/render.c') diff --git a/include/celeritas.h b/include/celeritas.h index 425436c..9ba741b 100644 --- a/include/celeritas.h +++ b/include/celeritas.h @@ -33,9 +33,9 @@ void core_bringup(); void core_shutdown(); bool should_window_close(); -void render_frame_begin(); -void render_frame_draw(); -void render_frame_end(); +void frame_begin(); +void frame_draw(); +void frame_end(); // Assets model_handle model_load(const char* filepath); @@ -47,6 +47,8 @@ typedef struct render_entity { transform3d transform; } render_entity; +void render_frame_begin(); + // Scene typedef struct directional_light {} directional_light; typedef struct point_light {} point_light; diff --git a/src/core.c b/src/core.c index 17424b3..ffd72a5 100644 --- a/src/core.c +++ b/src/core.c @@ -58,7 +58,7 @@ void core_bringup() { #include -bool should_window_close(core* core) { glfwWindowShouldClose(core->renderer.window); } +/* bool should_window_close(core* core) { glfwWindowShouldClose(core->renderer.window); } */ void core_input_update() { input_update(&g_core.input); } void core_frame_begin(core* core) { render_frame_begin(&core->renderer); } void core_frame_end(core* core) { render_frame_end(&core->renderer); } @@ -72,3 +72,7 @@ void core_shutdown() { bool should_exit() { return key_just_released(KEYCODE_ESCAPE) || glfwWindowShouldClose(g_core.renderer.window); } + +void frame_begin() { render_frame_begin(&g_core.renderer); } +void frame_draw() {} +void frame_end() { render_frame_end(&g_core.renderer); } diff --git a/src/core.h b/src/core.h index ec8cde9..db711d0 100644 --- a/src/core.h +++ b/src/core.h @@ -31,4 +31,8 @@ void core_bringup(); void core_shutdown(); bool should_exit(); +void frame_begin(); +void frame_draw(); +void frame_end(); + void core_input_update(); diff --git a/src/renderer/render.c b/src/renderer/render.c index f73578e..10589e5 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -1,6 +1,7 @@ #include "render.h" #include #include "camera.h" +#include "file.h" #include "log.h" #include "ral.h" @@ -35,20 +36,15 @@ bool renderer_init(renderer* ren) { glfwMakeContextCurrent(ren->window); - DEBUG("Start backend init"); + DEBUG("Start gpu backend init"); - gpu_backend_init("Celeritas Engine - Vulkan", window); + if (!gpu_backend_init("Celeritas Engine - Vulkan", window)) { + FATAL("Couldnt load graphics api backend"); + return false; + } gpu_device_create(&ren->device); // TODO: handle errors gpu_swapchain_create(&ren->swapchain); - // DEBUG("init graphics api backend"); - // if (!gfx_backend_init(ren)) { - // FATAL("Couldnt load graphics api backend"); - // return false; - // } - - default_pipelines_init(ren); - // ren->blinn_phong = // shader_create_separate("assets/shaders/blinn_phong.vert", // "assets/shaders/blinn_phong.frag"); @@ -58,6 +54,9 @@ bool renderer_init(renderer* ren) { // default_material_init(); + // Create default rendering pipeline + default_pipelines_init(ren); + return true; } void renderer_shutdown(renderer* ren) { @@ -71,10 +70,63 @@ void default_pipelines_init(renderer* ren) { // graphics_pipeline_desc gfx = { // }; // ren->static_opaque_pipeline = gpu_graphics_pipeline_create(); + arena scratch = arena_create(malloc(1024 * 1024), 1024 * 1024); + + gpu_renderpass_desc pass_description = {}; + gpu_renderpass* renderpass = gpu_renderpass_create(&pass_description); + + ren->default_renderpass = *renderpass; + + str8 vert_path = str8lit("celeritas-core/build/linux/x86_64/debug/triangle.vert.spv"); + str8 frag_path = str8lit("celeritas-core/build/linux/x86_64/debug/triangle.frag.spv"); + str8_opt vertex_shader = str8_from_file(&scratch, vert_path); + str8_opt fragment_shader = str8_from_file(&scratch, frag_path); + if (!vertex_shader.has_value || !fragment_shader.has_value) { + ERROR_EXIT("Failed to load shaders from disk") + } + + struct graphics_pipeline_desc pipeline_description = { + .debug_name = "Basic Pipeline", + .vs = { .debug_name = "Triangle Vertex Shader", + .filepath = vert_path, + .code = vertex_shader.contents, + .is_spirv = true }, + .fs = { .debug_name = "Triangle Fragment Shader", + .filepath = frag_path, + .code = fragment_shader.contents, + .is_spirv = true }, + .renderpass = renderpass, + .wireframe = false, + .depth_test = false + }; + gpu_pipeline* gfx_pipeline = gpu_graphics_pipeline_create(pipeline_description); + ren->static_opaque_pipeline = *gfx_pipeline; } -void render_frame_begin(renderer* ren) {} -void render_frame_end(renderer* ren) {} +void render_frame_begin(renderer* ren) { + ren->frame_aborted = false; + if (!gpu_backend_begin_frame()) { + ren->frame_aborted = true; + return; + } + gpu_cmd_encoder* enc = gpu_get_default_cmd_encoder(); + // begin recording + gpu_cmd_encoder_begin(*enc); + gpu_cmd_encoder_begin_render(enc, &ren->default_renderpass); + encode_bind_pipeline(enc, PIPELINE_GRAPHICS, &ren->static_opaque_pipeline); + encode_set_default_settings(enc); +} +void render_frame_end(renderer* ren) { + if (ren->frame_aborted) { + return; + } + gpu_temp_draw(); + gpu_cmd_encoder* enc = gpu_get_default_cmd_encoder(); + gpu_cmd_encoder_end_render(enc); + gpu_cmd_buffer buf = gpu_cmd_encoder_finish(enc); + gpu_queue_submit(&buf); + gpu_backend_end_frame(); +} void render_frame_draw(renderer* ren) {} void gfx_backend_draw_frame(renderer* ren, camera* camera, mat4 model, texture* tex) {} diff --git a/src/renderer/render_types.h b/src/renderer/render_types.h index 3763967..06a8415 100644 --- a/src/renderer/render_types.h +++ b/src/renderer/render_types.h @@ -32,7 +32,9 @@ typedef struct renderer { renderer_config config; gpu_device device; gpu_swapchain swapchain; + gpu_renderpass default_renderpass; gpu_pipeline static_opaque_pipeline; + bool frame_aborted; } renderer; typedef struct geometry_data { diff --git a/src/systems/input.c b/src/systems/input.c index fc62db8..5df1159 100644 --- a/src/systems/input.c +++ b/src/systems/input.c @@ -25,6 +25,7 @@ bool input_system_init(input_state *input, GLFWwindow *window) { assert(input->mouse.x_delta == 0); assert(input->mouse.y_delta == 0); + INFO("Finish input init"); return true; } -- cgit v1.2.3-70-g09d2 From eedd332680dbdd367197616658903f00252f5a9c Mon Sep 17 00:00:00 2001 From: Omniscient <17525998+omnisci3nce@users.noreply.github.com> Date: Sat, 11 May 2024 00:22:13 +1000 Subject: upload data using staging buffer. use vertex buffer data --- assets/shaders/triangle.vert | 11 ++- examples/triangle/ex_triangle.c | 23 ++++- src/core.c | 9 +- src/core.h | 2 +- src/maths/maths.h | 5 +- src/renderer/backends/backend_vulkan.c | 170 +++++++++++++++++++++++++++++++-- src/renderer/backends/backend_vulkan.h | 8 +- src/renderer/ral.h | 7 +- src/renderer/ral_types.h | 22 ++++- src/renderer/render.c | 43 ++++----- src/scene.h | 2 +- 11 files changed, 250 insertions(+), 52 deletions(-) (limited to 'src/renderer/render.c') diff --git a/assets/shaders/triangle.vert b/assets/shaders/triangle.vert index ee3675d..b8c8f63 100644 --- a/assets/shaders/triangle.vert +++ b/assets/shaders/triangle.vert @@ -1,12 +1,13 @@ #version 450 -vec2 positions[3] = vec2[](vec2(-0.5, 0.5), vec2(0.5, 0.5), vec2(0.0, -0.5)); - -vec3 colors[3] = vec3[](vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, 0.0, 1.0)); +layout(location = 0) in vec2 inPos; +layout(location = 1) in vec3 inColor; +// layout(location = 1) in vec3 inNormal; +// layout(location = 2) in vec2 inTexCoords; layout(location = 0) out vec3 fragColor; void main() { - gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); - fragColor = colors[gl_VertexIndex]; + gl_Position = vec4(inPos, 0.0, 1.0); + fragColor = inColor; } diff --git a/examples/triangle/ex_triangle.c b/examples/triangle/ex_triangle.c index 7b8a1d0..c4f90f2 100644 --- a/examples/triangle/ex_triangle.c +++ b/examples/triangle/ex_triangle.c @@ -1,6 +1,7 @@ #include #include "backend_vulkan.h" +#include "buf.h" #include "camera.h" #include "core.h" #include "file.h" @@ -8,12 +9,20 @@ #include "maths.h" #include "mem.h" #include "ral.h" +#include "ral_types.h" #include "render.h" // Example setting up a renderer extern core g_core; +const custom_vertex vertices[] = { + (custom_vertex){ .pos = vec2(-0.5, 0.5), .color = vec3(0.0, 0.0, 1.0) }, + (custom_vertex){ .pos = vec2(0.5, 0.5), .color = vec3(0.0, 1.0, 0.0) }, + (custom_vertex){ .pos = vec2(0.0, -0.5), .color = vec3(1.0, 0.0, 0.0) }, +}; +_Static_assert(sizeof(vertices) == (5 * 3 * sizeof(float)), ""); + int main() { core_bringup(); arena scratch = arena_create(malloc(1024 * 1024), 1024 * 1024); @@ -47,12 +56,19 @@ int main() { }; gpu_pipeline* gfx_pipeline = gpu_graphics_pipeline_create(pipeline_description); + buffer_handle triangle_vert_buf = gpu_buffer_create( + 3 * sizeof(custom_vertex), CEL_BUFFER_VERTEX, CEL_BUFFER_FLAG_GPU, + vertices); // gpu_buffer_create(3 * sizeof(custom_vertex), CEL_BUFFER_VERTEX); + /* buffer_upload_bytes(triangle_vert_buf, (bytebuffer){ .buf = vertices, .size = sizeof(vertices) + * }, */ + /* 0, sizeof(vertices)); */ + // Main loop while (!should_exit(&g_core)) { glfwPollEvents(); input_update(&g_core.input); - render_frame_begin(&g_core.renderer); + // render_frame_begin(&g_core.renderer); static f64 x = 0.0; x += 0.01; @@ -68,7 +84,8 @@ int main() { encode_set_default_settings(enc); // Record draw calls - gpu_temp_draw(); + encode_set_vertex_buffer(enc, triangle_vert_buf); + gpu_temp_draw(3); // End recording gpu_cmd_encoder_end_render(enc); @@ -78,7 +95,7 @@ int main() { // Submit gpu_backend_end_frame(); - render_frame_end(&g_core.renderer); + // render_frame_end(&g_core.renderer); // glfwSwapBuffers(core->renderer.window); } diff --git a/src/core.c b/src/core.c index 26e7613..84c9cae 100644 --- a/src/core.c +++ b/src/core.c @@ -59,9 +59,7 @@ void core_bringup() { #include /* bool should_window_close(core* core) { glfwWindowShouldClose(core->renderer.window); } */ -void core_input_update() { - input_update(&g_core.input); -} +void core_input_update() { input_update(&g_core.input); } void core_frame_begin(core* core) { render_frame_begin(&core->renderer); } void core_frame_end(core* core) { render_frame_end(&core->renderer); } @@ -76,7 +74,8 @@ bool should_exit() { } void frame_begin() { - glfwPollEvents(); - render_frame_begin(&g_core.renderer); } + glfwPollEvents(); + render_frame_begin(&g_core.renderer); +} void frame_draw() {} void frame_end() { render_frame_end(&g_core.renderer); } diff --git a/src/core.h b/src/core.h index db711d0..1031868 100644 --- a/src/core.h +++ b/src/core.h @@ -1,8 +1,8 @@ #pragma once #include "input.h" -#include "screenspace.h" #include "scene.h" +#include "screenspace.h" #include "terrain.h" #include "text.h" // #include "threadpool.h" diff --git a/src/maths/maths.h b/src/maths/maths.h index 88a5215..217f2e0 100644 --- a/src/maths/maths.h +++ b/src/maths/maths.h @@ -21,7 +21,7 @@ // Dimension 3 static inline vec3 vec3_create(f32 x, f32 y, f32 z) { return (vec3){ x, y, z }; } -#define vec3(x, y, z) (vec3_create(x, y, z)) +#define vec3(x, y, z) ((vec3){ x, y, z }) static inline vec3 vec3_add(vec3 a, vec3 b) { return (vec3){ a.x + b.x, a.y + b.y, a.z + b.z }; } static inline vec3 vec3_sub(vec3 a, vec3 b) { return (vec3){ a.x - b.x, a.y - b.y, a.z - b.z }; } static inline vec3 vec3_mult(vec3 a, f32 s) { return (vec3){ a.x * s, a.y * s, a.z * s }; } @@ -53,6 +53,7 @@ static inline void print_vec3(vec3 v) { printf("{ x: %f, y: %f, z: %f )\n", v.x, // TODO: Dimension 2 static inline vec2 vec2_create(f32 x, f32 y) { return (vec2){ x, y }; } +#define vec2(x, y) ((vec2){ x, y }) static inline vec2 vec2_div(vec2 a, f32 s) { return (vec2){ a.x / s, a.y / s }; } // TODO: Dimension 4 @@ -320,4 +321,4 @@ static inline mat4 transform_to_mat(transform *tf) { _Static_assert(alignof(vec3) == 4, "vec3 is 4 byte aligned"); _Static_assert(sizeof(vec3) == 12, "vec3 is 12 bytes so has no padding"); -_Static_assert(alignof(vec4) == 4, "vec4 is 4 byte aligned"); \ No newline at end of file +_Static_assert(alignof(vec4) == 4, "vec4 is 4 byte aligned"); diff --git a/src/renderer/backends/backend_vulkan.c b/src/renderer/backends/backend_vulkan.c index 5390f1f..e30d3c1 100644 --- a/src/renderer/backends/backend_vulkan.c +++ b/src/renderer/backends/backend_vulkan.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -8,8 +9,10 @@ #include #include "backend_vulkan.h" +#include "buf.h" #include "maths_types.h" #include "mem.h" +#include "ral_types.h" #include "str.h" #include "vulkan_helpers.h" @@ -54,6 +57,10 @@ typedef struct vulkan_context { bool is_resizing; GLFWwindow* window; + // Storage + gpu_buffer buffers[1024]; + size_t buffer_count; + VkDebugUtilsMessengerEXT vk_debugger; } vulkan_context; @@ -366,17 +373,31 @@ gpu_pipeline* gpu_graphics_pipeline_create(struct graphics_pipeline_desc descrip VkPipelineShaderStageCreateInfo shader_stages[2] = { vert_shader_stage_info, frag_shader_stage_info }; - // Vertex Input - // TODO: Attributes + VkVertexInputAttributeDescription attribute_descs[2]; + attribute_descs[0].binding = 0; + attribute_descs[0].location = 0; + attribute_descs[0].format = VK_FORMAT_R32G32_SFLOAT; + attribute_descs[0].offset = offsetof(custom_vertex, pos); + + attribute_descs[1].binding = 0; + attribute_descs[1].location = 1; + attribute_descs[1].format = VK_FORMAT_R32G32B32_SFLOAT; + attribute_descs[1].offset = offsetof(custom_vertex, color); + + // Vertex input + VkVertexInputBindingDescription binding_desc; + binding_desc.binding = 0; + binding_desc.stride = sizeof(custom_vertex); + binding_desc.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; VkPipelineVertexInputStateCreateInfo vertex_input_info = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; - vertex_input_info.vertexBindingDescriptionCount = 0; - vertex_input_info.pVertexBindingDescriptions = NULL; - vertex_input_info.vertexAttributeDescriptionCount = 0; - vertex_input_info.pVertexAttributeDescriptions = NULL; + vertex_input_info.vertexBindingDescriptionCount = 1; + vertex_input_info.pVertexBindingDescriptions = &binding_desc; + vertex_input_info.vertexAttributeDescriptionCount = 2; + vertex_input_info.pVertexAttributeDescriptions = attribute_descs; // Input Assembly VkPipelineInputAssemblyStateCreateInfo input_assembly = { @@ -633,6 +654,10 @@ gpu_cmd_encoder gpu_cmd_encoder_create() { return encoder; } +void gpu_cmd_encoder_destroy(gpu_cmd_encoder* encoder) { + vkFreeCommandBuffers(context.device->logical_device, context.device->pool, 1, + &encoder->cmd_buffer); +} void gpu_cmd_encoder_begin(gpu_cmd_encoder encoder) { VkCommandBufferBeginInfo begin_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; @@ -674,6 +699,12 @@ gpu_cmd_buffer gpu_cmd_encoder_finish(gpu_cmd_encoder* encoder) { void encode_bind_pipeline(gpu_cmd_encoder* encoder, pipeline_kind kind, gpu_pipeline* pipeline) { vkCmdBindPipeline(encoder->cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->handle); } +void encode_set_vertex_buffer(gpu_cmd_encoder* encoder, buffer_handle buf) { + gpu_buffer buffer = context.buffers[buf.raw]; + VkBuffer vbs[] = { buffer.handle }; + VkDeviceSize offsets[] = { 0 }; + vkCmdBindVertexBuffers(encoder->cmd_buffer, 0, 1, vbs, offsets); +} // TEMP void encode_set_default_settings(gpu_cmd_encoder* encoder) { @@ -720,9 +751,9 @@ bool gpu_backend_begin_frame() { return true; } -void gpu_temp_draw() { +void gpu_temp_draw(size_t n_verts) { gpu_cmd_encoder* encoder = gpu_get_default_cmd_encoder(); // &context.main_cmd_buf; - vkCmdDraw(encoder->cmd_buffer, 3, 1, 0, 0); + vkCmdDraw(encoder->cmd_buffer, n_verts, 1, 0, 0); } void gpu_backend_end_frame() { @@ -971,3 +1002,126 @@ void create_sync_objects() { &context.in_flight_fences[i])); } } + +static i32 find_memory_index(u32 type_filter, u32 property_flags) { + VkPhysicalDeviceMemoryProperties memory_properties; + vkGetPhysicalDeviceMemoryProperties(context.device->physical_device, &memory_properties); + + for (u32 i = 0; i < memory_properties.memoryTypeCount; ++i) { + // Check each memory type to see if its bit is set to 1. + if (type_filter & (1 << i) && + (memory_properties.memoryTypes[i].propertyFlags & property_flags) == property_flags) { + return i; + } + } + + WARN("Unable to find suitable memory type!"); + return -1; +} + +buffer_handle gpu_buffer_create(u64 size, gpu_buffer_type buf_type, gpu_buffer_flags flags, + const void* data) { + VkBufferCreateInfo buffer_info = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; + buffer_info.size = size; + buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + + switch (buf_type) { + case CEL_BUFFER_DEFAULT: + buffer_info.usage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + break; + case CEL_BUFFER_VERTEX: + buffer_info.usage |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + break; + case CEL_BUFFER_COUNT: + WARN("Incorrect gpu_buffer_type provided. using default"); + break; + } + + buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + // "allocating" the cpu-side buffer struct + gpu_buffer buffer; + buffer.size = size; + buffer_handle handle = { .raw = (u32)context.buffer_count }; + + VK_CHECK(vkCreateBuffer(context.device->logical_device, &buffer_info, context.allocator, + &buffer.handle)); + + VkMemoryRequirements requirements; + vkGetBufferMemoryRequirements(context.device->logical_device, buffer.handle, &requirements); + + // Just make them always need all of them for now + i32 memory_index = + find_memory_index(requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + // Allocate the actual VRAM + VkMemoryAllocateInfo allocate_info = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + allocate_info.allocationSize = requirements.size; + allocate_info.memoryTypeIndex = (u32)memory_index; + + vkAllocateMemory(context.device->logical_device, &allocate_info, context.allocator, + &buffer.memory); + vkBindBufferMemory(context.device->logical_device, buffer.handle, buffer.memory, 0); + + /* Now there are two options: + * 1. create CPU-accessible memory -> map memory -> memcpy -> unmap + * 2. use a staging buffer thats CPU-accessible and copy its contents to a + * GPU-only buffer + */ + + context.buffers[context.buffer_count] = buffer; + context.buffer_count++; + + if (data) { + if (flags & CEL_BUFFER_FLAG_CPU) { + buffer_upload_bytes(handle, (bytebuffer){ .buf = (u8*)data, .size = size }, 0, size); + } else if (flags & CEL_BUFFER_FLAG_GPU) { + TRACE("Uploading data to buffer using staging buffer"); + buffer_handle staging = + gpu_buffer_create(size, CEL_BUFFER_DEFAULT, CEL_BUFFER_FLAG_CPU, data); + gpu_cmd_encoder temp_encoder = gpu_cmd_encoder_create(); + gpu_cmd_encoder_begin(temp_encoder); + encode_buffer_copy(&temp_encoder, handle, 0, staging, 0, size); + gpu_cmd_buffer copy_cmd_buffer = gpu_cmd_encoder_finish(&temp_encoder); + + VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &temp_encoder.cmd_buffer; + vkQueueSubmit(context.device->graphics_queue, 1, &submit_info, VK_NULL_HANDLE); + + vkQueueWaitIdle(context.device->graphics_queue); + gpu_cmd_encoder_destroy(&temp_encoder); + gpu_buffer_destroy(staging); + } + } + + return handle; +} + +void gpu_buffer_destroy(buffer_handle buffer) { + gpu_buffer b = context.buffers[buffer.raw]; + vkDestroyBuffer(context.device->logical_device, b.handle, context.allocator); + vkFreeMemory(context.device->logical_device, b.memory, context.allocator); +} + +// Upload data to a +void buffer_upload_bytes(buffer_handle gpu_buf, bytebuffer cpu_buf, u64 offset, u64 size) { + gpu_buffer buffer = context.buffers[gpu_buf.raw]; + void* data_ptr; + vkMapMemory(context.device->logical_device, buffer.memory, 0, size, 0, &data_ptr); + memcpy(data_ptr, cpu_buf.buf, size); + vkUnmapMemory(context.device->logical_device, buffer.memory); +} + +void encode_buffer_copy(gpu_cmd_encoder* encoder, buffer_handle src, u64 src_offset, + buffer_handle dst, u64 dst_offset, u64 copy_size) { + VkBufferCopy copy_region; + copy_region.srcOffset = src_offset; + copy_region.dstOffset = dst_offset; + copy_region.size = copy_size; + + vkCmdCopyBuffer(encoder->cmd_buffer, context.buffers[src.raw].handle, + context.buffers[dst.raw].handle, 1, ©_region); +} diff --git a/src/renderer/backends/backend_vulkan.h b/src/renderer/backends/backend_vulkan.h index 330ba18..0b0a492 100644 --- a/src/renderer/backends/backend_vulkan.h +++ b/src/renderer/backends/backend_vulkan.h @@ -75,4 +75,10 @@ typedef struct gpu_cmd_encoder { typedef struct gpu_cmd_buffer { VkCommandBuffer cmd_buffer; -} gpu_cmd_buffer; \ No newline at end of file +} gpu_cmd_buffer; + +typedef struct gpu_buffer { + VkBuffer handle; + VkDeviceMemory memory; + u64 size; +} gpu_buffer; diff --git a/src/renderer/ral.h b/src/renderer/ral.h index ec9793e..2fb0166 100644 --- a/src/renderer/ral.h +++ b/src/renderer/ral.h @@ -28,6 +28,7 @@ typedef struct gpu_pipeline gpu_pipeline; typedef struct gpu_renderpass gpu_renderpass; typedef struct gpu_cmd_encoder gpu_cmd_encoder; // Recording typedef struct gpu_cmd_buffer gpu_cmd_buffer; // Ready for submission +typedef struct gpu_buffer gpu_buffer; /** @brief A*/ // typedef struct gpu_bind_group @@ -85,6 +86,7 @@ bool gpu_swapchain_create(gpu_swapchain* out_swapchain); void gpu_swapchain_destroy(gpu_swapchain* swapchain); gpu_cmd_encoder gpu_cmd_encoder_create(); +void gpu_cmd_encoder_destroy(gpu_cmd_encoder* encoder); void gpu_cmd_encoder_begin(gpu_cmd_encoder encoder); void gpu_cmd_encoder_begin_render(gpu_cmd_encoder* encoder, gpu_renderpass* renderpass); void gpu_cmd_encoder_end_render(gpu_cmd_encoder* encoder); @@ -117,7 +119,8 @@ gpu_cmd_buffer gpu_cmd_encoder_finish(gpu_cmd_encoder* encoder); void gpu_queue_submit(gpu_cmd_buffer* buffer); // Buffers -void gpu_buffer_create(u64 size); +buffer_handle gpu_buffer_create(u64 size, gpu_buffer_type buf_type, gpu_buffer_flags flags, + const void* data); void gpu_buffer_destroy(buffer_handle buffer); void gpu_buffer_upload(); void gpu_buffer_bind(buffer_handle buffer); @@ -137,4 +140,4 @@ bytebuffer vertices_as_bytebuffer(arena* a, vertex_format format, vertex_darray* // TEMP -void gpu_temp_draw(); +void gpu_temp_draw(size_t n_verts); diff --git a/src/renderer/ral_types.h b/src/renderer/ral_types.h index ae54b53..7dd254e 100644 --- a/src/renderer/ral_types.h +++ b/src/renderer/ral_types.h @@ -55,6 +55,20 @@ typedef struct texture_desc { u32x2 extents; } texture_desc; +typedef enum gpu_buffer_type { + CEL_BUFFER_DEFAULT, // on Vulkan this would be a storage buffer? + CEL_BUFFER_VERTEX, + CEL_BUFFER_COUNT +} gpu_buffer_type; + +typedef enum gpu_buffer_flag { + CEL_BUFFER_FLAG_CPU = 1 << 0, + CEL_BUFFER_FLAG_GPU = 1 << 1, + CEL_BUFFER_FLAG_STORAGE = 1 << 2, + CEL_BUFFER_FLAG_COUNT +} gpu_buffer_flag; +typedef u32 gpu_buffer_flags; + typedef enum vertex_format { VERTEX_STATIC_3D, VERTEX_SPRITE, @@ -99,6 +113,12 @@ KITC_DECL_TYPED_ARRAY(u32) #define TYPED_VERTEX_ARRAY #endif +// TEMP +typedef struct custom_vertex { + vec2 pos; + vec3 color; +} custom_vertex; + typedef enum gpu_cull_mode { CULL_BACK_FACE, CULL_FRONT_FACE, CULL_COUNT } gpu_cull_mode; // ? How to tie together materials and shaders @@ -110,4 +130,4 @@ typedef enum gpu_cull_mode { CULL_BACK_FACE, CULL_FRONT_FACE, CULL_COUNT } gpu_c // 3 - you don't need to know how the renderer works at all // 2 - you need to know how the overall renderer is designed -// 1 - you need to understand graphics API specifics \ No newline at end of file +// 1 - you need to understand graphics API specifics diff --git a/src/renderer/render.c b/src/renderer/render.c index 10589e5..9f5b21a 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -55,7 +55,7 @@ bool renderer_init(renderer* ren) { // default_material_init(); // Create default rendering pipeline - default_pipelines_init(ren); + /* default_pipelines_init(ren); */ return true; } @@ -67,9 +67,6 @@ void renderer_shutdown(renderer* ren) { void default_pipelines_init(renderer* ren) { // Static opaque geometry - // graphics_pipeline_desc gfx = { - // }; - // ren->static_opaque_pipeline = gpu_graphics_pipeline_create(); arena scratch = arena_create(malloc(1024 * 1024), 1024 * 1024); gpu_renderpass_desc pass_description = {}; @@ -77,8 +74,8 @@ void default_pipelines_init(renderer* ren) { ren->default_renderpass = *renderpass; - str8 vert_path = str8lit("celeritas-core/build/linux/x86_64/debug/triangle.vert.spv"); - str8 frag_path = str8lit("celeritas-core/build/linux/x86_64/debug/triangle.frag.spv"); + str8 vert_path = str8lit("build/linux/x86_64/debug/triangle.vert.spv"); + str8 frag_path = str8lit("build/linux/x86_64/debug/triangle.frag.spv"); str8_opt vertex_shader = str8_from_file(&scratch, vert_path); str8_opt fragment_shader = str8_from_file(&scratch, frag_path); if (!vertex_shader.has_value || !fragment_shader.has_value) { @@ -104,28 +101,28 @@ void default_pipelines_init(renderer* ren) { } void render_frame_begin(renderer* ren) { - ren->frame_aborted = false; - if (!gpu_backend_begin_frame()) { - ren->frame_aborted = true; - return; - } - gpu_cmd_encoder* enc = gpu_get_default_cmd_encoder(); - // begin recording - gpu_cmd_encoder_begin(*enc); - gpu_cmd_encoder_begin_render(enc, &ren->default_renderpass); - encode_bind_pipeline(enc, PIPELINE_GRAPHICS, &ren->static_opaque_pipeline); - encode_set_default_settings(enc); + ren->frame_aborted = false; + if (!gpu_backend_begin_frame()) { + ren->frame_aborted = true; + return; + } + gpu_cmd_encoder* enc = gpu_get_default_cmd_encoder(); + // begin recording + gpu_cmd_encoder_begin(*enc); + gpu_cmd_encoder_begin_render(enc, &ren->default_renderpass); + encode_bind_pipeline(enc, PIPELINE_GRAPHICS, &ren->static_opaque_pipeline); + encode_set_default_settings(enc); } void render_frame_end(renderer* ren) { if (ren->frame_aborted) { return; } - gpu_temp_draw(); - gpu_cmd_encoder* enc = gpu_get_default_cmd_encoder(); - gpu_cmd_encoder_end_render(enc); - gpu_cmd_buffer buf = gpu_cmd_encoder_finish(enc); - gpu_queue_submit(&buf); - gpu_backend_end_frame(); + gpu_temp_draw(3); + gpu_cmd_encoder* enc = gpu_get_default_cmd_encoder(); + gpu_cmd_encoder_end_render(enc); + gpu_cmd_buffer buf = gpu_cmd_encoder_finish(enc); + gpu_queue_submit(&buf); + gpu_backend_end_frame(); } void render_frame_draw(renderer* ren) {} diff --git a/src/scene.h b/src/scene.h index 5399ab7..24429b3 100644 --- a/src/scene.h +++ b/src/scene.h @@ -11,8 +11,8 @@ #pragma once #include "camera.h" #include "defines.h" -#include "render_types.h" #include "maths_types.h" +#include "render_types.h" typedef struct scene { // camera -- cgit v1.2.3-70-g09d2 From e61a2e43947cebaafe4c3725414d33e092bb6fad Mon Sep 17 00:00:00 2001 From: Omniscient Date: Fri, 17 May 2024 08:39:51 +1000 Subject: cube working --- assets/shaders/cube.vert | 9 +- assets/shaders/triangle.frag | 4 +- examples/README.md | 11 ++ examples/cube/ex_cube.c | 58 ++++---- src/maths/primitives.c | 253 +++++++++++++-------------------- src/renderer/backends/backend_vulkan.c | 103 ++++++++++++-- src/renderer/ral_types.h | 3 +- src/renderer/render.c | 43 ++++++ src/renderer/render.h | 21 ++- src/renderer/render_types.h | 3 + 10 files changed, 302 insertions(+), 206 deletions(-) create mode 100644 examples/README.md (limited to 'src/renderer/render.c') diff --git a/assets/shaders/cube.vert b/assets/shaders/cube.vert index 80ad854..1818c3c 100644 --- a/assets/shaders/cube.vert +++ b/assets/shaders/cube.vert @@ -7,12 +7,13 @@ layout(binding = 0) uniform UniformBufferObject { } ubo; -layout(location = 0) in vec2 inPosition; -layout(location = 1) in vec3 inColor; +layout(location = 0) in vec3 inPosition; +layout(location = 1) in vec3 inNormal; +layout(location = 2) in vec3 inTexCoords; layout(location = 0) out vec3 fragColor; void main() { - gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0); - fragColor = inColor; + gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0); + fragColor = abs(inNormal); } diff --git a/assets/shaders/triangle.frag b/assets/shaders/triangle.frag index 2151162..a8c6e69 100644 --- a/assets/shaders/triangle.frag +++ b/assets/shaders/triangle.frag @@ -3,4 +3,6 @@ layout(location = 0) in vec3 fragColor; layout(location = 0) out vec4 outColor; -void main() { outColor = vec4(fragColor, 1.0); } \ No newline at end of file +void main() { + outColor = vec4(fragColor, 0.0); +} diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..f289e80 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,11 @@ + +### RAL examples + +These examples show off how the lowest register of rendering, the Rendering Abstraction Layer (RAL), is used to draw things to the screen. + +- triangle +- cube + +### Renderer + +The 'renderer' is the default renderer provided by Celeritas. diff --git a/examples/cube/ex_cube.c b/examples/cube/ex_cube.c index a115fe3..7c7831d 100644 --- a/examples/cube/ex_cube.c +++ b/examples/cube/ex_cube.c @@ -8,20 +8,14 @@ #include "maths.h" #include "maths_types.h" #include "mem.h" +#include "primitives.h" #include "ral.h" #include "ral_types.h" #include "render.h" +#include "render_types.h" extern core g_core; -const custom_vertex vertices[] = { - (custom_vertex){ .pos = vec2(-0.5, 0.5), .color = vec3(0.0, 0.0, 1.0) }, - (custom_vertex){ .pos = vec2(0.5, 0.5), .color = vec3(0.0, 1.0, 0.0) }, - (custom_vertex){ .pos = vec2(0.5, -0.5), .color = vec3(1.0, 0.0, 0.0) }, - (custom_vertex){ .pos = vec2(-0.5, -0.5), .color = vec3(1.0, 1.0, 1.0) }, -}; -const u16 indices[] = { 0, 1, 2, 2, 3, 0 }; - // Define the shader data typedef struct mvp_uniforms { mat4 model; @@ -29,6 +23,7 @@ typedef struct mvp_uniforms { mat4 projection; } mvp_uniforms; +// We also must create a function that knows how to return a `shader_data_layout` shader_data_layout mvp_uniforms_layout(void* data) { mvp_uniforms* d = (mvp_uniforms*)data; bool has_data = data != NULL; @@ -40,9 +35,6 @@ shader_data_layout mvp_uniforms_layout(void* data) { if (has_data) { b1.data.bytes.data = d; } - /* char* name; */ - /* shader_binding bindings[MAX_LAYOUT_BINDINGS]; */ - /* u32 bindings_count; */ return (shader_data_layout){ .name = "global_ubo", .bindings = { b1 }, .bindings_count = 1 }; } @@ -52,9 +44,15 @@ int main() { DEBUG("render capacity %d", g_core.default_scene.renderables->capacity); - vertex_description vertex_input = {0}; - vertex_desc_add(&vertex_input, "inPosition", ATTR_F32x2); - vertex_desc_add(&vertex_input, "inColor", ATTR_F32x3); + vec3 camera_pos = vec3(2., 2., 2.); + vec3 camera_front = vec3_normalise(vec3_negate(camera_pos)); + camera cam = camera_create(camera_pos, camera_front, VEC3_Y, deg_to_rad(45.0)); + + vertex_description vertex_input; + vertex_input.debug_label = "Standard Static 3D Vertex Format"; + vertex_desc_add(&vertex_input, "inPosition", ATTR_F32x3); + vertex_desc_add(&vertex_input, "inNormal", ATTR_F32x3); + vertex_desc_add(&vertex_input, "inTexCoords", ATTR_F32x2); shader_data mvp_uniforms_data = { .data = NULL, .shader_data_get_layout = &mvp_uniforms_layout }; @@ -88,19 +86,13 @@ int main() { }; gpu_pipeline* gfx_pipeline = gpu_graphics_pipeline_create(pipeline_description); - buffer_handle triangle_vert_buf = - gpu_buffer_create(sizeof(vertices), CEL_BUFFER_VERTEX, CEL_BUFFER_FLAG_GPU, vertices); - - buffer_handle triangle_index_buf = - gpu_buffer_create(sizeof(indices), CEL_BUFFER_INDEX, CEL_BUFFER_FLAG_GPU, indices); + geometry_data cube_data = geo_create_cuboid(f32x3(1, 1, 1)); + mesh cube = mesh_create(&cube_data, false); // Main loop while (!should_exit(&g_core)) { - glfwPollEvents(); input_update(&g_core.input); - // render_frame_begin(&g_core.renderer); - if (!gpu_backend_begin_frame()) { continue; } @@ -111,20 +103,26 @@ int main() { encode_bind_pipeline(enc, PIPELINE_GRAPHICS, gfx_pipeline); encode_set_default_settings(enc); - /* shader_data_layout mvp_layout = mvp_uniforms_layout(NULL); */ - static f32 x = 0.0; x += 0.01; - quat rotation = quat_from_axis_angle(VEC3_Z, x, true); + quat rotation = quat_from_axis_angle(VEC3_Y, x, true); + mat4 translation = mat4_translation(vec3(-0.5, -0.5, -0.5)); mat4 model = mat4_rotation(rotation); - mvp_uniforms mvp_data = { .model = model, .view = mat4_ident(), .projection = mat4_ident() }; + model = mat4_mult(translation, model); + mat4 view, proj; + camera_view_projection(&cam, g_core.renderer.swapchain.extent.width, + g_core.renderer.swapchain.extent.height, &view, &proj); + mvp_uniforms mvp_data = { .model = model, .view = view, .projection = proj }; mvp_uniforms_data.data = &mvp_data; + encode_bind_shader_data(enc, 0, &mvp_uniforms_data); // Record draw calls - encode_set_vertex_buffer(enc, triangle_vert_buf); - encode_set_index_buffer(enc, triangle_index_buf); - encode_bind_shader_data(enc, 0, &mvp_uniforms_data); - gpu_temp_draw(6); + // -- NEW + draw_mesh(&cube, &model); + // -- OLD + /* encode_set_vertex_buffer(enc, triangle_vert_buf); */ + /* encode_set_index_buffer(enc, triangle_index_buf); */ + /* gpu_temp_draw(6); */ // End recording gpu_cmd_encoder_end_render(enc); diff --git a/src/maths/primitives.c b/src/maths/primitives.c index 459a535..b9ec868 100644 --- a/src/maths/primitives.c +++ b/src/maths/primitives.c @@ -1,6 +1,18 @@ #include "primitives.h" +#include "colours.h" #include "maths.h" #include "ral_types.h" +#include "render_types.h" + +// TODO: move to another file +void geo_free_data(geometry_data* geo) { + vertex_darray_free(geo->vertices); + geo->vertices = NULL; + // TODO: do indices as well + /* if (geo->has_indices) { */ + /* u32_darray_free(&geo->indices); */ + /* } */ +} // vertices f32 plane_vertex_positions[] = { @@ -31,169 +43,96 @@ static const vec3 FRONT_BOT_RIGHT = (vec3){ 1, 0, 1 }; static const vec3 FRONT_TOP_LEFT = (vec3){ 0, 1, 1 }; static const vec3 FRONT_TOP_RIGHT = (vec3){ 1, 1, 1 }; -#define VERT_3D(arr, pos, norm, uv) \ - { \ - vertex v = { - .static_3d = { \ - .position = pos, \ - .normal = norm, \ - .tex_coords = uv \ - }}; \ - vertex_darray_push(arr, v); \ - } - -static mesh prim_cube_mesh_create() { - mesh cube = { 0 }; - cube.vertices = vertex_darray_new(36); - - // back faces - VERT_3D(cube.vertices, BACK_BOT_LEFT, VEC3_NEG_Z, (vec2){ 0, 1 })) - // vertex_darray_push( - // cube.vertices, - // (vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_Z, .uv = (vec2){ 0, 1 } }); - // vertex_darray_push( - // cube.vertices, - // (vertex){ .position = BACK_TOP_LEFT, .normal = VEC3_NEG_Z, .uv = (vec2){ 0, 0 } }); - // vertex_darray_push( - // cube.vertices, - // (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_NEG_Z, .uv = (vec2){ 1, 0 } }); - // vertex_darray_push( - // cube.vertices, - // (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_NEG_Z, .uv = (vec2){ 1, 0 } }); - // vertex_darray_push( - // cube.vertices, - // (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_NEG_Z, .uv = (vec2){ 1, 1 } }); - // vertex_darray_push( - // cube.vertices, - // (vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_Z, .uv = (vec2){ 0, 1 } }); - - // front faces - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_BOT_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0, 1 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 1, 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_TOP_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0, 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_BOT_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0, 1 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 1, 1 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 1, 0 } }); - - // top faces - vertex_darray_push(cube.vertices, - (vertex){ .position = BACK_TOP_LEFT, .normal = VEC3_Y, .uv = (vec2){ 0, 0 - } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_TOP_LEFT, .normal = VEC3_Y, .uv = (vec2){ 0, 1 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 1, 1 } }); - vertex_darray_push(cube.vertices, - (vertex){ .position = BACK_TOP_LEFT, .normal = VEC3_Y, .uv = (vec2){ 0, 0 - } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 1, 1 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 1, 0 } }); - - // bottom faces - vertex_darray_push( - cube.vertices, - (vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_Y, .uv = (vec2){ 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_NEG_Y, .uv = (vec2){ 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_BOT_LEFT, .normal = VEC3_NEG_Y, .uv = (vec2){ 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_Y, .uv = (vec2){ 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_NEG_Y, .uv = (vec2){ 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_NEG_Y, .uv = (vec2){ 0 } }); - - // right faces - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0, 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 1, 1 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 1, 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 1, 1 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0, 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0, 1 } }); - - // left faces - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_TOP_LEFT, .normal = VEC3_NEG_X, .uv = (vec2){ 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = BACK_TOP_LEFT, .normal = VEC3_NEG_X, .uv = (vec2){ 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_X, .uv = (vec2){ 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_X, .uv = (vec2){ 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_BOT_LEFT, .normal = VEC3_NEG_X, .uv = (vec2){ 0 } }); - vertex_darray_push( - cube.vertices, - (vertex){ .position = FRONT_TOP_LEFT, .normal = VEC3_NEG_X, .uv = (vec2){ 0 } }); - - cube.indices_len = cube.vertices->len; - cube.indices = malloc(sizeof(u32) * cube.indices_len); - - for (u32 i = 0; i < cube.indices_len; i++) { - cube.indices[i] = i; - } - - cube.has_indices = true; - - return cube; +#define VERT_3D(arr, pos, norm, uv) \ + { \ + vertex v = { .static_3d = { .position = pos, .normal = norm, .tex_coords = uv } }; \ + vertex_darray_push(arr, v); \ + } + +geometry_data geo_create_cuboid(f32x3 extents) { + /* static mesh prim_cube_mesh_create() { */ + vertex_darray* vertices = vertex_darray_new(36); + + // back faces + VERT_3D(vertices, BACK_TOP_RIGHT, VEC3_NEG_Z, vec2(1, 0)); + VERT_3D(vertices, BACK_BOT_LEFT, VEC3_NEG_Z, vec2(0, 1)); + VERT_3D(vertices, BACK_TOP_LEFT, VEC3_NEG_Z, vec2(0, 0)); + VERT_3D(vertices, BACK_TOP_RIGHT, VEC3_NEG_Z, vec2(1, 0)); + VERT_3D(vertices, BACK_BOT_RIGHT, VEC3_NEG_Z, vec2(1, 1)); + VERT_3D(vertices, BACK_BOT_LEFT, VEC3_NEG_Z, vec2(0, 1)); + + // front faces + VERT_3D(vertices, FRONT_BOT_LEFT, VEC3_Z, vec2(0, 1)); + VERT_3D(vertices, FRONT_TOP_RIGHT, VEC3_Z, vec2(1, 0)); + VERT_3D(vertices, FRONT_TOP_LEFT, VEC3_Z, vec2(0, 0)); + VERT_3D(vertices, FRONT_BOT_LEFT, VEC3_Z, vec2(0, 1)); + VERT_3D(vertices, FRONT_BOT_RIGHT, VEC3_Z, vec2(1, 1)); + VERT_3D(vertices, FRONT_TOP_RIGHT, VEC3_Z, vec2(1, 0)); + + // top faces + VERT_3D(vertices, BACK_TOP_LEFT, VEC3_Y, vec2(0, 0)); + VERT_3D(vertices, FRONT_TOP_LEFT, VEC3_Y, vec2(0, 1)); + VERT_3D(vertices, FRONT_TOP_RIGHT, VEC3_Y, vec2(1, 1)); + VERT_3D(vertices, BACK_TOP_LEFT, VEC3_Y, vec2(0, 0)); + VERT_3D(vertices, FRONT_TOP_RIGHT, VEC3_Y, vec2(1, 1)); + VERT_3D(vertices, BACK_TOP_RIGHT, VEC3_Y, vec2(1, 0)); + + // bottom faces + VERT_3D(vertices, BACK_BOT_LEFT, VEC3_NEG_Y, vec2(0, 1)); + VERT_3D(vertices, FRONT_BOT_RIGHT, VEC3_NEG_Y, vec2(1, 1)); + VERT_3D(vertices, FRONT_BOT_LEFT, VEC3_NEG_Y, vec2(0, 1)); + VERT_3D(vertices, BACK_BOT_LEFT, VEC3_NEG_Y, vec2(0, 1)); + VERT_3D(vertices, BACK_BOT_RIGHT, VEC3_NEG_Y, vec2(1, 1)); + VERT_3D(vertices, FRONT_BOT_RIGHT, VEC3_NEG_Y, vec2(0, 1)); + + // right faces + VERT_3D(vertices, FRONT_TOP_RIGHT, VEC3_X, vec2(0, 0)); + VERT_3D(vertices, BACK_BOT_RIGHT, VEC3_X, vec2(1, 1)); + VERT_3D(vertices, BACK_TOP_RIGHT, VEC3_X, vec2(1, 0)); + VERT_3D(vertices, BACK_BOT_RIGHT, VEC3_X, vec2(1, 1)); + VERT_3D(vertices, FRONT_TOP_RIGHT, VEC3_X, vec2(0, 0)); + VERT_3D(vertices, FRONT_BOT_RIGHT, VEC3_X, vec2(0, 1)); + + // left faces + VERT_3D(vertices, FRONT_TOP_LEFT, VEC3_NEG_X, vec2(0, 0)); + VERT_3D(vertices, BACK_TOP_LEFT, VEC3_NEG_X, vec2(0, 0)); + VERT_3D(vertices, BACK_BOT_LEFT, VEC3_NEG_X, vec2(0, 0)); + VERT_3D(vertices, BACK_BOT_LEFT, VEC3_NEG_X, vec2(0, 0)); + VERT_3D(vertices, FRONT_BOT_LEFT, VEC3_NEG_X, vec2(0, 0)); + VERT_3D(vertices, FRONT_TOP_LEFT, VEC3_NEG_X, vec2(0, 0)); + + u32_darray* indices = u32_darray_new(vertices->len); + + for (u32 i = 0; i < vertices->len; i++) { + u32_darray_push(indices, i); + } + + geometry_data geo = { + .format = VERTEX_STATIC_3D, + .vertices = vertices, + .has_indices = true, + .indices = *indices, // FIXME: make darray methods that return stack allocated struct + .colour = vec3(0, 0, 0), + }; + + return geo; } -/** @brief create a new model with the shape of a cube */ -static model_handle prim_cube_new(core* core) { - model model = { 0 }; - mesh cube = prim_cube_mesh_create(); +/* /\** @brief create a new model with the shape of a cube *\/ */ +/* static model_handle prim_cube_new(core* core) { */ +/* model model = { 0 }; */ +/* mesh cube = prim_cube_mesh_create(); */ - mesh_darray_push(model.meshes, cube); - assert(mesh_darray_len(model.meshes) == 1); +/* mesh_darray_push(model.meshes, cube); */ +/* assert(mesh_darray_len(model.meshes) == 1); */ - u32 index = (u32)model_darray_len(core->models); - model_darray_push_copy(core->models, &model); - return (model_handle){ .raw = index }; -} +/* u32 index = (u32)model_darray_len(core->models); */ +/* model_darray_push_copy(core->models, &model); */ +/* return (model_handle){ .raw = index }; */ +/* } */ // --- Spheres geometry_data geo_create_uvsphere(f32 radius, f32 north_south_lines, f32 east_west_lines) { // TODO -} \ No newline at end of file +} diff --git a/src/renderer/backends/backend_vulkan.c b/src/renderer/backends/backend_vulkan.c index f83ef84..3a9c4e1 100644 --- a/src/renderer/backends/backend_vulkan.c +++ b/src/renderer/backends/backend_vulkan.c @@ -84,6 +84,7 @@ bool create_logical_device(gpu_device* out_device); void create_swapchain_framebuffers(); void create_sync_objects(); void create_descriptor_pools(); +size_t vertex_attrib_size(vertex_attrib_type attr); VkShaderModule create_shader_module(str8 spirv); @@ -106,7 +107,7 @@ bool gpu_backend_init(const char* window_name, GLFWwindow* window) { // Setup Vulkan instance VkApplicationInfo app_info = { VK_STRUCTURE_TYPE_APPLICATION_INFO }; - app_info.apiVersion = VK_API_VERSION_1_3; + app_info.apiVersion = VK_API_VERSION_1_2; app_info.pApplicationName = window_name; app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0); app_info.pEngineName = "Celeritas Engine"; @@ -351,6 +352,35 @@ static void recreate_swapchain(gpu_swapchain* swapchain) { create_swapchain_framebuffers(); } +VkFormat format_from_vertex_attr(vertex_attrib_type attr) { + switch (attr) { + case ATTR_F32: + return VK_FORMAT_R32_SFLOAT; + case ATTR_U32: + return VK_FORMAT_R32_UINT; + case ATTR_I32: + return VK_FORMAT_R32_SINT; + case ATTR_F32x2: + return VK_FORMAT_R32G32_SFLOAT; + case ATTR_U32x2: + return VK_FORMAT_R32G32_UINT; + case ATTR_I32x2: + return VK_FORMAT_R32G32_UINT; + case ATTR_F32x3: + return VK_FORMAT_R32G32B32_SFLOAT; + case ATTR_U32x3: + return VK_FORMAT_R32G32B32_UINT; + case ATTR_I32x3: + return VK_FORMAT_R32G32B32_SINT; + case ATTR_F32x4: + return VK_FORMAT_R32G32B32A32_SFLOAT; + case ATTR_U32x4: + return VK_FORMAT_R32G32B32A32_UINT; + case ATTR_I32x4: + return VK_FORMAT_R32G32B32A32_SINT; + } +} + gpu_pipeline* gpu_graphics_pipeline_create(struct graphics_pipeline_desc description) { // Allocate gpu_pipeline_layout* layout = malloc(sizeof(gpu_pipeline_layout)); @@ -381,22 +411,35 @@ gpu_pipeline* gpu_graphics_pipeline_create(struct graphics_pipeline_desc descrip frag_shader_stage_info }; // TODO: Attributes - VkVertexInputAttributeDescription attribute_descs[2]; + VkVertexInputAttributeDescription attribute_descs[2] = {0}; + /* u32 offset = 0; */ + /* for (u32 i = 0; i < description.vertex_desc.attributes_count; i++) { */ + /* attribute_descs[i].binding = 0; */ + /* attribute_descs[i].location = i; */ + /* attribute_descs[i].format = format_from_vertex_attr(description.vertex_desc.attributes[i]); */ + /* attribute_descs[i].offset = offset; */ + /* size_t this_offset = vertex_attrib_size(description.vertex_desc.attributes[i]); */ + /* printf("offset total %d this attr %ld\n", offset, this_offset); */ + /* printf("sizeof vertex %ld\n", sizeof(vertex)); */ + /* printf("%d \n", offsetof(vertex, static_3d)); */ + /* offset += this_offset; */ + /* } */ + printf("Vertex attributes\n"); attribute_descs[0].binding = 0; attribute_descs[0].location = 0; - attribute_descs[0].format = VK_FORMAT_R32G32_SFLOAT; - attribute_descs[0].offset = offsetof(custom_vertex, pos); + attribute_descs[0].format = VK_FORMAT_R32G32B32_SFLOAT; + attribute_descs[0].offset = 0; // offsetof(custom_vertex, pos); attribute_descs[1].binding = 0; attribute_descs[1].location = 1; attribute_descs[1].format = VK_FORMAT_R32G32B32_SFLOAT; - attribute_descs[1].offset = offsetof(custom_vertex, color); + attribute_descs[1].offset = 12; // offsetof(custom_vertex, color); // Vertex input // TODO: Generate this from descroiption now VkVertexInputBindingDescription binding_desc; binding_desc.binding = 0; - binding_desc.stride = sizeof(custom_vertex); + binding_desc.stride = sizeof(vertex); // description.vertex_desc.stride; binding_desc.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; VkPipelineVertexInputStateCreateInfo vertex_input_info = { @@ -404,7 +447,7 @@ gpu_pipeline* gpu_graphics_pipeline_create(struct graphics_pipeline_desc descrip }; vertex_input_info.vertexBindingDescriptionCount = 1; vertex_input_info.pVertexBindingDescriptions = &binding_desc; - vertex_input_info.vertexAttributeDescriptionCount = 2; + vertex_input_info.vertexAttributeDescriptionCount = 2; // description.vertex_desc.attributes_count; vertex_input_info.pVertexAttributeDescriptions = attribute_descs; // Input Assembly @@ -441,6 +484,7 @@ gpu_pipeline* gpu_graphics_pipeline_create(struct graphics_pipeline_desc descrip rasterizer_create_info.lineWidth = 1.0f; rasterizer_create_info.cullMode = VK_CULL_MODE_BACK_BIT; rasterizer_create_info.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + /* rasterizer_create_info.frontFace = VK_FRONT_FACE_CLOCKWISE; */ rasterizer_create_info.depthBiasEnable = VK_FALSE; rasterizer_create_info.depthBiasConstantFactor = 0.0; rasterizer_create_info.depthBiasClamp = 0.0; @@ -584,6 +628,7 @@ gpu_pipeline* gpu_graphics_pipeline_create(struct graphics_pipeline_desc descrip VK_CHECK(vkCreateDescriptorSetLayout(context.device->logical_device, &desc_set_layout_info, context.allocator, &desc_set_layouts[i])); } + printf("Descriptor set layouts\n"); // Layout VkPipelineLayoutCreateInfo pipeline_layout_create_info = { @@ -621,13 +666,18 @@ gpu_pipeline* gpu_graphics_pipeline_create(struct graphics_pipeline_desc descrip pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE; pipeline_create_info.basePipelineIndex = -1; + printf("Here\n"); + VkResult result = vkCreateGraphicsPipelines(context.device->logical_device, VK_NULL_HANDLE, 1, &pipeline_create_info, context.allocator, &pipeline->handle); if (result != VK_SUCCESS) { + printf("BAD\n"); FATAL("graphics pipeline creation failed. its fked mate"); ERROR_EXIT("Doomed"); } + printf("Here2\n"); + TRACE("Vulkan Graphics pipeline created"); // once the pipeline has been created we can destroy these vkDestroyShaderModule(context.device->logical_device, vertex_shader, context.allocator); @@ -820,9 +870,7 @@ void encode_bind_shader_data(gpu_cmd_encoder* encoder, u32 group, shader_data* d alloc_info.pSetLayouts = &encoder->pipeline->desc_set_layouts[group]; VkDescriptorSet sets[1]; - VK_CHECK( - vkAllocateDescriptorSets(context.device->logical_device, &alloc_info, sets) - ); + VK_CHECK(vkAllocateDescriptorSets(context.device->logical_device, &alloc_info, sets)); VkDescriptorSet_darray_push(context.free_set_queue, sets[0]); shader_data_layout sdl = data->shader_data_get_layout(NULL); @@ -870,7 +918,7 @@ void encode_set_vertex_buffer(gpu_cmd_encoder* encoder, buffer_handle buf) { void encode_set_index_buffer(gpu_cmd_encoder* encoder, buffer_handle buf) { gpu_buffer buffer = context.buffers[buf.raw]; - vkCmdBindIndexBuffer(encoder->cmd_buffer, buffer.handle, 0, VK_INDEX_TYPE_UINT16); + vkCmdBindIndexBuffer(encoder->cmd_buffer, buffer.handle, 0, VK_INDEX_TYPE_UINT32); } // TEMP @@ -1310,3 +1358,36 @@ void encode_buffer_copy(gpu_cmd_encoder* encoder, buffer_handle src, u64 src_off vkCmdCopyBuffer(encoder->cmd_buffer, context.buffers[src.raw].handle, context.buffers[dst.raw].handle, 1, ©_region); } + +size_t vertex_attrib_size(vertex_attrib_type attr) { + switch (attr) { + case ATTR_F32: + case ATTR_U32: + case ATTR_I32: + return 4; + case ATTR_F32x2: + case ATTR_U32x2: + case ATTR_I32x2: + return 8; + case ATTR_F32x3: + case ATTR_U32x3: + case ATTR_I32x3: + return 12; + case ATTR_F32x4: + case ATTR_U32x4: + case ATTR_I32x4: + return 16; + break; + } +} + +void vertex_desc_add(vertex_description* builder, const char* name, vertex_attrib_type type) { + u32 i = builder->attributes_count; + + size_t size = vertex_attrib_size(type); + builder->attributes[i] = type; + builder->stride += size; + builder->attr_names[i] = name; + + builder->attributes_count++; +} diff --git a/src/renderer/ral_types.h b/src/renderer/ral_types.h index 48695ea..c802a9b 100644 --- a/src/renderer/ral_types.h +++ b/src/renderer/ral_types.h @@ -85,8 +85,8 @@ typedef enum vertex_format { typedef union vertex { struct { vec3 position; - vec2 tex_coords; vec3 normal; + vec2 tex_coords; } static_3d; /** @brief standard vertex format for static geometry in 3D */ struct { @@ -149,6 +149,7 @@ typedef struct vertex_description { char* debug_label; const char* attr_names[MAX_VERTEX_ATTRIBUTES]; vertex_attrib_type attributes[MAX_VERTEX_ATTRIBUTES]; + u32 attributes_count; size_t stride; } vertex_description; diff --git a/src/renderer/render.c b/src/renderer/render.c index 9f5b21a..efef6a6 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -4,6 +4,7 @@ #include "file.h" #include "log.h" #include "ral.h" +#include "ral_types.h" /** @brief Creates the pipelines built into Celeritas such as rendering static opaque geometry, debug visualisations, immediate mode UI, etc */ @@ -126,6 +127,48 @@ void render_frame_end(renderer* ren) { } void render_frame_draw(renderer* ren) {} +void draw_mesh(mesh* mesh, mat4* model) { // , mat4* view, mat4* proj) { + gpu_cmd_encoder* enc = gpu_get_default_cmd_encoder(); + encode_set_vertex_buffer(enc, mesh->vertex_buffer); + if (mesh->has_indices) { + encode_set_index_buffer(enc, mesh->index_buffer); + } + // Assume this has already been done + /* encode_bind_shader_data(enc, 0, &mvp_uniforms_data); */ + encode_draw_indexed(enc, mesh->index_count); +} + void gfx_backend_draw_frame(renderer* ren, camera* camera, mat4 model, texture* tex) {} void geo_set_vertex_colours(geometry_data* geo, vec4 colour) {} + +// --- NEW + +mesh mesh_create(geometry_data* geometry, bool free_on_upload) { + mesh m = { 0 }; + + // Create and upload vertex buffer + size_t vert_bytes = geometry->vertices->len * sizeof(vertex); + INFO("Creating vertex buffer with size %d (%d x %d)", vert_bytes, geometry->vertices->len, + sizeof(vertex)); + m.vertex_buffer = gpu_buffer_create(vert_bytes, CEL_BUFFER_VERTEX, CEL_BUFFER_FLAG_GPU, + geometry->vertices->data); + + // Create and upload index buffer + size_t index_bytes = geometry->indices.len * sizeof(u32); + INFO("Creating index buffer with size %d (len: %d)", index_bytes, geometry->indices.len); + m.index_buffer = + gpu_buffer_create(index_bytes, CEL_BUFFER_INDEX, CEL_BUFFER_FLAG_GPU, geometry->indices.data); + + m.is_uploaded = true; + m.has_indices = geometry->has_indices; + m.index_count = geometry->indices.len; + m.vertices = geometry; + if (free_on_upload) { + geo_free_data(geometry); + } + + // TODO: materials? + + return m; +} diff --git a/src/renderer/render.h b/src/renderer/render.h index 4477121..c87e5f7 100644 --- a/src/renderer/render.h +++ b/src/renderer/render.h @@ -25,6 +25,11 @@ void render_frame_draw(renderer* ren); typedef struct camera camera; void gfx_backend_draw_frame(renderer* ren, camera* camera, mat4 model, texture* tex); +typedef struct render_ctx { + mat4 view; + mat4 projection; +} render_ctx; + // frontend -- these can be called from say a loop in an example, or via FFI texture_handle texture_create(const char* debug_name, texture_desc description, const u8* data); @@ -39,10 +44,22 @@ sampler_handle sampler_create(); void shader_hot_reload(const char* filepath); // models and meshes are implemented **in terms of the above** -mesh mesh_create(geometry_data* geometry); + +/** + * @brief Creates buffers and returns a struct that holds handles to our resources + * + * @param geometry + * @param free_on_upload frees the CPU-side vertex/index data stored in geometry_data when we successfully upload + that data to the GPU-side buffer + * @return mesh + */ +mesh mesh_create(geometry_data* geometry, bool free_on_upload); + +void draw_mesh(mesh* mesh, mat4* model);//, mat4* view, mat4* proj); // TODO: material model_handle model_load(const char* debug_name, const char* filepath); +void geo_free_data(geometry_data* geo); void geo_set_vertex_colours(geometry_data* geo, vec4 colour); -vertex_description static_3d_vertex_description(); \ No newline at end of file +vertex_description static_3d_vertex_description(); diff --git a/src/renderer/render_types.h b/src/renderer/render_types.h index 06a8415..ac98627 100644 --- a/src/renderer/render_types.h +++ b/src/renderer/render_types.h @@ -45,12 +45,15 @@ typedef struct geometry_data { vec3 colour; /** Optional: set vertex colours */ } geometry_data; +// 'Upload' a geometry_data (to GPU) -> get back a mesh typedef struct mesh { buffer_handle vertex_buffer; buffer_handle index_buffer; u32 index_count; bool has_indices; geometry_data* vertices; // NULL means it has been freed + bool is_uploaded; + bool is_latent; } mesh; /* Hot reloading: -- cgit v1.2.3-70-g09d2 From f99fef4eb5b2c29a84d9e835a66b1afd333bcfc0 Mon Sep 17 00:00:00 2001 From: Omniscient Date: Fri, 17 May 2024 23:27:35 +1000 Subject: create default pipelines when initialising renderer --- src/core.c | 4 +++- src/renderer/render.c | 20 ++++++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) (limited to 'src/renderer/render.c') diff --git a/src/core.c b/src/core.c index 84c9cae..83c7b6e 100644 --- a/src/core.c +++ b/src/core.c @@ -78,4 +78,6 @@ void frame_begin() { render_frame_begin(&g_core.renderer); } void frame_draw() {} -void frame_end() { render_frame_end(&g_core.renderer); } +void frame_end() { + render_frame_end(&g_core.renderer); +} diff --git a/src/renderer/render.c b/src/renderer/render.c index efef6a6..6eacc0d 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -56,7 +56,7 @@ bool renderer_init(renderer* ren) { // default_material_init(); // Create default rendering pipeline - /* default_pipelines_init(ren); */ + default_pipelines_init(ren); return true; } @@ -75,16 +75,28 @@ void default_pipelines_init(renderer* ren) { ren->default_renderpass = *renderpass; - str8 vert_path = str8lit("build/linux/x86_64/debug/triangle.vert.spv"); - str8 frag_path = str8lit("build/linux/x86_64/debug/triangle.frag.spv"); + // str8 vert_path = str8lit("build/linux/x86_64/debug/triangle.vert.spv"); + // str8 frag_path = str8lit("build/linux/x86_64/debug/triangle.frag.spv"); + printf("Load shaders\n"); + str8 vert_path = str8lit("/home/void/code/celeritas-engine/celeritas-core/build/linux/x86_64/debug/triangle.vert.spv"); + str8 frag_path = str8lit("/home/void/code/celeritas-engine/celeritas-core/build/linux/x86_64/debug/triangle.frag.spv"); str8_opt vertex_shader = str8_from_file(&scratch, vert_path); str8_opt fragment_shader = str8_from_file(&scratch, frag_path); if (!vertex_shader.has_value || !fragment_shader.has_value) { ERROR_EXIT("Failed to load shaders from disk") } + vertex_description vertex_input; + vertex_input.debug_label = "Standard Static 3D Vertex Format"; + vertex_desc_add(&vertex_input, "inPosition", ATTR_F32x3); + vertex_desc_add(&vertex_input, "inNormal", ATTR_F32x3); + vertex_desc_add(&vertex_input, "inTexCoords", ATTR_F32x2); + struct graphics_pipeline_desc pipeline_description = { .debug_name = "Basic Pipeline", + .vertex_desc = vertex_input, + // .data_layouts + // .data_layouts_count .vs = { .debug_name = "Triangle Vertex Shader", .filepath = vert_path, .code = vertex_shader.contents, @@ -118,7 +130,7 @@ void render_frame_end(renderer* ren) { if (ren->frame_aborted) { return; } - gpu_temp_draw(3); + // gpu_temp_draw(3); gpu_cmd_encoder* enc = gpu_get_default_cmd_encoder(); gpu_cmd_encoder_end_render(enc); gpu_cmd_buffer buf = gpu_cmd_encoder_finish(enc); -- cgit v1.2.3-70-g09d2 From fcaf838886efc3c12f60b47c021c6a85965eb6e9 Mon Sep 17 00:00:00 2001 From: Omniscient Date: Sat, 18 May 2024 14:15:23 +1000 Subject: initialise gpu resource pools in renderer --- assets/textures/texture.jpg | Bin 0 -> 77120 bytes scene.c | 1 + src/core.c | 4 +--- src/renderer/backends/backend_vulkan.c | 24 ++++++++++++++++++++++++ src/renderer/ral.c | 12 ++++++------ src/renderer/ral.h | 17 +++++++++++++++-- src/renderer/render.c | 19 ++++++++++++++----- src/renderer/render_types.h | 1 + src/scene.h | 6 ++++++ 9 files changed, 68 insertions(+), 16 deletions(-) create mode 100644 assets/textures/texture.jpg create mode 100644 scene.c (limited to 'src/renderer/render.c') diff --git a/assets/textures/texture.jpg b/assets/textures/texture.jpg new file mode 100644 index 0000000..975e338 Binary files /dev/null and b/assets/textures/texture.jpg differ diff --git a/scene.c b/scene.c new file mode 100644 index 0000000..69a8b3c --- /dev/null +++ b/scene.c @@ -0,0 +1 @@ +#include "scene.h" diff --git a/src/core.c b/src/core.c index 83c7b6e..84c9cae 100644 --- a/src/core.c +++ b/src/core.c @@ -78,6 +78,4 @@ void frame_begin() { render_frame_begin(&g_core.renderer); } void frame_draw() {} -void frame_end() { - render_frame_end(&g_core.renderer); -} +void frame_end() { render_frame_end(&g_core.renderer); } diff --git a/src/renderer/backends/backend_vulkan.c b/src/renderer/backends/backend_vulkan.c index 37f9b59..61527ee 100644 --- a/src/renderer/backends/backend_vulkan.c +++ b/src/renderer/backends/backend_vulkan.c @@ -65,6 +65,7 @@ typedef struct vulkan_context { gpu_buffer buffers[1024]; size_t buffer_count; VkDescriptorSet_darray* free_set_queue; + struct resource_pools* resource_pools; VkDebugUtilsMessengerEXT vk_debugger; } vulkan_context; @@ -1360,6 +1361,17 @@ void encode_buffer_copy(gpu_cmd_encoder* encoder, buffer_handle src, u64 src_off context.buffers[dst.raw].handle, 1, ©_region); } +texture_handle gpu_texture_create(texture_desc desc, const void* data) { + VkDeviceSize image_size = desc.extents.x * desc.extents.y * 4; + + TRACE("Uploading pixel data to texture using staging buffer"); + // Create a staging buffer + buffer_handle staging = + gpu_buffer_create(image_size, CEL_BUFFER_DEFAULT, CEL_BUFFER_FLAG_CPU, NULL); + // Copy data into it + buffer_upload_bytes(staging, (bytebuffer){ .buf = (u8*)data, .size = image_size }, 0, image_size); +} + size_t vertex_attrib_size(vertex_attrib_type attr) { switch (attr) { case ATTR_F32: @@ -1392,3 +1404,15 @@ void vertex_desc_add(vertex_description* builder, const char* name, vertex_attri builder->attributes_count++; } + +/* TYPED_POOL(gpu_buffer, buffer); */ +/* TYPED_POOL(gpu_texture, texture); */ + +void resource_pools_init(arena* a, struct resource_pools* res_pools) { + buffer_pool buf_pool = buffer_pool_create(a, MAX_BUFFERS, sizeof(gpu_buffer)); + res_pools->buffers = buf_pool; + texture_pool tex_pool = texture_pool_create(a, MAX_TEXTURES, sizeof(gpu_texture)); + res_pools->textures = tex_pool; + + context.resource_pools = res_pools; +} diff --git a/src/renderer/ral.c b/src/renderer/ral.c index 25c2909..6a417dd 100644 --- a/src/renderer/ral.c +++ b/src/renderer/ral.c @@ -13,10 +13,10 @@ /* } buffer_handle; */ /* typedef struct gpu_buffer gpu_buffer; */ -TYPED_POOL(gpu_buffer, buffer); -TYPED_POOL(gpu_texture, texture); +/* TYPED_POOL(gpu_buffer, buffer); */ +/* TYPED_POOL(gpu_texture, texture); */ -struct resource_pools { - buffer_pool buffers; - texture_pool textures; -}; +/* struct resource_pools { */ +/* buffer_pool buffers; */ +/* texture_pool textures; */ +/* }; */ diff --git a/src/renderer/ral.h b/src/renderer/ral.h index 03bdeab..da9eb93 100644 --- a/src/renderer/ral.h +++ b/src/renderer/ral.h @@ -32,6 +32,8 @@ typedef struct gpu_cmd_buffer gpu_cmd_buffer; // Ready for submission typedef struct gpu_buffer gpu_buffer; #define MAX_SHADER_DATA_LAYOUTS 5 +#define MAX_BUFFERS 256 +#define MAX_TEXTURES 256 /** @brief A*/ // typedef struct gpu_bind_group @@ -41,7 +43,7 @@ typedef struct gpu_backend_pools { // pools for each gpu structure } gpu_backend_pools; -typedef struct resource_pools resource_pools; +/* typedef struct resource_pools resource_pools; */ typedef enum pipeline_kind { PIPELINE_GRAPHICS, @@ -144,7 +146,7 @@ void gpu_buffer_upload(); void gpu_buffer_bind(buffer_handle buffer); // Textures -void gpu_texture_create(); +texture_handle gpu_texture_create(texture_desc desc, const void* data); void gpu_texture_destroy(); void gpu_texture_upload(); @@ -161,3 +163,14 @@ void vertex_desc_add(vertex_description* builder, const char* name, vertex_attri // TEMP void gpu_temp_draw(size_t n_verts); + +TYPED_POOL(gpu_buffer, buffer); +TYPED_POOL(gpu_texture, texture); + +struct resource_pools { + buffer_pool buffers; + texture_pool textures; +}; + +// Must be implemented by backends +void resource_pools_init(arena* a, struct resource_pools* res_pools); diff --git a/src/renderer/render.c b/src/renderer/render.c index 6eacc0d..c83678b 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -3,6 +3,7 @@ #include "camera.h" #include "file.h" #include "log.h" +#include "mem.h" #include "ral.h" #include "ral_types.h" @@ -46,6 +47,10 @@ bool renderer_init(renderer* ren) { gpu_device_create(&ren->device); // TODO: handle errors gpu_swapchain_create(&ren->swapchain); + DEBUG("Initialise GPU resource pools"); + arena pool_arena = arena_create(malloc(1024 * 1024), 1024 * 1024); + resource_pools_init(&pool_arena, ren->resource_pools); + // ren->blinn_phong = // shader_create_separate("assets/shaders/blinn_phong.vert", // "assets/shaders/blinn_phong.frag"); @@ -56,7 +61,7 @@ bool renderer_init(renderer* ren) { // default_material_init(); // Create default rendering pipeline - default_pipelines_init(ren); + /* default_pipelines_init(ren); */ return true; } @@ -75,11 +80,15 @@ void default_pipelines_init(renderer* ren) { ren->default_renderpass = *renderpass; - // str8 vert_path = str8lit("build/linux/x86_64/debug/triangle.vert.spv"); - // str8 frag_path = str8lit("build/linux/x86_64/debug/triangle.frag.spv"); printf("Load shaders\n"); - str8 vert_path = str8lit("/home/void/code/celeritas-engine/celeritas-core/build/linux/x86_64/debug/triangle.vert.spv"); - str8 frag_path = str8lit("/home/void/code/celeritas-engine/celeritas-core/build/linux/x86_64/debug/triangle.frag.spv"); + str8 vert_path = str8lit("build/linux/x86_64/debug/triangle.vert.spv"); + str8 frag_path = str8lit("build/linux/x86_64/debug/triangle.frag.spv"); + /* str8 vert_path = + * str8lit("/home/void/code/celeritas-engine/celeritas-core/build/linux/x86_64/debug/triangle.vert.spv"); + */ + /* str8 frag_path = + * str8lit("/home/void/code/celeritas-engine/celeritas-core/build/linux/x86_64/debug/triangle.frag.spv"); + */ str8_opt vertex_shader = str8_from_file(&scratch, vert_path); str8_opt fragment_shader = str8_from_file(&scratch, frag_path); if (!vertex_shader.has_value || !fragment_shader.has_value) { diff --git a/src/renderer/render_types.h b/src/renderer/render_types.h index ac98627..bd9ef3c 100644 --- a/src/renderer/render_types.h +++ b/src/renderer/render_types.h @@ -35,6 +35,7 @@ typedef struct renderer { gpu_renderpass default_renderpass; gpu_pipeline static_opaque_pipeline; bool frame_aborted; + struct resource_pools* resource_pools; } renderer; typedef struct geometry_data { diff --git a/src/scene.h b/src/scene.h index 24429b3..5a85e2e 100644 --- a/src/scene.h +++ b/src/scene.h @@ -32,7 +32,13 @@ void scene_free(scene* s); // Simplified API - no scene pointer; gets and sets global scene // Add/Remove objects from the scene + /* vec3 direction; */ + /* vec3 ambient; */ + /* vec3 diffuse; */ + /* vec3 specular; */ void scene_set_dir_light(directional_light light); +void _scene_set_dir_light(vec3 ambient, vec3 diffuse, vec3 specular, vec3 direction); + void scene_add_point_light(point_light light); void scene_add_model(model_handle model, transform3d transform); bool scene_remove_model(model_handle model); -- cgit v1.2.3-70-g09d2 From c69fab91c3cd8976ad660939765ca9a5e32a239a Mon Sep 17 00:00:00 2001 From: Omniscient Date: Sat, 18 May 2024 15:30:41 +1000 Subject: use the new pool allocators for buffers in backend --- src/renderer/backends/backend_vulkan.c | 58 ++++++++++++++++++++-------------- src/renderer/render.c | 1 + 2 files changed, 35 insertions(+), 24 deletions(-) (limited to 'src/renderer/render.c') diff --git a/src/renderer/backends/backend_vulkan.c b/src/renderer/backends/backend_vulkan.c index 61527ee..2dc11b5 100644 --- a/src/renderer/backends/backend_vulkan.c +++ b/src/renderer/backends/backend_vulkan.c @@ -92,6 +92,9 @@ VkShaderModule create_shader_module(str8 spirv); /** @brief Helper function for creating array of all extensions we want */ cstr_darray* get_all_extensions(); +// --- Handy macros +#define BUFFER_GET(h) (buffer_pool_get(&context.resource_pools->buffers, h)) + bool gpu_backend_init(const char* window_name, GLFWwindow* window) { memset(&context, 0, sizeof(vulkan_context)); context.allocator = 0; // TODO: use an allocator @@ -587,9 +590,11 @@ gpu_pipeline* gpu_graphics_pipeline_create(struct graphics_pipeline_desc descrip for (size_t frame_i = 0; frame_i < MAX_FRAMES_IN_FLIGHT; frame_i++) { buffer_handle uniform_buf_handle = gpu_buffer_create(buffer_size, CEL_BUFFER_UNIFORM, CEL_BUFFER_FLAG_CPU, NULL); - gpu_buffer created_gpu_buffer = context.buffers[uniform_buf_handle.raw]; - buffers[frame_i] = created_gpu_buffer.handle; - uniform_buf_memorys[frame_i] = created_gpu_buffer.memory; + + gpu_buffer* created_gpu_buffer = + BUFFER_GET(uniform_buf_handle); // context.buffers[uniform_buf_handle.raw]; + buffers[frame_i] = created_gpu_buffer->handle; + uniform_buf_memorys[frame_i] = created_gpu_buffer->memory; vkMapMemory(context.device->logical_device, uniform_buf_memorys[frame_i], 0, uniform_buf_size, 0, &uniform_buf_mem_mappings[frame_i]); // now we have a pointer in unifrom_buf_mem_mappings we can write to @@ -913,15 +918,15 @@ void encode_bind_shader_data(gpu_cmd_encoder* encoder, u32 group, shader_data* d } void encode_set_vertex_buffer(gpu_cmd_encoder* encoder, buffer_handle buf) { - gpu_buffer buffer = context.buffers[buf.raw]; - VkBuffer vbs[] = { buffer.handle }; + gpu_buffer* buffer = BUFFER_GET(buf); // context.buffers[buf.raw]; + VkBuffer vbs[] = { buffer->handle }; VkDeviceSize offsets[] = { 0 }; vkCmdBindVertexBuffers(encoder->cmd_buffer, 0, 1, vbs, offsets); } void encode_set_index_buffer(gpu_cmd_encoder* encoder, buffer_handle buf) { - gpu_buffer buffer = context.buffers[buf.raw]; - vkCmdBindIndexBuffer(encoder->cmd_buffer, buffer.handle, 0, VK_INDEX_TYPE_UINT32); + gpu_buffer* buffer = BUFFER_GET(buf); // context.buffers[buf.raw]; + vkCmdBindIndexBuffer(encoder->cmd_buffer, buffer->handle, 0, VK_INDEX_TYPE_UINT32); } // TEMP @@ -1266,15 +1271,17 @@ buffer_handle gpu_buffer_create(u64 size, gpu_buffer_type buf_type, gpu_buffer_f buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; // "allocating" the cpu-side buffer struct - gpu_buffer buffer; - buffer.size = size; - buffer_handle handle = { .raw = (u32)context.buffer_count }; + /* gpu_buffer buffer; */ + /* buffer.size = size; */ + buffer_handle handle; + gpu_buffer* buffer = buffer_pool_alloc(&context.resource_pools->buffers, &handle); + buffer->size = size; VK_CHECK(vkCreateBuffer(context.device->logical_device, &buffer_info, context.allocator, - &buffer.handle)); + &buffer->handle)); VkMemoryRequirements requirements; - vkGetBufferMemoryRequirements(context.device->logical_device, buffer.handle, &requirements); + vkGetBufferMemoryRequirements(context.device->logical_device, buffer->handle, &requirements); // Just make them always need all of them for now i32 memory_index = @@ -1288,8 +1295,8 @@ buffer_handle gpu_buffer_create(u64 size, gpu_buffer_type buf_type, gpu_buffer_f allocate_info.memoryTypeIndex = (u32)memory_index; vkAllocateMemory(context.device->logical_device, &allocate_info, context.allocator, - &buffer.memory); - vkBindBufferMemory(context.device->logical_device, buffer.handle, buffer.memory, 0); + &buffer->memory); + vkBindBufferMemory(context.device->logical_device, buffer->handle, buffer->memory, 0); /* Now there are two options: * 1. create CPU-accessible memory -> map memory -> memcpy -> unmap @@ -1297,8 +1304,8 @@ buffer_handle gpu_buffer_create(u64 size, gpu_buffer_type buf_type, gpu_buffer_f * GPU-only buffer */ - context.buffers[context.buffer_count] = buffer; - context.buffer_count++; + /* context.buffers[context.buffer_count] = buffer; */ + /* context.buffer_count++; */ if (data) { TRACE("Upload data as part of buffer creation"); @@ -1335,19 +1342,21 @@ buffer_handle gpu_buffer_create(u64 size, gpu_buffer_type buf_type, gpu_buffer_f } void gpu_buffer_destroy(buffer_handle buffer) { - gpu_buffer b = context.buffers[buffer.raw]; - vkDestroyBuffer(context.device->logical_device, b.handle, context.allocator); - vkFreeMemory(context.device->logical_device, b.memory, context.allocator); + gpu_buffer* b = + buffer_pool_get(&context.resource_pools->buffers, buffer); // context.buffers[buffer.raw]; + vkDestroyBuffer(context.device->logical_device, b->handle, context.allocator); + vkFreeMemory(context.device->logical_device, b->memory, context.allocator); } // Upload data to a void buffer_upload_bytes(buffer_handle gpu_buf, bytebuffer cpu_buf, u64 offset, u64 size) { - gpu_buffer buffer = context.buffers[gpu_buf.raw]; + gpu_buffer* buffer = buffer_pool_get(&context.resource_pools->buffers, gpu_buf); + /* gpu_buffer buffer = context.buffers[gpu_buf.raw]; */ void* data_ptr; - vkMapMemory(context.device->logical_device, buffer.memory, 0, size, 0, &data_ptr); + vkMapMemory(context.device->logical_device, buffer->memory, 0, size, 0, &data_ptr); DEBUG("Uploading %d bytes to buffer", size); memcpy(data_ptr, cpu_buf.buf, size); - vkUnmapMemory(context.device->logical_device, buffer.memory); + vkUnmapMemory(context.device->logical_device, buffer->memory); } void encode_buffer_copy(gpu_cmd_encoder* encoder, buffer_handle src, u64 src_offset, @@ -1357,8 +1366,9 @@ void encode_buffer_copy(gpu_cmd_encoder* encoder, buffer_handle src, u64 src_off copy_region.dstOffset = dst_offset; copy_region.size = copy_size; - vkCmdCopyBuffer(encoder->cmd_buffer, context.buffers[src.raw].handle, - context.buffers[dst.raw].handle, 1, ©_region); + gpu_buffer* src_buf = buffer_pool_get(&context.resource_pools->buffers, src); + gpu_buffer* dst_buf = buffer_pool_get(&context.resource_pools->buffers, dst); + vkCmdCopyBuffer(encoder->cmd_buffer, src_buf->handle, dst_buf->handle, 1, ©_region); } texture_handle gpu_texture_create(texture_desc desc, const void* data) { diff --git a/src/renderer/render.c b/src/renderer/render.c index c83678b..b06620b 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -49,6 +49,7 @@ bool renderer_init(renderer* ren) { DEBUG("Initialise GPU resource pools"); arena pool_arena = arena_create(malloc(1024 * 1024), 1024 * 1024); + ren->resource_pools = arena_alloc(&pool_arena, sizeof(struct resource_pools)); resource_pools_init(&pool_arena, ren->resource_pools); // ren->blinn_phong = -- cgit v1.2.3-70-g09d2 From d79a8aa200bd64b14b85d2ec0c207601ba5c7922 Mon Sep 17 00:00:00 2001 From: Omniscient Date: Sat, 18 May 2024 23:53:11 +1000 Subject: working on image creation --- assets/shaders/triangle.vert | 2 - examples/cube/ex_cube.c | 5 + examples/triangle/ex_triangle.c | 18 ++- src/renderer/backends/backend_vulkan.c | 224 ++++++++++++++++++++++++++++----- src/renderer/backends/backend_vulkan.h | 8 ++ src/renderer/ral.c | 21 ---- src/renderer/ral.h | 7 ++ src/renderer/ral_types.h | 3 - src/renderer/render.c | 44 ++++++- src/renderer/render.h | 16 ++- src/renderer/render_types.h | 15 +-- 11 files changed, 289 insertions(+), 74 deletions(-) (limited to 'src/renderer/render.c') diff --git a/assets/shaders/triangle.vert b/assets/shaders/triangle.vert index b8c8f63..8030561 100644 --- a/assets/shaders/triangle.vert +++ b/assets/shaders/triangle.vert @@ -2,8 +2,6 @@ layout(location = 0) in vec2 inPos; layout(location = 1) in vec3 inColor; -// layout(location = 1) in vec3 inNormal; -// layout(location = 2) in vec2 inTexCoords; layout(location = 0) out vec3 fragColor; diff --git a/examples/cube/ex_cube.c b/examples/cube/ex_cube.c index 7c7831d..264bf71 100644 --- a/examples/cube/ex_cube.c +++ b/examples/cube/ex_cube.c @@ -86,9 +86,14 @@ int main() { }; gpu_pipeline* gfx_pipeline = gpu_graphics_pipeline_create(pipeline_description); + // Geometry geometry_data cube_data = geo_create_cuboid(f32x3(1, 1, 1)); mesh cube = mesh_create(&cube_data, false); + // Texture + texture_data tex_data = texture_data_load("assets/textures/texture.jpg", false); + texture_handle texture = texture_data_upload(tex_data, true); + // Main loop while (!should_exit(&g_core)) { input_update(&g_core.input); diff --git a/examples/triangle/ex_triangle.c b/examples/triangle/ex_triangle.c index 5d8f0cf..886637a 100644 --- a/examples/triangle/ex_triangle.c +++ b/examples/triangle/ex_triangle.c @@ -16,17 +16,22 @@ extern core g_core; const custom_vertex vertices[] = { + (custom_vertex){ .pos = vec2(-0.5, -0.5), .color = vec3(1.0, 1.0, 1.0) }, + (custom_vertex){ .pos = vec2(0.5, -0.5), .color = vec3(1.0, 0.0, 0.0) }, (custom_vertex){ .pos = vec2(-0.5, 0.5), .color = vec3(0.0, 0.0, 1.0) }, (custom_vertex){ .pos = vec2(0.5, 0.5), .color = vec3(0.0, 1.0, 0.0) }, - (custom_vertex){ .pos = vec2(0.5, -0.5), .color = vec3(1.0, 0.0, 0.0) }, - (custom_vertex){ .pos = vec2(-0.5, -0.5), .color = vec3(1.0, 1.0, 1.0) }, }; -const u16 indices[] = { 0, 1, 2, 2, 3, 0 }; +const u32 indices[] = { 2, 1, 0, 1, 2, 3}; int main() { core_bringup(); arena scratch = arena_create(malloc(1024 * 1024), 1024 * 1024); + vertex_description vertex_input = {0}; + vertex_input.debug_label = "Hello"; + vertex_desc_add(&vertex_input, "inPos", ATTR_F32x2); + vertex_desc_add(&vertex_input, "inColor", ATTR_F32x3); + gpu_renderpass_desc pass_description = {}; gpu_renderpass* renderpass = gpu_renderpass_create(&pass_description); @@ -40,6 +45,9 @@ int main() { struct graphics_pipeline_desc pipeline_description = { .debug_name = "Basic Pipeline", + .vertex_desc = vertex_input, + // .data_layouts = {0}, + // .data_layouts_count = 0, .vs = { .debug_name = "Triangle Vertex Shader", .filepath = vert_path, .code = vertex_shader.contents, @@ -56,13 +64,13 @@ int main() { // Load triangle vertex and index data buffer_handle triangle_vert_buf = - gpu_buffer_create(sizeof(vertices), CEL_BUFFER_VERTEX, CEL_BUFFER_FLAG_GPU, vertices); + gpu_buffer_create(4 * sizeof(vertex) , CEL_BUFFER_VERTEX, CEL_BUFFER_FLAG_GPU, vertices); buffer_handle triangle_index_buf = gpu_buffer_create(sizeof(indices), CEL_BUFFER_INDEX, CEL_BUFFER_FLAG_GPU, indices); // Main loop - while (!should_exit(&g_core)) { + while (!should_exit()) { input_update(&g_core.input); if (!gpu_backend_begin_frame()) { diff --git a/src/renderer/backends/backend_vulkan.c b/src/renderer/backends/backend_vulkan.c index 2dc11b5..208aef0 100644 --- a/src/renderer/backends/backend_vulkan.c +++ b/src/renderer/backends/backend_vulkan.c @@ -414,37 +414,39 @@ gpu_pipeline* gpu_graphics_pipeline_create(struct graphics_pipeline_desc descrip VkPipelineShaderStageCreateInfo shader_stages[2] = { vert_shader_stage_info, frag_shader_stage_info }; - // TODO: Attributes - VkVertexInputAttributeDescription attribute_descs[2] = { 0 }; - /* u32 offset = 0; */ - /* for (u32 i = 0; i < description.vertex_desc.attributes_count; i++) { */ - /* attribute_descs[i].binding = 0; */ - /* attribute_descs[i].location = i; */ - /* attribute_descs[i].format = format_from_vertex_attr(description.vertex_desc.attributes[i]); - */ - /* attribute_descs[i].offset = offset; */ - /* size_t this_offset = vertex_attrib_size(description.vertex_desc.attributes[i]); */ - /* printf("offset total %d this attr %ld\n", offset, this_offset); */ - /* printf("sizeof vertex %ld\n", sizeof(vertex)); */ - /* printf("%d \n", offsetof(vertex, static_3d)); */ - /* offset += this_offset; */ - /* } */ - printf("Vertex attributes\n"); - attribute_descs[0].binding = 0; - attribute_descs[0].location = 0; - attribute_descs[0].format = VK_FORMAT_R32G32B32_SFLOAT; - attribute_descs[0].offset = 0; // offsetof(custom_vertex, pos); - - attribute_descs[1].binding = 0; - attribute_descs[1].location = 1; - attribute_descs[1].format = VK_FORMAT_R32G32B32_SFLOAT; - attribute_descs[1].offset = 12; // offsetof(custom_vertex, color); + // Attributes + u32 attr_count = description.vertex_desc.attributes_count; + printf("N attributes %d\n", attr_count); + VkVertexInputAttributeDescription attribute_descs[attr_count]; + memset(attribute_descs, 0, attr_count * sizeof(VkVertexInputAttributeDescription)); + u32 offset = 0; + for (u32 i = 0; i < description.vertex_desc.attributes_count; i++) { + attribute_descs[i].binding = 0; + attribute_descs[i].location = i; + attribute_descs[i].format = format_from_vertex_attr(description.vertex_desc.attributes[i]); + attribute_descs[i].offset = offset; + size_t this_offset = vertex_attrib_size(description.vertex_desc.attributes[i]); + printf("offset total %d this attr %ld\n", offset, this_offset); + printf("sizeof vertex %ld\n", sizeof(vertex)); + /* printf("%d \n", offsetof(vertex, static_3d)); */ + offset += this_offset; + } + /* printf("Vertex attributes\n"); */ + /* attribute_descs[0].binding = 0; */ + /* attribute_descs[0].location = 0; */ + /* attribute_descs[0].format = VK_FORMAT_R32G32B32_SFLOAT; */ + /* attribute_descs[0].offset = 0; // offsetof(custom_vertex, pos); */ + + /* attribute_descs[1].binding = 0; */ + /* attribute_descs[1].location = 1; */ + /* attribute_descs[1].format = VK_FORMAT_R32G32B32_SFLOAT; */ + /* attribute_descs[1].offset = 12; // offsetof(custom_vertex, color); */ // Vertex input // TODO: Generate this from descroiption now VkVertexInputBindingDescription binding_desc; binding_desc.binding = 0; - binding_desc.stride = sizeof(vertex); // description.vertex_desc.stride; + binding_desc.stride = description.vertex_desc.stride; binding_desc.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; VkPipelineVertexInputStateCreateInfo vertex_input_info = { @@ -453,7 +455,7 @@ gpu_pipeline* gpu_graphics_pipeline_create(struct graphics_pipeline_desc descrip vertex_input_info.vertexBindingDescriptionCount = 1; vertex_input_info.pVertexBindingDescriptions = &binding_desc; vertex_input_info.vertexAttributeDescriptionCount = - 2; // description.vertex_desc.attributes_count; + attr_count; // description.vertex_desc.attributes_count; vertex_input_info.pVertexAttributeDescriptions = attribute_descs; // Input Assembly @@ -558,10 +560,15 @@ gpu_pipeline* gpu_graphics_pipeline_create(struct graphics_pipeline_desc descrip malloc(description.data_layouts_count * sizeof(VkDescriptorSetLayout)); pipeline->desc_set_layouts = desc_set_layouts; pipeline->desc_set_layouts_count = description.data_layouts_count; + if (description.data_layouts_count > 0) { pipeline->uniform_pointers = malloc(description.data_layouts_count * sizeof(desc_set_uniform_buffer)); + } else { + pipeline->uniform_pointers = NULL; + } // assert(description.data_layouts_count == 1); + printf("data layouts %d\n", description.data_layouts_count); for (u32 i = 0; i < description.data_layouts_count; i++) { shader_data_layout sdl = description.data_layouts[i].shader_data_get_layout(NULL); @@ -1343,15 +1350,15 @@ buffer_handle gpu_buffer_create(u64 size, gpu_buffer_type buf_type, gpu_buffer_f void gpu_buffer_destroy(buffer_handle buffer) { gpu_buffer* b = - buffer_pool_get(&context.resource_pools->buffers, buffer); // context.buffers[buffer.raw]; + buffer_pool_get(&context.resource_pools->buffers, buffer); vkDestroyBuffer(context.device->logical_device, b->handle, context.allocator); vkFreeMemory(context.device->logical_device, b->memory, context.allocator); + buffer_pool_dealloc(&context.resource_pools->buffers, buffer); } // Upload data to a void buffer_upload_bytes(buffer_handle gpu_buf, bytebuffer cpu_buf, u64 offset, u64 size) { gpu_buffer* buffer = buffer_pool_get(&context.resource_pools->buffers, gpu_buf); - /* gpu_buffer buffer = context.buffers[gpu_buf.raw]; */ void* data_ptr; vkMapMemory(context.device->logical_device, buffer->memory, 0, size, 0, &data_ptr); DEBUG("Uploading %d bytes to buffer", size); @@ -1371,6 +1378,80 @@ void encode_buffer_copy(gpu_cmd_encoder* encoder, buffer_handle src, u64 src_off vkCmdCopyBuffer(encoder->cmd_buffer, src_buf->handle, dst_buf->handle, 1, ©_region); } +// one-shot command buffers +VkCommandBuffer vulkan_command_buffer_create_oneshot() { + VkCommandBufferAllocateInfo alloc_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; + alloc_info.commandPool = context.device->pool; + alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + alloc_info.commandBufferCount = 1; + alloc_info.pNext = 0; + + VkCommandBuffer cmd_buffer; + vkAllocateCommandBuffers(context.device->logical_device, &alloc_info, &cmd_buffer); + + VkCommandBufferBeginInfo begin_info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; + begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + + vkBeginCommandBuffer(cmd_buffer, &begin_info); + + return cmd_buffer; +} + +void vulkan_command_buffer_finish_oneshot(VkCommandBuffer cmd_buffer) { + VK_CHECK(vkEndCommandBuffer(cmd_buffer)); + + // submit to queue + VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &cmd_buffer; + VK_CHECK(vkQueueSubmit(context.device->graphics_queue, 1, &submit_info, 0)); + VK_CHECK(vkQueueWaitIdle(context.device->graphics_queue)); + + vkFreeCommandBuffers(context.device->logical_device, context.device->pool, 1, &cmd_buffer); +} + +void copy_buffer_to_buffer_oneshot(buffer_handle src, u64 src_offset, buffer_handle dst, + u64 dst_offset, u64 copy_size) { + VkBufferCopy copy_region; + copy_region.srcOffset = src_offset; + copy_region.dstOffset = dst_offset; + copy_region.size = copy_size; + + gpu_buffer* src_buf = buffer_pool_get(&context.resource_pools->buffers, src); + gpu_buffer* dst_buf = buffer_pool_get(&context.resource_pools->buffers, dst); + VkCommandBuffer temp_cmd_buffer = vulkan_command_buffer_create_oneshot(); + vkCmdCopyBuffer(temp_cmd_buffer, src_buf->handle, dst_buf->handle, 1, ©_region); + vulkan_command_buffer_finish_oneshot(temp_cmd_buffer); +} + +void copy_buffer_to_image_oneshot(buffer_handle src, texture_handle dst) { + gpu_buffer* src_buf = buffer_pool_get(&context.resource_pools->buffers, src); + gpu_texture* dst_tex = texture_pool_get(&context.resource_pools->textures, dst); + + VkCommandBuffer temp_cmd_buffer = vulkan_command_buffer_create_oneshot(); + + VkBufferImageCopy region; + region.bufferOffset = 0; + region.bufferRowLength = 0; + region.bufferImageHeight = 0; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.mipLevel = 0; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = 1; + printf("Image details width: %d height %d\n", dst_tex->desc.extents.x, dst_tex->desc.extents.y); + region.imageOffset.x = 0; + region.imageOffset.y = 0; + region.imageOffset.z = 0; + region.imageExtent.width = dst_tex->desc.extents.x; + region.imageExtent.height = dst_tex->desc.extents.y; + region.imageExtent.depth = 1; + + vkCmdCopyBufferToImage(temp_cmd_buffer, src_buf->handle, dst_tex->handle, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + + vulkan_command_buffer_finish_oneshot(temp_cmd_buffer); +} + texture_handle gpu_texture_create(texture_desc desc, const void* data) { VkDeviceSize image_size = desc.extents.x * desc.extents.y * 4; @@ -1380,6 +1461,91 @@ texture_handle gpu_texture_create(texture_desc desc, const void* data) { gpu_buffer_create(image_size, CEL_BUFFER_DEFAULT, CEL_BUFFER_FLAG_CPU, NULL); // Copy data into it buffer_upload_bytes(staging, (bytebuffer){ .buf = (u8*)data, .size = image_size }, 0, image_size); + + VkImage image; + VkDeviceMemory image_memory; + + VkImageCreateInfo image_create_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; + image_create_info.imageType = VK_IMAGE_TYPE_2D; + image_create_info.extent.width = desc.extents.x; + image_create_info.extent.height = desc.extents.y; + image_create_info.extent.depth = 1; + image_create_info.mipLevels = 1; + image_create_info.arrayLayers = 1; + image_create_info.format = VK_FORMAT_R8G8B8A8_SRGB; + image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; + image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; + + VK_CHECK( + vkCreateImage(context.device->logical_device, &image_create_info, context.allocator, &image)); + + VkMemoryRequirements memory_reqs; + vkGetImageMemoryRequirements(context.device->logical_device, image, &memory_reqs); + + VkMemoryAllocateInfo alloc_info = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + alloc_info.allocationSize = memory_reqs.size; + alloc_info.memoryTypeIndex = + find_memory_index(memory_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + vkAllocateMemory(context.device->logical_device, &alloc_info, context.allocator, &image_memory); + + vkBindImageMemory(context.device->logical_device, image, image_memory, 0); + +gpu_buffer_destroy(staging); + + texture_handle handle; + gpu_texture* texture = texture_pool_alloc(&context.resource_pools->textures, &handle); + texture->handle = image; + texture->memory = image_memory; + texture->size = image_size; + return handle; +} + +void vulkan_transition_image_layout(gpu_texture* texture, VkFormat format, VkImageLayout old_layout, + VkImageLayout new_layout) { + VkCommandBuffer temp_cmd_buffer = vulkan_command_buffer_create_oneshot(); + + VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; + barrier.oldLayout = old_layout; + barrier.newLayout = new_layout; + barrier.srcQueueFamilyIndex = context.device->queue_family_indicies.graphics_family_index; + barrier.dstQueueFamilyIndex = context.device->queue_family_indicies.graphics_family_index; + barrier.image = texture->handle; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + barrier.srcAccessMask = 0; // TODO + barrier.dstAccessMask = 0; // TODO + + VkPipelineStageFlags source_stage; + VkPipelineStageFlags dest_stage; + + if (old_layout == VK_IMAGE_LAYOUT_UNDEFINED && + new_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + source_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + dest_stage = VK_PIPELINE_STAGE_TRANSFER_BIT; + + } else if (old_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && + new_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + source_stage = VK_PIPELINE_STAGE_TRANSFER_BIT; + dest_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + } else { + FATAL("Unsupported image layout transition"); + return; + } + + vkCmdPipelineBarrier(temp_cmd_buffer, source_stage, dest_stage, 0, 0, 0, 0, 0, 1, &barrier); + + vulkan_command_buffer_finish_oneshot(temp_cmd_buffer); } size_t vertex_attrib_size(vertex_attrib_type attr) { diff --git a/src/renderer/backends/backend_vulkan.h b/src/renderer/backends/backend_vulkan.h index 77b9f94..1e36ca3 100644 --- a/src/renderer/backends/backend_vulkan.h +++ b/src/renderer/backends/backend_vulkan.h @@ -6,6 +6,7 @@ #include "defines.h" #include "mem.h" #include "ral.h" +#include "ral_types.h" #define MAX_FRAMES_IN_FLIGHT 2 #define GPU_SWAPCHAIN_IMG_COUNT 2 @@ -102,3 +103,10 @@ typedef struct gpu_buffer { VkDeviceMemory memory; u64 size; } gpu_buffer; + +typedef struct gpu_texture { + VkImage handle; + VkDeviceMemory memory; + u64 size; + texture_desc desc; +} gpu_texture; diff --git a/src/renderer/ral.c b/src/renderer/ral.c index 6a417dd..304017d 100644 --- a/src/renderer/ral.c +++ b/src/renderer/ral.c @@ -1,22 +1 @@ #include "ral.h" - -/* typedef struct foo { */ -/* u32 a; */ -/* f32 b; */ -/* char c; */ -/* } foo; */ - -/* TYPED_POOL(gpu_buffer, buffer); */ - -/* typedef struct buffer_handle { */ -/* u32 raw; */ -/* } buffer_handle; */ - -/* typedef struct gpu_buffer gpu_buffer; */ -/* TYPED_POOL(gpu_buffer, buffer); */ -/* TYPED_POOL(gpu_texture, texture); */ - -/* struct resource_pools { */ -/* buffer_pool buffers; */ -/* texture_pool textures; */ -/* }; */ diff --git a/src/renderer/ral.h b/src/renderer/ral.h index da9eb93..48be83a 100644 --- a/src/renderer/ral.h +++ b/src/renderer/ral.h @@ -30,6 +30,7 @@ typedef struct gpu_renderpass gpu_renderpass; typedef struct gpu_cmd_encoder gpu_cmd_encoder; // Recording typedef struct gpu_cmd_buffer gpu_cmd_buffer; // Ready for submission typedef struct gpu_buffer gpu_buffer; +typedef struct gpu_texture gpu_texture; #define MAX_SHADER_DATA_LAYOUTS 5 #define MAX_BUFFERS 256 @@ -174,3 +175,9 @@ struct resource_pools { // Must be implemented by backends void resource_pools_init(arena* a, struct resource_pools* res_pools); + +void copy_buffer_to_buffer_oneshot(buffer_handle src, u64 src_offset, buffer_handle dst, u64 dst_offset, + u64 copy_size); +void copy_buffer_to_image_oneshot(buffer_handle src, texture_handle dst); + +// --- Helpers diff --git a/src/renderer/ral_types.h b/src/renderer/ral_types.h index 5d4e88a..fc8bb8b 100644 --- a/src/renderer/ral_types.h +++ b/src/renderer/ral_types.h @@ -66,9 +66,6 @@ typedef struct texture_desc { u32x2 extents; } texture_desc; -typedef struct gpu_texture { -} gpu_texture; - typedef enum gpu_buffer_type { CEL_BUFFER_DEFAULT, // on Vulkan this would be a storage buffer? CEL_BUFFER_VERTEX, diff --git a/src/renderer/render.c b/src/renderer/render.c index b06620b..fc45093 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -1,11 +1,15 @@ -#include "render.h" #include +#include "maths_types.h" +#define STB_IMAGE_IMPLEMENTATION +#include + #include "camera.h" #include "file.h" #include "log.h" #include "mem.h" #include "ral.h" #include "ral_types.h" +#include "render.h" /** @brief Creates the pipelines built into Celeritas such as rendering static opaque geometry, debug visualisations, immediate mode UI, etc */ @@ -194,3 +198,41 @@ mesh mesh_create(geometry_data* geometry, bool free_on_upload) { return m; } + +// --- Textures + +texture_data texture_data_load(const char* path, bool invert_y) { + TRACE("Load texture %s", path); + + // load the file data + int width, height, num_channels; + stbi_set_flip_vertically_on_load(invert_y); + +#pragma GCC diagnostic ignored "-Wpointer-sign" + char* data = stbi_load(path, &width, &height, &num_channels, 0); // STBI_rgb_alpha); + if (data) { + DEBUG("loaded texture: %s", path); + } else { + WARN("failed to load texture"); + } + + unsigned int channel_type; + if (num_channels == 4) { + channel_type = GL_RGBA; + } else { + channel_type = GL_RGB; + } + texture_desc desc = { .extents = { width, height }, + .format = CEL_TEXTURE_FORMAT_8_8_8_8_RGBA_UNORM, + .tex_type = CEL_TEXTURE_TYPE_2D }; + + return (texture_data){ .description = desc, .image_data = data }; +} + +texture_handle texture_data_upload(texture_data data, bool free_on_upload) { + texture_handle handle = gpu_texture_create(data.description, data.image_data); + if (free_on_upload) { + stbi_image_free(data.image_data); + } + return handle; +} diff --git a/src/renderer/render.h b/src/renderer/render.h index 5657fc1..9a7ff2f 100644 --- a/src/renderer/render.h +++ b/src/renderer/render.h @@ -34,9 +34,17 @@ typedef struct render_ctx { texture_handle texture_create(const char* debug_name, texture_desc description, const u8* data); // Frontend Resources -// TODO: void texture_data_upload(texture_handle texture); -void texture_data_upload(texture* tex); -texture texture_data_load(const char* path, bool invert_y); +texture_data texture_data_load(const char* path, bool invert_y); + +/** + * @brief + * + * @param data + * @param free_on_upload frees the CPU-side pixel data stored in `data` + * @return texture_handle + */ +texture_handle texture_data_upload(texture_data data, bool free_on_upload); + buffer_handle buffer_create(const char* debug_name, u64 size); bool buffer_destroy(buffer_handle buffer); sampler_handle sampler_create(); @@ -49,7 +57,7 @@ void shader_hot_reload(const char* filepath); * @brief Creates buffers and returns a struct that holds handles to our resources * * @param geometry - * @param free_on_upload frees the CPU-side vertex/index data stored in geometry_data when we + * @param free_on_upload frees the CPU-side vertex/index data stored in `geometry` when we successfully upload that data to the GPU-side buffer * @return mesh */ diff --git a/src/renderer/render_types.h b/src/renderer/render_types.h index bd9ef3c..c6aec8e 100644 --- a/src/renderer/render_types.h +++ b/src/renderer/render_types.h @@ -69,16 +69,13 @@ typedef struct model { u32 mesh_count; } model; -typedef struct texture { - u32 texture_id; - char name[256]; +typedef struct texture {} texture; + +typedef struct texture_data { + texture_desc description; void* image_data; - void* backend_data; - u32 width; - u32 height; - u8 channel_count; - u32 channel_type; -} texture; +} texture_data; + typedef struct blinn_phong_material { char name[256]; -- cgit v1.2.3-70-g09d2 From b1b2d988e8f2fa3b0f63ff3aeb02849497433647 Mon Sep 17 00:00:00 2001 From: Omniscient Date: Sun, 19 May 2024 00:01:50 +1000 Subject: cube compiles again --- examples/cube/ex_cube.c | 2 +- examples/triangle/ex_triangle.c | 2 +- src/renderer/backends/backend_vulkan.c | 2 +- src/renderer/ral_types.h | 1 + src/renderer/render.c | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) (limited to 'src/renderer/render.c') diff --git a/examples/cube/ex_cube.c b/examples/cube/ex_cube.c index 264bf71..346dd10 100644 --- a/examples/cube/ex_cube.c +++ b/examples/cube/ex_cube.c @@ -48,7 +48,7 @@ int main() { vec3 camera_front = vec3_normalise(vec3_negate(camera_pos)); camera cam = camera_create(camera_pos, camera_front, VEC3_Y, deg_to_rad(45.0)); - vertex_description vertex_input; + vertex_description vertex_input = {.use_full_vertex_size = true}; vertex_input.debug_label = "Standard Static 3D Vertex Format"; vertex_desc_add(&vertex_input, "inPosition", ATTR_F32x3); vertex_desc_add(&vertex_input, "inNormal", ATTR_F32x3); diff --git a/examples/triangle/ex_triangle.c b/examples/triangle/ex_triangle.c index 886637a..69c29ac 100644 --- a/examples/triangle/ex_triangle.c +++ b/examples/triangle/ex_triangle.c @@ -27,7 +27,7 @@ int main() { core_bringup(); arena scratch = arena_create(malloc(1024 * 1024), 1024 * 1024); - vertex_description vertex_input = {0}; + vertex_description vertex_input = {.use_full_vertex_size=false}; vertex_input.debug_label = "Hello"; vertex_desc_add(&vertex_input, "inPos", ATTR_F32x2); vertex_desc_add(&vertex_input, "inColor", ATTR_F32x3); diff --git a/src/renderer/backends/backend_vulkan.c b/src/renderer/backends/backend_vulkan.c index 208aef0..ae49cc3 100644 --- a/src/renderer/backends/backend_vulkan.c +++ b/src/renderer/backends/backend_vulkan.c @@ -446,7 +446,7 @@ gpu_pipeline* gpu_graphics_pipeline_create(struct graphics_pipeline_desc descrip // TODO: Generate this from descroiption now VkVertexInputBindingDescription binding_desc; binding_desc.binding = 0; - binding_desc.stride = description.vertex_desc.stride; + binding_desc.stride = description.vertex_desc.use_full_vertex_size ? sizeof(vertex) : description.vertex_desc.stride; binding_desc.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; VkPipelineVertexInputStateCreateInfo vertex_input_info = { diff --git a/src/renderer/ral_types.h b/src/renderer/ral_types.h index fc8bb8b..8339625 100644 --- a/src/renderer/ral_types.h +++ b/src/renderer/ral_types.h @@ -160,6 +160,7 @@ typedef struct vertex_description { vertex_attrib_type attributes[MAX_VERTEX_ATTRIBUTES]; u32 attributes_count; size_t stride; + bool use_full_vertex_size; } vertex_description; // --- Shaders & Bindings diff --git a/src/renderer/render.c b/src/renderer/render.c index fc45093..9f9a97c 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -209,7 +209,7 @@ texture_data texture_data_load(const char* path, bool invert_y) { stbi_set_flip_vertically_on_load(invert_y); #pragma GCC diagnostic ignored "-Wpointer-sign" - char* data = stbi_load(path, &width, &height, &num_channels, 0); // STBI_rgb_alpha); + char* data = stbi_load(path, &width, &height, &num_channels, STBI_rgb_alpha); if (data) { DEBUG("loaded texture: %s", path); } else { -- cgit v1.2.3-70-g09d2 From b6a4ac7b2d9d94a25ecdff007f87587512f5711d Mon Sep 17 00:00:00 2001 From: Omniscient Date: Sun, 19 May 2024 22:25:31 +1000 Subject: cube texture mapping working --- assets/shaders/cube.frag | 12 ++ assets/shaders/cube.vert | 7 +- examples/cube/ex_cube.c | 25 ++++- src/renderer/backends/backend_vulkan.c | 200 +++++++++++++++++++++++---------- src/renderer/backends/backend_vulkan.h | 3 + src/renderer/ral.h | 2 +- src/renderer/ral_types.h | 6 +- src/renderer/render.c | 3 +- xmake.lua | 1 + 9 files changed, 191 insertions(+), 68 deletions(-) (limited to 'src/renderer/render.c') diff --git a/assets/shaders/cube.frag b/assets/shaders/cube.frag index e69de29..11f1efa 100644 --- a/assets/shaders/cube.frag +++ b/assets/shaders/cube.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(location = 0) in vec3 fragColor; +layout(location = 1) in vec2 fragTexCoord; + +layout(binding = 1) uniform sampler2D texSampler; + +layout(location = 0) out vec4 outColor; + +void main() { + outColor = texture(texSampler, fragTexCoord); // vec4(fragTexCoord, 0.0); +} diff --git a/assets/shaders/cube.vert b/assets/shaders/cube.vert index 1818c3c..fa9f85b 100644 --- a/assets/shaders/cube.vert +++ b/assets/shaders/cube.vert @@ -4,16 +4,17 @@ layout(binding = 0) uniform UniformBufferObject { mat4 model; mat4 view; mat4 proj; -} -ubo; +} ubo; layout(location = 0) in vec3 inPosition; layout(location = 1) in vec3 inNormal; -layout(location = 2) in vec3 inTexCoords; +layout(location = 2) in vec2 inTexCoords; layout(location = 0) out vec3 fragColor; +layout(location = 1) out vec2 fragTexCoord; void main() { gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0); fragColor = abs(inNormal); + fragTexCoord = inTexCoords; } diff --git a/examples/cube/ex_cube.c b/examples/cube/ex_cube.c index 80a4e26..5d6cbbe 100644 --- a/examples/cube/ex_cube.c +++ b/examples/cube/ex_cube.c @@ -22,20 +22,30 @@ typedef struct mvp_uniforms { mat4 view; mat4 projection; } mvp_uniforms; +typedef struct my_shader_bind_group { + mvp_uniforms mvp; + texture_handle tex; +} my_shader_bind_group; // We also must create a function that knows how to return a `shader_data_layout` shader_data_layout mvp_uniforms_layout(void* data) { - mvp_uniforms* d = (mvp_uniforms*)data; + my_shader_bind_group* d = (my_shader_bind_group*)data; bool has_data = data != NULL; shader_binding b1 = { .label = "mvp_uniforms", .type = SHADER_BINDING_BYTES, .stores_data = has_data, .data = { .bytes = { .size = sizeof(mvp_uniforms) } } }; + + shader_binding b2 = { .label = "texture_sampler", + .type = SHADER_BINDING_TEXTURE, + .stores_data = has_data + }; if (has_data) { - b1.data.bytes.data = d; + b1.data.bytes.data = &d->mvp; + b2.data.texture.handle = d->tex; } - return (shader_data_layout){ .name = "global_ubo", .bindings = { b1 }, .bindings_count = 1 }; + return (shader_data_layout){ .name = "global_ubo", .bindings = { b1, b2 }, .bindings_count = 2 }; } int main() { @@ -60,7 +70,7 @@ int main() { gpu_renderpass* renderpass = gpu_renderpass_create(&pass_description); str8 vert_path = str8lit("build/linux/x86_64/debug/cube.vert.spv"); - str8 frag_path = str8lit("build/linux/x86_64/debug/triangle.frag.spv"); + str8 frag_path = str8lit("build/linux/x86_64/debug/cube.frag.spv"); str8_opt vertex_shader = str8_from_file(&scratch, vert_path); str8_opt fragment_shader = str8_from_file(&scratch, frag_path); if (!vertex_shader.has_value || !fragment_shader.has_value) { @@ -93,6 +103,7 @@ int main() { // Texture texture_data tex_data = texture_data_load("assets/textures/texture.jpg", false); texture_handle texture = texture_data_upload(tex_data, true); + printf("Texture %d", texture.raw); // Main loop while (!should_exit(&g_core)) { @@ -118,7 +129,11 @@ int main() { camera_view_projection(&cam, g_core.renderer.swapchain.extent.width, g_core.renderer.swapchain.extent.height, &view, &proj); mvp_uniforms mvp_data = { .model = model, .view = view, .projection = proj }; - mvp_uniforms_data.data = &mvp_data; + my_shader_bind_group shader_bind_data = { + .mvp = mvp_data, + .tex = texture + }; + mvp_uniforms_data.data = &shader_bind_data; encode_bind_shader_data(enc, 0, &mvp_uniforms_data); // Record draw calls diff --git a/src/renderer/backends/backend_vulkan.c b/src/renderer/backends/backend_vulkan.c index 33e0860..e8b54d9 100644 --- a/src/renderer/backends/backend_vulkan.c +++ b/src/renderer/backends/backend_vulkan.c @@ -92,8 +92,12 @@ VkShaderModule create_shader_module(str8 spirv); /** @brief Helper function for creating array of all extensions we want */ cstr_darray* get_all_extensions(); +void vulkan_transition_image_layout(gpu_texture* texture, VkFormat format, VkImageLayout old_layout, + VkImageLayout new_layout); + // --- Handy macros #define BUFFER_GET(h) (buffer_pool_get(&context.resource_pools->buffers, h)) +#define TEXTURE_GET(h) (texture_pool_get(&context.resource_pools->textures, h)) bool gpu_backend_init(const char* window_name, GLFWwindow* window) { memset(&context, 0, sizeof(vulkan_context)); @@ -571,24 +575,24 @@ gpu_pipeline* gpu_graphics_pipeline_create(struct graphics_pipeline_desc descrip // assert(description.data_layouts_count == 1); printf("data layouts %d\n", description.data_layouts_count); - for (u32 i = 0; i < description.data_layouts_count; i++) { - shader_data_layout sdl = description.data_layouts[i].shader_data_get_layout(NULL); + for (u32 layout_i = 0; layout_i < description.data_layouts_count; layout_i++) { + shader_data_layout sdl = description.data_layouts[layout_i].shader_data_get_layout(NULL); + TRACE("Got shader data layout %d's bindings! . found %d", layout_i, sdl.bindings_count); - // NOTE: is using VLA generally ok? - VkDescriptorSetLayoutBinding desc_set_bindings[description.data_layouts_count]; + VkDescriptorSetLayoutBinding desc_set_bindings[sdl.bindings_count]; // Bindings - assert(sdl.bindings_count == 1); - for (u32 j = 0; j < sdl.bindings_count; j++) { - desc_set_bindings[j].binding = j; - desc_set_bindings[j].descriptorCount = 1; - switch (sdl.bindings[j].type) { + assert(sdl.bindings_count == 2); + for (u32 binding_j = 0; binding_j < sdl.bindings_count; binding_j++) { + desc_set_bindings[binding_j].binding = binding_j; + desc_set_bindings[binding_j].descriptorCount = 1; + switch (sdl.bindings[binding_j].type) { case SHADER_BINDING_BUFFER: case SHADER_BINDING_BYTES: - desc_set_bindings[j].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - desc_set_bindings[j].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; // FIXME: dont hardcode + desc_set_bindings[binding_j].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + desc_set_bindings[binding_j].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; // FIXME: dont hardcode - u64 buffer_size = sdl.bindings[j].data.bytes.size; + u64 buffer_size = sdl.bindings[binding_j].data.bytes.size; VkDeviceSize uniform_buf_size = buffer_size; // TODO: Create backing buffer @@ -617,18 +621,24 @@ gpu_pipeline* gpu_graphics_pipeline_create(struct graphics_pipeline_desc descrip sizeof(uniform_buf_mem_mappings)); uniform_data.size = buffer_size; - pipeline->uniform_pointers[j] = uniform_data; + pipeline->uniform_pointers[binding_j] = uniform_data; + + break; + case SHADER_BINDING_TEXTURE: + desc_set_bindings[binding_j].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + desc_set_bindings[binding_j].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; // FIXME: dont hardcode + desc_set_bindings[binding_j].pImmutableSamplers = NULL; break; default: ERROR_EXIT("Unimplemented binding type!! in backend_vulkan"); } - switch (sdl.bindings[j].vis) { + switch (sdl.bindings[binding_j].vis) { case VISIBILITY_VERTEX: - desc_set_bindings[j].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + desc_set_bindings[binding_j].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; break; case VISIBILITY_FRAGMENT: - desc_set_bindings[j].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + desc_set_bindings[binding_j].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; break; case VISIBILITY_COMPUTE: WARN("Compute is not implemented yet"); @@ -643,7 +653,7 @@ gpu_pipeline* gpu_graphics_pipeline_create(struct graphics_pipeline_desc descrip desc_set_layout_info.pBindings = desc_set_bindings; VK_CHECK(vkCreateDescriptorSetLayout(context.device->logical_device, &desc_set_layout_info, - context.allocator, &desc_set_layouts[i])); + context.allocator, &desc_set_layouts[layout_i])); } printf("Descriptor set layouts\n"); @@ -683,17 +693,15 @@ gpu_pipeline* gpu_graphics_pipeline_create(struct graphics_pipeline_desc descrip pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE; pipeline_create_info.basePipelineIndex = -1; - printf("Here\n"); + printf("About to create graphics pipeline\n"); VkResult result = vkCreateGraphicsPipelines(context.device->logical_device, VK_NULL_HANDLE, 1, &pipeline_create_info, context.allocator, &pipeline->handle); if (result != VK_SUCCESS) { - printf("BAD\n"); FATAL("graphics pipeline creation failed. its fked mate"); ERROR_EXIT("Doomed"); } - printf("Here2\n"); TRACE("Vulkan Graphics pipeline created"); // once the pipeline has been created we can destroy these @@ -810,13 +818,17 @@ gpu_cmd_encoder gpu_cmd_encoder_create() { VK_CHECK(vkAllocateCommandBuffers(context.device->logical_device, &allocate_info, &encoder.cmd_buffer);); - VkDescriptorPoolSize uniform_pool_size; - uniform_pool_size.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - uniform_pool_size.descriptorCount = MAX_FRAMES_IN_FLIGHT * MAX_DESCRIPTOR_SETS; + VkDescriptorPoolSize pool_sizes[2]; + // Uniforms pool + pool_sizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + pool_sizes[0].descriptorCount = MAX_FRAMES_IN_FLIGHT * MAX_DESCRIPTOR_SETS; + // Samplers pool + pool_sizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + pool_sizes[1].descriptorCount = MAX_FRAMES_IN_FLIGHT * MAX_DESCRIPTOR_SETS; VkDescriptorPoolCreateInfo pool_info = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; - pool_info.poolSizeCount = 1; - pool_info.pPoolSizes = &uniform_pool_size; + pool_info.poolSizeCount = 2; + pool_info.pPoolSizes = pool_sizes; pool_info.maxSets = 100; VK_CHECK(vkCreateDescriptorPool(context.device->logical_device, &pool_info, context.allocator, @@ -875,6 +887,8 @@ void encode_bind_pipeline(gpu_cmd_encoder* encoder, pipeline_kind kind, gpu_pipe } void encode_bind_shader_data(gpu_cmd_encoder* encoder, u32 group, shader_data* data) { + arena tmp = arena_create(malloc(1024), 1024); + assert(data->data != NULL); // Update the local buffer @@ -886,44 +900,62 @@ void encode_bind_shader_data(gpu_cmd_encoder* encoder, u32 group, shader_data* d alloc_info.descriptorSetCount = 1; alloc_info.pSetLayouts = &encoder->pipeline->desc_set_layouts[group]; - VkDescriptorSet sets[1]; + shader_data_layout sdl = data->shader_data_get_layout(data->data); + size_t binding_count = sdl.bindings_count; + assert(binding_count == 2); + + VkDescriptorSet sets[0]; VK_CHECK(vkAllocateDescriptorSets(context.device->logical_device, &alloc_info, sets)); + // FIXME: hardcoded VkDescriptorSet_darray_push(context.free_set_queue, sets[0]); + /* VkDescriptorSet_darray_push(context.free_set_queue, sets[1]); */ - shader_data_layout sdl = data->shader_data_get_layout(NULL); - assert(sdl.bindings_count == 1); - - VkWriteDescriptorSet write_sets[1]; - memset(&write_sets, 0, sizeof(write_sets)); + VkWriteDescriptorSet write_sets[binding_count]; + memset(&write_sets, 0, binding_count * sizeof(VkWriteDescriptorSet)); - assert(sdl.bindings_count == 1); for (u32 i = 0; i < sdl.bindings_count; i++) { shader_binding binding = sdl.bindings[i]; if (binding.type == SHADER_BINDING_BUFFER || binding.type == SHADER_BINDING_BYTES) { - VkDescriptorBufferInfo buffer_info; - buffer_info.buffer = ubo.buffers[context.current_frame]; - buffer_info.offset = 0; - buffer_info.range = binding.data.bytes.size; - - write_sets[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - write_sets[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - write_sets[0].descriptorCount = 1; - write_sets[0].dstSet = sets[0]; - write_sets[0].dstBinding = 0; - write_sets[0].dstArrayElement = 0; - write_sets[0].pBufferInfo = &buffer_info; - } else { + VkDescriptorBufferInfo* buffer_info = arena_alloc(&tmp, sizeof(VkDescriptorBufferInfo)); + buffer_info->buffer = ubo.buffers[context.current_frame]; + buffer_info->offset = 0; + buffer_info->range = binding.data.bytes.size; + + write_sets[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write_sets[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + write_sets[i].descriptorCount = 1; + write_sets[i].dstSet = sets[0]; + write_sets[i].dstBinding = i; + write_sets[i].dstArrayElement = 0; + write_sets[i].pBufferInfo = buffer_info; + } else if (binding.type == SHADER_BINDING_TEXTURE) { + gpu_texture* texture = TEXTURE_GET(binding.data.texture.handle); + VkDescriptorImageInfo* image_info = arena_alloc(&tmp, sizeof(VkDescriptorImageInfo)); + image_info->imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + image_info->imageView = texture->view; + image_info->sampler = texture->sampler; + + write_sets[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write_sets[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + write_sets[i].descriptorCount = 1; + write_sets[i].dstSet = sets[0]; + write_sets[i].dstBinding = i; + write_sets[i].dstArrayElement = 0; + write_sets[i].pImageInfo = image_info; + }else { WARN("Unknown binding"); } } // Update - vkUpdateDescriptorSets(context.device->logical_device, 1, write_sets, 0, NULL); + vkUpdateDescriptorSets(context.device->logical_device, binding_count, write_sets, 0, NULL); // Bind vkCmdBindDescriptorSets(encoder->cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, encoder->pipeline->layout_handle, 0, 1, sets, 0, NULL); + + arena_free_storage(&tmp); } void encode_set_vertex_buffer(gpu_cmd_encoder* encoder, buffer_handle buf) { @@ -1453,19 +1485,15 @@ void copy_buffer_to_image_oneshot(buffer_handle src, texture_handle dst) { vulkan_command_buffer_finish_oneshot(temp_cmd_buffer); } -texture_handle gpu_texture_create(texture_desc desc, const void* data) { +texture_handle gpu_texture_create(texture_desc desc, bool create_view, const void* data) { VkDeviceSize image_size = desc.extents.x * desc.extents.y * 4; - TRACE("Uploading pixel data to texture using staging buffer"); - // Create a staging buffer - buffer_handle staging = - gpu_buffer_create(image_size, CEL_BUFFER_DEFAULT, CEL_BUFFER_FLAG_CPU, NULL); - // Copy data into it - buffer_upload_bytes(staging, (bytebuffer){ .buf = (u8*)data, .size = image_size }, 0, image_size); - VkImage image; VkDeviceMemory image_memory; + // FIXME: get from desc + VkFormat format = VK_FORMAT_R8G8B8A8_SRGB; + VkImageCreateInfo image_create_info = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; image_create_info.imageType = VK_IMAGE_TYPE_2D; image_create_info.extent.width = desc.extents.x; @@ -1473,7 +1501,7 @@ texture_handle gpu_texture_create(texture_desc desc, const void* data) { image_create_info.extent.depth = 1; image_create_info.mipLevels = 1; image_create_info.arrayLayers = 1; - image_create_info.format = VK_FORMAT_R8G8B8A8_SRGB; + image_create_info.format = format; image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL; image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; @@ -1494,13 +1522,71 @@ texture_handle gpu_texture_create(texture_desc desc, const void* data) { vkBindImageMemory(context.device->logical_device, image, image_memory, 0); - gpu_buffer_destroy(staging); - texture_handle handle; gpu_texture* texture = texture_pool_alloc(&context.resource_pools->textures, &handle); + DEBUG("Allocated texture with handle %d", handle.raw); texture->handle = image; + texture->debug_label = "Test Texture"; + texture->desc = desc; texture->memory = image_memory; texture->size = image_size; + + if (data) { + TRACE("Uploading pixel data to texture using staging buffer"); + // Create a staging buffer + buffer_handle staging = + gpu_buffer_create(image_size, CEL_BUFFER_DEFAULT, CEL_BUFFER_FLAG_CPU, NULL); + // Copy data into it + vulkan_transition_image_layout(texture, format, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + buffer_upload_bytes(staging, (bytebuffer){ .buf = (u8*)data, .size = image_size }, 0, image_size); + copy_buffer_to_image_oneshot(staging, handle); + vulkan_transition_image_layout(texture, format, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + gpu_buffer_destroy(staging); + } + + // Texture View + if (create_view) { + VkImageViewCreateInfo view_create_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; + view_create_info.image = image; + view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + view_create_info.format = format; + view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + + view_create_info.subresourceRange.baseMipLevel = 0; + view_create_info.subresourceRange.levelCount = 1; + view_create_info.subresourceRange.baseArrayLayer = 0; + view_create_info.subresourceRange.layerCount = 1; + + VK_CHECK(vkCreateImageView(context.device->logical_device, &view_create_info, context.allocator, + &texture->view)); + } + + // Sampler + VkSamplerCreateInfo sampler_info = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; + sampler_info.magFilter = VK_FILTER_LINEAR; + sampler_info.minFilter = VK_FILTER_LINEAR; + sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler_info.anisotropyEnable = VK_TRUE; + sampler_info.maxAnisotropy = 16; + sampler_info.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; + sampler_info.unnormalizedCoordinates = VK_FALSE; + sampler_info.compareEnable = VK_FALSE; + sampler_info.compareOp = VK_COMPARE_OP_ALWAYS; + sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + sampler_info.mipLodBias = 0.0; + sampler_info.minLod = 0.0; + sampler_info.maxLod = 0.0; + + VkResult res = vkCreateSampler(context.device->logical_device, &sampler_info, context.allocator, + &texture->sampler); + if (res != VK_SUCCESS) { + ERROR("Error creating texture sampler for image %s", texture->debug_label); + return; + } + return handle; } diff --git a/src/renderer/backends/backend_vulkan.h b/src/renderer/backends/backend_vulkan.h index 1e36ca3..dc0f7bd 100644 --- a/src/renderer/backends/backend_vulkan.h +++ b/src/renderer/backends/backend_vulkan.h @@ -109,4 +109,7 @@ typedef struct gpu_texture { VkDeviceMemory memory; u64 size; texture_desc desc; + VkImageView view; + VkSampler sampler; + char* debug_label; } gpu_texture; diff --git a/src/renderer/ral.h b/src/renderer/ral.h index a82a2ba..3697ea5 100644 --- a/src/renderer/ral.h +++ b/src/renderer/ral.h @@ -147,7 +147,7 @@ void gpu_buffer_upload(); void gpu_buffer_bind(buffer_handle buffer); // Textures -texture_handle gpu_texture_create(texture_desc desc, const void* data); +texture_handle gpu_texture_create(texture_desc desc, bool create_view, const void* data); void gpu_texture_destroy(); void gpu_texture_upload(); diff --git a/src/renderer/ral_types.h b/src/renderer/ral_types.h index 8339625..62a2f1d 100644 --- a/src/renderer/ral_types.h +++ b/src/renderer/ral_types.h @@ -139,6 +139,7 @@ typedef struct custom_vertex { } custom_vertex; // Vertex attributes +/// @strip_prefix(ATTR_) typedef enum vertex_attrib_type { ATTR_F32, ATTR_F32x2, @@ -211,7 +212,10 @@ typedef struct shader_binding { void* data; size_t size; } bytes; - } data; /** @brief */ + struct { + texture_handle handle; + } texture; + } data; /** @brief can store any kind of data that we can bind to a shader / descriptor set */ } shader_binding; #define MAX_LAYOUT_BINDINGS 8 diff --git a/src/renderer/render.c b/src/renderer/render.c index 9f9a97c..5723c9e 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -230,8 +230,9 @@ texture_data texture_data_load(const char* path, bool invert_y) { } texture_handle texture_data_upload(texture_data data, bool free_on_upload) { - texture_handle handle = gpu_texture_create(data.description, data.image_data); + texture_handle handle = gpu_texture_create(data.description, true, data.image_data); if (free_on_upload) { + TRACE("Freed stb_image data"); stbi_image_free(data.image_data); } return handle; diff --git a/xmake.lua b/xmake.lua index 9b06db0..40ef961 100644 --- a/xmake.lua +++ b/xmake.lua @@ -123,6 +123,7 @@ target("core_config") add_files("assets/shaders/triangle.vert") add_files("assets/shaders/triangle.frag") add_files("assets/shaders/cube.vert") + add_files("assets/shaders/cube.frag") -- add_files("assets/shaders/*.frag") if is_plat("windows") then add_includedirs("$(env VULKAN_SDK)/Include", {public = true}) -- cgit v1.2.3-70-g09d2