Program Listing for File VkRenderer.cpp¶
↰ Return to documentation for file (Source\Azura\RenderSystem\Src\Vulkan\VkRenderer.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 <fstream>
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<const char*> 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<Vector<VkCommandBuffer>> secondaryCmdBuffers(m_renderPasses.GetSize(), allocatorTemporary);
for (U32 idx = 0; idx < m_renderPasses.GetSize(); ++idx) {
Vector<VkCommandBuffer> poolCmdBuffers(m_drawablePools.GetSize(), allocatorTemporary);
secondaryCmdBuffers.PushBack(poolCmdBuffers);
}
for (auto& drawablePool : m_drawablePools) {
drawablePool.Submit();
}
for (auto& drawablePool : m_drawablePools) {
Vector<std::pair<U32, VkCommandBuffer>> 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<int> 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<VkDescriptorSetLayoutBinding> 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<VkDescriptorPoolSize> 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<uint64_t>::max());
vkResetFences(m_device, 1, &m_inFlightFences[currentFrame]);
U32 imageIndex;
const VkResult result = vkAcquireNextImageKHR(m_device, m_swapChain.Real(), std::numeric_limits<uint64_t>::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<VkSemaphore, 1> initialWaitSemaphores = {};
// std::array<VkSemaphore, 1> finalSignalSemaphores = {m_renderFinishedSemaphores[currentFrame]};
// std::array<VkPipelineStageFlags, 1> 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<VkSemaphore> waitSemaphores(2, m_perFrameAllocator);
Vector<VkPipelineStageFlags> waitStages(2, m_perFrameAllocator);
Vector<VkSemaphore> 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<VkSemaphore> 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<VkSwapchainKHR, 1> 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<char> 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