diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c47570c..636c34df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,11 @@ if (POLICY CMP0141) set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$,$>,$<$:EditAndContinue>,$<$:ProgramDatabase>>") endif() +# Add to your CMakeLists.txt +if(MSVC) + add_compile_options(/Zm200 /d2FastFail) +endif() + add_library(${PROJECT_NAME}) add_subdirectory(src) diff --git a/src/graphics/CMakeLists.txt b/src/graphics/CMakeLists.txt index e1d6ada1..cd17dd6e 100644 --- a/src/graphics/CMakeLists.txt +++ b/src/graphics/CMakeLists.txt @@ -7,6 +7,8 @@ target_sources(${PROJECT_NAME} vulkan/vk_initializers.cpp vulkan/vk_loader.cpp vulkan/vk_pipelines.cpp + vulkan/vk_renderer.cpp + vulkan/vk_default_data.cpp vulkan/pipelines.cpp vulkan/ComputePipeline.cpp vulkan/GraphicsPipeline.cpp diff --git a/src/graphics/vulkan/vk_default_data.cpp b/src/graphics/vulkan/vk_default_data.cpp new file mode 100644 index 00000000..0a14ba19 --- /dev/null +++ b/src/graphics/vulkan/vk_default_data.cpp @@ -0,0 +1,113 @@ +#include "graphics/vulkan/vk_default_data.h" + +#include +#include +#include +#include + +#include "core/config.h" +#include "graphics/vulkan/vk_engine.h" +#include "graphics/vulkan/vk_loader.h" +#include "graphics/vulkan/vk_types.h" + +#define GLM_ENABLE_EXPERIMENTAL +#include + +DefaultDataManager::DefaultDataManager(VulkanEngine* engine) : _engine(engine) { + assert(engine != nullptr); +} + +void DefaultDataManager::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; + + const auto path_to_assets = std::string(ASSETS_DIR) + "/basicmesh.glb"; + _engine->testMeshes = loadGltfMeshes(_engine, path_to_assets).value(); + + // 3 default textures, white, grey, black. 1 pixel each + const uint32_t white = glm::packUnorm4x8(glm::vec4(1, 1, 1, 1)); + _engine->_whiteImage = + _engine->create_image(&white, VkExtent3D{1, 1, 1}, VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_USAGE_SAMPLED_BIT); + + const uint32_t grey = glm::packUnorm4x8(glm::vec4(0.66f, 0.66f, 0.66f, 1)); + _engine->_greyImage = + _engine->create_image(&grey, VkExtent3D{1, 1, 1}, VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_USAGE_SAMPLED_BIT); + + const uint32_t black = glm::packUnorm4x8(glm::vec4(0, 0, 0, 0)); + _engine->_blackImage = + _engine->create_image(&black, VkExtent3D{1, 1, 1}, VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_USAGE_SAMPLED_BIT); + + // checkerboard image + const uint32_t magenta = glm::packUnorm4x8(glm::vec4(1, 0, 1, 1)); + std::array pixels{}; // for 16x16 checkerboard texture + for (size_t x = 0; x < 16; x++) { + for (size_t y = 0; y < 16; y++) { + pixels[y * 16 + x] = ((x % 2) ^ (y % 2)) ? magenta : black; + } + } + _engine->_errorCheckerboardImage = + _engine->create_image(pixels.data(), VkExtent3D{16, 16, 1}, + VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); + + VkSamplerCreateInfo sampl = {.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO}; + + sampl.magFilter = VK_FILTER_NEAREST; + sampl.minFilter = VK_FILTER_NEAREST; + + vkCreateSampler(_engine->_device, &sampl, nullptr, &_engine->_defaultSamplerNearest); + + sampl.magFilter = VK_FILTER_LINEAR; + sampl.minFilter = VK_FILTER_LINEAR; + vkCreateSampler(_engine->_device, &sampl, nullptr, &_engine->_defaultSamplerLinear); + + GLTFMetallic_Roughness::MaterialResources materialResources{}; + // default the material textures + materialResources.colorImage = _engine->_whiteImage; + materialResources.colorSampler = _engine->_defaultSamplerLinear; + materialResources.metalRoughImage = _engine->_whiteImage; + materialResources.metalRoughSampler = _engine->_defaultSamplerLinear; + + // set the uniform buffer for the material data + const AllocatedBuffer materialConstants = _engine->create_buffer( + sizeof(GLTFMetallic_Roughness::MaterialConstants), + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); + + void* mappedData; + vmaMapMemory(_engine->_allocator, materialConstants.allocation, &mappedData); + + GLTFMetallic_Roughness::MaterialConstants* sceneUniformData = + static_cast(mappedData); + + sceneUniformData->colorFactors = glm::vec4{1.0f, 1.0f, 1.0f, 1.0f}; + sceneUniformData->metal_rough_factors = glm::vec4{1.0f, 0.5f, 0.0f, 0.0f}; + + vmaUnmapMemory(_engine->_allocator, materialConstants.allocation); + + _engine->_mainDeletionQueue.push_function( + [=, this] { _engine->destroy_buffer(materialConstants); }); + + materialResources.dataBuffer = materialConstants.buffer; + materialResources.dataBufferOffset = 0; + + _engine->defaultData = _engine->metalRoughMaterial.write_material( + _engine->_device, MaterialPass::MainColor, materialResources, + _engine->globalDescriptorAllocator); +} diff --git a/src/graphics/vulkan/vk_engine.cpp b/src/graphics/vulkan/vk_engine.cpp index 97fa88ae..c6e70d34 100644 --- a/src/graphics/vulkan/vk_engine.cpp +++ b/src/graphics/vulkan/vk_engine.cpp @@ -36,6 +36,7 @@ #include "graphics/vulkan/vk_pipelines.h" #include "graphics/vulkan/vk_types.h" #include "graphics/vulkan/vk_command_buffers.h" +#include "graphics/vulkan/vk_renderer.h" VulkanEngine* loadedEngine = nullptr; @@ -98,97 +99,6 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanEngine::debugCallback( return VK_FALSE; } -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; - - const auto path_to_assets = std::string(ASSETS_DIR) + "/basicmesh.glb"; - testMeshes = loadGltfMeshes(this, path_to_assets).value(); - - // 3 default textures, white, grey, black. 1 pixel each - const uint32_t white = glm::packUnorm4x8(glm::vec4(1, 1, 1, 1)); - _whiteImage = - create_image(&white, VkExtent3D{1, 1, 1}, VK_FORMAT_R8G8B8A8_UNORM, - VK_IMAGE_USAGE_SAMPLED_BIT); - - const uint32_t grey = glm::packUnorm4x8(glm::vec4(0.66f, 0.66f, 0.66f, 1)); - _greyImage = - create_image(&grey, VkExtent3D{1, 1, 1}, VK_FORMAT_R8G8B8A8_UNORM, - VK_IMAGE_USAGE_SAMPLED_BIT); - - const uint32_t black = glm::packUnorm4x8(glm::vec4(0, 0, 0, 0)); - _blackImage = - create_image(&black, VkExtent3D{1, 1, 1}, VK_FORMAT_R8G8B8A8_UNORM, - VK_IMAGE_USAGE_SAMPLED_BIT); - - // checkerboard image - const uint32_t magenta = glm::packUnorm4x8(glm::vec4(1, 0, 1, 1)); - std::array pixels{}; // for 16x16 checkerboard texture - for (size_t x = 0; x < 16; x++) { - for (size_t y = 0; y < 16; y++) { - pixels[y * 16 + x] = ((x % 2) ^ (y % 2)) ? magenta : black; - } - } - _errorCheckerboardImage = - create_image(pixels.data(), VkExtent3D{16, 16, 1}, - VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); - - VkSamplerCreateInfo sampl = {.sType = - VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO}; - - sampl.magFilter = VK_FILTER_NEAREST; - sampl.minFilter = VK_FILTER_NEAREST; - - vkCreateSampler(_device, &sampl, nullptr, &_defaultSamplerNearest); - - sampl.magFilter = VK_FILTER_LINEAR; - sampl.minFilter = VK_FILTER_LINEAR; - vkCreateSampler(_device, &sampl, nullptr, &_defaultSamplerLinear); - - GLTFMetallic_Roughness::MaterialResources materialResources{}; - // default the material textures - materialResources.colorImage = _whiteImage; - materialResources.colorSampler = _defaultSamplerLinear; - materialResources.metalRoughImage = _whiteImage; - materialResources.metalRoughSampler = _defaultSamplerLinear; - - // set the uniform buffer for the material data - const AllocatedBuffer materialConstants = create_buffer( - sizeof(GLTFMetallic_Roughness::MaterialConstants), - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); - - // write the buffer - auto* sceneUniformData = - (GLTFMetallic_Roughness::MaterialConstants*) - materialConstants.allocation->GetMappedData(); - sceneUniformData->colorFactors = glm::vec4{1, 1, 1, 1}; - sceneUniformData->metal_rough_factors = glm::vec4{1, 0.5, 0, 0}; - - _mainDeletionQueue.push_function( - [=, this] { destroy_buffer(materialConstants); }); - - materialResources.dataBuffer = materialConstants.buffer; - materialResources.dataBufferOffset = 0; - - defaultData = metalRoughMaterial.write_material( - _device, MaterialPass::MainColor, materialResources, - globalDescriptorAllocator); -} void VulkanEngine::init_imgui() { // 1: create descriptor pool for IMGUI @@ -337,7 +247,11 @@ void VulkanEngine::init(SDL_Window* window) { init_descriptors(); init_pipelines(); init_imgui(); - init_default_data(); + + defaultDataManager.init_default_data(); + + // Initialize the renderer + renderer.init(this); mainCamera->velocity = glm::vec3(0.f); mainCamera->position = glm::vec3(0, 0, 5); @@ -703,133 +617,12 @@ GPUMeshBuffers VulkanEngine::uploadMesh(std::span indices, return newSurface; } -void VulkanEngine::draw_background(VkCommandBuffer cmd) const { - // bind the gradient drawing compute pipeline - - pipelines.gradientPipeline->bind(cmd); - - // bind descriptor sets - pipelines.gradientPipeline->bindDescriptorSets(cmd, &_drawImageDescriptors, 1); - - // dispatch the compute shader - pipelines.gradientPipeline->dispatch(cmd, - static_cast(std::ceil(_drawExtent.width / 16.0)), - static_cast(std::ceil(_drawExtent.height / 16.0)), - 1); - -} - -void VulkanEngine::draw_imgui(VkCommandBuffer cmd, - VkImageView targetImageView) const { - const VkRenderingAttachmentInfo colorAttachment = vkinit::attachment_info( - targetImageView, nullptr, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - const VkRenderingInfo renderInfo = - vkinit::rendering_info(_swapchainExtent, &colorAttachment, nullptr); - - vkCmdBeginRendering(cmd, &renderInfo); - - ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmd); - - vkCmdEndRendering(cmd); -} - -void VulkanEngine::draw_geometry(VkCommandBuffer cmd) { - // allocate a new uniform buffer for the scene data - AllocatedBuffer gpuSceneDataBuffer = create_buffer( - sizeof(GPUSceneData), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VMA_MEMORY_USAGE_CPU_TO_GPU); - - // add it to the deletion queue of this frame, so it gets deleted once it's - // been used - get_current_frame()._deletionQueue.push_function( - [=, this] { destroy_buffer(gpuSceneDataBuffer); }); - - // write the buffer - auto* sceneUniformData = - (GPUSceneData*)gpuSceneDataBuffer.allocation->GetMappedData(); - *sceneUniformData = sceneData; - - // create a descriptor set that binds that buffer and update it - VkDescriptorSet globalDescriptor = - get_current_frame()._frameDescriptors.allocate( - _device, _gpuSceneDataDescriptorLayout); - - DescriptorWriter writer; - writer.write_buffer(0, gpuSceneDataBuffer.buffer, sizeof(GPUSceneData), 0, - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER); - writer.update_set(_device, globalDescriptor); - - VkRenderingAttachmentInfo colorAttachment = vkinit::attachment_info( - _drawImage.imageView, nullptr, VK_IMAGE_LAYOUT_GENERAL); - - VkRenderingInfo renderInfo = - vkinit::rendering_info(_drawExtent, &colorAttachment, nullptr); - vkCmdBeginRendering(cmd, &renderInfo); - - pipelines.trianglePipeline->bind(cmd); - - // set dynamic viewport and scissor - VkViewport viewport = {}; - viewport.x = 0; - viewport.y = 0; - viewport.width = static_cast(_drawExtent.width); - viewport.height = static_cast(_drawExtent.height); - viewport.minDepth = 0.f; - viewport.maxDepth = 1.f; - - vkCmdSetViewport(cmd, 0, 1, &viewport); - - VkRect2D scissor = {}; - scissor.offset.x = 0; - scissor.offset.y = 0; - scissor.extent.width = _drawExtent.width; - scissor.extent.height = _drawExtent.height; - - vkCmdSetScissor(cmd, 0, 1, &scissor); - - // bind a texture - VkDescriptorSet imageSet = get_current_frame()._frameDescriptors.allocate( - _device, _singleImageDescriptorLayout); - DescriptorWriter single_image_writer; - single_image_writer.write_image(0, _errorCheckerboardImage.imageView, - _defaultSamplerNearest, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); - single_image_writer.update_set(_device, imageSet); - - pipelines.meshPipeline->bindDescriptorSets(cmd, &imageSet, 1); - - for (const auto& [indexCount, firstIndex, indexBuffer, material, transform, - vertexBufferAddress] : mainDrawContext.OpaqueSurfaces) { - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, - material->pipeline->pipeline); - vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, - material->pipeline->layout, 0, 1, - &globalDescriptor, 0, nullptr); - vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, - material->pipeline->layout, 1, 1, - &material->materialSet, 0, nullptr); - - vkCmdBindIndexBuffer(cmd, indexBuffer, 0, VK_INDEX_TYPE_UINT32); - - GPUDrawPushConstants pushConstants{}; - pushConstants.vertexBuffer = vertexBufferAddress; - pushConstants.worldMatrix = transform; - vkCmdPushConstants(cmd, material->pipeline->layout, - VK_SHADER_STAGE_VERTEX_BIT, 0, - sizeof(GPUDrawPushConstants), &pushConstants); - - vkCmdDrawIndexed(cmd, indexCount, 1, firstIndex, 0, 0); - } - - vkCmdEndRendering(cmd); -} - void VulkanEngine::draw() { update_scene(); // wait until the gpu has finished rendering the last frame. Timeout of 1 - // second + // sec + //ond VK_CHECK(vkWaitForFences(_device, 1, &get_current_frame()._renderFence, true, 1000000000)); @@ -872,53 +665,15 @@ void VulkanEngine::draw() { renderScale); VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo)); + + // Use the renderer to handle all drawing operations + renderer.draw_frame(cmd, _swapchainImageViews[swapchainImageIndex], swapchainImageIndex); - // transition our main draw image into general layout, so we can write into - // it, we will overwrite it all, so we don't care about what was the older - // layout - 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_COLOR_ATTACHMENT_OPTIMAL); - - draw_geometry(cmd); - - // transition the draw image and the swapchain image into their correct - // transfer layouts - vkutil::transition_image(cmd, _drawImage.image, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - - // execute a copy from the draw image into the swapchain - vkutil::copy_image_to_image(cmd, _drawImage.image, - _swapchainImages[swapchainImageIndex], - _drawExtent, _swapchainExtent); - - // set swapchain image layout to Present, so we can show it on the screen - vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); - - // draw imgui into the swapchain image - draw_imgui(cmd, _swapchainImageViews[swapchainImageIndex]); - - // set swapchain image layout to Present, so we can draw it - // vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], - // VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - // VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); - - // finalize the command buffer (we can no longer add commands, but it can - // now be executed) + // finalize the command buffer (we can no longer add commands, but it can now be executed) VK_CHECK(vkEndCommandBuffer(cmd)); // prepare the submission to the queue. - // we want to wait on the _presentSemaphore, as that semaphore is signaled +// 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 @@ -940,7 +695,7 @@ void VulkanEngine::draw() { VK_CHECK(vkQueueSubmit2(_graphicsQueue, 1, &submit, get_current_frame()._renderFence)); - // prepare present + // 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 diff --git a/src/graphics/vulkan/vk_renderer.cpp b/src/graphics/vulkan/vk_renderer.cpp new file mode 100644 index 00000000..64123179 --- /dev/null +++ b/src/graphics/vulkan/vk_renderer.cpp @@ -0,0 +1,169 @@ +#include "graphics/vulkan/vk_renderer.h" +#include "graphics/vulkan/vk_engine.h" +#include "graphics/vulkan/vk_initializers.h" +#include "graphics/vulkan/vk_images.h" + +#include +#include +#include +#include +#include + +void VulkanRenderer::init(VulkanEngine* engine) { + _engine = engine; +} + +void VulkanRenderer::draw_background(VkCommandBuffer cmd) const { + // Bind the gradient drawing compute pipeline + _engine->pipelines.gradientPipeline->bind(cmd); + + // Bind descriptor sets + _engine->pipelines.gradientPipeline->bindDescriptorSets(cmd, &_engine->_drawImageDescriptors, 1); + + // Dispatch the compute shader + _engine->pipelines.gradientPipeline->dispatch(cmd, + static_cast(std::ceil(_engine->_drawExtent.width / 16.0)), + static_cast(std::ceil(_engine->_drawExtent.height / 16.0)), + 1); +} + +void VulkanRenderer::draw_imgui(VkCommandBuffer cmd, VkImageView targetImageView) const { + const VkRenderingAttachmentInfo colorAttachment = vkinit::attachment_info( + targetImageView, nullptr, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + const VkRenderingInfo renderInfo = + vkinit::rendering_info(_engine->_swapchainExtent, &colorAttachment, nullptr); + + vkCmdBeginRendering(cmd, &renderInfo); + + ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmd); + + vkCmdEndRendering(cmd); +} + +void VulkanRenderer::draw_geometry(VkCommandBuffer cmd) { + // Allocate a new uniform buffer for the scene data + AllocatedBuffer gpuSceneDataBuffer = _engine->create_buffer( + sizeof(GPUSceneData), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VMA_MEMORY_USAGE_CPU_TO_GPU); + + // Add it to the deletion queue of this frame, so it gets deleted once it's been used + _engine->get_current_frame()._deletionQueue.push_function( + [=, this] { _engine->destroy_buffer(gpuSceneDataBuffer); }); + + void* mappedData; + vmaMapMemory(_engine->_allocator, gpuSceneDataBuffer.allocation, &mappedData); + GPUSceneData* sceneUniformData = static_cast(mappedData); + *sceneUniformData = _engine->sceneData; + vmaUnmapMemory(_engine->_allocator, gpuSceneDataBuffer.allocation); + + // Create a descriptor set that binds that buffer and update it + VkDescriptorSet globalDescriptor = + _engine->get_current_frame()._frameDescriptors.allocate( + _engine->_device, _engine->_gpuSceneDataDescriptorLayout); + + DescriptorWriter writer; + writer.write_buffer(0, gpuSceneDataBuffer.buffer, sizeof(GPUSceneData), 0, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER); + writer.update_set(_engine->_device, globalDescriptor); + + VkRenderingAttachmentInfo colorAttachment = vkinit::attachment_info( + _engine->_drawImage.imageView, nullptr, VK_IMAGE_LAYOUT_GENERAL); + + VkRenderingInfo renderInfo = + vkinit::rendering_info(_engine->_drawExtent, &colorAttachment, nullptr); + vkCmdBeginRendering(cmd, &renderInfo); + + _engine->pipelines.trianglePipeline->bind(cmd); + + // Set dynamic viewport and scissor + VkViewport viewport = {}; + viewport.x = 0; + viewport.y = 0; + viewport.width = static_cast(_engine->_drawExtent.width); + viewport.height = static_cast(_engine->_drawExtent.height); + viewport.minDepth = 0.f; + viewport.maxDepth = 1.f; + + vkCmdSetViewport(cmd, 0, 1, &viewport); + + VkRect2D scissor = {}; + scissor.offset.x = 0; + scissor.offset.y = 0; + scissor.extent.width = _engine->_drawExtent.width; + scissor.extent.height = _engine->_drawExtent.height; + + vkCmdSetScissor(cmd, 0, 1, &scissor); + + // Bind a texture + VkDescriptorSet imageSet = _engine->get_current_frame()._frameDescriptors.allocate( + _engine->_device, _engine->_singleImageDescriptorLayout); + DescriptorWriter single_image_writer; + single_image_writer.write_image(0, _engine->_errorCheckerboardImage.imageView, + _engine->_defaultSamplerNearest, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); + single_image_writer.update_set(_engine->_device, imageSet); + + _engine->pipelines.meshPipeline->bindDescriptorSets(cmd, &imageSet, 1); + + for (const auto& [indexCount, firstIndex, indexBuffer, material, transform, + vertexBufferAddress] : _engine->mainDrawContext.OpaqueSurfaces) { + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, + material->pipeline->pipeline); + vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, + material->pipeline->layout, 0, 1, + &globalDescriptor, 0, nullptr); + vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, + material->pipeline->layout, 1, 1, + &material->materialSet, 0, nullptr); + + vkCmdBindIndexBuffer(cmd, indexBuffer, 0, VK_INDEX_TYPE_UINT32); + + GPUDrawPushConstants pushConstants{}; + pushConstants.vertexBuffer = vertexBufferAddress; + pushConstants.worldMatrix = transform; + vkCmdPushConstants(cmd, material->pipeline->layout, + VK_SHADER_STAGE_VERTEX_BIT, 0, + sizeof(GPUDrawPushConstants), &pushConstants); + + vkCmdDrawIndexed(cmd, indexCount, 1, firstIndex, 0, 0); + } + + vkCmdEndRendering(cmd); +} + +void VulkanRenderer::draw_frame(VkCommandBuffer cmd, VkImageView targetImageView, uint32_t swapchainImageIndex) { + // Transition our main draw image into general layout + vkutil::transition_image(cmd, _engine->_drawImage.image, VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_GENERAL); + + // Draw the background + draw_background(cmd); + + vkutil::transition_image(cmd, _engine->_drawImage.image, VK_IMAGE_LAYOUT_GENERAL, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + // Draw geometry + draw_geometry(cmd); + + // Transition the draw image and the swapchain image into their correct transfer layouts + vkutil::transition_image(cmd, _engine->_drawImage.image, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + vkutil::transition_image(cmd, _engine->_swapchainImages[swapchainImageIndex], + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + // Execute a copy from the draw image into the swapchain + vkutil::copy_image_to_image(cmd, _engine->_drawImage.image, + _engine->_swapchainImages[swapchainImageIndex], + _engine->_drawExtent, _engine->_swapchainExtent); + + // Set swapchain image layout to Present, so we can show it on the screen + vkutil::transition_image(cmd, _engine->_swapchainImages[swapchainImageIndex], + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); + + // Draw imgui into the swapchain image + draw_imgui(cmd, targetImageView); +} \ No newline at end of file diff --git a/src/include/graphics/vulkan/vk_default_data.h b/src/include/graphics/vulkan/vk_default_data.h new file mode 100644 index 00000000..d4ec86a4 --- /dev/null +++ b/src/include/graphics/vulkan/vk_default_data.h @@ -0,0 +1,18 @@ +#pragma once + +#include "vk_types.h" +#include +#include + +class VulkanEngine; +struct MeshAsset; + +class DefaultDataManager { +public: + DefaultDataManager(VulkanEngine* engine); + + void init_default_data(); + +private: + VulkanEngine* _engine; +}; \ No newline at end of file diff --git a/src/include/graphics/vulkan/vk_engine.h b/src/include/graphics/vulkan/vk_engine.h index 3124bfc7..ff8ef84b 100644 --- a/src/include/graphics/vulkan/vk_engine.h +++ b/src/include/graphics/vulkan/vk_engine.h @@ -23,6 +23,8 @@ #include "ComputePipeline.h" #include "vk_command_buffers.h" +#include "vk_renderer.h" +#include "vk_default_data.h" class Camera; class VulkanEngine; @@ -220,6 +222,14 @@ class VulkanEngine { AllocatedBuffer create_buffer(size_t allocSize, VkBufferUsageFlags usage, VmaMemoryUsage memoryUsage) const; + // Make VulkanRenderer a friend class to access private members + friend class VulkanRenderer; + + // Add the renderer as a member + VulkanRenderer renderer; + + friend class DefaultDataManager; + private: static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, @@ -234,23 +244,16 @@ class VulkanEngine { void create_swapchain(uint32_t width, uint32_t height); void destroy_swapchain(); - void draw_background(VkCommandBuffer cmd) const; - void init_descriptors(); void init_pipelines(); void init_imgui(); - - void draw_imgui(VkCommandBuffer cmd, VkImageView targetImageView) const; - - void draw_geometry(VkCommandBuffer cmd); - void destroy_buffer(const AllocatedBuffer& buffer) const; void resize_swapchain(); void init_mesh_pipeline(); - void init_default_data(); + DefaultDataManager defaultDataManager{this}; }; \ No newline at end of file diff --git a/src/include/graphics/vulkan/vk_renderer.h b/src/include/graphics/vulkan/vk_renderer.h new file mode 100644 index 00000000..699d7b9a --- /dev/null +++ b/src/include/graphics/vulkan/vk_renderer.h @@ -0,0 +1,26 @@ +#pragma once + +#include "vk_types.h" +#include "vk_pipelines.h" +#include + +struct DrawContext; +class VulkanEngine; + +class VulkanRenderer { +public: + VulkanRenderer() = default; + + void init(VulkanEngine* engine); + + // Main drawing method that coordinates the entire frame + void draw_frame(VkCommandBuffer cmd, VkImageView targetImageView, uint32_t swapchainImageIndex); + + // Individual drawing methods + void draw_background(VkCommandBuffer cmd) const; + void draw_imgui(VkCommandBuffer cmd, VkImageView targetImageView) const; + void draw_geometry(VkCommandBuffer cmd); + +private: + VulkanEngine* _engine = nullptr; +}; \ No newline at end of file