diff --git a/.gitignore b/.gitignore index ca37570..8e5e5f8 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,5 @@ backup coverage coverage/* .venv +*.glb +imgui.ini diff --git a/Makefile b/Makefile index 73487f8..7536b9a 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,9 @@ shaders: glslangValidator -V gradient.comp -o gradient.comp.spv glslangValidator -V sky.comp -o sky.comp.spv glslangValidator -V gradient_color.comp -o gradient_color.comp.spv + glslangValidator -V colored_triangle.frag -o colored_triangle.frag.spv + glslangValidator -V colored_triangle.vert -o colored_triangle.vert.spv + glslangValidator -V colored_triangle_mesh.vert -o colored_triangle_mesh.vert.spv %.cpp : %.rl ragel -I $(ROOT_DIR) -G1 -o $@ $< @@ -23,9 +26,15 @@ shaders: %.png: %.dot dot -Tpng $< -o $@ +%.glb: %.glb.gz + gunzip -k $< + build: meson compile -j 10 -C $(ROOT_DIR)/builddir +meshes: basicmesh.glb + @echo "Meshes made" + release_build: meson --wipe builddir -Db_ndebug=true --buildtype release meson compile -j 10 -C builddir @@ -50,9 +59,10 @@ debug_run: build clean: meson compile --clean -C builddir + rm -f basicmesh.glb debug_test: build gdb --nx -x .gdbinit --ex run --ex bt --ex q --args builddir/runtests -e "[pathing]" money: - scc --exclude-dir subprojects + scc --exclude-dir subprojects,vendor diff --git a/basicmesh.glb.gz b/basicmesh.glb.gz new file mode 100644 index 0000000..5082934 Binary files /dev/null and b/basicmesh.glb.gz differ diff --git a/colored_triangle.frag b/colored_triangle.frag new file mode 100644 index 0000000..2f05038 --- /dev/null +++ b/colored_triangle.frag @@ -0,0 +1,13 @@ +#version 450 + +//shader input +layout (location = 0) in vec3 inColor; + +//output write +layout (location = 0) out vec4 outFragColor; + +void main() +{ + //return red + outFragColor = vec4(inColor,1.0f); +} diff --git a/colored_triangle.frag.spv b/colored_triangle.frag.spv new file mode 100644 index 0000000..ad41591 Binary files /dev/null and b/colored_triangle.frag.spv differ diff --git a/colored_triangle.vert b/colored_triangle.vert new file mode 100644 index 0000000..8b0db4a --- /dev/null +++ b/colored_triangle.vert @@ -0,0 +1,24 @@ +#version 450 + +layout (location = 0) out vec3 outColor; + +void main() +{ + //const array of positions for the triangle + const vec3 positions[3] = vec3[3]( + vec3(1.f,1.f, 0.0f), + vec3(-1.f,1.f, 0.0f), + vec3(0.f,-1.f, 0.0f) + ); + + //const array of colors for the triangle + const vec3 colors[3] = vec3[3]( + vec3(1.0f, 0.0f, 0.0f), //red + vec3(0.0f, 1.0f, 0.0f), //green + vec3(00.f, 0.0f, 1.0f) //blue + ); + + //output the position of each vertex + gl_Position = vec4(positions[gl_VertexIndex], 1.0f); + outColor = colors[gl_VertexIndex]; +} diff --git a/colored_triangle.vert.spv b/colored_triangle.vert.spv new file mode 100644 index 0000000..ffd8bf3 Binary files /dev/null and b/colored_triangle.vert.spv differ diff --git a/colored_triangle_mesh.vert b/colored_triangle_mesh.vert new file mode 100644 index 0000000..fe9bec5 --- /dev/null +++ b/colored_triangle_mesh.vert @@ -0,0 +1,37 @@ +#version 450 +#extension GL_EXT_buffer_reference : require + +layout (location = 0) out vec3 outColor; +layout (location = 1) out vec2 outUV; + +struct Vertex { + + vec3 position; + float uv_x; + vec3 normal; + float uv_y; + vec4 color; +}; + +layout(buffer_reference, std430) readonly buffer VertexBuffer{ + Vertex vertices[]; +}; + +//push constants block +layout( push_constant ) uniform constants +{ + mat4 render_matrix; + VertexBuffer vertexBuffer; +} PushConstants; + +void main() +{ + //load vertex data from device adress + Vertex v = PushConstants.vertexBuffer.vertices[gl_VertexIndex]; + + //output data + gl_Position = PushConstants.render_matrix *vec4(v.position, 1.0f); + outColor = v.color.xyz; + outUV.x = v.uv_x; + outUV.y = v.uv_y; +} diff --git a/colored_triangle_mesh.vert.spv b/colored_triangle_mesh.vert.spv new file mode 100644 index 0000000..7461a40 Binary files /dev/null and b/colored_triangle_mesh.vert.spv differ diff --git a/meson.build b/meson.build index deacd3f..054c8ef 100644 --- a/meson.build +++ b/meson.build @@ -69,7 +69,6 @@ elif build_machine.system() == 'darwin' endif vma = subproject('vulkan-memory-allocator').get_variable('vma_allocator_dep') -# vulkan_headers = subproject('vulkan-headers').get_variable('vulkan_headers_dep') imgui = subproject('imgui', default_options: { @@ -80,6 +79,13 @@ imgui = subproject('imgui', sdl2 = subproject('sdl2').get_variable('sdl2_dep') +fastgltf_opts = cmake.subproject_options() +fastgltf_opts.add_cmake_defines({ + 'FASTGLTF_DOWNLOAD_SIMDJSON': false, +}) + +fastgltf_proj = cmake.subproject('fastgltf', options: fastgltf_opts) +fastgltf = fastgltf_proj.get_variable('fastgltf_dep') glm_opts = cmake.subproject_options() glm_opts.add_cmake_defines({ @@ -113,6 +119,7 @@ dependencies += [ glm, imgui, sdl2, + fastgltf, ] sources = [ @@ -121,15 +128,21 @@ sources = [ 'vk_images.cpp', 'vk_descriptors.cpp', 'vk_pipelines.cpp', + 'vk_loader.cpp', + 'vk_gui.cpp', 'main.cpp', ] +# have to force this here for...cmake reasons +include_dirs = include_directories('subprojects/fastgltf/include') + tests = [ ] executable('hellovulk', sources, cpp_args: cpp_args, link_args: link_args, + include_directories: include_dirs, win_subsystem: 'windows', override_options: exe_defaults, dependencies: dependencies) diff --git a/stb_image.h b/vendor/stb_image.h similarity index 100% rename from stb_image.h rename to vendor/stb_image.h diff --git a/vk_descriptors.cpp b/vk_descriptors.cpp index bfad725..d8d4f62 100644 --- a/vk_descriptors.cpp +++ b/vk_descriptors.cpp @@ -2,11 +2,11 @@ void DescriptorLayoutBuilder::add_binding(uint32_t binding, VkDescriptorType type) { - - VkDescriptorSetLayoutBinding newbind {}; - newbind.binding = binding; - newbind.descriptorCount = 1; - newbind.descriptorType = type; + VkDescriptorSetLayoutBinding newbind{ + .binding = binding, + .descriptorType = type, + .descriptorCount = 1, + }; bindings.push_back(newbind); } @@ -25,18 +25,16 @@ VkDescriptorSetLayout DescriptorLayoutBuilder::build(VkDevice device, b.stageFlags |= shaderStages; } - VkDescriptorSetLayoutCreateInfo info = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO + VkDescriptorSetLayoutCreateInfo info{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .pNext = pNext, + .flags = flags, + .bindingCount = (uint32_t)bindings.size(), + .pBindings = bindings.data(), }; - info.pNext = pNext; - - info.pBindings = bindings.data(); - info.bindingCount = (uint32_t)bindings.size(); - info.flags = flags; VkDescriptorSetLayout set; - VK_CHECK(vkCreateDescriptorSetLayout(device, - &info, nullptr, &set)); + VK_CHECK(vkCreateDescriptorSetLayout(device, &info, nullptr, &set)); return set; } @@ -46,19 +44,19 @@ void DescriptorAllocator::init_pool(VkDevice device, uint32_t maxSets, { std::vector poolSizes; for(PoolSizeRatio ratio : poolRatios) { - poolSizes.push_back(VkDescriptorPoolSize{ + poolSizes.push_back({ .type = ratio.type, .descriptorCount = uint32_t(ratio.ratio * maxSets) }); } - VkDescriptorPoolCreateInfo pool_info = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO + VkDescriptorPoolCreateInfo pool_info{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .flags = 0, + .maxSets = maxSets, + .poolSizeCount = (uint32_t)poolSizes.size(), + .pPoolSizes = poolSizes.data(), }; - pool_info.flags = 0; - pool_info.maxSets = maxSets; - pool_info.poolSizeCount = (uint32_t)poolSizes.size(); - pool_info.pPoolSizes = poolSizes.data(); vkCreateDescriptorPool(device, &pool_info, nullptr, &pool); } @@ -75,15 +73,13 @@ void DescriptorAllocator::destroy_pool(VkDevice device) VkDescriptorSet DescriptorAllocator::allocate(VkDevice device, VkDescriptorSetLayout layout) { - VkDescriptorSetAllocateInfo allocInfo = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO + VkDescriptorSetAllocateInfo allocInfo{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .descriptorPool = pool, + .descriptorSetCount = 1, + .pSetLayouts = &layout, }; - allocInfo.pNext = nullptr; - allocInfo.descriptorPool = pool; - allocInfo.descriptorSetCount = 1; - allocInfo.pSetLayouts = &layout; - VkDescriptorSet ds; VK_CHECK(vkAllocateDescriptorSets(device, &allocInfo, &ds)); diff --git a/vk_engine.cpp b/vk_engine.cpp index da914ab..cfcc16f 100644 --- a/vk_engine.cpp +++ b/vk_engine.cpp @@ -3,23 +3,18 @@ #include "vk_pipelines.h" #include -#include -#include - #include #include #include "VkBootstrap.h" #include #include -#include "imgui.h" -#include "imgui_impl_sdl2.h" -#include "imgui_impl_vulkan.h" +#include #define VMA_IMPLEMENTATION #include "vk_mem_alloc.h" -constexpr bool bUseValidationLayers = true; +constexpr bool bUseValidationLayers = false; VulkanEngine* loadedEngine = nullptr; @@ -32,18 +27,7 @@ void VulkanEngine::init() assert(loadedEngine == nullptr); loadedEngine = this; - // We initialize SDL and create a window with it. - SDL_Init(SDL_INIT_VIDEO); - - SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_VULKAN); - - _window = SDL_CreateWindow( - "Vulkan Engine", - SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, - int(_windowExtent.width), - int(_windowExtent.height), - window_flags); + _window = _gui.init_window(_windowExtent); init_vulkan(); init_swapchain(); @@ -51,7 +35,8 @@ void VulkanEngine::init() init_sync_structures(); init_descriptors(); init_pipelines(); - init_imgui(); + _gui.init_imgui(VulkanEngine::Get()); + init_default_data(); //everything went fine _isInitialized = true; @@ -81,14 +66,14 @@ void VulkanEngine::cleanup() vkDestroyDevice(_device, nullptr); vkb::destroy_debug_utils_messenger(_instance, _debug_messenger); vkDestroyInstance(_instance, nullptr); - SDL_DestroyWindow(_window); + _gui.destroy(_window); } loadedEngine = nullptr; } -void VulkanEngine::draw() -{ +Transaction VulkanEngine::begin_transaction() { + // ZED: begin command transaction // wait until the gpu has finished rendering the last frame. Timeout of 1 second VK_CHECK(vkWaitForFences(_device, 1, &get_current_frame()._renderFence, true, 1000000000)); get_current_frame()._deletionQueue.flush(); @@ -103,44 +88,24 @@ void VulkanEngine::draw() VkCommandBufferBeginInfo cmdBeginInfo = vkinit::command_buffer_begin_info(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); - _drawExtent.width = _drawImage.imageExtent.width; - _drawExtent.height = _drawImage.imageExtent.height; - VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo)); - // transition our main draw image into general layout so we can write into it - // we will overwrite it all so we dont care about what was the older layout + return {cmd, swapchainImageIndex}; +} - vkutil::transition_image(cmd, _drawImage.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL); - - draw_background(cmd); - - vkutil::transition_image(cmd, _drawImage.image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - - vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - - vkutil::copy_image_to_image(cmd, _drawImage.image, - _swapchainImages[swapchainImageIndex], - _drawExtent, _swapchainExtent); - - vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - - draw_imgui(cmd, _swapchainImageViews[swapchainImageIndex]); - - vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); - - VK_CHECK(vkEndCommandBuffer(cmd)); +void VulkanEngine::commit_transaction(Transaction& t) { + VK_CHECK(vkEndCommandBuffer(t.cmd)); + // ZED: submit command to queue //prepare the submission to the queue. //we want to wait on the _presentSemaphore, as that semaphore is signaled when the swapchain is ready //we will signal the _renderSemaphore, to signal that rendering has finished - - VkCommandBufferSubmitInfo cmdinfo = vkinit::command_buffer_submit_info(cmd); + VkCommandBufferSubmitInfo cmdinfo = vkinit::command_buffer_submit_info(t.cmd); VkSemaphoreSubmitInfo waitInfo = vkinit::semaphore_submit_info(VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT_KHR,get_current_frame()._swapchainSemaphore); VkSemaphoreSubmitInfo signalInfo = vkinit::semaphore_submit_info(VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT, get_current_frame()._renderSemaphore); - VkSubmitInfo2 submit = vkinit::submit_info(&cmdinfo,&signalInfo,&waitInfo); + VkSubmitInfo2 submit = vkinit::submit_info(&cmdinfo, &signalInfo, &waitInfo); //submit command buffer to the queue and execute it. // _renderFence will now block until the graphic commands finish execution @@ -150,16 +115,15 @@ void VulkanEngine::draw() // this will put the image we just rendered to into the visible window. // we want to wait on the _renderSemaphore for that, // as its necessary that drawing commands have finished before the image is displayed to the user - VkPresentInfoKHR presentInfo{}; - presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - presentInfo.pNext = nullptr; - presentInfo.pSwapchains = &_swapchain; - presentInfo.swapchainCount = 1; - - presentInfo.pWaitSemaphores = &get_current_frame()._renderSemaphore; - presentInfo.waitSemaphoreCount = 1; - - presentInfo.pImageIndices = &swapchainImageIndex; + VkPresentInfoKHR presentInfo{ + .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + .pNext = nullptr, + .waitSemaphoreCount = 1, + .pWaitSemaphores = &get_current_frame()._renderSemaphore, + .swapchainCount = 1, + .pSwapchains = &_swapchain, + .pImageIndices = &t.swapchainImageIndex, + }; VK_CHECK(vkQueuePresentKHR(_graphicsQueue, &presentInfo)); @@ -167,70 +131,135 @@ void VulkanEngine::draw() _frameNumber++; } +void VulkanEngine::draw() +{ + auto t = begin_transaction(); + + // ZED: draw the things + _drawExtent.width = _drawImage.imageExtent.width; + _drawExtent.height = _drawImage.imageExtent.height; + + // transition our main draw image into general layout so we can write into it + // we will overwrite it all so we dont care about what was the older layout + vkutil::transition_image(t.cmd, _drawImage.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL); + + draw_background(t.cmd); + + vkutil::transition_image(t.cmd, _drawImage.image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + draw_geometry(t.cmd); + + // ZED: ? + vkutil::transition_image(t.cmd, _drawImage.image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + + // ZED: ? + vkutil::transition_image(t.cmd, _swapchainImages[t.swapchainImageIndex], + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + vkutil::copy_image_to_image(t.cmd, _drawImage.image, + _swapchainImages[t.swapchainImageIndex], + _drawExtent, _swapchainExtent); + + // ZED: ? + vkutil::transition_image(t.cmd, _swapchainImages[t.swapchainImageIndex], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + // ZED: end drawing the things + _gui.draw_imgui(_swapchainExtent, t.cmd, _swapchainImageViews[t.swapchainImageIndex]); + + // ZED: finalize image and commit command buffer + vkutil::transition_image(t.cmd, _swapchainImages[t.swapchainImageIndex], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); + + commit_transaction(t); +} + +void VulkanEngine::draw_geometry(VkCommandBuffer cmd) +{ + VkRenderingAttachmentInfo colorAttachment = vkinit::attachment_info(_drawImage.imageView, nullptr, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + VkRenderingInfo renderInfo = vkinit::rendering_info(_drawExtent, &colorAttachment, nullptr); + vkCmdBeginRendering(cmd, &renderInfo); + + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _trianglePipeline); + + VkViewport viewport{ + .x = 0, + .y = 0, + .width = (float)_drawExtent.width, + .height = (float)_drawExtent.height, + .minDepth = 0.0f, + .maxDepth = 1.0f, + }; + + vkCmdSetViewport(cmd, 0, 1, &viewport); + + VkRect2D scissor{ + .offset = { .x = 0, .y = 0, }, + .extent = _drawExtent + }; + + vkCmdSetScissor(cmd, 0, 1, &scissor); + + vkCmdDraw(cmd, 3, 1, 0, 0); + + // draw rectangle + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _meshPipeline); + + GPUDrawPushConstants push_constants{ + .worldMatrix = glm::mat4{ 1.0f }, + .vertexBuffer = rectangle.vertexBufferAddress, + }; + + vkCmdPushConstants(cmd, _meshPipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(GPUDrawPushConstants), +&push_constants); + vkCmdBindIndexBuffer(cmd, rectangle.indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32); + + vkCmdDrawIndexed(cmd, 6, 1, 0, 0, 0); + + + // draw monkey + size_t model_idx = 2; // 0 cube; 1 sphere; 2 monkey + + push_constants.vertexBuffer = testMeshes[model_idx]->meshBuffers.vertexBufferAddress; + + vkCmdPushConstants(cmd, + _meshPipelineLayout, + VK_SHADER_STAGE_VERTEX_BIT, + 0, + sizeof(GPUDrawPushConstants), + &push_constants); + + vkCmdBindIndexBuffer(cmd, + testMeshes[model_idx]->meshBuffers.indexBuffer.buffer, + 0, + VK_INDEX_TYPE_UINT32); + + vkCmdDrawIndexed(cmd, + testMeshes[model_idx]->surfaces[0].count, + 1, + testMeshes[model_idx]->surfaces[0].startIndex, + 0, 0); + + vkCmdEndRendering(cmd); +} + void VulkanEngine::run() { - SDL_Event e; - bool bQuit = false; - bool stop_rendering = false; - //main loop - while(!bQuit) + while(!_gui.should_quit) { - //Handle events on queue - while(SDL_PollEvent(&e) != 0) - { - //close the window when user alt-f4s or clicks the X button - if(e.type == SDL_QUIT) { - bQuit = true; - } + _gui.poll_event(); - if(e.type == SDL_WINDOWEVENT) { - if(e.window.event == SDL_WINDOWEVENT_MINIMIZED) { - stop_rendering = true; - } - - if(e.window.event == SDL_WINDOWEVENT_RESTORED) { - stop_rendering = false; - } - } - - ImGui_ImplSDL2_ProcessEvent(&e); - } - - if(stop_rendering) { + if(_gui.stop_rendering) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); continue; } - render_imgui(); + _gui.render_imgui(backgroundEffects, ¤tBackgroundEffect); draw(); } } -void VulkanEngine::render_imgui() { - ImGui_ImplVulkan_NewFrame(); - ImGui_ImplSDL2_NewFrame(); - - ImGui::NewFrame(); - - if (ImGui::Begin("background")) { - - ComputeEffect& selected = backgroundEffects[currentBackgroundEffect]; - - ImGui::Text("Selected effect: %s", selected.name); - - ImGui::SliderInt("Effect Index", ¤tBackgroundEffect,0, backgroundEffects.size() - 1); - - ImGui::InputFloat4("data1",(float*)& selected.data.data1); - ImGui::InputFloat4("data2",(float*)& selected.data.data2); - ImGui::InputFloat4("data3",(float*)& selected.data.data3); - ImGui::InputFloat4("data4",(float*)& selected.data.data4); - } - ImGui::End(); - - ImGui::Render(); -} - void VulkanEngine::init_vulkan() { vkb::InstanceBuilder builder; @@ -251,17 +280,18 @@ void VulkanEngine::init_vulkan() { SDL_Vulkan_CreateSurface(_window, _instance, &_surface); //vulkan 1.3 features - VkPhysicalDeviceVulkan13Features features13{}; - - features13.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES; - features13.dynamicRendering = true; - features13.synchronization2 = true; + VkPhysicalDeviceVulkan13Features features13{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES, + .synchronization2 = true, + .dynamicRendering = true, + }; //vulkan 1.2 features - VkPhysicalDeviceVulkan12Features features12{}; - features12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; - features12.bufferDeviceAddress = true; - features12.descriptorIndexing = true; + VkPhysicalDeviceVulkan12Features features12{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, + .descriptorIndexing = true, + .bufferDeviceAddress = true, + }; // use vkbootstrap to select a gpu // We want a gpu that can write to the SDL surface @@ -284,11 +314,13 @@ void VulkanEngine::init_vulkan() { _graphicsQueueFamily = vkbDevice.get_queue_index(vkb::QueueType::graphics).value(); // initialize the memory allocator - VmaAllocatorCreateInfo allocatorInfo{}; - allocatorInfo.physicalDevice = _chosenGPU; - allocatorInfo.device = _device; - allocatorInfo.instance = _instance; - allocatorInfo.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT; + VmaAllocatorCreateInfo allocatorInfo{ + .flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT, + .physicalDevice = _chosenGPU, + .device = _device, + .instance = _instance, + }; + vmaCreateAllocator(&allocatorInfo, &_allocator); _mainDeletionQueue.push_function([&]() { @@ -339,17 +371,18 @@ void VulkanEngine::init_swapchain() { _drawImage.imageFormat = VK_FORMAT_R16G16B16A16_SFLOAT; _drawImage.imageExtent = drawImageExtent; - VkImageUsageFlags drawImageUsages{}; - drawImageUsages |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - drawImageUsages |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; - drawImageUsages |= VK_IMAGE_USAGE_STORAGE_BIT; - drawImageUsages |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + VkImageUsageFlags drawImageUsages = + VK_IMAGE_USAGE_TRANSFER_SRC_BIT + | VK_IMAGE_USAGE_TRANSFER_DST_BIT + | VK_IMAGE_USAGE_STORAGE_BIT + | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; VkImageCreateInfo rimg_info = vkinit::image_create_info(_drawImage.imageFormat, drawImageUsages, drawImageExtent); - VmaAllocationCreateInfo rimg_allocinfo{}; - rimg_allocinfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; - rimg_allocinfo.requiredFlags = VkMemoryPropertyFlags(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VmaAllocationCreateInfo rimg_allocinfo{ + .usage = VMA_MEMORY_USAGE_GPU_ONLY, + .requiredFlags = VkMemoryPropertyFlags(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT), + }; //allocate and create the image vmaCreateImage(_allocator, &rimg_info, &rimg_allocinfo, &_drawImage.image, &_drawImage.allocation, nullptr); @@ -430,7 +463,7 @@ void VulkanEngine::init_descriptors() { { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1 } }; - globalDescriptorAllocator.init_pool(_device, 10, sizes); + _globalDescriptorAllocator.init_pool(_device, 10, sizes); // make the descriptor set layout for our compute draw { @@ -441,27 +474,27 @@ void VulkanEngine::init_descriptors() { // other code //allocate a descriptor set for our draw image - _drawImageDescriptors = globalDescriptorAllocator.allocate(_device,_drawImageDescriptorLayout); + _drawImageDescriptors = _globalDescriptorAllocator.allocate(_device,_drawImageDescriptorLayout); - VkDescriptorImageInfo imgInfo{}; - imgInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL; - imgInfo.imageView = _drawImage.imageView; + VkDescriptorImageInfo imgInfo{ + .imageView = _drawImage.imageView, + .imageLayout = VK_IMAGE_LAYOUT_GENERAL, + }; - VkWriteDescriptorSet drawImageWrite = {}; - drawImageWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - drawImageWrite.pNext = nullptr; - - drawImageWrite.dstBinding = 0; - drawImageWrite.dstSet = _drawImageDescriptors; - drawImageWrite.descriptorCount = 1; - drawImageWrite.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; - drawImageWrite.pImageInfo = &imgInfo; + VkWriteDescriptorSet drawImageWrite{ + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = _drawImageDescriptors, + .dstBinding = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .pImageInfo = &imgInfo, + }; vkUpdateDescriptorSets(_device, 1, &drawImageWrite, 0, nullptr), //make sure both the descriptor allocator and the new layout get cleaned up properly _mainDeletionQueue.push_function([&]() { - globalDescriptorAllocator.destroy_pool(_device); + _globalDescriptorAllocator.destroy_pool(_device); vkDestroyDescriptorSetLayout(_device, _drawImageDescriptorLayout, nullptr); }); @@ -470,77 +503,83 @@ void VulkanEngine::init_descriptors() { void VulkanEngine::init_pipelines() { init_background_pipelines(); + init_triangle_pipelines(); + init_mesh_pipeline(); + init_shaders(); } +void VulkanEngine::load_shader(const char *file_name, const char *entry_point, ComputeEffect effect) { + VkShaderModule shader; + bool good = vkutil::load_shader_module(file_name, _device, &shader); + assert(good && "failed to load shader"); + + VkPipelineShaderStageCreateInfo stageinfo{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .stage = VK_SHADER_STAGE_COMPUTE_BIT, + .module = shader, + .pName = entry_point, + }; + + VkComputePipelineCreateInfo computePipelineCreateInfo{ + .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, + .stage = stageinfo, + .layout = _gradientPipelineLayout, + }; + + effect.layout = _gradientPipelineLayout; + + VK_CHECK(vkCreateComputePipelines(_device, VK_NULL_HANDLE, 1, &computePipelineCreateInfo, nullptr, &effect.pipeline)); + + backgroundEffects.push_back(effect); + + vkDestroyShaderModule(_device, shader, nullptr); + + _mainDeletionQueue.push_function([=,this]() { + vkDestroyPipeline(_device, effect.pipeline, nullptr); + }); +} + + void VulkanEngine::init_background_pipelines() { - VkPipelineLayoutCreateInfo computeLayout{}; - computeLayout.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - computeLayout.pNext = nullptr; - computeLayout.pSetLayouts = &_drawImageDescriptorLayout; - computeLayout.setLayoutCount = 1; + VkPushConstantRange pushConstant{ + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, + .offset = 0, + .size = sizeof(ComputePushConstants), + }; - VkPushConstantRange pushConstant{}; - pushConstant.offset = 0; - pushConstant.size = sizeof(ComputePushConstants) ; - pushConstant.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; - - computeLayout.pPushConstantRanges = &pushConstant; - computeLayout.pushConstantRangeCount = 1; + VkPipelineLayoutCreateInfo computeLayout{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .setLayoutCount = 1, + .pSetLayouts = &_drawImageDescriptorLayout, + .pushConstantRangeCount = 1, + .pPushConstantRanges = &pushConstant, + }; VK_CHECK(vkCreatePipelineLayout(_device, &computeLayout, nullptr, &_gradientPipelineLayout)); - VkShaderModule gradientShader; - bool good = vkutil::load_shader_module("gradient_color.comp.spv", _device, &gradientShader); - assert(good && "failed to load gradient_color.comp.spv"); - - VkShaderModule skyShader; - good = vkutil::load_shader_module("sky.comp.spv", _device, &skyShader); - - VkPipelineShaderStageCreateInfo stageinfo{}; - stageinfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - stageinfo.pNext = nullptr; - stageinfo.stage = VK_SHADER_STAGE_COMPUTE_BIT; - stageinfo.module = gradientShader; - stageinfo.pName = "main"; - - VkComputePipelineCreateInfo computePipelineCreateInfo{}; - computePipelineCreateInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; - computePipelineCreateInfo.pNext = nullptr; - computePipelineCreateInfo.layout = _gradientPipelineLayout; - computePipelineCreateInfo.stage = stageinfo; - - ComputeEffect gradient; - gradient.layout = _gradientPipelineLayout; - gradient.name = "gradient"; - gradient.data = {}; - gradient.data.data1 = glm::vec4(1, 0, 0, 1); - gradient.data.data2 = glm::vec4(0, 0, 1, 1); - - VK_CHECK(vkCreateComputePipelines(_device, VK_NULL_HANDLE, 1, &computePipelineCreateInfo, nullptr, &gradient.pipeline)); - - // change the shader module only to create the sky - computePipelineCreateInfo.stage.module = skyShader; - ComputeEffect sky; - sky.layout = _gradientPipelineLayout; - sky.name = "sky"; - sky.data = {}; - // default sky - sky.data.data1 = glm::vec4(0.1, 0.2, 0.4, 0.97); - - VK_CHECK(vkCreateComputePipelines(_device, VK_NULL_HANDLE, 1, &computePipelineCreateInfo, nullptr, &sky.pipeline)); - - - backgroundEffects.push_back(gradient); - backgroundEffects.push_back(sky); - - vkDestroyShaderModule(_device, gradientShader, nullptr); - vkDestroyShaderModule(_device, skyShader, nullptr); - + // final cleanup _mainDeletionQueue.push_function([=,this]() { vkDestroyPipelineLayout(_device, _gradientPipelineLayout, nullptr); - vkDestroyPipeline(_device, sky.pipeline, nullptr); - vkDestroyPipeline(_device, sky.pipeline, nullptr); + }); +} + + +void VulkanEngine::init_shaders() { + /// gradient shader + load_shader("gradient_color.comp.spv", "main", { + .name="gradient", + .data={ + .data1 = glm::vec4(1, 0, 0, 1), + .data2 = glm::vec4(0, 0, 1, 1), + } + }); + + load_shader("sky.comp.spv", "main", { + .name="sky", + .data = { + .data1 = glm::vec4(0.1, 0.2, 0.4, 0.97) + } }); } @@ -564,76 +603,230 @@ void VulkanEngine::immediate_submit(std::function&& f VkSubmitInfo2 submit = vkinit::submit_info(&cmdinfo, nullptr, nullptr); VK_CHECK(vkQueueSubmit2(_graphicsQueue, 1, &submit, _immFence)); - VK_CHECK(vkWaitForFences(_device, 1, &_immFence, true,9999999999)); - + VK_CHECK(vkWaitForFences(_device, 1, &_immFence, true,9999999999)); } - -void VulkanEngine::init_imgui() +void VulkanEngine::init_mesh_pipeline() { -// 1: create descriptor pool for IMGUI - // the size of the pool is very oversize, but it's copied from imgui demo - // itself. - VkDescriptorPoolSize pool_sizes[] = { { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 }, - { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 }, - { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 }, - { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 }, - { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 }, - { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 }, - { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 }, - { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 }, - { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 }, - { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 }, - { VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 } }; - - VkDescriptorPoolCreateInfo pool_info{}; - pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; - pool_info.maxSets = 1000; - pool_info.poolSizeCount = (uint32_t)std::size(pool_sizes); - pool_info.pPoolSizes = pool_sizes; + VkShaderModule triangleFragShader; + if (!vkutil::load_shader_module("colored_triangle.frag.spv", _device, &triangleFragShader)) { + std::print("Error when building the triangle fragment shader module"); + } + else { + std::print("Triangle fragment shader succesfully loaded"); + } - VkDescriptorPool imguiPool; - VK_CHECK(vkCreateDescriptorPool(_device, &pool_info, nullptr, &imguiPool)); + VkShaderModule triangleVertexShader; + if (!vkutil::load_shader_module("colored_triangle_mesh.vert.spv", _device, &triangleVertexShader)) { + std::print("Error when building the triangle vertex shader module"); + } + else { + std::print("Triangle vertex shader succesfully loaded"); + } - // 2: initialize the imgui library - ImGui::CreateContext(); + VkPushConstantRange bufferRange{ + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, + .offset = 0, + .size = sizeof(GPUDrawPushConstants), + }; - ImGui_ImplSDL2_InitForVulkan(_window); - ImGui_ImplVulkan_InitInfo init_info{}; - init_info.Instance = _instance; - init_info.PhysicalDevice = _chosenGPU; - init_info.Device = _device; - init_info.Queue = _graphicsQueue; - init_info.DescriptorPool = imguiPool; - init_info.MinImageCount = 3; - init_info.ImageCount = 3; - init_info.UseDynamicRendering = true; + VkPipelineLayoutCreateInfo pipeline_layout_info = vkinit::pipeline_layout_create_info(); + pipeline_layout_info.pPushConstantRanges = &bufferRange; + pipeline_layout_info.pushConstantRangeCount = 1; - init_info.PipelineRenderingCreateInfo = {.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO}; - init_info.PipelineRenderingCreateInfo.colorAttachmentCount = 1; - init_info.PipelineRenderingCreateInfo.pColorAttachmentFormats = &_swapchainImageFormat; + VK_CHECK(vkCreatePipelineLayout(_device, &pipeline_layout_info, nullptr, &_meshPipelineLayout)); - init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT; - ImGui_ImplVulkan_Init(&init_info); - ImGui_ImplVulkan_CreateFontsTexture(); - _mainDeletionQueue.push_function([=,this]() { - ImGui_ImplVulkan_Shutdown(); - vkDestroyDescriptorPool(_device, imguiPool, nullptr); + PipelineBuilder pipelineBuilder; + + pipelineBuilder._pipelineLayout = _meshPipelineLayout; + pipelineBuilder.set_shaders(triangleVertexShader, triangleFragShader); + pipelineBuilder.set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST); + pipelineBuilder.set_polygon_mode(VK_POLYGON_MODE_FILL); + pipelineBuilder.set_cull_mode(VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE); + pipelineBuilder.set_multisampling_none(); + pipelineBuilder.disable_blending(); + pipelineBuilder.disable_depthtest(); + pipelineBuilder.set_color_attachment_format(_drawImage.imageFormat); + pipelineBuilder.set_depth_format(VK_FORMAT_UNDEFINED); + + _meshPipeline = pipelineBuilder.build_pipeline(_device); + vkDestroyShaderModule(_device, triangleFragShader, nullptr); + vkDestroyShaderModule(_device, triangleVertexShader, nullptr); + + _mainDeletionQueue.push_function([&]() { + vkDestroyPipelineLayout(_device, _meshPipelineLayout, nullptr); + vkDestroyPipeline(_device, _meshPipeline, nullptr); }); } -void VulkanEngine::draw_imgui(VkCommandBuffer cmd, VkImageView targetImageView) +void VulkanEngine::init_triangle_pipelines() { - VkRenderingAttachmentInfo colorAttachment = vkinit::attachment_info(targetImageView, nullptr, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + VkShaderModule triangleFragShader; - VkRenderingInfo renderInfo = vkinit::rendering_info(_swapchainExtent, &colorAttachment, nullptr); + if(!vkutil::load_shader_module("colored_triangle.frag.spv",_device, &triangleFragShader)) { + std::print("Error when building the triangle fragment shaders module"); + } else { + std::print("Triangle fragment shader successfully loaded"); + } - vkCmdBeginRendering(cmd, &renderInfo); + VkShaderModule triangleVertexShader; + if(!vkutil::load_shader_module("colored_triangle.vert.spv", _device, &triangleVertexShader)) { + std::println("Error when building the triangle, vertex shader module"); + } else { + std::println("Triangle vertex shader successfully loaded"); + } - ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmd); + VkPipelineLayoutCreateInfo pipeline_layout_info = vkinit::pipeline_layout_create_info(); - vkCmdEndRendering(cmd); + VK_CHECK(vkCreatePipelineLayout(_device, &pipeline_layout_info, nullptr, &_trianglePipelineLayout)); + + PipelineBuilder pipelineBuilder; + + pipelineBuilder._pipelineLayout = _trianglePipelineLayout; + pipelineBuilder.set_shaders(triangleVertexShader, triangleFragShader); + pipelineBuilder.set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST); + pipelineBuilder.set_polygon_mode(VK_POLYGON_MODE_FILL); + pipelineBuilder.set_cull_mode(VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE); + pipelineBuilder.set_multisampling_none(); + pipelineBuilder.disable_blending(); + pipelineBuilder.disable_depthtest(); + + // connect the image format we will drw into, from draw image + pipelineBuilder.set_color_attachment_format(_drawImage.imageFormat); + pipelineBuilder.set_depth_format(VK_FORMAT_UNDEFINED); + + _trianglePipeline = pipelineBuilder.build_pipeline(_device); + + vkDestroyShaderModule(_device, triangleFragShader, nullptr); + vkDestroyShaderModule(_device, triangleVertexShader, nullptr); + + _mainDeletionQueue.push_function([&]() { + vkDestroyPipelineLayout(_device, _trianglePipelineLayout, nullptr); + vkDestroyPipeline(_device, _trianglePipeline, nullptr); + }); +} + + +AllocatedBuffer VulkanEngine::create_buffer(size_t allocSize, VkBufferUsageFlags usage, VmaMemoryUsage memoryUsage) +{ + VkBufferCreateInfo bufferInfo{ + .sType=VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .size = allocSize, + .usage = usage, + }; + + VmaAllocationCreateInfo vmaallocInfo{ + .flags = VMA_ALLOCATION_CREATE_MAPPED_BIT, + .usage = memoryUsage, + }; + + AllocatedBuffer newBuffer; + + VK_CHECK(vmaCreateBuffer(_allocator, &bufferInfo, + &vmaallocInfo, &newBuffer.buffer, + &newBuffer.allocation, &newBuffer.info)); + + return newBuffer; +} + +void VulkanEngine::destroy_buffer(const AllocatedBuffer& buffer) +{ + vmaDestroyBuffer(_allocator, buffer.buffer, buffer.allocation); +} + +GPUMeshBuffers VulkanEngine::uploadMesh(std::span indices, std::span vertices) +{ + const size_t vertexBufferSize = vertices.size() * sizeof(Vertex); + const size_t indexBufferSize = indices.size() * sizeof(uint32_t); + + GPUMeshBuffers newSurface; + + newSurface.vertexBuffer = create_buffer( + vertexBufferSize, + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT + | VK_BUFFER_USAGE_TRANSFER_DST_BIT + | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + VMA_MEMORY_USAGE_GPU_ONLY); + + VkBufferDeviceAddressInfo deviceAddressInfo{ + .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, + .buffer = newSurface.vertexBuffer.buffer + }; + + newSurface.vertexBufferAddress = vkGetBufferDeviceAddress(_device, &deviceAddressInfo); + + newSurface.indexBuffer = create_buffer(indexBufferSize, + VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VMA_MEMORY_USAGE_GPU_ONLY); + + AllocatedBuffer staging = create_buffer(vertexBufferSize + indexBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY); + + void* data = staging.allocation->GetMappedData(); + + memcpy(data, vertices.data(), vertexBufferSize); + memcpy((char *)data + vertexBufferSize, indices.data(), indexBufferSize); + + immediate_submit([&](VkCommandBuffer cmd) { + VkBufferCopy vertexCopy{ + .srcOffset = 0, + .dstOffset = 0, + .size = vertexBufferSize, + }; + + vkCmdCopyBuffer(cmd, staging.buffer, + newSurface.vertexBuffer.buffer, 1, + &vertexCopy); + + VkBufferCopy indexCopy{ + .srcOffset = vertexBufferSize, + .dstOffset = 0, + .size = indexBufferSize, + }; + + vkCmdCopyBuffer(cmd, staging.buffer, + newSurface.indexBuffer.buffer, 1, + &indexCopy); + }); + + destroy_buffer(staging); + + return newSurface; +} + +void VulkanEngine::init_default_data() { + std::array rect_vertices; + + rect_vertices[0].position = {0.5,-0.5, 0}; + rect_vertices[1].position = {0.5,0.5, 0}; + rect_vertices[2].position = {-0.5,-0.5, 0}; + rect_vertices[3].position = {-0.5,0.5, 0}; + + rect_vertices[0].color = {0,0, 0,1}; + rect_vertices[1].color = { 0.5,0.5,0.5 ,1}; + rect_vertices[2].color = { 1,0, 0,1 }; + rect_vertices[3].color = { 0,1, 0,1 }; + + std::array rect_indices; + + rect_indices[0] = 0; + rect_indices[1] = 1; + rect_indices[2] = 2; + + rect_indices[3] = 2; + rect_indices[4] = 1; + rect_indices[5] = 3; + + rectangle = uploadMesh(rect_indices, rect_vertices); + + auto basicmesh = loadGltfMeshes(this, "basicmesh.glb"); + assert(basicmesh != std::nullopt && "Failed to load basicmesh.glb"); + + testMeshes = *basicmesh; + + _mainDeletionQueue.push_function([&](){ + destroy_buffer(rectangle.indexBuffer); + destroy_buffer(rectangle.vertexBuffer); + }); } diff --git a/vk_engine.h b/vk_engine.h index 5a7903b..1875146 100644 --- a/vk_engine.h +++ b/vk_engine.h @@ -5,23 +5,14 @@ #include #include +#include "vk_gui.h" +#include "vk_loader.h" -struct ComputePushConstants { - glm::vec4 data1; - glm::vec4 data2; - glm::vec4 data3; - glm::vec4 data4; +struct Transaction { + VkCommandBuffer cmd; + uint32_t swapchainImageIndex = 0; }; -struct ComputeEffect { - const char *name; - VkPipeline pipeline; - VkPipelineLayout layout; - - ComputePushConstants data; -}; - - struct DeletionQueue { std::deque> deletors; @@ -48,7 +39,6 @@ struct FrameData { DeletionQueue _deletionQueue; }; -constexpr unsigned int FRAME_OVERLAP=2; class VulkanEngine { public: @@ -58,7 +48,7 @@ public: VkPipeline _gradientPipeline; VkPipelineLayout _gradientPipelineLayout; - DescriptorAllocator globalDescriptorAllocator; + DescriptorAllocator _globalDescriptorAllocator; VkDescriptorSet _drawImageDescriptors; VkDescriptorSetLayout _drawImageDescriptorLayout; @@ -77,6 +67,7 @@ public: std::vector _swapchainImageViews; VkExtent2D _swapchainExtent; + // VMA stuff VmaAllocator _allocator; @@ -104,6 +95,16 @@ public: // imgui shader stuff std::vector backgroundEffects; int currentBackgroundEffect{0}; + VkPipelineLayout _trianglePipelineLayout; + VkPipeline _trianglePipeline; + VkPipelineLayout _meshPipelineLayout; + VkPipeline _meshPipeline; + + GPUMeshBuffers rectangle; + std::vector> testMeshes; + + // ZED's REFACTOR + VkGUI _gui; static VulkanEngine& Get(); @@ -121,6 +122,11 @@ public: void immediate_submit(std::function&& function); + Transaction begin_transaction(); + void commit_transaction(Transaction& cmd); + + GPUMeshBuffers uploadMesh(std::span indices, std::span vertices); + private: void init_vulkan(); void init_swapchain(); @@ -129,11 +135,19 @@ private: void init_descriptors(); void init_pipelines(); void init_background_pipelines(); - void init_imgui(); + void init_shaders(); + void init_triangle_pipelines(); + void init_mesh_pipeline(); + void init_default_data(); + + void load_shader(const char *file_name, const char *entry_point, ComputeEffect data); void create_swapchain(uint32_t width, uint32_t height); + AllocatedBuffer create_buffer(size_t allocSize, VkBufferUsageFlags usage, VmaMemoryUsage memoryUsage); + void destroy_swapchain(); + void destroy_buffer(const AllocatedBuffer& buffer); void draw_background(VkCommandBuffer cmd); - void draw_imgui(VkCommandBuffer cmd, VkImageView targetImageView); - void render_imgui(); + void draw_geometry(VkCommandBuffer cmd); + }; diff --git a/vk_gui.cpp b/vk_gui.cpp new file mode 100644 index 0000000..86e4bd3 --- /dev/null +++ b/vk_gui.cpp @@ -0,0 +1,151 @@ +#include "vk_gui.h" +#include "vk_initializers.h" +#include +#include +#include +#include +#include +#include "vk_engine.h" + +struct SDL_Window* VkGUI::init_window(VkExtent2D windowExtent) +{ + SDL_Init(SDL_INIT_VIDEO); + + SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_VULKAN); + + return SDL_CreateWindow( + "Vulkan Engine", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + int(windowExtent.width), + int(windowExtent.height), + window_flags); +} + +void VkGUI::init_imgui(VulkanEngine& engine) +{ + // 1. create descriptor pool for IMGUI + // the size of the pool is very oversize, but it's copied from imgui demo + // itself. + VkDescriptorPoolSize pool_sizes[] = { + { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 }, + { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 }, + { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 }, + { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 }, + { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 }, + { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 }, + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 }, + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 }, + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 }, + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 }, + { VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 } + }; + + VkDescriptorPoolCreateInfo pool_info{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, + .maxSets = 1000, + .poolSizeCount = (uint32_t)std::size(pool_sizes), + .pPoolSizes = pool_sizes, + }; + + VkDescriptorPool imguiPool; + VK_CHECK(vkCreateDescriptorPool(engine._device, &pool_info, nullptr, &imguiPool)); + + // 2: initialize the imgui library + ImGui::CreateContext(); + + ImGui_ImplSDL2_InitForVulkan(engine._window); + + ImGui_ImplVulkan_InitInfo init_info{ + .Instance = engine._instance, + .PhysicalDevice = engine._chosenGPU, + .Device = engine._device, + .Queue = engine._graphicsQueue, + .DescriptorPool = imguiPool, + .MinImageCount = 3, + .ImageCount = 3, + .MSAASamples = VK_SAMPLE_COUNT_1_BIT, + .UseDynamicRendering = true, + .PipelineRenderingCreateInfo = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, + .colorAttachmentCount = 1, + .pColorAttachmentFormats = &engine._swapchainImageFormat, + }, + }; + + ImGui_ImplVulkan_Init(&init_info); + ImGui_ImplVulkan_CreateFontsTexture(); + + engine._mainDeletionQueue.push_function([=,this]() { + ImGui_ImplVulkan_Shutdown(); + vkDestroyDescriptorPool(engine._device, imguiPool, nullptr); + }); +} + +void VkGUI::draw_imgui(VkExtent2D& swapchainExtent, VkCommandBuffer cmd, VkImageView targetImageView) +{ + VkRenderingAttachmentInfo colorAttachment = vkinit::attachment_info(targetImageView, nullptr, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + VkRenderingInfo renderInfo = vkinit::rendering_info(swapchainExtent, &colorAttachment, nullptr); + + vkCmdBeginRendering(cmd, &renderInfo); + + ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmd); + + vkCmdEndRendering(cmd); +} + +void VkGUI::render_imgui(std::vector& backgroundEffects, int* currentBackgroundEffect) +{ + ImGui_ImplVulkan_NewFrame(); + ImGui_ImplSDL2_NewFrame(); + + ImGui::NewFrame(); + + if (ImGui::Begin("background")) { + + ComputeEffect& selected = backgroundEffects[*currentBackgroundEffect]; + + ImGui::Text("Selected effect: %s", selected.name); + + ImGui::SliderInt("Effect Index", currentBackgroundEffect,0, backgroundEffects.size() - 1); + + ImGui::InputFloat4("data1",(float*)& selected.data.data1); + ImGui::InputFloat4("data2",(float*)& selected.data.data2); + ImGui::InputFloat4("data3",(float*)& selected.data.data3); + ImGui::InputFloat4("data4",(float*)& selected.data.data4); + } + ImGui::End(); + + ImGui::Render(); +} + +void VkGUI::destroy(struct SDL_Window* _window) +{ + SDL_DestroyWindow(_window); +} + + +void VkGUI::poll_event() { + SDL_Event event; + + while(SDL_PollEvent(&event) != 0) { + //close the window when user alt-f4s or clicks the X button + if(event.type == SDL_QUIT) { + should_quit = true; + } + + if(event.type == SDL_WINDOWEVENT) { + if(event.window.event == SDL_WINDOWEVENT_MINIMIZED) { + stop_rendering = true; + } + + if(event.window.event == SDL_WINDOWEVENT_RESTORED) { + stop_rendering = false; + } + } + + ImGui_ImplSDL2_ProcessEvent(&event); + } +} diff --git a/vk_gui.h b/vk_gui.h new file mode 100644 index 0000000..8890504 --- /dev/null +++ b/vk_gui.h @@ -0,0 +1,18 @@ +#pragma once +#include "vk_types.h" + +class VulkanEngine; + +class VkGUI { + public: + bool should_quit = false; + bool stop_rendering = false; + + struct SDL_Window* init_window(VkExtent2D windowExtent); + void init_imgui(VulkanEngine& engine); + void draw_imgui(VkExtent2D& swapchainExtent, VkCommandBuffer cmd, VkImageView targetImageView); + void render_imgui(std::vector& backgroundEffects, int* currentBackgroundEffect); + void destroy(struct SDL_Window* _window); + + void poll_event(); +}; diff --git a/vk_images.cpp b/vk_images.cpp index a457d61..c97537e 100644 --- a/vk_images.cpp +++ b/vk_images.cpp @@ -3,28 +3,25 @@ void vkutil::transition_image(VkCommandBuffer cmd, VkImage image, VkImageLayout currentLayout, VkImageLayout newLayout) { - VkImageMemoryBarrier2 imageBarrier{}; - imageBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2; - imageBarrier.pNext = nullptr; - - imageBarrier.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT; - imageBarrier.srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT; - imageBarrier.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT; - imageBarrier.dstAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT | VK_ACCESS_2_MEMORY_READ_BIT; - - - imageBarrier.oldLayout = currentLayout; - imageBarrier.newLayout = newLayout; - VkImageAspectFlags aspectMask = (newLayout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; - imageBarrier.subresourceRange = vkinit::image_subresource_range(aspectMask); - imageBarrier.image = image; - VkDependencyInfo depInfo{}; - depInfo.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO; - depInfo.pNext = nullptr; - depInfo.imageMemoryBarrierCount = 1; - depInfo.pImageMemoryBarriers = &imageBarrier; + VkImageMemoryBarrier2 imageBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + .srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + .srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT, + .dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + .dstAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT | VK_ACCESS_2_MEMORY_READ_BIT, + .oldLayout = currentLayout, + .newLayout = newLayout, + .image = image, + .subresourceRange = vkinit::image_subresource_range(aspectMask), + }; + + VkDependencyInfo depInfo{ + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &imageBarrier, + }; vkCmdPipelineBarrier2(cmd, &depInfo); } @@ -33,39 +30,45 @@ void vkutil::copy_image_to_image(VkCommandBuffer cmd, VkImage source, VkImage de { VkImageBlit2 blitRegion{ .sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2, - .pNext = nullptr }; - blitRegion.srcOffsets[1].x = srcSize.width; - blitRegion.srcOffsets[1].y = srcSize.height; - blitRegion.srcOffsets[1].z = 1; + blitRegion.srcSubresource = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }; - blitRegion.dstOffsets[1].x = dstSize.width; - blitRegion.dstOffsets[1].y = dstSize.height; - blitRegion.dstOffsets[1].z = 1; + blitRegion.dstSubresource = { + .aspectMask=VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel=0, + .baseArrayLayer=0, + .layerCount=1, + }; - blitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - blitRegion.srcSubresource.baseArrayLayer = 0; - blitRegion.srcSubresource.layerCount = 1; - blitRegion.srcSubresource.mipLevel = 0; + blitRegion.srcOffsets[1] = { + .x = int32_t(srcSize.width), + .y = int32_t(srcSize.height), + .z = 1, + }; - blitRegion.dstSubresource.aspectMask=VK_IMAGE_ASPECT_COLOR_BIT; - blitRegion.dstSubresource.baseArrayLayer=0; - blitRegion.dstSubresource.layerCount=1; - blitRegion.dstSubresource.mipLevel=0; + blitRegion.dstOffsets[1] = { + .x = int32_t(dstSize.width), + .y = int32_t(dstSize.height), + .z = 1, + }; VkBlitImageInfo2 blitInfo{ - .sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2, .pNext = nullptr + .sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2, + .srcImage = source, + .srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + .dstImage = destination, + .dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .regionCount = 1, + .pRegions = &blitRegion, + .filter = VK_FILTER_LINEAR, }; - blitInfo.dstImage = destination; - blitInfo.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - blitInfo.srcImage = source; - blitInfo.srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - blitInfo.filter = VK_FILTER_LINEAR; - blitInfo.regionCount = 1; - blitInfo.pRegions = &blitRegion; - vkCmdBlitImage2(cmd, &blitInfo); } diff --git a/vk_initializers.cpp b/vk_initializers.cpp index 3c30974..b54c78c 100644 --- a/vk_initializers.cpp +++ b/vk_initializers.cpp @@ -3,163 +3,137 @@ VkCommandPoolCreateInfo vkinit::command_pool_create_info(uint32_t queueFamilyIndex, VkCommandPoolCreateFlags flags /*= 0*/) { - VkCommandPoolCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - info.pNext = nullptr; - info.queueFamilyIndex = queueFamilyIndex; - info.flags = flags; - return info; + return { + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .flags = flags, + .queueFamilyIndex = queueFamilyIndex, + }; } VkCommandBufferAllocateInfo vkinit::command_buffer_allocate_info( VkCommandPool pool, uint32_t count /*= 1*/) { - VkCommandBufferAllocateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - info.pNext = nullptr; - - info.commandPool = pool; - info.commandBufferCount = count; - info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - return info; + return { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .commandPool = pool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = count, + }; } VkFenceCreateInfo vkinit::fence_create_info(VkFenceCreateFlags flags /*=0*/) { - VkFenceCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - info.pNext = nullptr; - info.flags = flags; - return info; + return { + .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + .flags = flags + }; } VkSemaphoreCreateInfo vkinit::semaphore_create_info(VkSemaphoreCreateFlags flags/*=0*/) { - VkSemaphoreCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - info.pNext = nullptr; - info.flags = flags; - return info; + return { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + .flags = flags, + }; } VkCommandBufferBeginInfo vkinit::command_buffer_begin_info(VkCommandBufferUsageFlags flags /*= 0*/) { - VkCommandBufferBeginInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - info.pNext = nullptr; - - info.pInheritanceInfo = nullptr; - info.flags = flags; - return info; + return { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = flags, + .pInheritanceInfo = nullptr, + }; } VkImageSubresourceRange vkinit::image_subresource_range(VkImageAspectFlags aspectMask) { - VkImageSubresourceRange subImage {}; - subImage.aspectMask = aspectMask; - subImage.baseMipLevel = 0; - subImage.levelCount = VK_REMAINING_MIP_LEVELS; - subImage.baseArrayLayer = 0; - subImage.layerCount = VK_REMAINING_ARRAY_LAYERS; - - return subImage; + return { + .aspectMask = aspectMask, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }; } VkSemaphoreSubmitInfo vkinit::semaphore_submit_info(VkPipelineStageFlags2 stageMask, VkSemaphore semaphore) { - VkSemaphoreSubmitInfo submitInfo{}; - submitInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO; - submitInfo.pNext = nullptr; - submitInfo.semaphore = semaphore; - submitInfo.stageMask = stageMask; - submitInfo.deviceIndex = 0; - submitInfo.value = 1; - - return submitInfo; + return { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, + .semaphore = semaphore, + .value = 1, + .stageMask = stageMask, + .deviceIndex = 0, + }; } VkCommandBufferSubmitInfo vkinit::command_buffer_submit_info(VkCommandBuffer cmd) { - VkCommandBufferSubmitInfo info{}; - info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO; - info.pNext = nullptr; - info.commandBuffer = cmd; - info.deviceMask = 0; - - return info; + return { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO, + .commandBuffer = cmd, + .deviceMask = 0, + }; } VkSubmitInfo2 vkinit::submit_info(VkCommandBufferSubmitInfo* cmd, VkSemaphoreSubmitInfo* signalSemaphoreInfo, VkSemaphoreSubmitInfo* waitSemaphoreInfo) { - VkSubmitInfo2 info = {}; - info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2; - info.pNext = nullptr; - - info.waitSemaphoreInfoCount = waitSemaphoreInfo == nullptr ? 0 : 1; - info.pWaitSemaphoreInfos = waitSemaphoreInfo; - - info.signalSemaphoreInfoCount = signalSemaphoreInfo == nullptr ? 0 : 1; - info.pSignalSemaphoreInfos = signalSemaphoreInfo; - - info.commandBufferInfoCount = 1; - info.pCommandBufferInfos = cmd; - - return info; + return { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2, + .waitSemaphoreInfoCount = (waitSemaphoreInfo == nullptr ? 0u : 1u), + .pWaitSemaphoreInfos = waitSemaphoreInfo, + .commandBufferInfoCount = 1, + .pCommandBufferInfos = cmd, + .signalSemaphoreInfoCount = (signalSemaphoreInfo == nullptr ? 0u : 1u), + .pSignalSemaphoreInfos = signalSemaphoreInfo, + }; } VkImageCreateInfo vkinit::image_create_info(VkFormat format, VkImageUsageFlags usageFlags, VkExtent3D extent) { - VkImageCreateInfo info{}; - info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - info.pNext = nullptr; - - info.imageType = VK_IMAGE_TYPE_2D; - - info.format = format; - info.extent = extent; - - info.mipLevels = 1; - info.arrayLayers = 1; - - info.samples = VK_SAMPLE_COUNT_1_BIT; - - info.tiling = VK_IMAGE_TILING_OPTIMAL; - info.usage = usageFlags; - - return info; + return { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .imageType = VK_IMAGE_TYPE_2D, + .format = format, + .extent = extent, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .usage = usageFlags, + }; } VkImageViewCreateInfo vkinit::imageview_create_info(VkFormat format, VkImage image, VkImageAspectFlags aspectFlags) { - VkImageViewCreateInfo info{}; - info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - info.pNext = nullptr; - - info.viewType = VK_IMAGE_VIEW_TYPE_2D; - info.image = image; - info.format = format; - info.subresourceRange.baseMipLevel = 0; - info.subresourceRange.levelCount = 1; - info.subresourceRange.baseArrayLayer = 0; - info.subresourceRange.layerCount = 1; - info.subresourceRange.aspectMask = aspectFlags; - - return info; + return { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = image, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = format, + .subresourceRange = { + .aspectMask = aspectFlags, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + } + }; } VkRenderingAttachmentInfo vkinit::attachment_info( VkImageView view, VkClearValue* clear ,VkImageLayout layout /*= VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL*/) { - VkRenderingAttachmentInfo colorAttachment {}; - colorAttachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; - colorAttachment.pNext = nullptr; - - colorAttachment.imageView = view; - colorAttachment.imageLayout = layout; - colorAttachment.loadOp = clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD; - colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + VkRenderingAttachmentInfo colorAttachment{ + .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, + .imageView = view, + .imageLayout = layout, + .loadOp = clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + }; if (clear) { colorAttachment.clearValue = *clear; @@ -172,16 +146,38 @@ VkRenderingAttachmentInfo vkinit::attachment_info( VkRenderingInfo vkinit::rendering_info(VkExtent2D renderExtent, VkRenderingAttachmentInfo* colorAttachment, VkRenderingAttachmentInfo* depthAttachment) { - VkRenderingInfo renderInfo {}; - renderInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO; - renderInfo.pNext = nullptr; - - renderInfo.renderArea = VkRect2D { VkOffset2D { 0, 0 }, renderExtent }; - renderInfo.layerCount = 1; - renderInfo.colorAttachmentCount = 1; - renderInfo.pColorAttachments = colorAttachment; - renderInfo.pDepthAttachment = depthAttachment; - renderInfo.pStencilAttachment = nullptr; - - return renderInfo; + return { + .sType = VK_STRUCTURE_TYPE_RENDERING_INFO, + .renderArea = VkRect2D { VkOffset2D { 0, 0 }, renderExtent }, + .layerCount = 1, + .colorAttachmentCount = 1, + .pColorAttachments = colorAttachment, + .pDepthAttachment = depthAttachment, + .pStencilAttachment = nullptr, + }; } + + +VkPipelineShaderStageCreateInfo vkinit::pipeline_shader_stage_create_info(VkShaderStageFlagBits stage, VkShaderModule shader, const char* entry) { + return { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .stage = stage, + .module = shader, + .pName = entry, + }; +} + + +VkPipelineLayoutCreateInfo vkinit::pipeline_layout_create_info() +{ + return { + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .setLayoutCount = 0, + .pSetLayouts = nullptr, + .pushConstantRangeCount = 0, + .pPushConstantRanges = nullptr, + }; +} + diff --git a/vk_initializers.h b/vk_initializers.h index 000b378..3cec09a 100644 --- a/vk_initializers.h +++ b/vk_initializers.h @@ -35,5 +35,9 @@ namespace vkinit { VkImageView view, VkClearValue* clear ,VkImageLayout layout=VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); VkRenderingInfo rendering_info(VkExtent2D renderExtent, VkRenderingAttachmentInfo* colorAttachment, VkRenderingAttachmentInfo* depthAttachment); + + VkPipelineShaderStageCreateInfo pipeline_shader_stage_create_info(VkShaderStageFlagBits stage, VkShaderModule shader, const char* entry="main"); + + VkPipelineLayoutCreateInfo pipeline_layout_create_info(); } diff --git a/vk_loader.cpp b/vk_loader.cpp new file mode 100644 index 0000000..c978f60 --- /dev/null +++ b/vk_loader.cpp @@ -0,0 +1,141 @@ +#include "vk_loader.h" +#include "vendor/stb_image.h" +#include + +#include "vk_engine.h" +#include "vk_initializers.h" +#include "vk_types.h" +#include + +#include +#include +#include + +constexpr bool OverrideColors = true; + +std::optional>> loadGltfMeshes(VulkanEngine* engine, std::filesystem::path filePath) +{ + std::println("\nLoading GLTF: {}", filePath.string()); + auto data = fastgltf::GltfDataBuffer::FromPath(filePath); + + if(data.error() != fastgltf::Error::None) { + std::println("Failed to load glTF: {}={}\n", + typeid(data.error()).name(), + fastgltf::to_underlying(data.error())); + return std::nullopt; + } + + constexpr auto gltfOptions = fastgltf::Options::LoadExternalBuffers; + + fastgltf::Asset gltf{}; + fastgltf::Parser parser{}; + + auto load = parser.loadGltfBinary(data.get(), filePath.parent_path(), gltfOptions); + + switch(load.error()) { + case fastgltf::Error::None: + gltf = std::move(load.get()); + break; + case fastgltf::Error::InvalidGLB: + std::println("fastgltf says Error::InvalidGLB {}", filePath.string()); + return std::nullopt; + break; + case fastgltf::Error::UnsupportedVersion: + std::println("fastgltf says Error::UnsupportedVersion {}", filePath.string()); + return std::nullopt; + break; + case fastgltf::Error::InvalidPath: + std::println("fastgltf says Error::UnsupportedVersion {}", + filePath.string()); + return std::nullopt; + break; + default: + std::println("Unknown fastgltf error loading {}", filePath.string()); + break; + } + + std::vector> meshes; + // use the same vectors for all meshes + std::vector indices; + std::vector vertices; + + for(auto& mesh : gltf.meshes) { + MeshAsset newmesh; + newmesh.name = mesh.name; + + indices.clear(); + indices.clear(); + + for(auto&& p : mesh.primitives) { + auto& indexAccessor = gltf.accessors[p.indicesAccessor.value()]; + + GeoSurface newSurface { + .startIndex = (uint32_t)indices.size(), + .count = (uint32_t)indexAccessor.count, + }; + + size_t initial_vtx = vertices.size(); + + // load indices + { + indices.reserve(indices.size() + indexAccessor.count); + fastgltf::iterateAccessor( + gltf, indexAccessor, [&](std::uint32_t idx) { + indices.push_back(idx + initial_vtx); + }); + } + + // load vertex positions + { + fastgltf::Accessor& posAccessor = gltf.accessors[ + p.findAttribute("POSITIONS")->accessorIndex]; + vertices.resize(vertices.size() + posAccessor.count); + + fastgltf::iterateAccessorWithIndex( + gltf, posAccessor, [&](glm::vec3 v, size_t index) + { + vertices[initial_vtx + index] = { + .position = v, + .uv_x = 0, + .normal = {1, 0, 0}, + .uv_y = 0, + .color = glm::vec4{1.0f}, + }; + }); + } + + // load vertex normals + auto normals = p.findAttribute("NORMA:"); + if(normals != p.attributes.end()) { + fastgltf::iterateAccessorWithIndex( + gltf, gltf.accessors[(*normals).accessorIndex], + [&](glm::vec3 v, size_t index) + { + vertices[initial_vtx + index].normal = v; + }); + } + + auto uv = p.findAttribute("TEXCOORD_0"); + if(uv != p.attributes.end()) { + fastgltf::iterateAccessorWithIndex( + gltf, gltf.accessors[(*uv).accessorIndex], + [&](glm::vec2 v, size_t index) { + vertices[initial_vtx + index].uv_x = v.x; + vertices[initial_vtx + index].uv_y = v.y; + }); + } + + if(OverrideColors) { + for(Vertex& vtx : vertices) { + vtx.color = glm::vec4(vtx.normal, 1.0f); + } + } + + newmesh.meshBuffers = engine->uploadMesh(indices, vertices); + meshes.emplace_back(std::make_shared(std::move(newmesh))); + + } + } + + return meshes; +} diff --git a/vk_loader.h b/vk_loader.h new file mode 100644 index 0000000..7e553f1 --- /dev/null +++ b/vk_loader.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include +#include + +struct GeoSurface { + uint32_t startIndex; + uint32_t count; +}; + +struct MeshAsset { + std::string name; + + std::vector surfaces; + GPUMeshBuffers meshBuffers; +}; + +class VulkanEngine; + +std::optional>> loadGltfMeshes(VulkanEngine* engine, std::filesystem::path filePath); + diff --git a/vk_pipelines.cpp b/vk_pipelines.cpp index 4067f1d..b7b3c8d 100644 --- a/vk_pipelines.cpp +++ b/vk_pipelines.cpp @@ -23,13 +23,13 @@ bool vkutil::load_shader_module(const char* filePath, file.close(); - VkShaderModuleCreateInfo createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - createInfo.pNext = nullptr; - - // codeSize has to be in bytes - createInfo.codeSize = buffer.size() * sizeof(uint32_t); - createInfo.pCode = buffer.data(); + VkShaderModuleCreateInfo createInfo = { + .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + .pNext = nullptr, + // codeSize has to be in bytes + .codeSize = buffer.size() * sizeof(uint32_t), + .pCode = buffer.data(), + }; VkShaderModule shaderModule; if(vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { @@ -40,3 +40,165 @@ bool vkutil::load_shader_module(const char* filePath, *outShaderModule = shaderModule; return true; } + + +void PipelineBuilder::clear() { + // clear all of the structs we need back to 0 with their correct stype + + _inputAssembly = { .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; + + _rasterizer = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; + + _colorBlendAttachment = {}; + + _multisampling = { .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; + + _pipelineLayout = {}; + + _depthStencil = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO }; + + _renderInfo = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO }; + + _shaderStages.clear(); +} + +VkPipeline PipelineBuilder::build_pipeline(VkDevice device) +{ + VkPipelineViewportStateCreateInfo viewportState{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, + .viewportCount = 1, + .scissorCount = 1, + }; + + VkPipelineColorBlendStateCreateInfo colorBlending{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + .logicOpEnable = VK_FALSE, + .logicOp = VK_LOGIC_OP_COPY, + .attachmentCount = 1, + .pAttachments = &_colorBlendAttachment, + }; + + VkPipelineVertexInputStateCreateInfo _vertexInputInfo{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO + }; + + VkDynamicState state[] = { + VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR + }; + + VkPipelineDynamicStateCreateInfo dynamicInfo{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, + .dynamicStateCount = 2, + // ZED: we don't need to do this? state is also &state[0] + .pDynamicStates = &state[0], + }; + + // build the actual pipeline + VkGraphicsPipelineCreateInfo pipelineInfo = { + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .pNext = &_renderInfo, + .stageCount = (uint32_t)_shaderStages.size(), + .pStages = _shaderStages.data(), + .pVertexInputState = &_vertexInputInfo, + .pInputAssemblyState = &_inputAssembly, + .pViewportState = &viewportState, + .pRasterizationState = &_rasterizer, + .pMultisampleState = &_multisampling, + .pDepthStencilState = &_depthStencil, + .pColorBlendState = &colorBlending, + .pDynamicState = &dynamicInfo, + .layout = _pipelineLayout, + }; + + // its easy to error out on create graphics pipeline, so we handle it a bit + // better than the common VK_CHECK case + + VkPipeline newPipeline; + auto works = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &newPipeline); + + if(works != VK_SUCCESS) { + std::println("failed to create pipeline"); + return VK_NULL_HANDLE; + } else { + return newPipeline; + } +} + +void PipelineBuilder::set_shaders(VkShaderModule vertexShader, VkShaderModule fragmentShader) +{ + _shaderStages.clear(); + + _shaderStages.push_back( + vkinit::pipeline_shader_stage_create_info( + VK_SHADER_STAGE_VERTEX_BIT, + vertexShader)); + + _shaderStages.push_back( + vkinit::pipeline_shader_stage_create_info( + VK_SHADER_STAGE_FRAGMENT_BIT, + fragmentShader)); +} + +void PipelineBuilder::set_input_topology(VkPrimitiveTopology topology) +{ + _inputAssembly.topology = topology; + _inputAssembly.primitiveRestartEnable = VK_FALSE; +} + +void PipelineBuilder::set_polygon_mode(VkPolygonMode mode) +{ + _rasterizer.polygonMode = mode; + _rasterizer.lineWidth = 1.0f; +} + +void PipelineBuilder::set_cull_mode(VkCullModeFlags cullMode, VkFrontFace frontFace) +{ + _rasterizer.cullMode = cullMode; + _rasterizer.frontFace = frontFace; +} + +void PipelineBuilder::set_multisampling_none() +{ + _multisampling.sampleShadingEnable = VK_FALSE; + _multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + _multisampling.minSampleShading = 1.0f; + _multisampling.pSampleMask = nullptr; + _multisampling.alphaToCoverageEnable = VK_FALSE; + _multisampling.alphaToOneEnable = VK_FALSE; +} + +void PipelineBuilder::disable_blending() +{ + _colorBlendAttachment.colorWriteMask = + VK_COLOR_COMPONENT_R_BIT + | VK_COLOR_COMPONENT_G_BIT + | VK_COLOR_COMPONENT_B_BIT + | VK_COLOR_COMPONENT_A_BIT; + + _colorBlendAttachment.blendEnable = VK_FALSE; +} + +void PipelineBuilder::set_color_attachment_format(VkFormat format) +{ + _colorAttachmentFormat = format; + _renderInfo.colorAttachmentCount = 1; + _renderInfo.pColorAttachmentFormats = &_colorAttachmentFormat; +} + +void PipelineBuilder::set_depth_format(VkFormat format) +{ + _renderInfo.depthAttachmentFormat = format; +} + +void PipelineBuilder::disable_depthtest() +{ + _depthStencil.depthTestEnable = VK_FALSE; + _depthStencil.depthWriteEnable = VK_FALSE; + _depthStencil.depthCompareOp = VK_COMPARE_OP_NEVER; + _depthStencil.depthBoundsTestEnable = VK_FALSE; + _depthStencil.stencilTestEnable = VK_FALSE; + _depthStencil.front = {}; + _depthStencil.back = {}; + _depthStencil.minDepthBounds = 0.0f; + _depthStencil.maxDepthBounds = 1.0f; +} diff --git a/vk_pipelines.h b/vk_pipelines.h index df9c74d..16b4067 100644 --- a/vk_pipelines.h +++ b/vk_pipelines.h @@ -2,6 +2,37 @@ #include "vk_types.h" +class PipelineBuilder { + public: + std::vector _shaderStages; + + VkPipelineInputAssemblyStateCreateInfo _inputAssembly; + VkPipelineRasterizationStateCreateInfo _rasterizer; + VkPipelineColorBlendAttachmentState _colorBlendAttachment; + VkPipelineMultisampleStateCreateInfo _multisampling; + VkPipelineLayout _pipelineLayout; + VkPipelineDepthStencilStateCreateInfo _depthStencil; + VkPipelineRenderingCreateInfo _renderInfo; + VkFormat _colorAttachmentFormat; + + PipelineBuilder() { + clear(); + } + + void clear(); + + VkPipeline build_pipeline(VkDevice device); + void set_shaders(VkShaderModule vertexShader, VkShaderModule fragmentShader); + void set_input_topology(VkPrimitiveTopology topology); + void set_polygon_mode(VkPolygonMode mode); + void set_cull_mode(VkCullModeFlags cullMode, VkFrontFace frontFace); + void set_multisampling_none(); + void disable_blending(); + void set_color_attachment_format(VkFormat format); + void set_depth_format(VkFormat format); + void disable_depthtest(); +}; + namespace vkutil { bool load_shader_module(const char* filePath, VkDevice device, diff --git a/vk_types.h b/vk_types.h index 9b880a6..d491e6f 100644 --- a/vk_types.h +++ b/vk_types.h @@ -18,6 +18,7 @@ #include +#define GLM_ENABLE_EXPERIMENTAL 1 #include #include @@ -30,6 +31,14 @@ } while (0) +constexpr unsigned int FRAME_OVERLAP=2; + +struct AllocatedBuffer { + VkBuffer buffer; + VmaAllocation allocation; + VmaAllocationInfo info; +}; + struct AllocatedImage { VkImage image; VkImageView imageView; @@ -37,3 +46,38 @@ struct AllocatedImage { VkExtent3D imageExtent; VkFormat imageFormat; }; + +struct ComputePushConstants { + glm::vec4 data1; + glm::vec4 data2; + glm::vec4 data3; + glm::vec4 data4; +}; + +struct ComputeEffect { + const char *name; + VkPipeline pipeline; + VkPipelineLayout layout; + + ComputePushConstants data; +}; + +struct Vertex { + glm::vec3 position; + float uv_x; + glm::vec3 normal; + float uv_y; + glm::vec4 color; +}; + +struct GPUMeshBuffers { + AllocatedBuffer indexBuffer; + AllocatedBuffer vertexBuffer; + VkDeviceAddress vertexBufferAddress; +}; + +struct GPUDrawPushConstants { + glm::mat4 worldMatrix; + VkDeviceAddress vertexBuffer; +}; + diff --git a/wraps/fastgltf.wrap b/wraps/fastgltf.wrap new file mode 100644 index 0000000..db7da81 --- /dev/null +++ b/wraps/fastgltf.wrap @@ -0,0 +1,9 @@ +[wrap-git] +directory=fastgltf +url=https://github.com/spnda/fastgltf.git +revision=v0.9.0 +depth=1 +method=cmake + +[provide] +fastgltf = fastgltf_dep diff --git a/wraps/simdjson.wrap b/wraps/simdjson.wrap new file mode 100644 index 0000000..a3e383c --- /dev/null +++ b/wraps/simdjson.wrap @@ -0,0 +1,13 @@ +[wrap-file] +directory = simdjson-3.3.0 +source_url = https://github.com/simdjson/simdjson/archive/refs/tags/v3.3.0.tar.gz +source_filename = simdjson-3.3.0.tar.gz +source_hash = a8c9feff2f19c3ff281d42f0b6b4b18f02236513b99229756fa9a1b14787a58a +patch_filename = simdjson_3.3.0-2_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/simdjson_3.3.0-2/get_patch +patch_hash = 3c39f8a5abac17732b9599a416e6edb09f5f987bcf4e8b46808dbe88040eb40f +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/simdjson_3.3.0-2/simdjson-3.3.0.tar.gz +wrapdb_version = 3.3.0-2 + +[provide] +simdjson = simdjson_dep