diff options
-rwxr-xr-x | compile_shaders.sh | 5 | ||||
-rwxr-xr-x | main.cpp | 327 | ||||
-rwxr-xr-x | shaders/triangle.vert | 8 |
3 files changed, 309 insertions, 31 deletions
diff --git a/compile_shaders.sh b/compile_shaders.sh new file mode 100755 index 0000000..2e985f0 --- /dev/null +++ b/compile_shaders.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +shader_dir="./shaders" +glslc $shader_dir/triangle.vert -o $shader_dir/triangle.vert.spv +glslc $shader_dir/triangle.frag -o $shader_dir/triangle.frag.spv @@ -21,6 +21,8 @@ Progress Notes: Hello Triangle Lesson Completed - Graphics Pipeline chapter completed - Drawing chapter completed. - Swapchain recreation (resizing completed) +- Vextex Buffer completed +- Uniform Buffer completed */ /* @@ -39,24 +41,33 @@ Progress Notes: Hello Triangle Lesson Completed * 7. resize is broken on linux, no idea why. It could just be linux being linux but like, * the resize window is very choppy/laggy on wayland and the resize operation is laggy on x11. * Meanwhile, everything works correctly on W11. +* 8. Driver developers recommend that you also store multiple +* buffers, like the vertex and index buffer, into a single VkBuffer +* and use offsets in commands like vkCmdBindVertexBuffers +* 9. Push Constants: a more effective way of passing a small buffer of data to +* shaders +* 10. Desriptors -> Descriptor Sets -> Descriptor Pools, I need to clarify the +* relationship between these items a bit better. */ #include <array> -#include <chrono> -#include <iostream> #include <stdint.h> #include <stdlib.h> #include <string.h> -#include <thread> #include <vector> #include <vulkan/vulkan_core.h> #include "SDL2/SDL.h" +#include "SDL2/SDL_scancode.h" #include "SDL2/SDL_timer.h" #include "SDL2/SDL_video.h" #include "SDL2/SDL_vulkan.h" #include "vulkan/vulkan.h" + +#define GLM_FORCE_RADIANS +#define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES #include "glm/glm.hpp" +#include "glm/gtc/matrix_transform.hpp" // Start utils @@ -94,6 +105,12 @@ struct Vertex { glm::vec3 col; }; +struct UniformBufferObject { + alignas(16) glm::mat4 model; + alignas(16) glm::mat4 view; + alignas(16) glm::mat4 proj; +}; + // string struct Str256 { char buffer[256]; @@ -196,10 +213,19 @@ struct MegaState { VkRenderPass vk_render_pass; - VkBuffer vk_staging_vertex_buffer; - VkDeviceMemory vk_mem_staging_vertex_buffer; VkBuffer vk_vertex_buffer; VkDeviceMemory vk_mem_vertex_buffer; + VkBuffer vk_index_buffer; + VkDeviceMemory vk_mem_index_buffer; + std::vector<VkBuffer> vk_uniform_buffers; + std::vector<VkDeviceMemory> vk_mem_uniform_buffers; + std::vector<void*> vk_mapped_uniform_buffers; + + VkDescriptorSetLayout vk_descriptor_set_layout; + VkDescriptorPool vk_descriptor_pool; + std::vector<VkDescriptorSet> vk_descriptor_sets; + VkPipelineLayout vk_pipeline_layout; + VkPipeline vk_pipeline; }; internal b8 resizable = 0; @@ -1044,16 +1070,40 @@ int main() { return -1; } } + // @func: create_descriptor_set_layout: + { + VkDescriptorSetLayoutBinding lb_ubo{}; + lb_ubo.binding = 0; + lb_ubo.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + lb_ubo.descriptorCount = 1; + lb_ubo.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + lb_ubo.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutCreateInfo ci_desc_set{}; + ci_desc_set.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + ci_desc_set.bindingCount = 1; + ci_desc_set.pBindings = &lb_ubo; + + if (vkCreateDescriptorSetLayout(state.vk_device, &ci_desc_set, nullptr, + &state.vk_descriptor_set_layout) + != VK_SUCCESS) { + printf("Failed to create descriptor set layout\n"); + return -1; + } + } // @func: create_graphics_pipeline - VkPipelineLayout vk_pipeline_layout; - VkPipeline vk_pipeline; - // Vertex Data const std::vector<Vertex> vertices = { - {{0.0f, -0.5f}, {1.0f, 0.0f, 0.0f}}, - {{0.5f, 0.5f}, {0.0f, 1.0f, 0.0f}}, - {{-0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}} + {{-0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}}, + {{0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}}, + {{0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}}, + {{-0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}} + }; + + const std::vector<u16> indices = { + 0, 1, 2, 2, 3, 0 }; + { Arena temp_pipeline_arena = state.arena; ArrUChar vertex_shader = {}; @@ -1199,7 +1249,7 @@ int main() { ci_pipeline_raster.polygonMode = VK_POLYGON_MODE_FILL; ci_pipeline_raster.lineWidth = 1.0f; ci_pipeline_raster.cullMode = VK_CULL_MODE_BACK_BIT; - ci_pipeline_raster.frontFace = VK_FRONT_FACE_CLOCKWISE; + ci_pipeline_raster.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; ci_pipeline_raster.depthBiasEnable = VK_FALSE; ci_pipeline_raster.depthBiasClamp = 0.0f; ci_pipeline_raster.depthBiasConstantFactor = 0.0f; @@ -1246,11 +1296,14 @@ int main() { // pipeline layout VkPipelineLayoutCreateInfo ci_pipeline_layout = {}; ci_pipeline_layout.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - ci_pipeline_layout.setLayoutCount = 0; - ci_pipeline_layout.pSetLayouts = nullptr; + ci_pipeline_layout.setLayoutCount = 1; + ci_pipeline_layout.pSetLayouts = &state.vk_descriptor_set_layout; ci_pipeline_layout.pushConstantRangeCount = 0; ci_pipeline_layout.pPushConstantRanges = nullptr; - VkResult vk_result = vkCreatePipelineLayout(state.vk_device, &ci_pipeline_layout, nullptr, &vk_pipeline_layout); + VkResult vk_result = vkCreatePipelineLayout(state.vk_device, + &ci_pipeline_layout, + nullptr, + &state.vk_pipeline_layout); if (vk_result != VK_SUCCESS) { printf("failed to create a pipeline layout. Error: %d", vk_result); return -1; @@ -1271,14 +1324,18 @@ int main() { ci_pipeline_graphics.pDepthStencilState = ci_pipeline_depth_stencil; ci_pipeline_graphics.pColorBlendState = &ci_pipeline_color_blend; - ci_pipeline_graphics.layout = vk_pipeline_layout; + ci_pipeline_graphics.layout = state.vk_pipeline_layout; ci_pipeline_graphics.renderPass = state.vk_render_pass; ci_pipeline_graphics.subpass = 0; ci_pipeline_graphics.basePipelineHandle = VK_NULL_HANDLE; ci_pipeline_graphics.basePipelineIndex = -1; - if (vkCreateGraphicsPipelines(state.vk_device, VK_NULL_HANDLE, 1, &ci_pipeline_graphics, nullptr, &vk_pipeline) != VK_SUCCESS) { + if (vkCreateGraphicsPipelines(state.vk_device, + VK_NULL_HANDLE, 1, + &ci_pipeline_graphics, + nullptr, + &state.vk_pipeline) != VK_SUCCESS) { // no printf from this point as validation layers should cover it. // todo: remove older printf. return -1; @@ -1324,7 +1381,81 @@ int main() { return -1; } } - // create_vertex_buffer: + // @func: create_index_buffer: + { + u64 buffer_size = sizeof(indices[0]) * indices.size(); + VkBuffer staging_buffer; + VkDeviceMemory staging_buffer_memory; + vk_create_buffer(state.vk_device, state.vk_physical_device, + buffer_size, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &staging_buffer, + &staging_buffer_memory); + + // fill index buffer + void *index_data; + vkMapMemory(state.vk_device, staging_buffer_memory, 0, buffer_size, 0, + &index_data); + memcpy(index_data, indices.data(), buffer_size); + vkUnmapMemory(state.vk_device, staging_buffer_memory); + + vk_create_buffer(state.vk_device, state.vk_physical_device, + buffer_size, + VK_BUFFER_USAGE_TRANSFER_DST_BIT | + VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + &state.vk_index_buffer, + &state.vk_mem_index_buffer); + + // copy buffer from SRC -> DEST + // vk_copy_buffer: + { + VkCommandBufferAllocateInfo alloc_info{}; + alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + alloc_info.commandPool = vk_command_pool; + alloc_info.commandBufferCount = 1; + + VkCommandBuffer command_buffer; + vkAllocateCommandBuffers(state.vk_device, &alloc_info, &command_buffer); + + { + VkCommandBufferBeginInfo begin_info{}; + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + + vkBeginCommandBuffer(command_buffer, &begin_info); + + VkBufferCopy copy_region{}; + copy_region.srcOffset = 0; + copy_region.dstOffset = 0; + copy_region.size = buffer_size; + vkCmdCopyBuffer(command_buffer, staging_buffer, + state.vk_index_buffer, 1, ©_region); + + vkEndCommandBuffer(command_buffer); + + VkSubmitInfo submit_info{}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &command_buffer; + + vkQueueSubmit(state.vk_queue_gfx, 1, &submit_info, VK_NULL_HANDLE); + vkQueueWaitIdle(state.vk_queue_gfx); + + } + + vkFreeCommandBuffers(state.vk_device, vk_command_pool, + 1, &command_buffer); + } + + vkDestroyBuffer(state.vk_device, staging_buffer, nullptr); + vkFreeMemory(state.vk_device, staging_buffer_memory, nullptr); + + } + // @func: create_vertex_buffer: { // staging buffer @@ -1397,6 +1528,92 @@ int main() { vkDestroyBuffer(state.vk_device, staging_buffer, nullptr); vkFreeMemory(state.vk_device, staging_buffer_memory, nullptr); } + // create_uniform_buffer: + { + VkDeviceSize buffer_size = sizeof(UniformBufferObject); + + state.vk_uniform_buffers.resize(MAX_FRAMES_IN_FLIGHT); + state.vk_mem_uniform_buffers.resize(MAX_FRAMES_IN_FLIGHT); + state.vk_mapped_uniform_buffers.resize(MAX_FRAMES_IN_FLIGHT); + + for (u32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + vk_create_buffer(state.vk_device, + state.vk_physical_device, + buffer_size, + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + &state.vk_uniform_buffers[i], + &state.vk_mem_uniform_buffers[i]); + + vkMapMemory(state.vk_device, + state.vk_mem_uniform_buffers[i], + 0, buffer_size, 0, + &state.vk_mapped_uniform_buffers[i]); + } + } + // create_descriptor_pool: + { + VkDescriptorPoolSize pool_size; + pool_size.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + pool_size.descriptorCount = (u32)MAX_FRAMES_IN_FLIGHT; + + VkDescriptorPoolCreateInfo ci_desc_pool{}; + ci_desc_pool.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + ci_desc_pool.poolSizeCount = 1; + ci_desc_pool.pPoolSizes = &pool_size; + ci_desc_pool.maxSets = (u32)MAX_FRAMES_IN_FLIGHT; + + if (vkCreateDescriptorPool(state.vk_device, + &ci_desc_pool, nullptr, + &state.vk_descriptor_pool) != VK_SUCCESS) { + printf("Error :: Failed to create descriptor pool\n"); + return -1; + } + } + // create_descriptor_sets: + { + std::vector<VkDescriptorSetLayout> layouts(MAX_FRAMES_IN_FLIGHT, + state.vk_descriptor_set_layout); + VkDescriptorSetAllocateInfo alloc_info{}; + alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + alloc_info.descriptorPool = state.vk_descriptor_pool; + alloc_info.descriptorSetCount = (u32)MAX_FRAMES_IN_FLIGHT; + alloc_info.pSetLayouts = layouts.data(); + + state.vk_descriptor_sets.resize(MAX_FRAMES_IN_FLIGHT); + if(vkAllocateDescriptorSets(state.vk_device, + &alloc_info, + state.vk_descriptor_sets.data()) + != VK_SUCCESS) { + printf("Error :: Failed to allocate descriptor sets\n"); + return -1; + } + + // populate_descriptors: + { + for (u32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + VkDescriptorBufferInfo buffer_info{}; + buffer_info.buffer = state.vk_uniform_buffers[i]; + buffer_info.offset = 0; + buffer_info.range = sizeof(UniformBufferObject); + + VkWriteDescriptorSet desc_write{}; + desc_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + desc_write.dstSet = state.vk_descriptor_sets[i]; + desc_write.dstBinding = 0; + desc_write.dstArrayElement = 0; + desc_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + desc_write.descriptorCount = 1; + desc_write.pBufferInfo = &buffer_info; + desc_write.pImageInfo = nullptr; + desc_write.pTexelBufferView = nullptr; + + vkUpdateDescriptorSets(state.vk_device, 1, &desc_write, + 0, nullptr); + } + } + } // create command buffer VkCommandBuffer vk_command_buffer[MAX_FRAMES_IN_FLIGHT] = {}; { @@ -1442,7 +1659,9 @@ int main() { u32 current_frame = 0; b8 running = 1; + u32 start_time = SDL_GetTicks(); while (running) { + u32 current_time = SDL_GetTicks(); // draw frame SDL_Event event; while(SDL_PollEvent(&event)) { @@ -1454,7 +1673,6 @@ int main() { case SDL_WINDOWEVENT: { switch (event.window.event) { case SDL_WINDOWEVENT_RESIZED: { - printf("sdl window size changed\n"); resizable = 1; } break; @@ -1467,7 +1685,7 @@ int main() { switch (event.key.keysym.scancode) { case SDL_SCANCODE_ESCAPE:{ - printf("Closing game now\n"); + printf("Escape code, closing app\n"); running = 0; } break; @@ -1490,7 +1708,6 @@ int main() { VK_NULL_HANDLE, &sw_image_index); if (vk_result == VK_ERROR_OUT_OF_DATE_KHR) { - printf("swapchain is out of date\n"); // recreate swapchain -> currently only resizing is supported if (resize_swapchain(&state) != 0) { return -1; @@ -1510,6 +1727,26 @@ int main() { vkResetFences(state.vk_device, 1, &vk_fence_frame_flight[current_frame]); vkResetCommandBuffer(vk_command_buffer[current_frame], 0); + // @func: update_uniform_buffer: + { + r32 rotation_inc = (r32)(current_time - start_time)/1000.0f; + UniformBufferObject ubo{}; + ubo.model = glm::rotate(glm::mat4(1.0f), + rotation_inc*glm::radians(90.0f), + glm::vec3(0.0f, 0.0f, 1.0f)); + ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), + glm::vec3(0.0f, 0.0f, 0.0f), + glm::vec3(0.0f, 0.0f, 1.0f)); + ubo.proj = glm::perspective(glm::radians(45.0f), + state.surface_resolution.width / + (r32)state.surface_resolution.height, + 0.0f, 10.0f); + ubo.proj[1][1] *= -1; + + memcpy(state.vk_mapped_uniform_buffers[current_frame], + &ubo, sizeof(ubo)); + } + // @func: record_command_buffer { VkCommandBufferBeginInfo bi_command_buffer = {}; @@ -1534,9 +1771,13 @@ int main() { bi_render_pass.clearValueCount = 1; bi_render_pass.pClearValues = &clear_color; - vkCmdBeginRenderPass(vk_command_buffer[current_frame], &bi_render_pass, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBeginRenderPass(vk_command_buffer[current_frame], + &bi_render_pass, + VK_SUBPASS_CONTENTS_INLINE); { - vkCmdBindPipeline(vk_command_buffer[current_frame], VK_PIPELINE_BIND_POINT_GRAPHICS, vk_pipeline); + vkCmdBindPipeline(vk_command_buffer[current_frame], + VK_PIPELINE_BIND_POINT_GRAPHICS, + state.vk_pipeline); // viewport VkViewport vk_viewport = {}; @@ -1557,8 +1798,19 @@ int main() { // vertex buffer VkBuffer vertex_buffers[] = {state.vk_vertex_buffer}; VkDeviceSize offsets[] = {0}; - vkCmdBindVertexBuffers(vk_command_buffer[current_frame], 0, 1, vertex_buffers, offsets); - vkCmdDraw(vk_command_buffer[current_frame], (u32)vertices.size(), 1, 0, 0); + vkCmdBindVertexBuffers(vk_command_buffer[current_frame], + 0, 1, vertex_buffers, offsets); + vkCmdBindIndexBuffer(vk_command_buffer[current_frame], + state.vk_index_buffer, + 0, VK_INDEX_TYPE_UINT16); + vkCmdBindDescriptorSets(vk_command_buffer[current_frame], + VK_PIPELINE_BIND_POINT_GRAPHICS, + state.vk_pipeline_layout, + 0, 1, + &state.vk_descriptor_sets[current_frame], + 0, nullptr); + vkCmdDrawIndexed(vk_command_buffer[current_frame], + indices.size(), 1, 0, 0, 0); } vkCmdEndRenderPass(vk_command_buffer[current_frame]); } @@ -1611,11 +1863,11 @@ int main() { if ((vk_result == VK_ERROR_OUT_OF_DATE_KHR || vk_result == VK_SUBOPTIMAL_KHR) || resizable) { - // @note: the suboptimal check which usually works when the surface is changed - // will not change anything because the recreate function only resizes swapchain + // @note: the suboptimal check which usually works when the + // surface is changed will not change anything because + // the recreate function only resizes swapchain // @todo: I guess I can add that for completeness, // even though I have no way to test it out realistically - printf("swapchain is out of date\n"); if (resize_swapchain(&state) != 0) return -1; } @@ -1640,8 +1892,13 @@ int main() { vkDestroySemaphore(state.vk_device, vk_smp_render_done[i], nullptr); } vkDestroyCommandPool(state.vk_device, vk_command_pool, nullptr); + + // destroy buffers vkDestroyBuffer(state.vk_device, state.vk_vertex_buffer, nullptr); + vkDestroyBuffer(state.vk_device, state.vk_index_buffer, nullptr); vkFreeMemory(state.vk_device, state.vk_mem_vertex_buffer, nullptr); + vkFreeMemory(state.vk_device, state.vk_mem_index_buffer, nullptr); + // destroy framebuffers { for (u32 i= 0; i < state.vk_framebuffers.size; i++) { @@ -1649,8 +1906,8 @@ int main() { vkDestroyFramebuffer(state.vk_device, framebuffer_item, nullptr); } } - vkDestroyPipeline(state.vk_device, vk_pipeline, NULL); - vkDestroyPipelineLayout(state.vk_device, vk_pipeline_layout, NULL); + vkDestroyPipeline(state.vk_device, state.vk_pipeline, NULL); + vkDestroyPipelineLayout(state.vk_device, state.vk_pipeline_layout, NULL); vkDestroyRenderPass(state.vk_device, state.vk_render_pass, NULL); // destroy image views { @@ -1659,6 +1916,16 @@ int main() { } } vkDestroySwapchainKHR(state.vk_device, state.vk_khr_swapchain, NULL); + for (u32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + vkDestroyBuffer(state.vk_device, + state.vk_uniform_buffers[i], nullptr); + vkFreeMemory(state.vk_device, state.vk_mem_uniform_buffers[i], nullptr); + } + vkDestroyDescriptorPool(state.vk_device, + state.vk_descriptor_pool, + nullptr); + vkDestroyDescriptorSetLayout(state.vk_device, state.vk_descriptor_set_layout, + nullptr); vkDestroyDevice(state.vk_device, NULL); if (enable_validation_layers) { diff --git a/shaders/triangle.vert b/shaders/triangle.vert index 663fea0..c61f894 100755 --- a/shaders/triangle.vert +++ b/shaders/triangle.vert @@ -1,5 +1,11 @@ #version 450
+layout(binding = 0) uniform UniformBufferObject {
+ mat4 model;
+ mat4 view;
+ mat4 proj;
+} ubo;
+
layout(location = 0) in vec2 inPos;
layout(location = 1) in vec3 inCol;
@@ -7,6 +13,6 @@ layout(location = 1) in vec3 inCol; layout(location = 0) out vec3 fragColor;
void main() {
- gl_Position = vec4(inPos, 0.0, 1.0);
+ gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPos, 0.0, 1.0);
fragColor = inCol;
}
|