#include "common/assert.h" #include "common/common.h" #include "common/emulatorConfig.h" #include "common/logging/log.h" #include "common/profiler.h" #include "common/threads.h" #include "graphics/host_gpu/graphicContext.h" #include "graphics/host_gpu/renderer/colorRenderTarget.h" #include "graphics/host_gpu/renderer/debug.h" #include "graphics/host_gpu/renderer/depthRenderTarget.h" #include "graphics/host_gpu/renderer/descriptorCache.h" #include "graphics/host_gpu/renderer/framebufferCache.h" #include "graphics/host_gpu/renderer/render.h" #include "graphics/host_gpu/renderer/renderContext.h" #include "graphics/host_gpu/utils.h" #include "graphics/host_gpu/vma.h" #include "graphics/presentation/window.h" #include #include #include #include #include namespace Libs::Graphics { static std::atomic g_command_buffer_submit_seq = 0; class CommandPool { public: CommandPool() = default; ~CommandPool() // NOLINT { // TODO(): check if destructor is called from std::_Exit() // DeleteAll(); } KYTY_CLASS_NO_COPY(CommandPool); VulkanCommandPool* GetPool(int id) { if (m_pool[id] == nullptr) { Create(id); } return m_pool[id]; } private: void Create(int id); void DeleteAll(); VulkanCommandPool* m_pool[GraphicContext::QUEUES_NUM] = {}; }; RenderContext* g_render_ctx = nullptr; static thread_local CommandPool g_command_pool; FenceResourceRetainer::~FenceResourceRetainer() { if (!m_resources.empty()) { EXIT("fence resource retainer destroyed before release\n"); } } void FenceResourceRetainer::Retain(std::shared_ptr resource) { if (resource == nullptr) { EXIT("cannot retain a null fence resource\n"); } if (std::ranges::none_of(m_resources, [&resource](const auto& retained) { return retained.get() == resource.get(); })) { m_resources.push_back(std::move(resource)); } } void FenceResourceRetainer::ReleaseAfterFence() noexcept { m_resources.clear(); } void GraphicsRenderInit() { EXIT_IF(g_render_ctx != nullptr); g_render_ctx = new RenderContext; } void GraphicsRenderCreateContext() { EXIT_IF(g_render_ctx == nullptr); g_render_ctx->SetGraphicCtx(WindowGetGraphicContext()); } void CommandPool::Create(int id) { auto* ctx = g_render_ctx->GetGraphicCtx(); EXIT_IF(id < 0 || id >= GraphicContext::QUEUES_NUM); auto*& pool = m_pool[id]; EXIT_IF(pool != nullptr); EXIT_IF(ctx == nullptr); EXIT_IF(ctx->device == nullptr); EXIT_IF(ctx->queues[id].family == static_cast(-1)); pool = new VulkanCommandPool; VkCommandPoolCreateInfo pool_info {}; pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; pool_info.pNext = nullptr; pool_info.queueFamilyIndex = ctx->queues[id].family; pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; vkCreateCommandPool(ctx->device, &pool_info, nullptr, &pool->pool); EXIT_NOT_IMPLEMENTED(pool->pool == nullptr); pool->buffers_count = 8; pool->buffers = std::make_unique(pool->buffers_count); pool->fences = std::make_unique(pool->buffers_count); pool->semaphores = std::make_unique(pool->buffers_count); pool->busy = std::make_unique(pool->buffers_count); VkCommandBufferAllocateInfo alloc_info {}; alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; alloc_info.commandPool = pool->pool; alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; alloc_info.commandBufferCount = pool->buffers_count; if (vkAllocateCommandBuffers(ctx->device, &alloc_info, pool->buffers.get()) != VK_SUCCESS) { EXIT("Can't allocate command buffers"); } for (uint32_t i = 0; i < pool->buffers_count; i++) { pool->busy[i] = false; VkFenceCreateInfo fence_info {}; fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fence_info.pNext = nullptr; fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT; if (vkCreateFence(ctx->device, &fence_info, nullptr, &pool->fences[i]) != VK_SUCCESS) { EXIT("Can't create fence"); } VkSemaphoreCreateInfo semaphore_info {}; semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; semaphore_info.pNext = nullptr; semaphore_info.flags = 0; if (vkCreateSemaphore(ctx->device, &semaphore_info, nullptr, &pool->semaphores[i]) != VK_SUCCESS) { EXIT("Can't create semaphore"); } EXIT_IF(pool->buffers[i] == nullptr); EXIT_IF(pool->fences[i] == nullptr); EXIT_IF(pool->semaphores[i] == nullptr); } } void CommandPool::DeleteAll() { auto* ctx = g_render_ctx->GetGraphicCtx(); for (auto& pool: m_pool) { if (pool != nullptr) { EXIT_IF(ctx == nullptr); EXIT_IF(ctx->device == nullptr); for (uint32_t i = 0; i < pool->buffers_count; i++) { vkDestroySemaphore(ctx->device, pool->semaphores[i], nullptr); vkDestroyFence(ctx->device, pool->fences[i], nullptr); } vkFreeCommandBuffers(ctx->device, pool->pool, pool->buffers_count, pool->buffers.get()); vkDestroyCommandPool(ctx->device, pool->pool, nullptr); delete pool; pool = nullptr; } } } bool CommandBuffer::IsInvalid() const { EXIT_IF(g_render_ctx == nullptr); if (m_pool != nullptr) { Common::LockGuard lock(m_pool->mutex); return (m_index == static_cast(-1) || m_index >= m_pool->buffers_count); } return true; } void CommandBuffer::Allocate() { EXIT_IF(!IsInvalid()); m_pool = g_command_pool.GetPool(m_queue); Common::LockGuard lock(m_pool->mutex); for (uint32_t i = 0; i < m_pool->buffers_count; i++) { if (!m_pool->busy[i]) { m_pool->busy[i] = true; vkResetCommandBuffer(m_pool->buffers[i], VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT); m_index = i; break; } } EXIT_NOT_IMPLEMENTED(IsInvalid()); } void CommandBuffer::Free() { EXIT_IF(IsInvalid()); Common::LockGuard lock(m_pool->mutex); WaitForFence(); m_host_stream.Release(); m_pool->busy[m_index] = false; vkResetCommandBuffer(m_pool->buffers[m_index], VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT); RecycleDescriptorsAfterFence(); m_fence_resources.ReleaseAfterFence(); m_index = static_cast(-1); EXIT_NOT_IMPLEMENTED(!IsInvalid()); } void CommandBuffer::DeleteAfterFence(VulkanBuffer* buffer) { EXIT_IF(buffer == nullptr); m_delete_after_fence.push_back(buffer); } void CommandBuffer::RetainResourceUntilFence(std::shared_ptr resource) { if (IsInvalid() || m_execute) { EXIT("cannot retain a resource on an invalid or submitted command buffer\n"); } m_fence_resources.Retain(std::move(resource)); } void CommandBuffer::RecycleDescriptorAfterFence(VulkanDescriptorSet* set) { EXIT_IF(set == nullptr); m_descriptor_sets_after_fence.push_back(set); } void CommandBuffer::RecycleDescriptorsAfterFence() { for (auto* set: m_descriptor_sets_after_fence) { g_render_ctx->GetDescriptorCache()->Recycle(set); } m_descriptor_sets_after_fence.clear(); } void CommandBuffer::Begin() const { EXIT_IF(IsInvalid()); auto* buffer = m_pool->buffers[m_index]; VkCommandBufferBeginInfo begin_info {}; begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; begin_info.pNext = nullptr; begin_info.flags = 0; begin_info.pInheritanceInfo = nullptr; auto result = vkBeginCommandBuffer(buffer, &begin_info); EXIT_NOT_IMPLEMENTED(result != VK_SUCCESS); } void CommandBuffer::End() const { EXIT_IF(IsInvalid()); auto* buffer = m_pool->buffers[m_index]; auto result = vkEndCommandBuffer(buffer); EXIT_NOT_IMPLEMENTED(result != VK_SUCCESS); } void CommandBuffer::SetDebugInfo(uint32_t op, uint64_t submit_id, uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t arg3, uint64_t arg4) { m_debug_op = op; m_debug_submit_id = submit_id; m_debug_arg0 = arg0; m_debug_arg1 = arg1; m_debug_arg2 = arg2; m_debug_arg3 = arg3; m_debug_arg4 = arg4; } void CommandBuffer::Execute() { EXIT_IF(IsInvalid()); EXIT_IF(m_execute); auto* buffer = m_pool->buffers[m_index]; auto* fence = m_pool->fences[m_index]; VkSubmitInfo submit_info {}; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info.pNext = nullptr; submit_info.waitSemaphoreCount = 0; submit_info.pWaitSemaphores = nullptr; submit_info.pWaitDstStageMask = nullptr; submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &buffer; submit_info.signalSemaphoreCount = 0; submit_info.pSignalSemaphores = nullptr; EXIT_IF(m_queue < 0 || m_queue >= GraphicContext::QUEUES_NUM); const auto& queue = g_render_ctx->GetGraphicCtx()->queues[m_queue]; auto result = vkResetFences(g_render_ctx->GetGraphicCtx()->device, 1, &fence); if (result != VK_SUCCESS) { LOGF("vkResetFences failed before submit: %s (%d)\n", string_VkResult(result), static_cast(result)); } EXIT_NOT_IMPLEMENTED(result != VK_SUCCESS); if (queue.mutex != nullptr) { queue.mutex->Lock(); } if (Config::GraphicsDebugDumpEnabled()) { LOGF("vkQueueSubmit begin: queue=%d index=%u debug_op=%u debug_submit=%" PRIu64 " args=%u,%u,%u,%u,0x%016" PRIx64 "\n", m_queue, m_index, m_debug_op, m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2, m_debug_arg3, m_debug_arg4); } result = vkQueueSubmit(queue.vk_queue, 1, &submit_info, fence); if (queue.mutex != nullptr) { queue.mutex->Unlock(); } m_execute = true; m_fence_waited = false; m_submit_seq = g_command_buffer_submit_seq.fetch_add(1, std::memory_order_relaxed) + 1; if (result != VK_SUCCESS) { LOGF("vkQueueSubmit failed: %s (%d), queue=%d index=%u submit_seq=%" PRIu64 " debug_op=%u debug_submit=%" PRIu64 " args=%u,%u,%u,%u,0x%016" PRIx64 "\n", string_VkResult(result), static_cast(result), m_queue, m_index, m_submit_seq, m_debug_op, m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2, m_debug_arg3, m_debug_arg4); } EXIT_NOT_IMPLEMENTED(result != VK_SUCCESS); } void CommandBuffer::ExecuteWithSemaphore(VkSemaphore signal_semaphore) { EXIT_IF(IsInvalid()); EXIT_IF(m_execute); auto* buffer = m_pool->buffers[m_index]; auto* fence = m_pool->fences[m_index]; if (signal_semaphore == nullptr) { signal_semaphore = m_pool->semaphores[m_index]; } VkSubmitInfo submit_info {}; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info.pNext = nullptr; submit_info.waitSemaphoreCount = 0; submit_info.pWaitSemaphores = nullptr; submit_info.pWaitDstStageMask = nullptr; submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &buffer; submit_info.signalSemaphoreCount = 1; submit_info.pSignalSemaphores = &signal_semaphore; EXIT_IF(m_queue < 0 || m_queue >= GraphicContext::QUEUES_NUM); const auto& queue = g_render_ctx->GetGraphicCtx()->queues[m_queue]; auto result = vkResetFences(g_render_ctx->GetGraphicCtx()->device, 1, &fence); if (result != VK_SUCCESS) { LOGF("vkResetFences failed before submit: %s (%d)\n", string_VkResult(result), static_cast(result)); } EXIT_NOT_IMPLEMENTED(result != VK_SUCCESS); if (queue.mutex != nullptr) { queue.mutex->Lock(); } if (Config::GraphicsDebugDumpEnabled()) { LOGF("vkQueueSubmit begin: queue=%d index=%u signal_semaphore=%p debug_op=%u" " debug_submit=%" PRIu64 " args=%u,%u,%u,%u,0x%016" PRIx64 "\n", m_queue, m_index, static_cast(signal_semaphore), m_debug_op, m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2, m_debug_arg3, m_debug_arg4); } result = vkQueueSubmit(queue.vk_queue, 1, &submit_info, fence); if (queue.mutex != nullptr) { queue.mutex->Unlock(); } m_execute = true; m_fence_waited = false; m_submit_seq = g_command_buffer_submit_seq.fetch_add(1, std::memory_order_relaxed) + 1; if (result != VK_SUCCESS) { LOGF("vkQueueSubmit failed: %s (%d), queue=%d index=%u submit_seq=%" PRIu64 " debug_op=%u debug_submit=%" PRIu64 " args=%u,%u,%u,%u,0x%016" PRIx64 "\n", string_VkResult(result), static_cast(result), m_queue, m_index, m_submit_seq, m_debug_op, m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2, m_debug_arg3, m_debug_arg4); } EXIT_NOT_IMPLEMENTED(result != VK_SUCCESS); } void CommandBuffer::ExecuteWithSemaphore(VkSemaphore wait_semaphore, VkPipelineStageFlags wait_stage, VkSemaphore signal_semaphore) { EXIT_IF(IsInvalid()); EXIT_IF(m_execute); EXIT_IF(wait_semaphore == nullptr); auto* buffer = m_pool->buffers[m_index]; auto* fence = m_pool->fences[m_index]; if (signal_semaphore == nullptr) { signal_semaphore = m_pool->semaphores[m_index]; } VkSubmitInfo submit_info {}; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info.pNext = nullptr; submit_info.waitSemaphoreCount = 1; submit_info.pWaitSemaphores = &wait_semaphore; submit_info.pWaitDstStageMask = &wait_stage; submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &buffer; submit_info.signalSemaphoreCount = 1; submit_info.pSignalSemaphores = &signal_semaphore; EXIT_IF(m_queue < 0 || m_queue >= GraphicContext::QUEUES_NUM); const auto& queue = g_render_ctx->GetGraphicCtx()->queues[m_queue]; auto result = vkResetFences(g_render_ctx->GetGraphicCtx()->device, 1, &fence); if (result != VK_SUCCESS) { LOGF("vkResetFences failed before submit: %s (%d)\n", string_VkResult(result), static_cast(result)); } EXIT_NOT_IMPLEMENTED(result != VK_SUCCESS); if (queue.mutex != nullptr) { queue.mutex->Lock(); } if (Config::GraphicsDebugDumpEnabled()) { LOGF("vkQueueSubmit begin: queue=%d index=%u wait_semaphore=%p signal_semaphore=%p" " debug_op=%u debug_submit=%" PRIu64 " args=%u,%u,%u,%u,0x%016" PRIx64 "\n", m_queue, m_index, static_cast(wait_semaphore), static_cast(signal_semaphore), m_debug_op, m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2, m_debug_arg3, m_debug_arg4); } result = vkQueueSubmit(queue.vk_queue, 1, &submit_info, fence); if (queue.mutex != nullptr) { queue.mutex->Unlock(); } m_execute = true; m_fence_waited = false; m_submit_seq = g_command_buffer_submit_seq.fetch_add(1, std::memory_order_relaxed) + 1; if (result != VK_SUCCESS) { LOGF("vkQueueSubmit failed: %s (%d), queue=%d index=%u submit_seq=%" PRIu64 " debug_op=%u debug_submit=%" PRIu64 " args=%u,%u,%u,%u,0x%016" PRIx64 "\n", string_VkResult(result), static_cast(result), m_queue, m_index, m_submit_seq, m_debug_op, m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2, m_debug_arg3, m_debug_arg4); } EXIT_NOT_IMPLEMENTED(result != VK_SUCCESS); } void CommandBuffer::WaitForFence() { const bool was_executed = m_execute; WaitForFenceOnly(); if (was_executed) { m_execute = false; m_fence_waited = false; RecycleDescriptorsAfterFence(); m_fence_resources.ReleaseAfterFence(); } for (auto* buffer: m_delete_after_fence) { VulkanDeleteBuffer(g_render_ctx->GetGraphicCtx(), buffer); delete buffer; } m_delete_after_fence.clear(); } void CommandBuffer::WaitForFenceOnly() { EXIT_IF(IsInvalid()); if (!m_execute || m_fence_waited) { return; } auto* device = g_render_ctx->GetGraphicCtx()->device; auto result = vkWaitForFences(device, 1, &m_pool->fences[m_index], VK_TRUE, UINT64_MAX); if (result != VK_SUCCESS) { LOGF("vkWaitForFences failed: %s (%d), queue=%d index=%u submit_seq=%" PRIu64 " debug_op=%u debug_submit=%" PRIu64 " args=%u,%u,%u,%u,0x%016" PRIx64 "\n", string_VkResult(result), static_cast(result), m_queue, m_index, m_submit_seq, m_debug_op, m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2, m_debug_arg3, m_debug_arg4); } EXIT_NOT_IMPLEMENTED(result != VK_SUCCESS); m_fence_waited = true; } void CommandBuffer::WaitForFenceAndReset() { const bool was_executed = m_execute; WaitForFenceOnly(); if (was_executed) { m_execute = false; m_fence_waited = false; vkResetCommandBuffer(m_pool->buffers[m_index], VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT); m_recording_generation++; } m_host_stream.Reset(); if (was_executed) { RecycleDescriptorsAfterFence(); m_fence_resources.ReleaseAfterFence(); } for (auto* buffer: m_delete_after_fence) { VulkanDeleteBuffer(g_render_ctx->GetGraphicCtx(), buffer); delete buffer; } m_delete_after_fence.clear(); } void CommandBuffer::BeginRenderPass(VulkanFramebuffer* framebuffer, RenderColorInfo* colors, uint32_t requested_color_count, RenderDepthInfo* depth) const { EXIT_IF(IsInvalid()); auto* buffer = m_pool->buffers[m_index]; EXIT_IF(framebuffer == nullptr); EXIT_IF(colors == nullptr); EXIT_IF(requested_color_count > RENDER_COLOR_ATTACHMENTS_MAX); bool with_depth = (depth->format != VK_FORMAT_UNDEFINED && depth->vulkan_buffer != nullptr); uint32_t color_count = 0; for (uint32_t i = 0; i < requested_color_count; i++) { if (colors[i].vulkan_buffer == nullptr) { break; } color_count++; } bool with_color = (color_count != 0); EXIT_NOT_IMPLEMENTED(!with_depth && !with_color); VkClearValue clears[RENDER_COLOR_ATTACHMENTS_MAX + 1] = {}; for (uint32_t i = 0; i < color_count; i++) { clears[i].color = colors[i].color_clear_value; } clears[color_count].depthStencil = {depth->depth_clear_value, depth->stencil_clear_value}; VkExtent2D extent = (with_color ? colors[0].extent : depth->vulkan_buffer->extent); VkRenderPassBeginInfo render_pass_info {}; render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; render_pass_info.pNext = nullptr; render_pass_info.renderPass = framebuffer->render_pass; render_pass_info.framebuffer = framebuffer->framebuffer; render_pass_info.renderArea.offset = {0, 0}; render_pass_info.renderArea.extent = extent; render_pass_info.clearValueCount = color_count + (with_depth ? 1u : 0u); render_pass_info.pClearValues = clears; for (uint32_t i = 0; i < color_count; i++) { const auto color_initial_layout = framebuffer->color_layout[i]; if (colors[i].vulkan_buffer->layout != color_initial_layout) { if (graphics_debug_dump_enabled()) { LOGF("BeginRenderPass: color%u initial barrier image=%p mem=%" PRIu64 " %s -> %s\n", i, reinterpret_cast(colors[i].vulkan_buffer->image), colors[i].vulkan_buffer->memory.unique_id, string_VkImageLayout(colors[i].vulkan_buffer->layout), string_VkImageLayout(color_initial_layout)); } VkImageMemoryBarrier image_memory_barrier {}; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.pNext = nullptr; image_memory_barrier.srcAccessMask = 0; image_memory_barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; image_memory_barrier.oldLayout = colors[i].vulkan_buffer->layout; image_memory_barrier.newLayout = color_initial_layout; image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; image_memory_barrier.image = colors[i].vulkan_buffer->image; image_memory_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; image_memory_barrier.subresourceRange.baseMipLevel = 0; image_memory_barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; image_memory_barrier.subresourceRange.baseArrayLayer = 0; image_memory_barrier.subresourceRange.layerCount = colors[i].vulkan_buffer->layers; vkCmdPipelineBarrier(buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); colors[i].vulkan_buffer->layout = image_memory_barrier.newLayout; } else if (graphics_debug_dump_enabled()) { LOGF("BeginRenderPass: color%u initial image=%p mem=%" PRIu64 " layout=%s\n", i, reinterpret_cast(colors[i].vulkan_buffer->image), colors[i].vulkan_buffer->memory.unique_id, string_VkImageLayout(colors[i].vulkan_buffer->layout)); } } const auto depth_layout = (with_depth && framebuffer != nullptr ? framebuffer->depth_layout : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); if (with_depth && depth->vulkan_buffer->layout != depth_layout) { VkImageMemoryBarrier image_memory_barrier {}; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.pNext = nullptr; image_memory_barrier.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT; image_memory_barrier.dstAccessMask = (depth_layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL ? VK_ACCESS_MEMORY_READ_BIT : VK_ACCESS_MEMORY_WRITE_BIT); image_memory_barrier.oldLayout = depth->vulkan_buffer->layout; image_memory_barrier.newLayout = depth_layout; image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; image_memory_barrier.image = depth->vulkan_buffer->image; image_memory_barrier.subresourceRange.aspectMask = DepthStencilAspectMask(depth->vulkan_buffer->format); image_memory_barrier.subresourceRange.baseMipLevel = 0; image_memory_barrier.subresourceRange.levelCount = 1; image_memory_barrier.subresourceRange.baseArrayLayer = 0; image_memory_barrier.subresourceRange.layerCount = depth->vulkan_buffer->layers; vkCmdPipelineBarrier( buffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); depth->vulkan_buffer->layout = image_memory_barrier.newLayout; } vkCmdBeginRenderPass(buffer, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE); for (uint32_t i = 0; i < color_count; i++) { colors[i].vulkan_buffer->layout = RENDER_COLOR_IMAGE_LAYOUT; } } void CommandBuffer::EndRenderPass() const { EXIT_IF(IsInvalid()); auto* buffer = m_pool->buffers[m_index]; vkCmdEndRenderPass(buffer); } } // namespace Libs::Graphics