.. _program_listing_file_Source_Azura_RenderSystem_Src_Vulkan_VkRenderer.cpp: Program Listing for File VkRenderer.cpp ======================================= |exhale_lsh| :ref:`Return to documentation for file ` (``Source\Azura\RenderSystem\Src\Vulkan\VkRenderer.cpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #include "Vulkan/VkRenderer.h" #include "Generic/Window.h" #include "Memory/MemoryFactory.h" #include "Memory/MonotonicAllocator.h" #include "Utils/Macros.h" #include "Vulkan/VkShader.h" #include "Vulkan/VkTypeMapping.h" #include "Vulkan/VkMacros.h" #include using namespace Azura::Containers; // NOLINT - Freedom to use using namespace in CPP files. namespace Azura { namespace Vulkan { VkRenderer::VkRenderer(const ApplicationInfo& appInfo, const DeviceRequirements& deviceRequirements, const ApplicationRequirements& appRequirements, const SwapChainRequirements& swapChainRequirement, const RenderPassRequirements& renderPassRequirements, const DescriptorRequirements& descriptorRequirements, const ShaderRequirements& shaderRequirements, Memory::Allocator& mainAllocator, Memory::Allocator& drawAllocator, Window& window) : Renderer(appInfo, deviceRequirements, appRequirements, swapChainRequirement, descriptorRequirements, mainAllocator, drawAllocator, window), log_VulkanRenderSystem(Log("VulkanRenderSystem")), m_perFrameBuffer(4096), m_perFrameAllocator(m_perFrameBuffer, 4096), m_drawablePools(renderPassRequirements.m_maxPools, drawAllocator), m_computePools(renderPassRequirements.m_maxPools, drawAllocator), m_swapChain(mainAllocator, log_VulkanRenderSystem), m_renderPasses(renderPassRequirements.m_passSequence.GetSize(), mainAllocator), m_descriptorSetLayouts(mainAllocator), m_imageAvailableSemaphores(mainAllocator), m_renderFinishedSemaphores(mainAllocator), m_inFlightFences(mainAllocator), m_shaders(shaderRequirements.m_shaders.GetSize(), mainAllocator), m_renderPassAttachmentImages(renderPassRequirements.m_targets.GetSize(), mainAllocator) { HEAP_ALLOCATOR(Temporary, Memory::MonotonicAllocator, 16384); Vector extensions(4, allocatorTemporary); VkPlatform::GetInstanceExtensions(extensions); m_instance = VkCore::CreateInstance(GetApplicationInfo(), extensions, log_VulkanRenderSystem); #ifdef BUILD_DEBUG m_callback = VkCore::SetupDebug(m_instance, log_VulkanRenderSystem); #endif m_surface = VkPlatform::CreateSurface(m_window.get().GetHandle(), m_instance, log_VulkanRenderSystem); m_physicalDevice = VkCore::SelectPhysicalDevice(m_instance, m_surface, GetDeviceRequirements(), log_VulkanRenderSystem); m_queueIndices = VkCore::FindQueueFamiliesInDevice(m_physicalDevice, m_surface, GetDeviceRequirements(), log_VulkanRenderSystem); m_device = VkCore::CreateLogicalDevice(m_physicalDevice, m_queueIndices, GetDeviceRequirements(), log_VulkanRenderSystem); vkGetPhysicalDeviceProperties(m_physicalDevice, &m_physicalDeviceProperties); for (const auto& shaderCreateInfo : shaderRequirements.m_shaders) { VkRenderer::AddShader(shaderCreateInfo); } m_graphicsQueue = VkCore::GetQueueFromDevice(m_device, m_queueIndices.m_graphicsFamily); m_presentQueue = VkCore::GetQueueFromDevice(m_device, m_queueIndices.m_presentFamily); const SwapChainDeviceSupport swapChainDeviceSupport = VkCore::QuerySwapChainSupport(m_physicalDevice, m_surface, allocatorTemporary); if (m_queueIndices.m_isTransferQueueRequired) { m_transferQueue = VkCore::GetQueueFromDevice(m_device, m_queueIndices.m_transferFamily); } m_graphicsCommandPool = VkCore::CreateCommandPool(m_device, m_queueIndices.m_graphicsFamily, 0, log_VulkanRenderSystem); if (m_queueIndices.m_isTransferQueueRequired) { m_transferCommandPool = VkCore::CreateCommandPool(m_device, m_queueIndices.m_transferFamily, VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, log_VulkanRenderSystem); } VkPhysicalDeviceMemoryProperties memProperties; vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &memProperties); m_swapChain.Create(m_device, m_physicalDevice, m_graphicsQueue, m_graphicsCommandPool, m_surface, m_queueIndices, swapChainDeviceSupport, swapChainRequirement, memProperties); for (const auto& bufferCreateInfo : renderPassRequirements.m_targets) { TextureDesc desc = {}; desc.m_format = bufferCreateInfo.m_format; desc.m_bounds = Bounds3D{m_swapChain.GetExtent().width, m_swapChain.GetExtent().height, 1}; VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; if (HasDepthComponent(bufferCreateInfo.m_format) || HasStencilComponent(bufferCreateInfo.m_format)) { usageFlags = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; } LOG_DBG(log_VulkanRenderSystem, LOG_LEVEL, "Creating Attachment Input at: %d for %s", m_renderPassAttachmentImages. GetSize(), ToString(bufferCreateInfo.m_format).c_str()); m_renderPassAttachmentImages.PushBack(VkScopedImage(m_device, desc, usageFlags, memProperties, log_VulkanRenderSystem)); m_renderPassAttachmentImages.Last().CreateImageView(ImageViewType::ImageView2D); } U32 passCount = 0; for (const auto& passCreateInfo : renderPassRequirements.m_passSequence) { VkScopedRenderPass renderPass = VkScopedRenderPass(m_renderPasses.GetSize(), mainAllocator, log_VulkanRenderSystem); if (passCount != (renderPassRequirements.m_passSequence.GetSize() - 1)) { renderPass.Create(m_device, m_graphicsCommandPool, passCreateInfo, renderPassRequirements.m_targets, m_renderPassAttachmentImages, m_shaders, m_swapChain); } else { renderPass.CreateForSwapChain(m_device, m_graphicsCommandPool, passCreateInfo, m_shaders, m_swapChain); } m_renderPasses.PushBack(renderPass); ++passCount; } CreateDescriptorInfo(); const U32 syncCount = swapChainRequirement.m_framesInFlight; m_imageAvailableSemaphores.Resize(syncCount); m_renderFinishedSemaphores.Resize(syncCount); m_inFlightFences.Resize(syncCount); VkCore::CreateSemaphores(m_device, syncCount, m_imageAvailableSemaphores, log_VulkanRenderSystem); VkCore::CreateSemaphores(m_device, syncCount, m_renderFinishedSemaphores, log_VulkanRenderSystem); VkCore::CreateFences(m_device, syncCount, VK_FENCE_CREATE_SIGNALED_BIT, m_inFlightFences, log_VulkanRenderSystem); } VkRenderer::~VkRenderer() { vkDeviceWaitIdle(m_device); #ifdef BUILD_DEBUG VkCore::DestroyDebugReportCallbackEXT(m_instance, m_callback, nullptr); #endif for (const auto& semaphore : m_imageAvailableSemaphores) { vkDestroySemaphore(m_device, semaphore, nullptr); } for (const auto& semaphore : m_renderFinishedSemaphores) { vkDestroySemaphore(m_device, semaphore, nullptr); } for (const auto& fences : m_inFlightFences) { vkDestroyFence(m_device, fences, nullptr); } for (const auto& pool : m_drawablePools) { pool.CleanUp(); } for (const auto& shader : m_shaders) { shader.CleanUp(m_device); } for (const auto& setLayout : m_descriptorSetLayouts) { vkDestroyDescriptorSetLayout(m_device, setLayout, nullptr); } vkDestroyPipelineLayout(m_device, m_pipelineLayout, nullptr); vkDestroyDescriptorPool(m_device, m_descriptorPool, nullptr); m_swapChain.CleanUp(m_device); for (const auto& attachments : m_renderPassAttachmentImages) { attachments.CleanUp(); } for (const auto& renderPass : m_renderPasses) { renderPass.CleanUp(m_device, m_graphicsCommandPool); } vkDestroyCommandPool(m_device, m_graphicsCommandPool, nullptr); if (m_queueIndices.m_isTransferQueueRequired) { vkDestroyCommandPool(m_device, m_transferCommandPool, nullptr); } vkDestroySurfaceKHR(m_instance, m_surface, nullptr); vkDestroyDevice(m_device, nullptr); // Queues are also deleted vkDestroyInstance(m_instance, nullptr); // m_device also Deleted }; DrawablePool& VkRenderer::CreateDrawablePool(const DrawablePoolCreateInfo& createInfo) { STACK_ALLOCATOR(Temporary, Memory::MonotonicAllocator, 2048); VkPhysicalDeviceMemoryProperties memProperties; vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &memProperties); // TODO(vasumahesh1): This isn't as performance optimized as it should be. We can probably find a way to insert a // buffer inside each pool? // Also, using default Viewport. VkDrawablePool pool = VkDrawablePool(createInfo, m_device, m_graphicsQueue, VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, m_graphicsCommandPool, m_pipelineLayout, m_descriptorPool, m_descriptorSetLayouts, m_renderPasses, m_renderPassAttachmentImages, m_shaders, GetApplicationRequirements(), m_window.get().GetViewport(), memProperties, m_physicalDeviceProperties, m_swapChain, m_descriptorSlots, m_descriptorCount, m_drawPoolAllocator, m_mainAllocator, log_VulkanRenderSystem); m_drawablePools.PushBack(std::move(pool)); return m_drawablePools.Last(); } ComputePool& VkRenderer::CreateComputePool(const ComputePoolCreateInfo& createInfo) { VkComputePool pool = VkComputePool(createInfo, m_descriptorCount, m_mainAllocator); m_computePools.PushBack(std::move(pool)); return m_computePools.Last(); } VkDevice VkRenderer::GetDevice() const { return m_device; } String VkRenderer::GetRenderingAPI() const { return "Vulkan"; } void VkRenderer::Submit() { STACK_ALLOCATOR(Temporary, Memory::MonotonicAllocator, 4096); Vector> secondaryCmdBuffers(m_renderPasses.GetSize(), allocatorTemporary); for (U32 idx = 0; idx < m_renderPasses.GetSize(); ++idx) { Vector poolCmdBuffers(m_drawablePools.GetSize(), allocatorTemporary); secondaryCmdBuffers.PushBack(poolCmdBuffers); } for (auto& drawablePool : m_drawablePools) { drawablePool.Submit(); } for (auto& drawablePool : m_drawablePools) { Vector> drawableBuffer(allocatorTemporary); drawablePool.GetCommandBuffers(drawableBuffer); for (const auto& bufferPair : drawableBuffer) { secondaryCmdBuffers[bufferPair.first].PushBack(bufferPair.second); } } // Don't call last pass for (U32 idx = 0; idx < m_renderPasses.GetSize() - 1; ++idx) { const auto& renderPass = m_renderPasses[idx]; renderPass.Begin(m_swapChain); const auto& cmdBuffers = secondaryCmdBuffers[renderPass.GetId()]; vkCmdExecuteCommands(renderPass.GetCommandBuffer(0), cmdBuffers.GetSize(), cmdBuffers.Data()); renderPass.End(); } const auto& lastPass = m_renderPasses.Last(); const auto& lastPassCommands = secondaryCmdBuffers.Last(); lastPass.Begin(m_swapChain); for (U32 idx = 0; idx < lastPass.GetFrameBufferCount(); ++idx) { vkCmdExecuteCommands(lastPass.GetCommandBuffer(idx), lastPassCommands.GetSize(), lastPassCommands.Data()); } lastPass.End(); } void VkRenderer::CreateDescriptorInfo() { STACK_ALLOCATOR(Temporary, Memory::MonotonicAllocator, 4096); Vector bindingSetSizes{m_descriptorSlots.GetSize(), allocatorTemporary}; int lastSet = -1; for (const auto& slot : m_descriptorSlots) { if (lastSet == int(slot.m_setIdx)) { bindingSetSizes.Last() += 1; continue; } lastSet = int(slot.m_setIdx); bindingSetSizes.PushBack(1); } m_descriptorSetLayouts.Reserve(bindingSetSizes.GetSize() + m_renderPasses.GetSize()); int offset = 0; for (const auto& bindingSize : bindingSetSizes) { Vector currentBindings(bindingSize, allocatorTemporary); for (int idx = offset; idx < offset + bindingSize; ++idx) { const auto& slot = m_descriptorSlots[idx]; const auto combinedShaderFlagBits = GetCombinedShaderStageFlag(slot.m_stages); const auto bindingId = U32(idx - offset); switch (slot.m_type) { case DescriptorType::UniformBuffer: VkCore::CreateUniformBufferBinding(currentBindings, bindingId, 1, combinedShaderFlagBits); break; case DescriptorType::Sampler: VkCore::CreateSamplerBinding(currentBindings, bindingId, 1, combinedShaderFlagBits); break; case DescriptorType::SampledImage: VkCore::CreateSampledImageBinding(currentBindings, bindingId, 1, combinedShaderFlagBits); break; case DescriptorType::PushConstant: case DescriptorType::CombinedImageSampler: default: LOG_ERR(log_VulkanRenderSystem, LOG_LEVEL, "Unknown Descriptor Type"); break; } } m_descriptorSetLayouts. PushBack(VkCore::CreateDescriptorSetLayout(m_device, currentBindings, log_VulkanRenderSystem)); offset += bindingSize; } for (auto& renderPass : m_renderPasses) { const auto& setLayout = renderPass.GetDescriptorSetLayout(); if (setLayout != VK_NULL_HANDLE) { renderPass.SetDescriptorSetId(m_descriptorSetLayouts.GetSize()); m_descriptorSetLayouts.PushBack(setLayout); } } m_pipelineLayout = VkCore::CreatePipelineLayout(m_device, m_descriptorSetLayouts, log_VulkanRenderSystem); Vector descriptorPoolSizes(MAX_DESCRIPTOR_TYPE_COUNT, allocatorTemporary); // TODO(vasumahesh1):[DESCRIPTOR]: How to use Uniform Buffer Arrays? VkDescriptorPoolSize uniformPoolSize = {}; uniformPoolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; uniformPoolSize.descriptorCount = m_descriptorCount.m_numUniformSlots; descriptorPoolSizes.PushBack(uniformPoolSize); if (m_descriptorCount.m_numSamplerSlots > 0) { VkDescriptorPoolSize samplerPoolSize = {}; samplerPoolSize.type = VK_DESCRIPTOR_TYPE_SAMPLER; samplerPoolSize.descriptorCount = m_descriptorCount.m_numSamplerSlots; descriptorPoolSizes.PushBack(samplerPoolSize); } if (m_descriptorCount.m_numSampledImageSlots > 0) { VkDescriptorPoolSize sampledImagePoolSize = {}; sampledImagePoolSize.type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; sampledImagePoolSize.descriptorCount = m_descriptorCount.m_numSampledImageSlots; descriptorPoolSizes.PushBack(sampledImagePoolSize); } // TODO(vasumahesh1):[DESCRIPTOR]: 1 Set per Drawable? Need to Check VkDescriptorPoolCreateInfo poolInfo = {}; poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; poolInfo.poolSizeCount = descriptorPoolSizes.GetSize(); poolInfo.pPoolSizes = descriptorPoolSizes.Data(); // TODO(vasumahesh1):[DESCRIPTORS]: Max Sets issue! poolInfo.maxSets = m_drawablePools.GetMaxSize() * m_descriptorSetLayouts.GetSize(); VERIFY_VK_OP(log_VulkanRenderSystem, vkCreateDescriptorPool(m_device, &poolInfo, nullptr, &m_descriptorPool), "Unable to create Descriptor Pool"); } void VkRenderer::RenderFrame() { EnterRenderFrame(); const auto& currentFrame = GetCurrentFrame(); vkWaitForFences(m_device, 1, &m_inFlightFences[currentFrame], VK_TRUE, std::numeric_limits::max()); vkResetFences(m_device, 1, &m_inFlightFences[currentFrame]); U32 imageIndex; const VkResult result = vkAcquireNextImageKHR(m_device, m_swapChain.Real(), std::numeric_limits::max(), m_imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); if (result == VK_ERROR_OUT_OF_DATE_KHR) { // TODO(vasumahesh1):[RESIZE]: Recreate Swapchain // RecreateSwapChain(); return; } VERIFY_TRUE(log_VulkanRenderSystem, result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR, "Failed to acquire swap chain image"); // std::array initialWaitSemaphores = {}; // std::array finalSignalSemaphores = {m_renderFinishedSemaphores[currentFrame]}; // std::array waitStages = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; for (U32 idx = 0; idx < m_renderPasses.GetSize(); ++idx) { const auto& renderPass = m_renderPasses[idx]; VkCommandBuffer passBuffer; VkFence waitFence = VK_NULL_HANDLE; Vector waitSemaphores(2, m_perFrameAllocator); Vector waitStages(2, m_perFrameAllocator); Vector signalSemaphores(2, m_perFrameAllocator); // Start of Render if (idx == 0) { waitSemaphores.PushBack(m_imageAvailableSemaphores[currentFrame]); waitStages.PushBack(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); if (idx != m_renderPasses.GetSize() - 1) { const auto& nextPass = m_renderPasses[idx + 1]; signalSemaphores.PushBack(nextPass.GetRenderSemaphore()); } } // Somewhere in Middle else if (idx < m_renderPasses.GetSize() - 1) { const auto& nextPass = m_renderPasses[idx + 1]; waitSemaphores.PushBack(renderPass.GetRenderSemaphore()); waitStages.PushBack(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); signalSemaphores.PushBack(nextPass.GetRenderSemaphore()); } passBuffer = renderPass.GetCommandBuffer(0); // End of Render if (idx == m_renderPasses.GetSize() - 1) { if (idx != 0) { waitSemaphores.PushBack(renderPass.GetRenderSemaphore()); waitStages.PushBack(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); } signalSemaphores.PushBack(m_renderFinishedSemaphores[currentFrame]); passBuffer = renderPass.GetCommandBuffer(imageIndex); waitFence = m_inFlightFences[currentFrame]; } // SUCCESS OR SUBOPTIMAL VkSubmitInfo submitInfo = {}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.waitSemaphoreCount = waitSemaphores.GetSize(); submitInfo.pWaitSemaphores = waitSemaphores.Data(); submitInfo.pWaitDstStageMask = waitStages.Data(); submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &passBuffer; submitInfo.signalSemaphoreCount = signalSemaphores.GetSize(); submitInfo.pSignalSemaphores = signalSemaphores.Data(); VERIFY_VK_OP(log_VulkanRenderSystem, vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, waitFence), "Failed to submit draw command buffer"); } Vector presentWaitSemaphores({m_renderFinishedSemaphores[currentFrame]}, m_perFrameAllocator); VkPresentInfoKHR presentInfo = {}; presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; presentInfo.waitSemaphoreCount = presentWaitSemaphores.GetSize(); presentInfo.pWaitSemaphores = presentWaitSemaphores.Data(); std::array swapChains = {m_swapChain.Real()}; presentInfo.swapchainCount = U32(swapChains.size()); presentInfo.pSwapchains = swapChains.data(); presentInfo.pImageIndices = &imageIndex; presentInfo.pResults = nullptr; vkQueuePresentKHR(m_presentQueue, &presentInfo); m_perFrameAllocator.Reset(); ExitRenderFrame(); } void VkRenderer::SnapshotFrame(const String& exportPath) const { // TODO(vasumahesh):[TEXTURE]: VkScopedImage VkDeviceMemory dstMemory; // TODO(vasumahesh1):[SNAPSHOT]: Use same format as Swap Chain currently const RawStorageFormat storageFormat = GetSwapchainRequirements().m_format; const bool supportsBlit = [this, storageFormat]() -> bool { const auto vkFormat = ToVkFormat(storageFormat); VERIFY_OPT(log_VulkanRenderSystem, vkFormat, "Unknown Vk Format"); VkFormatProperties formatProps; vkGetPhysicalDeviceFormatProperties(m_physicalDevice, m_swapChain.GetSurfaceFormat(), &formatProps); if ((formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) == 0u) { LOG_WRN(log_VulkanRenderSystem, LOG_LEVEL, "Swapchain Format doesn't support Blit, Will use Image Copy"); return false; } vkGetPhysicalDeviceFormatProperties(m_physicalDevice, vkFormat.value(), &formatProps); if ((formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT) == 0u) { LOG_WRN(log_VulkanRenderSystem, LOG_LEVEL, "Destination Image Format doesn't support linear blit, Will use Image Copy"); return false; } return true; }(); const auto& currentFrame = GetCurrentFrame(); // TODO(vasumahesh):[TEXTURE]: VkScopedImage // TODO(vasumahesh):[LINT]: Remove Lint overrides const VkScopedImage& srcImage = m_swapChain.GetImage(currentFrame); const auto swapChainExtent = m_swapChain.GetExtent(); const VkImage dstImage = VkCore::CreateImage(m_device, storageFormat, ImageType::Image2D, Bounds2D{swapChainExtent.width, swapChainExtent.height}, 1, 1, 1, VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_TRANSFER_DST_BIT, log_VulkanRenderSystem); VkMemoryRequirements memRequirements; vkGetImageMemoryRequirements(m_device, dstImage, &memRequirements); VkPhysicalDeviceMemoryProperties memProperties; vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &memProperties); VkMemoryAllocateInfo allocInfo = {}; allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocInfo.allocationSize = memRequirements.size; allocInfo.memoryTypeIndex = VkCore::FindMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, memProperties); VERIFY_VK_OP(log_VulkanRenderSystem, vkAllocateMemory(m_device, &allocInfo, nullptr, &dstMemory), "Snapshot: Unable to allocate Texture Memory for snapshot"); VERIFY_VK_OP(log_VulkanRenderSystem, vkBindImageMemory(m_device, dstImage, dstMemory, 0), "Snapshot: Failed to bind Image and Image Memory"); VkCommandBuffer snapshotCmdBuffer = VkCore::CreateCommandBuffer(m_device, m_graphicsCommandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, log_VulkanRenderSystem); VkCore::BeginCommandBuffer(snapshotCmdBuffer, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, log_VulkanRenderSystem); VkCore::TransitionImageLayout( snapshotCmdBuffer, dstImage, 0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VkImageSubresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}); // Transition swapchain image from present to transfer source layout VkCore::TransitionImageLayout( snapshotCmdBuffer, srcImage.Real(), VK_ACCESS_MEMORY_READ_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VkImageSubresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} ); if (supportsBlit) { VkCore::ImageBlit(snapshotCmdBuffer, srcImage.Real(), dstImage, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_ASPECT_COLOR_BIT, Bounds3D{swapChainExtent.width, swapChainExtent.height, 1}); } else { VkCore::ImageCopy(snapshotCmdBuffer, srcImage.Real(), dstImage, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_ASPECT_COLOR_BIT, {swapChainExtent.width, swapChainExtent.height, 1}); } VkCore::TransitionImageLayout( snapshotCmdBuffer, dstImage, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_MEMORY_READ_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VkImageSubresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} ); // Transition source image back to its original layout VkCore::TransitionImageLayout( snapshotCmdBuffer, srcImage.Real(), VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_MEMORY_READ_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VkImageSubresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} ); VkCore::FlushCommandBuffer(m_device, snapshotCmdBuffer, m_graphicsQueue, log_VulkanRenderSystem); vkFreeCommandBuffers(m_device, m_graphicsCommandPool, 1, &snapshotCmdBuffer); // Get layout of the image (including row pitch) VkImageSubresource subResource{VK_IMAGE_ASPECT_COLOR_BIT, 0, 0}; VkSubresourceLayout subResourceLayout; vkGetImageSubresourceLayout(m_device, dstImage, &subResource, &subResourceLayout); const char* data; vkMapMemory(m_device, dstMemory, 0, VK_WHOLE_SIZE, 0, (void**)&data); // NOLINT data += subResourceLayout.offset; // NOLINT const U32 imageSize = swapChainExtent.width * swapChainExtent.height * (GetFormatSize(storageFormat)); std::vector imageData(imageSize); memcpy(imageData.data(), data, imageSize); std::ofstream file(exportPath, std::ios::out | std::ios::binary); file.write(&imageData[0], imageData.size()); file.close(); vkUnmapMemory(m_device, dstMemory); vkFreeMemory(m_device, dstMemory, nullptr); vkDestroyImage(m_device, dstImage, nullptr); LOG_INF(log_VulkanRenderSystem, LOG_LEVEL, "Snapshot Saved: Size: %d x %d", swapChainExtent.width, swapChainExtent. height); } void VkRenderer::BindRenderTarget(U32 renderTargetId, const TextureDesc& desc, const U8* buffer) { UNUSED(renderTargetId); UNUSED(desc); UNUSED(buffer); } void VkRenderer::BindBufferTarget(U32 bufferTargetId, const U8* buffer) { UNUSED(bufferTargetId); UNUSED(buffer); } void VkRenderer::AddShader(const ShaderCreateInfo& info) { // TODO(vasumahesh1):[ASSETS]: Manage assets const String fullPath = "Shaders/" + VkRenderer::GetRenderingAPI() + "/" + info.m_shaderFileName; m_shaders.EmplaceBack(m_device, fullPath, log_VulkanRenderSystem); m_shaders.Last().SetStage(info.m_stage); } } // namespace Vulkan } // namespace Azura