diff --git a/.gitignore b/.gitignore index 054aa7f..8c09441 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,7 @@ roleplay .cache *.o + +*.spv + +.DS_Store diff --git a/client/Makefile b/client/Makefile index 56db5cd..68a2382 100644 --- a/client/Makefile +++ b/client/Makefile @@ -6,7 +6,7 @@ CPP = /opt/homebrew/opt/llvm/bin/clang++ DSYM = /opt/homebrew/opt/llvm/bin/dsymutil GDB = /opt/homebrew/opt/llvm/bin/lldb -SOURCES = src/main.c src/render.c src/vma.cpp src/pipeline.c +SOURCES = src/main.c src/render.c src/vma.cpp src/pipeline.c src/command.c OBJECTS = $(addsuffix .o, $(basename $(SOURCES))) VERT_SPV = $(addsuffix .vert.spv, $(basename $(wildcard shader_src/*.vert))) FRAG_SPV = $(addsuffix .frag.spv, $(basename $(wildcard shader_src/*.frag))) @@ -38,13 +38,13 @@ clean_compdb: rm -rf .compdb rm compile_commands.json -run: roleplay +run: roleplay $(VERT_SPV) $(FRAG_SPV) ./roleplay roleplay.dSYM: roleplay $(DSYM) roleplay -debug: roleplay roleplay.dSYM +debug: roleplay roleplay.dSYM $(VERT_SPV) $(FRAG_SPV) $(GDB) roleplay diff --git a/client/include/command.h b/client/include/command.h new file mode 100644 index 0000000..42ca07b --- /dev/null +++ b/client/include/command.h @@ -0,0 +1,21 @@ +#ifndef COMMAND_H +#define COMMAND_H + +#include "vulkan/vulkan_core.h" + +typedef struct QueueStruct { + VkQueue handle; + uint32_t family; + uint32_t index; +} Queue; + + +VkCommandBuffer command_begin_single(VkDevice device, VkCommandPool transfer_pool); + +VkResult command_end_single(VkDevice device, VkCommandBuffer command_buffer, VkCommandPool transfer_pool, Queue transfer_queue); + +VkResult command_transition_image_layout(VkDevice device, VkCommandPool transfer_pool, Queue transfer_queue, VkImageLayout old_layout, VkImageLayout new_layout, VkImage image, VkAccessFlags src_mask, VkAccessFlags dst_mask, VkPipelineStageFlags source, VkPipelineStageFlags dest, uint32_t source_family, uint32_t dest_family, VkImageAspectFlags aspect_flags); + +VkResult command_transition_image_layout(VkDevice device, VkCommandPool transfer_pool, Queue transfer_queue, VkImageLayout old_layout, VkImageLayout new_layout, VkImage image, VkAccessFlags src_mask, VkAccessFlags dst_mask, VkPipelineStageFlags source, VkPipelineStageFlags dest, uint32_t source_family, uint32_t dest_family, VkImageAspectFlags aspect_flags); + +#endif diff --git a/client/include/pipeline.h b/client/include/pipeline.h index 47d2a8b..c262a8e 100644 --- a/client/include/pipeline.h +++ b/client/include/pipeline.h @@ -3,18 +3,24 @@ #include "vulkan/vulkan_core.h" #include "cglm/types.h" +#include "vk_mem_alloc.h" +#include "command.h" typedef struct GraphicsPipelineStruct { - VkDescriptorPool descriptor_pool; - VkDescriptorSet* descriptors; - - VkPipelineLayout layout; - VkPipeline pipeline; + VkPipelineLayout layout; + VkPipeline pipeline; } GraphicsPipeline; -struct Vertex2 { - vec2 pos; - vec3 color; +struct UIElement { + vec3 pos; + vec2 size; + vec4 color; +}; + +struct UIUniform { + vec2 size; }; +VkResult create_ui_polygon_pipeline(VkDevice device, VkRenderPass render_pass, VkDescriptorSetLayout descriptor_layout, VmaAllocator allocator, VkCommandPool transfer_pool, Queue transfer_queue, VmaAllocation* polygon_buffer_memory, VkBuffer* polygon_buffer, GraphicsPipeline* pipeline); + #endif diff --git a/client/include/render.h b/client/include/render.h index 43c3142..6ca72f0 100644 --- a/client/include/render.h +++ b/client/include/render.h @@ -3,7 +3,6 @@ #define VK_USE_PLATFORM_MACOS_MVK #include "vulkan/vulkan_core.h" -#include "vulkan/vk_enum_string_helper.h" #include "vk_mem_alloc.h" @@ -20,17 +19,11 @@ #include #include #include -#include -#include -#include -extern const uint32_t MAX_FRAMES_IN_FLIGHT; +#include "pipeline.h" +#include "command.h" -typedef struct QueueStruct { - VkQueue handle; - uint32_t family; - uint32_t index; -} Queue; +extern const uint32_t MAX_FRAMES_IN_FLIGHT; typedef struct SwapchainDetailsStruct { VkSurfaceCapabilitiesKHR capabilities; @@ -83,14 +76,22 @@ typedef struct RenderContextStruct { VkFence* in_flight_fences; - VkPipeline ui_pipeline_rect; - VkPipeline ui_pipeline_text; + GraphicsPipeline ui_pipeline_polygon; + VkBuffer ui_polygon_buffer; + VmaAllocation ui_polygon_buffer_memory; + + VkBuffer ui_descriptor_buffer; + VmaAllocation ui_descriptor_memory; + VkDescriptorSetLayout ui_descriptor_layout; + VkDescriptorPool ui_descriptor_pool; + VkDescriptorSet ui_descriptor_set; + GraphicsPipeline ui_pipeline_text; uint32_t current_frame; } RenderContext; GLFWwindow* init_window(); VkResult init_vulkan(GLFWwindow* window, RenderContext* context); -VkResult draw_frame(RenderContext* context); +VkResult draw_frame(RenderContext* context, VkBuffer ui_polygons, uint32_t polygon_count); #endif diff --git a/client/shader_src/ui_polygon.frag b/client/shader_src/ui_polygon.frag new file mode 100644 index 0000000..9d6b882 --- /dev/null +++ b/client/shader_src/ui_polygon.frag @@ -0,0 +1,10 @@ +#version 450 + +layout(location = 0) in vec4 fragColor; + +layout(location = 0) out vec4 outColor; + +void main() { + outColor = fragColor; +} + diff --git a/client/shader_src/ui_polygon.vert b/client/shader_src/ui_polygon.vert new file mode 100644 index 0000000..e56d6fb --- /dev/null +++ b/client/shader_src/ui_polygon.vert @@ -0,0 +1,19 @@ +#version 450 +#extension GL_EXT_buffer_reference : require + +layout(set = 0, binding = 0) uniform UIUniform { + vec2 screen; +} scene; + +layout(location = 0) in vec2 inVertexPosition; +layout(location = 1) in vec3 inPolygonPosition; +layout(location = 2) in vec2 inPolygonSize; +layout(location = 3) in vec4 inColor; + +layout(location = 0) out vec4 fragColor; + +void main() { + gl_Position = vec4(vec3(inVertexPosition * inPolygonSize, 0.0) + inPolygonPosition, 1.0); + fragColor = inColor; +} + diff --git a/client/src/command.c b/client/src/command.c new file mode 100644 index 0000000..7332dfc --- /dev/null +++ b/client/src/command.c @@ -0,0 +1,104 @@ +#include "command.h" + +VkCommandBuffer command_begin_single(VkDevice device, VkCommandPool transfer_pool) { + VkCommandBufferAllocateInfo command_info = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandPool = transfer_pool, + .commandBufferCount = 1, + }; + + VkCommandBuffer command_buffer; + VkResult result = vkAllocateCommandBuffers(device, &command_info, &command_buffer); + if(result != VK_SUCCESS) { + return VK_NULL_HANDLE; + } + + VkCommandBufferBeginInfo begin_info = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + }; + + result = vkBeginCommandBuffer(command_buffer, &begin_info); + if(result != VK_SUCCESS) { + vkFreeCommandBuffers(device, transfer_pool, 1, &command_buffer); + return VK_NULL_HANDLE; + } + + return command_buffer; +} + +VkResult command_end_single(VkDevice device, VkCommandBuffer command_buffer, VkCommandPool transfer_pool, Queue transfer_queue) { + VkResult result = vkEndCommandBuffer(command_buffer); + if(result != VK_SUCCESS) { + vkFreeCommandBuffers(device, transfer_pool, 1, &command_buffer); + return result; + } + + VkSubmitInfo submit_info = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .commandBufferCount = 1, + .pCommandBuffers = &command_buffer, + }; + + result = vkQueueSubmit(transfer_queue.handle, 1, &submit_info, 0); + if(result != VK_SUCCESS) { + vkFreeCommandBuffers(device, transfer_pool, 1, &command_buffer); + return result; + } + + result = vkQueueWaitIdle(transfer_queue.handle); + vkFreeCommandBuffers(device, transfer_pool, 1, &command_buffer); + return result; +} + +VkResult command_transition_image_layout(VkDevice device, VkCommandPool transfer_pool, Queue transfer_queue, VkImageLayout old_layout, VkImageLayout new_layout, VkImage image, VkAccessFlags src_mask, VkAccessFlags dst_mask, VkPipelineStageFlags source, VkPipelineStageFlags dest, uint32_t source_family, uint32_t dest_family, VkImageAspectFlags aspect_flags) { + VkCommandBuffer command_buffer = command_begin_single(device, transfer_pool); + + VkImageMemoryBarrier barrier = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .oldLayout = old_layout, + .newLayout = new_layout, + .srcQueueFamilyIndex = source_family, + .dstQueueFamilyIndex = dest_family, + .image = image, + .subresourceRange = { + .aspectMask = aspect_flags, + .levelCount = 1, + .layerCount = 1, + .baseMipLevel = 0, + .baseArrayLayer = 0, + }, + .srcAccessMask = src_mask, + .dstAccessMask = dst_mask, + }; + vkCmdPipelineBarrier(command_buffer, source, dest, 0, 0, 0, 0, 0, 1, &barrier); + + return command_end_single(device, command_buffer, transfer_pool, transfer_queue); +} + +VkResult command_copy_buffer_to_image(VkDevice device, VkCommandPool transfer_pool, Queue transfer_queue, VkExtent3D image_size, VkBuffer source, VkImage dest) { + VkCommandBuffer command_buffer = command_begin_single(device, transfer_pool); + + VkBufferImageCopy region = { + .bufferOffset = 0, + .bufferRowLength = 0, + .bufferImageHeight = 0, + .imageSubresource = { + .baseArrayLayer = 0, + .layerCount = 1, + .mipLevel = 0, + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + }, + .imageOffset = { + .x = 0, + .y = 0, + .z = 0, + }, + .imageExtent = image_size, + }; + + vkCmdCopyBufferToImage(command_buffer, source, dest, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + + return command_end_single(device, command_buffer, transfer_pool, transfer_queue); +} diff --git a/client/src/main.c b/client/src/main.c index 3f17653..53cddb8 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -2,10 +2,68 @@ #include "vulkan/vk_enum_string_helper.h" VkResult render_thread(GLFWwindow* window, RenderContext* render_context) { + VkBuffer test_ui_buffer; + VmaAllocation test_ui_memory; + + VkBufferCreateInfo test_ui_buffer_info = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + .size = 3*sizeof(struct UIElement), + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + }; + + VmaAllocationCreateInfo test_ui_memory_info = { + .usage = VMA_MEMORY_USAGE_CPU_TO_GPU, + }; + + VkResult result = vmaCreateBuffer(render_context->allocator, &test_ui_buffer_info, &test_ui_memory_info, &test_ui_buffer, &test_ui_memory, NULL); + if(result != VK_SUCCESS) { + return result; + } + + struct UIElement* mapped; + + result = vmaMapMemory(render_context->allocator, test_ui_memory, (void**)&mapped); + if(result != VK_SUCCESS) { + return result; + } + + mapped[0].size[0] = 0.5; + mapped[0].size[1] = 0.5; + mapped[0].color[0] = 1.0; + mapped[0].color[1] = 0.0; + mapped[0].color[2] = 0.0; + mapped[0].color[3] = 1.0; + mapped[0].pos[0] = 0.0; + mapped[0].pos[1] = 0.0; + mapped[0].pos[2] = 0.5; + + mapped[1].size[0] = 0.5; + mapped[1].size[1] = 0.5; + mapped[1].color[0] = 0.0; + mapped[1].color[1] = 1.0; + mapped[1].color[2] = 0.0; + mapped[1].color[3] = 1.0; + mapped[1].pos[0] = 0.5; + mapped[1].pos[1] = 0.0; + mapped[1].pos[2] = 0.5; + + mapped[2].size[0] = 0.5; + mapped[2].size[1] = 0.5; + mapped[2].color[0] = 0.0; + mapped[2].color[1] = 0.0; + mapped[2].color[2] = 1.0; + mapped[2].color[3] = 1.0; + mapped[2].pos[0] = 0.0; + mapped[2].pos[1] = 0.5; + mapped[2].pos[2] = 0.5; + + vmaUnmapMemory(render_context->allocator, test_ui_memory); + while(glfwWindowShouldClose(window) == 0) { glfwPollEvents(); - VkResult result = draw_frame(render_context); + result = draw_frame(render_context, test_ui_buffer, 3); if(result != VK_SUCCESS) { fprintf(stderr, "draw_frame error: %s\n", string_VkResult(result)); return result; diff --git a/client/src/pipeline.c b/client/src/pipeline.c index 5f8cc9c..26dcd7d 100644 --- a/client/src/pipeline.c +++ b/client/src/pipeline.c @@ -1,6 +1,7 @@ #include "pipeline.h" #include "stdio.h" #include "stdlib.h" +#include "vulkan/vulkan_core.h" VkShaderModule load_shader_file(const char* path, VkDevice device) { FILE* file; @@ -44,14 +45,10 @@ VkShaderModule load_shader_file(const char* path, VkDevice device) { return shader; } -VkResult create_ui_rect_pipeline(VkDevice device, VkRenderPass render_pass, GraphicsPipeline* out) { - if(out == NULL) { - return VK_ERROR_VALIDATION_FAILED_EXT; - } - - VkShaderModule vert_shader = load_shader_file("shader_src/ui_rect.vert.spv", device); - VkShaderModule frag_shader = load_shader_file("shader_src/ui_rect.frag.spv", device); - VkPipelineShaderStageCreateInfo shader_stages[2] = { +VkResult create_ui_polygon_pipeline(VkDevice device, VkRenderPass render_pass, VkDescriptorSetLayout descriptor_layout, VmaAllocator allocator, VkCommandPool transfer_pool, Queue transfer_queue, VmaAllocation* polygon_buffer_memory, VkBuffer* polygon_buffer, GraphicsPipeline* pipeline) { + VkShaderModule vert_shader = load_shader_file("shader_src/ui_polygon.vert.spv", device); + VkShaderModule frag_shader = load_shader_file("shader_src/ui_polygon.frag.spv", device); + VkPipelineShaderStageCreateInfo shader_stages[] = { { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_VERTEX_BIT, @@ -66,29 +63,47 @@ VkResult create_ui_rect_pipeline(VkDevice device, VkRenderPass render_pass, Grap }, }; - VkVertexInputBindingDescription bindings[1] = { + VkVertexInputBindingDescription bindings[] = { + { + .binding = 0, + .stride = sizeof(vec2), + .inputRate = VK_VERTEX_INPUT_RATE_VERTEX, + }, { - .binding = 0, // Which buffer 'binding' to use - .stride = sizeof(struct Vertex2), // How many bytes to increase the index between instance - .inputRate = VK_VERTEX_INPUT_RATE_VERTEX, // Whether an instance is a vertex or an index + .binding = 1, + .stride = sizeof(struct UIElement), + .inputRate = VK_VERTEX_INPUT_RATE_INSTANCE, }, }; - VkVertexInputAttributeDescription attributes[3] = { + VkVertexInputAttributeDescription attributes[] = { { - .binding = 0, // Which buffer 'binding' to use - .location = 0, // Which 'location' to export as to shader - .format = VK_FORMAT_R32G32_SFLOAT, // What format to interpret as for shader - .offset = offsetof(struct Vertex2, pos), // What offset from instance start + .binding = 0, + .location = 0, + .format = VK_FORMAT_R32G32_SFLOAT, + .offset = 0, }, { - .binding = 0, + .binding = 1, .location = 1, .format = VK_FORMAT_R32G32B32_SFLOAT, - .offset = offsetof(struct Vertex2, color), + .offset = offsetof(struct UIElement, pos), + }, + { + .binding = 1, + .location = 2, + .format = VK_FORMAT_R32G32_SFLOAT, + .offset = offsetof(struct UIElement, size), + }, + { + .binding = 1, + .location = 3, + .format = VK_FORMAT_R32G32B32A32_SFLOAT, + .offset = offsetof(struct UIElement, color), }, }; + VkPipelineVertexInputStateCreateInfo input_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, .pVertexBindingDescriptions = bindings, @@ -97,19 +112,13 @@ VkResult create_ui_rect_pipeline(VkDevice device, VkRenderPass render_pass, Grap .vertexAttributeDescriptionCount = sizeof(attributes)/sizeof(VkVertexInputAttributeDescription), }; - VkPushConstantRange pcr = { - .stageFlags = VK_SHADER_STAGE_ALL, - .offset = 0, - .size = sizeof(VkDeviceAddress), - }; - VkPipelineLayoutCreateInfo layout_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - .pushConstantRangeCount = 1, - .pPushConstantRanges = &pcr, + .setLayoutCount = 1, + .pSetLayouts = &descriptor_layout, }; - VkResult result = vkCreatePipelineLayout(device, &layout_info, 0, &out->layout); + VkResult result = vkCreatePipelineLayout(device, &layout_info, 0, &pipeline->layout); if(result != VK_SUCCESS) { return result; } @@ -223,7 +232,7 @@ VkResult create_ui_rect_pipeline(VkDevice device, VkRenderPass render_pass, Grap VkGraphicsPipelineCreateInfo draw_pipeline_info = { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .stageCount = 2, + .stageCount = sizeof(shader_stages)/sizeof(VkPipelineShaderStageCreateInfo), .pStages = shader_stages, .pVertexInputState = &input_info, .pInputAssemblyState = &input_assembly_info, @@ -233,17 +242,83 @@ VkResult create_ui_rect_pipeline(VkDevice device, VkRenderPass render_pass, Grap .pDynamicState = &dynamic_info, .pDepthStencilState = &depth_info, .pMultisampleState = &multisample_info, - .layout = out->layout, + .layout = pipeline->layout, .renderPass = render_pass, .subpass = 0, .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = -1, }; - result = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &draw_pipeline_info, 0, &out->pipeline); + result = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &draw_pipeline_info, 0, &pipeline->pipeline); + if(result != VK_SUCCESS) { + return result; + } + + // Create and populate polygon buffer + uint32_t polygon_buffer_size = 3 * sizeof(vec2); + VkBufferCreateInfo temp_buffer_info = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .size = polygon_buffer_size + }; + + VmaAllocationCreateInfo temp_allocation_info = { + .usage = VMA_MEMORY_USAGE_CPU_TO_GPU, + }; + + VkBuffer temp_buffer; + VmaAllocation temp_buffer_memory; + result = vmaCreateBuffer(allocator, &temp_buffer_info, &temp_allocation_info, &temp_buffer, &temp_buffer_memory, NULL); if(result != VK_SUCCESS) { return result; } + VkBufferCreateInfo buffer_info = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .size = polygon_buffer_size, + }; + + VmaAllocationCreateInfo allocation_info = { + .usage = VMA_MEMORY_USAGE_GPU_ONLY, + }; + + result = vmaCreateBuffer(allocator, &buffer_info, &allocation_info, polygon_buffer, polygon_buffer_memory, NULL); + if(result != VK_SUCCESS) { + return result; + } + + vec2* mapped; + result = vmaMapMemory(allocator, temp_buffer_memory, (void**)&mapped); + if(result != VK_SUCCESS) { + return result; + } + + mapped[0][0] = 0.0f; + mapped[0][1] = 0.0f; + mapped[1][0] = 1.0f; + mapped[1][1] = 0.0f; + mapped[2][0] = 0.0f; + mapped[2][1] = 1.0f; + + vmaUnmapMemory(allocator, temp_buffer_memory); + + VkCommandBuffer copy_buffer = command_begin_single(device, transfer_pool); + VkBufferCopy copy_region = { + .size = polygon_buffer_size, + .dstOffset = 0, + .srcOffset = 0, + }; + vkCmdCopyBuffer(copy_buffer, temp_buffer, *polygon_buffer, 1, ©_region); + + result = command_end_single(device, copy_buffer, transfer_pool, transfer_queue); + if(result != VK_SUCCESS) { + return result; + } + + vmaDestroyBuffer(allocator, temp_buffer, temp_buffer_memory); + return VK_SUCCESS; } diff --git a/client/src/render.c b/client/src/render.c index be35ef6..b1b6c8c 100644 --- a/client/src/render.c +++ b/client/src/render.c @@ -1,4 +1,6 @@ #include "render.h" +#include "pipeline.h" +#include "vk_mem_alloc.h" #include "vulkan/vulkan_core.h" const uint32_t MAX_FRAMES_IN_FLIGHT = 2; @@ -81,7 +83,6 @@ bool check_validation_layers(const char ** layers, uint32_t num_layers) { VkResult create_instance(VkInstance* instance) { if(check_validation_layers(validation_layers, validation_layer_count) == false) { - fprintf(stderr, "requested validation layers not supported\n"); return VK_ERROR_VALIDATION_FAILED_EXT; } @@ -122,7 +123,6 @@ VkResult create_instance(VkInstance* instance) { VkResult result = vkCreateInstance(&instance_info, 0, instance); if(result != VK_SUCCESS) { - fprintf(stderr, "vkCreateInstance: %s\n", string_VkResult(result)); return result; } @@ -159,7 +159,6 @@ VkResult create_debug_messenger(VkInstance instance, VkDebugUtilsMessengerEXT* d VkResult result; result = func(instance, &messenger_info, 0, debug_messenger); if(result != VK_SUCCESS) { - fprintf(stderr, "failed to create debug messenger\n"); return result; } @@ -646,109 +645,6 @@ VkResult create_render_pass(VkDevice device, VkSurfaceFormatKHR format, VkFormat return VK_SUCCESS; } -VkCommandBuffer command_begin_single(VkDevice device, VkCommandPool transfer_pool) { - VkCommandBufferAllocateInfo command_info = { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, - .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, - .commandPool = transfer_pool, - .commandBufferCount = 1, - }; - - VkCommandBuffer command_buffer; - VkResult result = vkAllocateCommandBuffers(device, &command_info, &command_buffer); - if(result != VK_SUCCESS) { - return VK_NULL_HANDLE; - } - - VkCommandBufferBeginInfo begin_info = { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, - }; - - result = vkBeginCommandBuffer(command_buffer, &begin_info); - if(result != VK_SUCCESS) { - vkFreeCommandBuffers(device, transfer_pool, 1, &command_buffer); - return VK_NULL_HANDLE; - } - - return command_buffer; -} - -VkResult command_end_single(VkDevice device, VkCommandBuffer command_buffer, VkCommandPool transfer_pool, Queue transfer_queue) { - VkResult result = vkEndCommandBuffer(command_buffer); - if(result != VK_SUCCESS) { - vkFreeCommandBuffers(device, transfer_pool, 1, &command_buffer); - return result; - } - - VkSubmitInfo submit_info = { - .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, - .commandBufferCount = 1, - .pCommandBuffers = &command_buffer, - }; - - result = vkQueueSubmit(transfer_queue.handle, 1, &submit_info, 0); - if(result != VK_SUCCESS) { - vkFreeCommandBuffers(device, transfer_pool, 1, &command_buffer); - return result; - } - - result = vkQueueWaitIdle(transfer_queue.handle); - vkFreeCommandBuffers(device, transfer_pool, 1, &command_buffer); - return result; -} - -VkResult command_transition_image_layout(VkDevice device, VkCommandPool transfer_pool, Queue transfer_queue, VkImageLayout old_layout, VkImageLayout new_layout, VkImage image, VkAccessFlags src_mask, VkAccessFlags dst_mask, VkPipelineStageFlags source, VkPipelineStageFlags dest, uint32_t source_family, uint32_t dest_family, VkImageAspectFlags aspect_flags) { - VkCommandBuffer command_buffer = command_begin_single(device, transfer_pool); - - VkImageMemoryBarrier barrier = { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .oldLayout = old_layout, - .newLayout = new_layout, - .srcQueueFamilyIndex = source_family, - .dstQueueFamilyIndex = dest_family, - .image = image, - .subresourceRange = { - .aspectMask = aspect_flags, - .levelCount = 1, - .layerCount = 1, - .baseMipLevel = 0, - .baseArrayLayer = 0, - }, - .srcAccessMask = src_mask, - .dstAccessMask = dst_mask, - }; - vkCmdPipelineBarrier(command_buffer, source, dest, 0, 0, 0, 0, 0, 1, &barrier); - - return command_end_single(device, command_buffer, transfer_pool, transfer_queue); -} - -VkResult command_copy_buffer_to_image(VkDevice device, VkCommandPool transfer_pool, Queue transfer_queue, VkExtent3D image_size, VkBuffer source, VkImage dest) { - VkCommandBuffer command_buffer = command_begin_single(device, transfer_pool); - - VkBufferImageCopy region = { - .bufferOffset = 0, - .bufferRowLength = 0, - .bufferImageHeight = 0, - .imageSubresource = { - .baseArrayLayer = 0, - .layerCount = 1, - .mipLevel = 0, - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - }, - .imageOffset = { - .x = 0, - .y = 0, - .z = 0, - }, - .imageExtent = image_size, - }; - - vkCmdCopyBufferToImage(command_buffer, source, dest, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); - - return command_end_single(device, command_buffer, transfer_pool, transfer_queue); -} - VkResult create_swapchain_framebuffers(VkDevice device, uint32_t image_count, VkImageView* image_views, VkImageView depth_image_view, VkRenderPass render_pass, VkExtent2D extent, VkFramebuffer** framebuffers) { *framebuffers = malloc(sizeof(VkFramebuffer)*image_count); if(*framebuffers == 0) { @@ -1043,12 +939,110 @@ VkResult init_vulkan(GLFWwindow* window, RenderContext* context) { return result; } - context->current_frame = 0; + VkDescriptorSetLayoutBinding ui_descriptor_bindings[] = { + { + .binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, + } + }; + + VkDescriptorSetLayoutCreateInfo ui_descriptor_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .pBindings = ui_descriptor_bindings, + .bindingCount = sizeof(ui_descriptor_bindings)/sizeof(VkDescriptorSetLayoutBinding), + }; + + result = vkCreateDescriptorSetLayout(context->device, &ui_descriptor_info, NULL, &context->ui_descriptor_layout); + if(result != VK_SUCCESS) { + return result; + } + + VkDescriptorPoolSize pool_size = { + .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .descriptorCount = 1, + }; + + VkDescriptorPoolCreateInfo ui_pool_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .pPoolSizes = &pool_size, + .poolSizeCount = 1, + .maxSets = 1, + }; + + result = vkCreateDescriptorPool(context->device, &ui_pool_info, NULL, &context->ui_descriptor_pool); + if(result != VK_SUCCESS) { + return result; + } + + VkDescriptorSetAllocateInfo ui_descriptor_allocate_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .pSetLayouts = &context->ui_descriptor_layout, + .descriptorSetCount = 1, + .descriptorPool = context->ui_descriptor_pool, + }; + + result = vkAllocateDescriptorSets(context->device, &ui_descriptor_allocate_info, &context->ui_descriptor_set); + if(result != VK_SUCCESS) { + return result; + } + VkBufferCreateInfo ui_uniform_buffer_info = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + .size = sizeof(struct UIUniform), + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + }; + + VmaAllocationCreateInfo ui_uniform_memory_info = { + .usage = VMA_MEMORY_USAGE_CPU_TO_GPU, + }; + + result = vmaCreateBuffer(context->allocator, &ui_uniform_buffer_info, &ui_uniform_memory_info, &context->ui_descriptor_buffer, &context->ui_descriptor_memory, NULL); + if(result != VK_SUCCESS) { + return result; + } + + void * mapped; + result = vmaMapMemory(context->allocator, context->ui_descriptor_memory, &mapped); + if(result != VK_SUCCESS) { + return result; + } + struct UIUniform ui_uniform = { + .size = {context->swapchain_extent.width, context->swapchain_extent.height} + }; + memcpy(mapped, &ui_uniform, sizeof(ui_uniform)); + vmaUnmapMemory(context->allocator, context->ui_descriptor_memory); + + VkDescriptorBufferInfo ui_uniform_info = { + .offset = 0, + .range = sizeof(ui_uniform), + .buffer = context->ui_descriptor_buffer, + }; + + VkWriteDescriptorSet ui_uniform_write = { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = context->ui_descriptor_set, + .dstBinding = 0, + .dstArrayElement = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .descriptorCount = 1, + .pBufferInfo = &ui_uniform_info, + }; + + vkUpdateDescriptorSets(context->device, 1, &ui_uniform_write, 0, NULL); + + result = create_ui_polygon_pipeline(context->device, context->render_pass, context->ui_descriptor_layout, context->allocator, context->transfer_pool, context->transfer_queue, &context->ui_polygon_buffer_memory, &context->ui_polygon_buffer, &context->ui_pipeline_polygon); + if(result != VK_SUCCESS) { + return result; + } + + context->current_frame = 0; return VK_SUCCESS; } -VkResult draw_frame(RenderContext* context) { +VkResult draw_frame(RenderContext* context, VkBuffer ui_polygons, uint32_t ui_polygon_count) { VkResult result; result = vkWaitForFences(context->device, 1, &context->in_flight_fences[context->current_frame], VK_TRUE, UINT64_MAX); @@ -1081,7 +1075,7 @@ VkResult draw_frame(RenderContext* context) { return result; } - VkClearValue clear_color = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; + VkClearValue clear_values[2] = {{.color={{0.0f, 0.0f, 0.0f, 1.0f}}}, {.depthStencil={1.0f, 0.0f}}}; VkRenderPassBeginInfo render_pass_begin = { .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, @@ -1089,10 +1083,19 @@ VkResult draw_frame(RenderContext* context) { .framebuffer = context->swapchain_framebuffers[image_index], .renderArea.offset = {0, 0}, .renderArea.extent = context->swapchain_extent, - .clearValueCount = 1, - .pClearValues = &clear_color, + .clearValueCount = 2, + .pClearValues = clear_values, }; vkCmdBeginRenderPass(context->swapchain_command_buffers[context->current_frame], &render_pass_begin, VK_SUBPASS_CONTENTS_INLINE); + + // Draw UI polygons + vkCmdBindPipeline(context->swapchain_command_buffers[context->current_frame], VK_PIPELINE_BIND_POINT_GRAPHICS, context->ui_pipeline_polygon.pipeline); + VkDeviceSize offset = 0; + vkCmdBindVertexBuffers(context->swapchain_command_buffers[context->current_frame], 0, 1, &context->ui_polygon_buffer, &offset); + VkDeviceSize offset2 = 0; + vkCmdBindVertexBuffers(context->swapchain_command_buffers[context->current_frame], 1, 1, &ui_polygons, &offset2); + vkCmdDraw(context->swapchain_command_buffers[context->current_frame], 3, ui_polygon_count, 0, 0); + vkCmdEndRenderPass(context->swapchain_command_buffers[context->current_frame]); result = vkEndCommandBuffer(context->swapchain_command_buffers[context->current_frame]);