diff options
Diffstat (limited to 'Source/Core/VideoBackends/Vulkan/VKTexture.cpp')
| -rw-r--r-- | Source/Core/VideoBackends/Vulkan/VKTexture.cpp | 812 |
1 files changed, 548 insertions, 264 deletions
diff --git a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp index 929307edb3..a19e169412 100644 --- a/Source/Core/VideoBackends/Vulkan/VKTexture.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKTexture.cpp @@ -13,23 +13,35 @@ #include "Common/MsgHandler.h" #include "VideoBackends/Vulkan/CommandBufferManager.h" -#include "VideoBackends/Vulkan/FramebufferManager.h" +#include "VideoBackends/Vulkan/ObjectCache.h" +#include "VideoBackends/Vulkan/Renderer.h" #include "VideoBackends/Vulkan/StagingBuffer.h" #include "VideoBackends/Vulkan/StateTracker.h" -#include "VideoBackends/Vulkan/Texture2D.h" -#include "VideoBackends/Vulkan/Util.h" +#include "VideoBackends/Vulkan/StreamBuffer.h" #include "VideoBackends/Vulkan/VKTexture.h" #include "VideoBackends/Vulkan/VulkanContext.h" -#include "VideoCommon/ImageWrite.h" -#include "VideoCommon/TextureConfig.h" - namespace Vulkan { -VKTexture::VKTexture(const TextureConfig& tex_config, std::unique_ptr<Texture2D> texture, - VkFramebuffer framebuffer) - : AbstractTexture(tex_config), m_texture(std::move(texture)), m_framebuffer(framebuffer) +VKTexture::VKTexture(const TextureConfig& tex_config, VkDeviceMemory device_memory, VkImage image, + VkImageLayout layout /* = VK_IMAGE_LAYOUT_UNDEFINED */, + ComputeImageLayout compute_layout /* = ComputeImageLayout::Undefined */) + : AbstractTexture(tex_config), m_device_memory(device_memory), m_image(image), m_layout(layout), + m_compute_layout(compute_layout) +{ +} + +VKTexture::~VKTexture() { + StateTracker::GetInstance()->UnbindTexture(m_view); + g_command_buffer_mgr->DeferImageViewDestruction(m_view); + + // If we don't have device memory allocated, the image is not owned by us (e.g. swapchain) + if (m_device_memory != VK_NULL_HANDLE) + { + g_command_buffer_mgr->DeferImageDestruction(m_image); + g_command_buffer_mgr->DeferDeviceMemoryDestruction(m_device_memory); + } } std::unique_ptr<VKTexture> VKTexture::Create(const TextureConfig& tex_config) @@ -37,91 +49,192 @@ std::unique_ptr<VKTexture> VKTexture::Create(const TextureConfig& tex_config) // Determine image usage, we need to flag as an attachment if it can be used as a rendertarget. VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; - if (tex_config.rendertarget) - usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + if (tex_config.IsRenderTarget()) + { + usage |= IsDepthFormat(tex_config.format) ? VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT : + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + } + if (tex_config.IsComputeImage()) + usage |= VK_IMAGE_USAGE_STORAGE_BIT; + + VkImageCreateInfo image_info = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + nullptr, + 0, + VK_IMAGE_TYPE_2D, + GetVkFormatForHostTextureFormat(tex_config.format), + {tex_config.width, tex_config.height, 1}, + tex_config.levels, + tex_config.layers, + static_cast<VkSampleCountFlagBits>(tex_config.samples), + VK_IMAGE_TILING_OPTIMAL, + usage, + VK_SHARING_MODE_EXCLUSIVE, + 0, + nullptr, + VK_IMAGE_LAYOUT_UNDEFINED}; + + VkImage image = VK_NULL_HANDLE; + VkResult res = vkCreateImage(g_vulkan_context->GetDevice(), &image_info, nullptr, &image); + if (res != VK_SUCCESS) + { + LOG_VULKAN_ERROR(res, "vkCreateImage failed: "); + return nullptr; + } + + // Allocate memory to back this texture, we want device local memory in this case + VkMemoryRequirements memory_requirements; + vkGetImageMemoryRequirements(g_vulkan_context->GetDevice(), image, &memory_requirements); - // Allocate texture object - VkFormat vk_format = Util::GetVkFormatForHostTextureFormat(tex_config.format); - auto texture = - Texture2D::Create(tex_config.width, tex_config.height, tex_config.levels, tex_config.layers, - vk_format, static_cast<VkSampleCountFlagBits>(tex_config.samples), - VK_IMAGE_VIEW_TYPE_2D_ARRAY, VK_IMAGE_TILING_OPTIMAL, usage); + VkMemoryAllocateInfo memory_info = { + VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, nullptr, memory_requirements.size, + g_vulkan_context->GetMemoryType(memory_requirements.memoryTypeBits, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)}; - if (!texture) + VkDeviceMemory device_memory; + res = vkAllocateMemory(g_vulkan_context->GetDevice(), &memory_info, nullptr, &device_memory); + if (res != VK_SUCCESS) { + LOG_VULKAN_ERROR(res, "vkAllocateMemory failed: "); + vkDestroyImage(g_vulkan_context->GetDevice(), image, nullptr); return nullptr; } - // If this is a render target (for efb copies), allocate a framebuffer - VkFramebuffer framebuffer = VK_NULL_HANDLE; - if (tex_config.rendertarget) + res = vkBindImageMemory(g_vulkan_context->GetDevice(), image, device_memory, 0); + if (res != VK_SUCCESS) { - VkImageView framebuffer_attachments[] = {texture->GetView()}; - VkRenderPass render_pass = - g_object_cache->GetRenderPass(texture->GetFormat(), VK_FORMAT_UNDEFINED, tex_config.samples, - VK_ATTACHMENT_LOAD_OP_DONT_CARE); - VkFramebufferCreateInfo framebuffer_info = { - VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, - nullptr, - 0, - render_pass, - static_cast<u32>(ArraySize(framebuffer_attachments)), - framebuffer_attachments, - texture->GetWidth(), - texture->GetHeight(), - texture->GetLayers()}; - - VkResult res = vkCreateFramebuffer(g_vulkan_context->GetDevice(), &framebuffer_info, nullptr, - &framebuffer); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkCreateFramebuffer failed: "); - return nullptr; - } + LOG_VULKAN_ERROR(res, "vkBindImageMemory failed: "); + vkDestroyImage(g_vulkan_context->GetDevice(), image, nullptr); + vkFreeMemory(g_vulkan_context->GetDevice(), device_memory, nullptr); + return nullptr; + } - if (!IsDepthFormat(tex_config.format)) - { - // Clear render targets before use to prevent reading uninitialized memory. - VkClearColorValue clear_value = {{0.0f, 0.0f, 0.0f, 1.0f}}; - VkImageSubresourceRange clear_range = {VK_IMAGE_ASPECT_COLOR_BIT, 0, tex_config.levels, 0, - tex_config.layers}; - texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - vkCmdClearColorImage(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), texture->GetImage(), - texture->GetLayout(), &clear_value, 1, &clear_range); - } - else - { - // Clear render targets before use to prevent reading uninitialized memory. - VkClearDepthStencilValue clear_value = {0.0f, 0}; - VkImageSubresourceRange clear_range = {Util::GetImageAspectForFormat(vk_format), 0, - tex_config.levels, 0, tex_config.layers}; - texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - vkCmdClearDepthStencilImage(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), - texture->GetImage(), texture->GetLayout(), &clear_value, 1, - &clear_range); - } + std::unique_ptr<VKTexture> texture = std::make_unique<VKTexture>( + tex_config, device_memory, image, VK_IMAGE_LAYOUT_UNDEFINED, ComputeImageLayout::Undefined); + if (!texture->CreateView(VK_IMAGE_VIEW_TYPE_2D_ARRAY)) + return nullptr; + + return texture; +} + +std::unique_ptr<VKTexture> VKTexture::CreateAdopted(const TextureConfig& tex_config, VkImage image, + VkImageViewType view_type, VkImageLayout layout) +{ + std::unique_ptr<VKTexture> texture = std::make_unique<VKTexture>( + tex_config, nullptr, image, layout, ComputeImageLayout::Undefined); + if (!texture->CreateView(VK_IMAGE_VIEW_TYPE_2D_ARRAY)) + return nullptr; + + return texture; +} + +bool VKTexture::CreateView(VkImageViewType type) +{ + VkImageViewCreateInfo view_info = { + VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + nullptr, + 0, + m_image, + type, + GetVkFormat(), + {VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY}, + {GetImageAspectForFormat(GetFormat()), 0, GetLevels(), 0, GetLayers()}}; + + VkResult res = vkCreateImageView(g_vulkan_context->GetDevice(), &view_info, nullptr, &m_view); + if (res != VK_SUCCESS) + { + LOG_VULKAN_ERROR(res, "vkCreateImageView failed: "); + return false; } - return std::unique_ptr<VKTexture>(new VKTexture(tex_config, std::move(texture), framebuffer)); + return true; } -VKTexture::~VKTexture() +VkFormat VKTexture::GetLinearFormat(VkFormat format) { - // Texture is automatically cleaned up, however, we don't want to leave it bound. - g_renderer->UnbindTexture(this); - if (m_framebuffer != VK_NULL_HANDLE) - g_command_buffer_mgr->DeferFramebufferDestruction(m_framebuffer); + switch (format) + { + case VK_FORMAT_R8_SRGB: + return VK_FORMAT_R8_UNORM; + case VK_FORMAT_R8G8_SRGB: + return VK_FORMAT_R8G8_UNORM; + case VK_FORMAT_R8G8B8_SRGB: + return VK_FORMAT_R8G8B8_UNORM; + case VK_FORMAT_R8G8B8A8_SRGB: + return VK_FORMAT_R8G8B8A8_UNORM; + case VK_FORMAT_B8G8R8_SRGB: + return VK_FORMAT_B8G8R8_UNORM; + case VK_FORMAT_B8G8R8A8_SRGB: + return VK_FORMAT_B8G8R8A8_UNORM; + default: + return format; + } } -Texture2D* VKTexture::GetRawTexIdentifier() const +VkFormat VKTexture::GetVkFormatForHostTextureFormat(AbstractTextureFormat format) { - return m_texture.get(); + switch (format) + { + case AbstractTextureFormat::DXT1: + return VK_FORMAT_BC1_RGBA_UNORM_BLOCK; + + case AbstractTextureFormat::DXT3: + return VK_FORMAT_BC2_UNORM_BLOCK; + + case AbstractTextureFormat::DXT5: + return VK_FORMAT_BC3_UNORM_BLOCK; + + case AbstractTextureFormat::BPTC: + return VK_FORMAT_BC7_UNORM_BLOCK; + + case AbstractTextureFormat::RGBA8: + return VK_FORMAT_R8G8B8A8_UNORM; + + case AbstractTextureFormat::BGRA8: + return VK_FORMAT_B8G8R8A8_UNORM; + + case AbstractTextureFormat::R16: + return VK_FORMAT_R16_UNORM; + + case AbstractTextureFormat::D16: + return VK_FORMAT_D16_UNORM; + + case AbstractTextureFormat::D24_S8: + return VK_FORMAT_D24_UNORM_S8_UINT; + + case AbstractTextureFormat::R32F: + return VK_FORMAT_R32_SFLOAT; + + case AbstractTextureFormat::D32F: + return VK_FORMAT_D32_SFLOAT; + + case AbstractTextureFormat::D32F_S8: + return VK_FORMAT_D32_SFLOAT_S8_UINT; + + case AbstractTextureFormat::Undefined: + return VK_FORMAT_UNDEFINED; + + default: + PanicAlert("Unhandled texture format."); + return VK_FORMAT_R8G8B8A8_UNORM; + } } -VkFramebuffer VKTexture::GetFramebuffer() const + +VkImageAspectFlags VKTexture::GetImageAspectForFormat(AbstractTextureFormat format) { - return m_framebuffer; + switch (format) + { + case AbstractTextureFormat::D24_S8: + case AbstractTextureFormat::D32F_S8: + return VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + + case AbstractTextureFormat::D16: + case AbstractTextureFormat::D32F: + return VK_IMAGE_ASPECT_DEPTH_BIT; + + default: + return VK_IMAGE_ASPECT_COLOR_BIT; + } } void VKTexture::CopyRectangleFromTexture(const AbstractTexture* src, @@ -129,7 +242,7 @@ void VKTexture::CopyRectangleFromTexture(const AbstractTexture* src, u32 src_level, const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer, u32 dst_level) { - Texture2D* src_texture = static_cast<const VKTexture*>(src)->GetRawTexIdentifier(); + const VKTexture* src_texture = static_cast<const VKTexture*>(src); ASSERT_MSG(VIDEO, static_cast<u32>(src_rect.GetWidth()) <= src_texture->GetWidth() && @@ -151,67 +264,18 @@ void VKTexture::CopyRectangleFromTexture(const AbstractTexture* src, // Must be called outside of a render pass. StateTracker::GetInstance()->EndRenderPass(); + const VkImageLayout old_src_layout = src_texture->GetLayout(); src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - vkCmdCopyImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), src_texture->GetImage(), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_texture->GetImage(), + vkCmdCopyImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), src_texture->m_image, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy); - // Ensure both textures remain in the SHADER_READ_ONLY layout so they can be bound. - src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); -} - -void VKTexture::ScaleRectangleFromTexture(const AbstractTexture* source, - const MathUtil::Rectangle<int>& src_rect, - const MathUtil::Rectangle<int>& dst_rect) -{ - Texture2D* src_texture = static_cast<const VKTexture*>(source)->GetRawTexIdentifier(); - - // Can't do this within a game render pass. - StateTracker::GetInstance()->EndRenderPass(); - StateTracker::GetInstance()->SetPendingRebind(); - - // Can't render to a non-rendertarget (no framebuffer). - ASSERT_MSG(VIDEO, m_config.rendertarget, - "Destination texture for partial copy is not a rendertarget"); - - // Render pass expects dst_texture to be in COLOR_ATTACHMENT_OPTIMAL state. - // src_texture should already be in SHADER_READ_ONLY state, but transition in case (XFB). - src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - - VkRenderPass render_pass = g_object_cache->GetRenderPass( - m_texture->GetFormat(), VK_FORMAT_UNDEFINED, 1, VK_ATTACHMENT_LOAD_OP_DONT_CARE); - UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(), - g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD), render_pass, - g_shader_cache->GetPassthroughVertexShader(), - g_shader_cache->GetPassthroughGeometryShader(), - TextureCache::GetInstance()->GetCopyShader()); - - VkRect2D region = { - {dst_rect.left, dst_rect.top}, - {static_cast<u32>(dst_rect.GetWidth()), static_cast<u32>(dst_rect.GetHeight())}}; - draw.BeginRenderPass(m_framebuffer, region); - draw.SetPSSampler(0, src_texture->GetView(), g_object_cache->GetLinearSampler()); - draw.DrawQuad(dst_rect.left, dst_rect.top, dst_rect.GetWidth(), dst_rect.GetHeight(), - src_rect.left, src_rect.top, 0, src_rect.GetWidth(), src_rect.GetHeight(), - static_cast<int>(src_texture->GetWidth()), - static_cast<int>(src_texture->GetHeight())); - draw.EndRenderPass(); - - // Ensure both textures remain in the SHADER_READ_ONLY layout so they can be bound. - src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + // Only restore the source layout. Destination is restored by FinishedRendering(). + src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), old_src_layout); } void VKTexture::ResolveFromTexture(const AbstractTexture* src, const MathUtil::Rectangle<int>& rect, @@ -225,11 +289,11 @@ void VKTexture::ResolveFromTexture(const AbstractTexture* src, const MathUtil::R // Resolving is considered to be a transfer operation. StateTracker::GetInstance()->EndRenderPass(); - VkImageLayout old_src_layout = srcentry->m_texture->GetLayout(); - srcentry->m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + VkImageLayout old_src_layout = srcentry->m_layout; + srcentry->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); VkImageResolve resolve = { {VK_IMAGE_ASPECT_COLOR_BIT, level, layer, 1}, // srcSubresource @@ -238,23 +302,18 @@ void VKTexture::ResolveFromTexture(const AbstractTexture* src, const MathUtil::R {rect.left, rect.top, 0}, // dstOffset {static_cast<u32>(rect.GetWidth()), static_cast<u32>(rect.GetHeight()), 1} // extent }; - vkCmdResolveImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), - srcentry->m_texture->GetImage(), srcentry->m_texture->GetLayout(), - m_texture->GetImage(), m_texture->GetLayout(), 1, &resolve); - - // Restore old source texture layout. Destination is assumed to be bound as a shader resource. - srcentry->m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - old_src_layout); - m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + vkCmdResolveImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), srcentry->m_image, + srcentry->m_layout, m_image, m_layout, 1, &resolve); + + srcentry->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), old_src_layout); } void VKTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, size_t buffer_size) { // Can't copy data larger than the texture extents. - width = std::max(1u, std::min(width, m_texture->GetWidth() >> level)); - height = std::max(1u, std::min(height, m_texture->GetHeight() >> level)); + width = std::max(1u, std::min(width, GetWidth() >> level)); + height = std::max(1u, std::min(height, GetHeight() >> level)); // We don't care about the existing contents of the texture, so we could the image layout to // VK_IMAGE_LAYOUT_UNDEFINED here. However, under section 2.2.1, Queue Operation of the Vulkan @@ -272,30 +331,29 @@ void VKTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8* // When the last mip level is uploaded, we transition to SHADER_READ_ONLY, ready for use. This is // because we can't transition in a render pass, and we don't necessarily know when this texture // is going to be used. - m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); // For unaligned textures, we can save some memory in the transfer buffer by skipping the rows // that lie outside of the texture's dimensions. - u32 upload_alignment = static_cast<u32>(g_vulkan_context->GetBufferImageGranularity()); - u32 block_size = Util::GetBlockSize(m_texture->GetFormat()); - u32 num_rows = Common::AlignUp(height, block_size) / block_size; - size_t source_pitch = CalculateStrideForFormat(m_config.format, row_length); - size_t upload_size = source_pitch * num_rows; + const u32 upload_alignment = static_cast<u32>(g_vulkan_context->GetBufferImageGranularity()); + const u32 block_size = GetBlockSizeForFormat(GetFormat()); + const u32 num_rows = Common::AlignUp(height, block_size) / block_size; + const u32 source_pitch = CalculateStrideForFormat(m_config.format, row_length); + const u32 upload_size = source_pitch * num_rows; std::unique_ptr<StagingBuffer> temp_buffer; VkBuffer upload_buffer; VkDeviceSize upload_buffer_offset; // Does this texture data fit within the streaming buffer? - if (upload_size <= STAGING_TEXTURE_UPLOAD_THRESHOLD && - upload_size <= MAXIMUM_TEXTURE_UPLOAD_BUFFER_SIZE) + if (upload_size <= STAGING_TEXTURE_UPLOAD_THRESHOLD) { StreamBuffer* stream_buffer = g_object_cache->GetTextureUploadBuffer(); if (!stream_buffer->ReserveMemory(upload_size, upload_alignment)) { // Execute the command buffer first. WARN_LOG(VIDEO, "Executing command list while waiting for space in texture upload buffer"); - Util::ExecuteCurrentCommandsAndRestoreState(false); + Renderer::GetInstance()->ExecuteCommandBuffer(false); // Try allocating again. This may cause a fence wait. if (!stream_buffer->ReserveMemory(upload_size, upload_alignment)) @@ -334,17 +392,282 @@ void VKTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8* {width, height, 1} // VkExtent3D imageExtent }; vkCmdCopyBufferToImage(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), upload_buffer, - m_texture->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, - &image_copy); + m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy); - // Last mip level? We shouldn't be doing any further uploads now, so transition for rendering. + // Preemptively transition to shader read only after uploading the last mip level, as we're + // likely finished with writes to this texture for now. We can't do this in common with a + // FinishedRendering() call because the upload happens in the init command buffer, and we + // don't want to interrupt the render pass with calls which were executed ages before. if (level == (m_config.levels - 1)) { - m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); } } +void VKTexture::FinishedRendering() +{ + if (m_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + return; + + StateTracker::GetInstance()->EndRenderPass(); + TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); +} + +void VKTexture::OverrideImageLayout(VkImageLayout new_layout) +{ + m_layout = new_layout; +} + +void VKTexture::TransitionToLayout(VkCommandBuffer command_buffer, VkImageLayout new_layout) const +{ + if (m_layout == new_layout) + return; + + VkImageMemoryBarrier barrier = { + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType + nullptr, // const void* pNext + 0, // VkAccessFlags srcAccessMask + 0, // VkAccessFlags dstAccessMask + m_layout, // VkImageLayout oldLayout + new_layout, // VkImageLayout newLayout + VK_QUEUE_FAMILY_IGNORED, // uint32_t srcQueueFamilyIndex + VK_QUEUE_FAMILY_IGNORED, // uint32_t dstQueueFamilyIndex + m_image, // VkImage image + {GetImageAspectForFormat(GetFormat()), 0, GetLevels(), 0, + GetLayers()} // VkImageSubresourceRange subresourceRange + }; + + // srcStageMask -> Stages that must complete before the barrier + // dstStageMask -> Stages that must wait for after the barrier before beginning + VkPipelineStageFlags srcStageMask, dstStageMask; + switch (m_layout) + { + case VK_IMAGE_LAYOUT_UNDEFINED: + // Layout undefined therefore contents undefined, and we don't care what happens to it. + barrier.srcAccessMask = 0; + srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + break; + + case VK_IMAGE_LAYOUT_PREINITIALIZED: + // Image has been pre-initialized by the host, so ensure all writes have completed. + barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; + srcStageMask = VK_PIPELINE_STAGE_HOST_BIT; + break; + + case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: + // Image was being used as a color attachment, so ensure all writes have completed. + barrier.srcAccessMask = + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + break; + + case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: + // Image was being used as a depthstencil attachment, so ensure all writes have completed. + barrier.srcAccessMask = + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + srcStageMask = + VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + break; + + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + // Image was being used as a shader resource, make sure all reads have finished. + barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + break; + + case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: + // Image was being used as a copy source, ensure all reads have finished. + barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + // Image was being used as a copy destination, ensure all writes have finished. + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + + default: + srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + break; + } + + switch (new_layout) + { + case VK_IMAGE_LAYOUT_UNDEFINED: + barrier.dstAccessMask = 0; + dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + break; + + case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: + barrier.dstAccessMask = + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + break; + + case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: + barrier.dstAccessMask = + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + dstStageMask = + VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + break; + + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + break; + + case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: + barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + + case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR: + srcStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; + dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + break; + + default: + dstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + break; + } + + // If we were using a compute layout, the stages need to reflect that + switch (m_compute_layout) + { + case ComputeImageLayout::Undefined: + break; + case ComputeImageLayout::ReadOnly: + barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + break; + case ComputeImageLayout::WriteOnly: + barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + break; + case ComputeImageLayout::ReadWrite: + barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + break; + } + m_compute_layout = ComputeImageLayout::Undefined; + + vkCmdPipelineBarrier(command_buffer, srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr, 1, + &barrier); + + m_layout = new_layout; +} + +void VKTexture::TransitionToLayout(VkCommandBuffer command_buffer, + ComputeImageLayout new_layout) const +{ + ASSERT(new_layout != ComputeImageLayout::Undefined); + if (m_compute_layout == new_layout) + return; + + VkImageMemoryBarrier barrier = { + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType + nullptr, // const void* pNext + 0, // VkAccessFlags srcAccessMask + 0, // VkAccessFlags dstAccessMask + m_layout, // VkImageLayout oldLayout + VK_IMAGE_LAYOUT_GENERAL, // VkImageLayout newLayout + VK_QUEUE_FAMILY_IGNORED, // uint32_t srcQueueFamilyIndex + VK_QUEUE_FAMILY_IGNORED, // uint32_t dstQueueFamilyIndex + m_image, // VkImage image + {GetImageAspectForFormat(GetFormat()), 0, GetLevels(), 0, + GetLayers()} // VkImageSubresourceRange subresourceRange + }; + + VkPipelineStageFlags srcStageMask, dstStageMask; + switch (m_layout) + { + case VK_IMAGE_LAYOUT_UNDEFINED: + // Layout undefined therefore contents undefined, and we don't care what happens to it. + barrier.srcAccessMask = 0; + srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + break; + + case VK_IMAGE_LAYOUT_PREINITIALIZED: + // Image has been pre-initialized by the host, so ensure all writes have completed. + barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; + srcStageMask = VK_PIPELINE_STAGE_HOST_BIT; + break; + + case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: + // Image was being used as a color attachment, so ensure all writes have completed. + barrier.srcAccessMask = + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + break; + + case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: + // Image was being used as a depthstencil attachment, so ensure all writes have completed. + barrier.srcAccessMask = + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + srcStageMask = + VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + break; + + case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: + // Image was being used as a shader resource, make sure all reads have finished. + barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + break; + + case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: + // Image was being used as a copy source, ensure all reads have finished. + barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + + case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: + // Image was being used as a copy destination, ensure all writes have finished. + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; + break; + + default: + srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + break; + } + + switch (new_layout) + { + case ComputeImageLayout::ReadOnly: + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + break; + case ComputeImageLayout::WriteOnly: + barrier.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; + dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + break; + case ComputeImageLayout::ReadWrite: + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; + dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + break; + default: + dstStageMask = 0; + break; + } + + m_layout = barrier.newLayout; + m_compute_layout = new_layout; + + vkCmdPipelineBarrier(command_buffer, srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr, 1, + &barrier); +} + VKStagingTexture::VKStagingTexture(StagingTextureType type, const TextureConfig& config, std::unique_ptr<StagingBuffer> buffer) : AbstractStagingTexture(type, config), m_staging_buffer(std::move(buffer)) @@ -407,38 +730,32 @@ void VKStagingTexture::CopyFromTexture(const AbstractTexture* src, const MathUtil::Rectangle<int>& src_rect, u32 src_layer, u32 src_level, const MathUtil::Rectangle<int>& dst_rect) { + const VKTexture* src_tex = static_cast<const VKTexture*>(src); ASSERT(m_type == StagingTextureType::Readback || m_type == StagingTextureType::Mutable); ASSERT(src_rect.GetWidth() == dst_rect.GetWidth() && src_rect.GetHeight() == dst_rect.GetHeight()); - ASSERT(src_rect.left >= 0 && static_cast<u32>(src_rect.right) <= src->GetConfig().width && - src_rect.top >= 0 && static_cast<u32>(src_rect.bottom) <= src->GetConfig().height); + ASSERT(src_rect.left >= 0 && static_cast<u32>(src_rect.right) <= src_tex->GetWidth() && + src_rect.top >= 0 && static_cast<u32>(src_rect.bottom) <= src_tex->GetHeight()); ASSERT(dst_rect.left >= 0 && static_cast<u32>(dst_rect.right) <= m_config.width && dst_rect.top >= 0 && static_cast<u32>(dst_rect.bottom) <= m_config.height); - Texture2D* src_tex = static_cast<const VKTexture*>(src)->GetRawTexIdentifier(); - CopyFromTexture(src_tex, src_rect, src_layer, src_level, dst_rect); -} - -void VKStagingTexture::CopyFromTexture(Texture2D* src, const MathUtil::Rectangle<int>& src_rect, - u32 src_layer, u32 src_level, - const MathUtil::Rectangle<int>& dst_rect) -{ if (m_needs_flush) { // Drop copy before reusing it. - g_command_buffer_mgr->RemoveFencePointCallback(this); + g_command_buffer_mgr->RemoveFenceSignaledCallback(this); m_flush_fence = VK_NULL_HANDLE; m_needs_flush = false; } - VkImageLayout old_layout = src->GetLayout(); - src->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + StateTracker::GetInstance()->EndRenderPass(); + + VkImageLayout old_layout = src_tex->GetLayout(); + src_tex->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); // Issue the image->buffer copy, but delay it for now. VkBufferImageCopy image_copy = {}; - VkImageAspectFlags aspect = - Util::IsDepthFormat(src->GetFormat()) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; + const VkImageAspectFlags aspect = VKTexture::GetImageAspectForFormat(src_tex->GetFormat()); image_copy.bufferOffset = static_cast<VkDeviceSize>(static_cast<size_t>(dst_rect.top) * m_config.GetStride() + static_cast<size_t>(dst_rect.left) * m_texel_size); @@ -448,58 +765,51 @@ void VKStagingTexture::CopyFromTexture(Texture2D* src, const MathUtil::Rectangle image_copy.imageOffset = {src_rect.left, src_rect.top, 0}; image_copy.imageExtent = {static_cast<u32>(src_rect.GetWidth()), static_cast<u32>(src_rect.GetHeight()), 1u}; - vkCmdCopyImageToBuffer(g_command_buffer_mgr->GetCurrentCommandBuffer(), src->GetImage(), + vkCmdCopyImageToBuffer(g_command_buffer_mgr->GetCurrentCommandBuffer(), src_tex->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_staging_buffer->GetBuffer(), 1, &image_copy); // Restore old source texture layout. - src->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), old_layout); + src_tex->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), old_layout); m_needs_flush = true; - g_command_buffer_mgr->AddFencePointCallback(this, - [this](VkCommandBuffer buf, VkFence fence) { - ASSERT(m_needs_flush); - if (m_flush_fence != VK_NULL_HANDLE) - return; - - m_flush_fence = fence; - }, - [this](VkFence fence) { - if (m_flush_fence != fence) - return; - - m_flush_fence = VK_NULL_HANDLE; - m_needs_flush = false; - g_command_buffer_mgr->RemoveFencePointCallback( - this); - m_staging_buffer->InvalidateCPUCache(); - }); + m_flush_fence = g_command_buffer_mgr->GetCurrentCommandBufferFence(); + g_command_buffer_mgr->AddFenceSignaledCallback(this, [this](VkFence fence) { + if (m_flush_fence != fence) + return; + + m_flush_fence = VK_NULL_HANDLE; + m_needs_flush = false; + g_command_buffer_mgr->RemoveFenceSignaledCallback(this); + m_staging_buffer->InvalidateCPUCache(); + }); } void VKStagingTexture::CopyToTexture(const MathUtil::Rectangle<int>& src_rect, AbstractTexture* dst, const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer, u32 dst_level) { + const VKTexture* dst_tex = static_cast<const VKTexture*>(dst); ASSERT(m_type == StagingTextureType::Upload || m_type == StagingTextureType::Mutable); ASSERT(src_rect.GetWidth() == dst_rect.GetWidth() && src_rect.GetHeight() == dst_rect.GetHeight()); ASSERT(src_rect.left >= 0 && static_cast<u32>(src_rect.right) <= m_config.width && src_rect.top >= 0 && static_cast<u32>(src_rect.bottom) <= m_config.height); - ASSERT(dst_rect.left >= 0 && static_cast<u32>(dst_rect.right) <= dst->GetConfig().width && - dst_rect.top >= 0 && static_cast<u32>(dst_rect.bottom) <= dst->GetConfig().height); + ASSERT(dst_rect.left >= 0 && static_cast<u32>(dst_rect.right) <= dst_tex->GetWidth() && + dst_rect.top >= 0 && static_cast<u32>(dst_rect.bottom) <= dst_tex->GetHeight()); if (m_needs_flush) { // Drop copy before reusing it. - g_command_buffer_mgr->RemoveFencePointCallback(this); + g_command_buffer_mgr->RemoveFenceSignaledCallback(this); m_flush_fence = VK_NULL_HANDLE; m_needs_flush = false; } // Flush caches before copying. m_staging_buffer->FlushCPUCache(); + StateTracker::GetInstance()->EndRenderPass(); - Texture2D* dst_tex = static_cast<const VKTexture*>(dst)->GetRawTexIdentifier(); VkImageLayout old_layout = dst_tex->GetLayout(); dst_tex->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); @@ -523,23 +833,15 @@ void VKStagingTexture::CopyToTexture(const MathUtil::Rectangle<int>& src_rect, A dst_tex->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), old_layout); m_needs_flush = true; - g_command_buffer_mgr->AddFencePointCallback(this, - [this](VkCommandBuffer buf, VkFence fence) { - ASSERT(m_needs_flush); - if (m_flush_fence != VK_NULL_HANDLE) - return; - - m_flush_fence = fence; - }, - [this](VkFence fence) { - if (m_flush_fence != fence) - return; - - m_flush_fence = VK_NULL_HANDLE; - m_needs_flush = false; - g_command_buffer_mgr->RemoveFencePointCallback( - this); - }); + m_flush_fence = g_command_buffer_mgr->GetCurrentCommandBufferFence(); + g_command_buffer_mgr->AddFenceSignaledCallback(this, [this](VkFence fence) { + if (m_flush_fence != fence) + return; + + m_flush_fence = VK_NULL_HANDLE; + m_needs_flush = false; + g_command_buffer_mgr->RemoveFenceSignaledCallback(this); + }); } bool VKStagingTexture::Map() @@ -559,19 +861,19 @@ void VKStagingTexture::Flush() return; // Either of the below two calls will cause the callback to fire. - g_command_buffer_mgr->RemoveFencePointCallback(this); - if (m_flush_fence != VK_NULL_HANDLE) + g_command_buffer_mgr->RemoveFenceSignaledCallback(this); + if (m_flush_fence == g_command_buffer_mgr->GetCurrentCommandBufferFence()) { - // WaitForFence should fire the callback. - g_command_buffer_mgr->WaitForFence(m_flush_fence); - m_flush_fence = VK_NULL_HANDLE; + // The readback is in the current command buffer, and we must execute it. + Renderer::GetInstance()->ExecuteCommandBuffer(false, true); } else { - // We don't have a fence, and are pending. That means the readback is in the current - // command buffer, and must execute it to populate the staging texture. - Util::ExecuteCurrentCommandsAndRestoreState(false, true); + // WaitForFence should fire the callback. + g_command_buffer_mgr->WaitForFence(m_flush_fence); } + + DEBUG_ASSERT(m_flush_fence == VK_NULL_HANDLE); m_needs_flush = false; // For readback textures, invalidate the CPU cache as there is new data there. @@ -579,16 +881,16 @@ void VKStagingTexture::Flush() m_staging_buffer->InvalidateCPUCache(); } -VKFramebuffer::VKFramebuffer(const VKTexture* color_attachment, const VKTexture* depth_attachment, - u32 width, u32 height, u32 layers, u32 samples, VkFramebuffer fb, +VKFramebuffer::VKFramebuffer(VKTexture* color_attachment, VKTexture* depth_attachment, u32 width, + u32 height, u32 layers, u32 samples, VkFramebuffer fb, VkRenderPass load_render_pass, VkRenderPass discard_render_pass, VkRenderPass clear_render_pass) : AbstractFramebuffer( + color_attachment, depth_attachment, color_attachment ? color_attachment->GetFormat() : AbstractTextureFormat::Undefined, depth_attachment ? depth_attachment->GetFormat() : AbstractTextureFormat::Undefined, width, height, layers, samples), - m_color_attachment(color_attachment), m_depth_attachment(depth_attachment), m_fb(fb), - m_load_render_pass(load_render_pass), m_discard_render_pass(discard_render_pass), + m_fb(fb), m_load_render_pass(load_render_pass), m_discard_render_pass(discard_render_pass), m_clear_render_pass(clear_render_pass) { } @@ -598,16 +900,16 @@ VKFramebuffer::~VKFramebuffer() g_command_buffer_mgr->DeferFramebufferDestruction(m_fb); } -std::unique_ptr<VKFramebuffer> VKFramebuffer::Create(const VKTexture* color_attachment, - const VKTexture* depth_attachment) +std::unique_ptr<VKFramebuffer> VKFramebuffer::Create(VKTexture* color_attachment, + VKTexture* depth_attachment) { if (!ValidateConfig(color_attachment, depth_attachment)) return nullptr; const VkFormat vk_color_format = - color_attachment ? color_attachment->GetRawTexIdentifier()->GetFormat() : VK_FORMAT_UNDEFINED; + color_attachment ? color_attachment->GetVkFormat() : VK_FORMAT_UNDEFINED; const VkFormat vk_depth_format = - depth_attachment ? depth_attachment->GetRawTexIdentifier()->GetFormat() : VK_FORMAT_UNDEFINED; + depth_attachment ? depth_attachment->GetVkFormat() : VK_FORMAT_UNDEFINED; const VKTexture* either_attachment = color_attachment ? color_attachment : depth_attachment; const u32 width = either_attachment->GetWidth(); const u32 height = either_attachment->GetHeight(); @@ -618,10 +920,10 @@ std::unique_ptr<VKFramebuffer> VKFramebuffer::Create(const VKTexture* color_atta u32 num_attachments = 0; if (color_attachment) - attachment_views[num_attachments++] = color_attachment->GetRawTexIdentifier()->GetView(); + attachment_views[num_attachments++] = color_attachment->GetView(); if (depth_attachment) - attachment_views[num_attachments++] = depth_attachment->GetRawTexIdentifier()->GetView(); + attachment_views[num_attachments++] = depth_attachment->GetView(); VkRenderPass load_render_pass = g_object_cache->GetRenderPass( vk_color_format, vk_depth_format, samples, VK_ATTACHMENT_LOAD_OP_LOAD); @@ -659,38 +961,20 @@ std::unique_ptr<VKFramebuffer> VKFramebuffer::Create(const VKTexture* color_atta clear_render_pass); } -void VKFramebuffer::TransitionForRender() const -{ - if (m_color_attachment) - { - m_color_attachment->GetRawTexIdentifier()->TransitionToLayout( - g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - } - - if (m_depth_attachment) - { - m_depth_attachment->GetRawTexIdentifier()->TransitionToLayout( - g_command_buffer_mgr->GetCurrentCommandBuffer(), - VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); - } -} - -void VKFramebuffer::TransitionForSample() const +void VKFramebuffer::TransitionForRender() { - if (StateTracker::GetInstance()->GetFramebuffer() == m_fb) - StateTracker::GetInstance()->EndRenderPass(); - if (m_color_attachment) { - m_color_attachment->GetRawTexIdentifier()->TransitionToLayout( - g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + static_cast<VKTexture*>(m_color_attachment) + ->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); } if (m_depth_attachment) { - m_depth_attachment->GetRawTexIdentifier()->TransitionToLayout( - g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + static_cast<VKTexture*>(m_depth_attachment) + ->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); } } - } // namespace Vulkan |
