diff --git a/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp b/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp index 61eb194e49..1f497416be 100644 --- a/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp +++ b/Source/Core/VideoBackends/Vulkan/FramebufferManager.cpp @@ -1007,10 +1007,8 @@ bool FramebufferManager::CreateReadbackTextures() VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); - // We can't copy to/from color<->depth formats, so using a linear texture is not an option here. - // TODO: Investigate if vkCmdBlitImage can be used. The documentation isn't that clear. - m_depth_readback_texture = StagingTexture2DBuffer::Create(STAGING_BUFFER_TYPE_READBACK, EFB_WIDTH, - EFB_HEIGHT, EFB_DEPTH_TEXTURE_FORMAT); + m_depth_readback_texture = StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, EFB_WIDTH, + EFB_HEIGHT, EFB_DEPTH_TEXTURE_FORMAT); if (!m_depth_copy_texture || !m_depth_readback_texture) { ERROR_LOG(VIDEO, "Failed to create EFB depth readback texture"); diff --git a/Source/Core/VideoBackends/Vulkan/StagingBuffer.cpp b/Source/Core/VideoBackends/Vulkan/StagingBuffer.cpp index f972492abb..6bd8170fc9 100644 --- a/Source/Core/VideoBackends/Vulkan/StagingBuffer.cpp +++ b/Source/Core/VideoBackends/Vulkan/StagingBuffer.cpp @@ -80,6 +80,9 @@ void StagingBuffer::InvalidateGPUCache(VkCommandBuffer command_buffer, VkPipelineStageFlagBits dest_pipeline_stage, VkDeviceSize offset, VkDeviceSize size) { + if (m_coherent) + return; + _assert_((offset + size) <= m_size || (offset < m_size && size == VK_WHOLE_SIZE)); Util::BufferMemoryBarrier(command_buffer, m_buffer, VK_ACCESS_HOST_WRITE_BIT, dest_access_flags, offset, size, VK_PIPELINE_STAGE_HOST_BIT, dest_pipeline_stage); @@ -90,6 +93,9 @@ void StagingBuffer::PrepareForGPUWrite(VkCommandBuffer command_buffer, VkPipelineStageFlagBits dst_pipeline_stage, VkDeviceSize offset, VkDeviceSize size) { + if (m_coherent) + return; + _assert_((offset + size) <= m_size || (offset < m_size && size == VK_WHOLE_SIZE)); Util::BufferMemoryBarrier(command_buffer, m_buffer, 0, dst_access_flags, offset, size, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, dst_pipeline_stage); @@ -99,6 +105,9 @@ void StagingBuffer::FlushGPUCache(VkCommandBuffer command_buffer, VkAccessFlagBi VkPipelineStageFlagBits src_pipeline_stage, VkDeviceSize offset, VkDeviceSize size) { + if (m_coherent) + return; + _assert_((offset + size) <= m_size || (offset < m_size && size == VK_WHOLE_SIZE)); Util::BufferMemoryBarrier(command_buffer, m_buffer, src_access_flags, VK_ACCESS_HOST_READ_BIT, offset, size, src_pipeline_stage, VK_PIPELINE_STAGE_HOST_BIT); @@ -136,8 +145,9 @@ void StagingBuffer::Write(VkDeviceSize offset, const void* data, size_t size, FlushCPUCache(offset, size); } -std::unique_ptr -StagingBuffer::Create(STAGING_BUFFER_TYPE type, VkDeviceSize size, VkBufferUsageFlags usage) +bool StagingBuffer::AllocateBuffer(STAGING_BUFFER_TYPE type, VkDeviceSize size, + VkBufferUsageFlags usage, VkBuffer* out_buffer, + VkDeviceMemory* out_memory, bool* out_coherent) { VkBufferCreateInfo buffer_create_info = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType @@ -149,24 +159,22 @@ StagingBuffer::Create(STAGING_BUFFER_TYPE type, VkDeviceSize size, VkBufferUsage 0, // uint32_t queueFamilyIndexCount nullptr // const uint32_t* pQueueFamilyIndices }; - VkBuffer buffer; VkResult res = - vkCreateBuffer(g_vulkan_context->GetDevice(), &buffer_create_info, nullptr, &buffer); + vkCreateBuffer(g_vulkan_context->GetDevice(), &buffer_create_info, nullptr, out_buffer); if (res != VK_SUCCESS) { LOG_VULKAN_ERROR(res, "vkCreateBuffer failed: "); - return nullptr; + return false; } VkMemoryRequirements requirements; - vkGetBufferMemoryRequirements(g_vulkan_context->GetDevice(), buffer, &requirements); + vkGetBufferMemoryRequirements(g_vulkan_context->GetDevice(), *out_buffer, &requirements); - bool is_coherent; u32 type_index; if (type == STAGING_BUFFER_TYPE_UPLOAD) - type_index = g_vulkan_context->GetUploadMemoryType(requirements.memoryTypeBits, &is_coherent); + type_index = g_vulkan_context->GetUploadMemoryType(requirements.memoryTypeBits, out_coherent); else - type_index = g_vulkan_context->GetReadbackMemoryType(requirements.memoryTypeBits, &is_coherent); + type_index = g_vulkan_context->GetReadbackMemoryType(requirements.memoryTypeBits, out_coherent); VkMemoryAllocateInfo memory_allocate_info = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // VkStructureType sType @@ -174,25 +182,36 @@ StagingBuffer::Create(STAGING_BUFFER_TYPE type, VkDeviceSize size, VkBufferUsage requirements.size, // VkDeviceSize allocationSize type_index // uint32_t memoryTypeIndex }; - VkDeviceMemory memory; - res = vkAllocateMemory(g_vulkan_context->GetDevice(), &memory_allocate_info, nullptr, &memory); + res = vkAllocateMemory(g_vulkan_context->GetDevice(), &memory_allocate_info, nullptr, out_memory); if (res != VK_SUCCESS) { LOG_VULKAN_ERROR(res, "vkAllocateMemory failed: "); - vkDestroyBuffer(g_vulkan_context->GetDevice(), buffer, nullptr); - return nullptr; + vkDestroyBuffer(g_vulkan_context->GetDevice(), *out_buffer, nullptr); + return false; } - res = vkBindBufferMemory(g_vulkan_context->GetDevice(), buffer, memory, 0); + res = vkBindBufferMemory(g_vulkan_context->GetDevice(), *out_buffer, *out_memory, 0); if (res != VK_SUCCESS) { LOG_VULKAN_ERROR(res, "vkBindBufferMemory failed: "); - vkDestroyBuffer(g_vulkan_context->GetDevice(), buffer, nullptr); - vkFreeMemory(g_vulkan_context->GetDevice(), memory, nullptr); - return nullptr; + vkDestroyBuffer(g_vulkan_context->GetDevice(), *out_buffer, nullptr); + vkFreeMemory(g_vulkan_context->GetDevice(), *out_memory, nullptr); + return false; } - return std::make_unique(type, buffer, memory, size, is_coherent); + return true; +} + +std::unique_ptr StagingBuffer::Create(STAGING_BUFFER_TYPE type, VkDeviceSize size, + VkBufferUsageFlags usage) +{ + VkBuffer buffer; + VkDeviceMemory memory; + bool coherent; + if (!AllocateBuffer(type, size, usage, &buffer, &memory, &coherent)) + return nullptr; + + return std::make_unique(type, buffer, memory, size, coherent); } } // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/StagingBuffer.h b/Source/Core/VideoBackends/Vulkan/StagingBuffer.h index 22f99d1780..65cfb1c5c7 100644 --- a/Source/Core/VideoBackends/Vulkan/StagingBuffer.h +++ b/Source/Core/VideoBackends/Vulkan/StagingBuffer.h @@ -16,7 +16,7 @@ class StagingBuffer public: StagingBuffer(STAGING_BUFFER_TYPE type, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize size, bool coherent); - ~StagingBuffer(); + virtual ~StagingBuffer(); STAGING_BUFFER_TYPE GetType() const { return m_type; } VkDeviceSize GetSize() const { return m_size; } @@ -33,6 +33,7 @@ public: void FlushCPUCache(VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE); // Upload part 2: Prepare for device read from the GPU side + // Implicit when submitting the command buffer, so rarely needed. void InvalidateGPUCache(VkCommandBuffer command_buffer, VkAccessFlagBits dst_access_flags, VkPipelineStageFlagBits dst_pipeline_stage, VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE); @@ -59,6 +60,10 @@ public: VkBufferUsageFlags usage); protected: + // Allocates the resources needed to create a staging buffer. + static bool AllocateBuffer(STAGING_BUFFER_TYPE type, VkDeviceSize size, VkBufferUsageFlags usage, + VkBuffer* out_buffer, VkDeviceMemory* out_memory, bool* out_coherent); + STAGING_BUFFER_TYPE m_type; VkBuffer m_buffer; VkDeviceMemory m_memory; diff --git a/Source/Core/VideoBackends/Vulkan/StagingTexture2D.cpp b/Source/Core/VideoBackends/Vulkan/StagingTexture2D.cpp index 1f5be7563c..ee726fc8f2 100644 --- a/Source/Core/VideoBackends/Vulkan/StagingTexture2D.cpp +++ b/Source/Core/VideoBackends/Vulkan/StagingTexture2D.cpp @@ -14,16 +14,16 @@ namespace Vulkan { -StagingTexture2D::StagingTexture2D(STAGING_BUFFER_TYPE type, u32 width, u32 height, VkFormat format, - u32 stride) - : m_type(type), m_width(width), m_height(height), m_format(format), - m_texel_size(Util::GetTexelSize(format)), m_row_stride(stride) +StagingTexture2D::StagingTexture2D(STAGING_BUFFER_TYPE type, VkBuffer buffer, VkDeviceMemory memory, + VkDeviceSize size, bool coherent, u32 width, u32 height, + VkFormat format, u32 stride) + : StagingBuffer(type, buffer, memory, size, coherent), m_width(width), m_height(height), + m_format(format), m_texel_size(Util::GetTexelSize(format)), m_row_stride(stride) { } StagingTexture2D::~StagingTexture2D() { - _assert_(!m_map_pointer); } void StagingTexture2D::ReadTexel(u32 x, u32 y, void* data, size_t data_size) const @@ -96,283 +96,13 @@ void StagingTexture2D::WriteTexels(u32 x, u32 y, u32 width, u32 height, const vo } } -std::unique_ptr StagingTexture2D::Create(STAGING_BUFFER_TYPE type, u32 width, - u32 height, VkFormat format) -{ -// TODO: Using a buffer here as opposed to a linear texture is faster on AMD. -// NVIDIA also seems faster with buffers over textures. -#if 0 - // Check for support for this format as a linear texture. - // Some drivers don't support this (e.g. adreno). - VkImageFormatProperties properties; - VkResult res = vkGetPhysicalDeviceImageFormatProperties( - g_object_cache->GetPhysicalDevice(), format, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_LINEAR, - VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, &properties); - if (res == VK_SUCCESS && width <= properties.maxExtent.width && - height <= properties.maxExtent.height) - { - return StagingTexture2DLinear::Create(type, width, height, format); - } -#endif - - // Fall back to a buffer copy. - return StagingTexture2DBuffer::Create(type, width, height, format); -} - -StagingTexture2DLinear::StagingTexture2DLinear(STAGING_BUFFER_TYPE type, u32 width, u32 height, - VkFormat format, u32 stride, VkImage image, - VkDeviceMemory memory, VkDeviceSize size, - bool coherent) - : StagingTexture2D(type, width, height, format, stride), m_image(image), m_memory(memory), - m_size(size), m_layout(VK_IMAGE_LAYOUT_PREINITIALIZED), m_coherent(coherent) -{ -} - -StagingTexture2DLinear::~StagingTexture2DLinear() -{ - if (m_map_pointer) - Unmap(); - - g_command_buffer_mgr->DeferDeviceMemoryDestruction(m_memory); - g_command_buffer_mgr->DeferImageDestruction(m_image); -} - -void StagingTexture2DLinear::CopyFromImage(VkCommandBuffer command_buffer, VkImage image, - VkImageAspectFlags src_aspect, u32 x, u32 y, u32 width, - u32 height, u32 level, u32 layer) -{ - // Prepare the buffer for copying. - // We don't care about the existing contents, so set to UNDEFINED. - VkImageMemoryBarrier before_transfer_barrier = { - VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType - nullptr, // const void* pNext - 0, // VkAccessFlags srcAccessMask - VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags dstAccessMask - VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout newLayout - VK_QUEUE_FAMILY_IGNORED, // uint32_t srcQueueFamilyIndex - VK_QUEUE_FAMILY_IGNORED, // uint32_t dstQueueFamilyIndex - m_image, // VkImage image - {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} // VkImageSubresourceRange subresourceRange - }; - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, - &before_transfer_barrier); - - // Issue the image copy, gpu -> host. - VkImageCopy copy_region = { - {src_aspect, level, layer, 1}, // VkImageSubresourceLayers srcSubresource - {static_cast(x), static_cast(y), 0}, // VkOffset3D srcOffset - {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, // VkImageSubresourceLayers dstSubresource - {0, 0, 0}, // VkOffset3D dstOffset - {width, height, 1} // VkExtent3D extent - }; - vkCmdCopyImage(command_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); - - // Ensure writes are visible to the host. - VkImageMemoryBarrier visible_barrier = { - VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType - nullptr, // const void* pNext - VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask - VK_ACCESS_HOST_READ_BIT, // VkAccessFlags dstAccessMask - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // 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 - {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} // VkImageSubresourceRange subresourceRange - }; - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, - 0, 0, nullptr, 0, nullptr, 1, &visible_barrier); - m_layout = VK_IMAGE_LAYOUT_GENERAL; - - // Invalidate memory range if currently mapped. - if (m_map_pointer && !m_coherent) - { - VkMappedMemoryRange range = {VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, nullptr, m_memory, - m_map_offset, m_map_size}; - vkInvalidateMappedMemoryRanges(g_vulkan_context->GetDevice(), 1, &range); - } -} - -void StagingTexture2DLinear::CopyToImage(VkCommandBuffer command_buffer, VkImage image, - VkImageAspectFlags dst_aspect, u32 x, u32 y, u32 width, - u32 height, u32 level, u32 layer) -{ - // Flush memory range if currently mapped. - if (m_map_pointer && !m_coherent) - { - VkMappedMemoryRange range = {VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, nullptr, m_memory, - m_map_offset, m_map_size}; - vkFlushMappedMemoryRanges(g_vulkan_context->GetDevice(), 1, &range); - } - - // Ensure any writes to the image are visible to the GPU. - VkImageMemoryBarrier barrier = { - VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType - nullptr, // const void* pNext - VK_ACCESS_HOST_WRITE_BIT, // VkAccessFlags srcAccessMask - VK_ACCESS_TRANSFER_READ_BIT, // VkAccessFlags dstAccessMask - m_layout, // VkImageLayout oldLayout - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, // VkImageLayout newLayout - VK_QUEUE_FAMILY_IGNORED, // uint32_t srcQueueFamilyIndex - VK_QUEUE_FAMILY_IGNORED, // uint32_t dstQueueFamilyIndex - m_image, // VkImage image - {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} // VkImageSubresourceRange subresourceRange - }; - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, - 0, 0, nullptr, 0, nullptr, 1, &barrier); - - m_layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - - // Issue the image copy, host -> gpu. - VkImageCopy copy_region = { - {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, // VkImageSubresourceLayers srcSubresource - {0, 0, 0}, // VkOffset3D srcOffset - {dst_aspect, level, layer, 1}, // VkImageSubresourceLayers dstSubresource - {static_cast(x), static_cast(y), 0}, // VkOffset3D dstOffset - {width, height, 1} // VkExtent3D extent - }; - vkCmdCopyImage(command_buffer, m_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); -} - -bool StagingTexture2DLinear::Map(VkDeviceSize offset /* = 0 */, - VkDeviceSize size /* = VK_WHOLE_SIZE */) -{ - m_map_offset = offset; - if (size == VK_WHOLE_SIZE) - m_map_size = m_size - offset; - else - m_map_size = size; - - _assert_(!m_map_pointer); - _assert_(m_map_offset + m_map_size <= m_size); - - void* map_pointer; - VkResult res = vkMapMemory(g_vulkan_context->GetDevice(), m_memory, m_map_offset, m_map_size, 0, - &map_pointer); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkMapMemory failed: "); - return false; - } - - m_map_pointer = reinterpret_cast(map_pointer); - return true; -} - -void StagingTexture2DLinear::Unmap() -{ - _assert_(m_map_pointer); - - vkUnmapMemory(g_vulkan_context->GetDevice(), m_memory); - m_map_pointer = nullptr; - m_map_offset = 0; - m_map_size = 0; -} - -std::unique_ptr -StagingTexture2DLinear::Create(STAGING_BUFFER_TYPE type, u32 width, u32 height, VkFormat format) -{ - VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - VkImageCreateInfo create_info = { - VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType - nullptr, // const void* pNext - 0, // VkImageCreateFlags flags - VK_IMAGE_TYPE_2D, // VkImageType imageType - format, // VkFormat format - {width, height, 1}, // VkExtent3D extent - 1, // uint32_t mipLevels - 1, // uint32_t arrayLayers - VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples - VK_IMAGE_TILING_LINEAR, // VkImageTiling tiling - usage, // VkImageUsageFlags usage - VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode - 0, // uint32_t queueFamilyIndexCount - nullptr, // const uint32_t* pQueueFamilyIndices - VK_IMAGE_LAYOUT_PREINITIALIZED // VkImageLayout initialLayout - }; - - VkImage image; - VkResult res = vkCreateImage(g_vulkan_context->GetDevice(), &create_info, nullptr, &image); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkCreateImage failed: "); - return nullptr; - } - - VkMemoryRequirements memory_requirements; - vkGetImageMemoryRequirements(g_vulkan_context->GetDevice(), image, &memory_requirements); - - bool is_coherent; - u32 memory_type_index; - if (type == STAGING_BUFFER_TYPE_READBACK) - { - memory_type_index = - g_vulkan_context->GetReadbackMemoryType(memory_requirements.memoryTypeBits, &is_coherent); - } - else - { - memory_type_index = - g_vulkan_context->GetUploadMemoryType(memory_requirements.memoryTypeBits, &is_coherent); - } - VkMemoryAllocateInfo memory_allocate_info = { - VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // VkStructureType sType - nullptr, // const void* pNext - memory_requirements.size, // VkDeviceSize allocationSize - memory_type_index // uint32_t memoryTypeIndex - }; - VkDeviceMemory memory; - res = vkAllocateMemory(g_vulkan_context->GetDevice(), &memory_allocate_info, nullptr, &memory); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkAllocateMemory failed: "); - vkDestroyImage(g_vulkan_context->GetDevice(), image, nullptr); - return nullptr; - } - - res = vkBindImageMemory(g_vulkan_context->GetDevice(), image, memory, 0); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkBindImageMemory failed: "); - vkDestroyImage(g_vulkan_context->GetDevice(), image, nullptr); - vkFreeMemory(g_vulkan_context->GetDevice(), memory, nullptr); - return nullptr; - } - - // Assume tight packing. Is this correct? - u32 stride = width * Util::GetTexelSize(format); - return std::make_unique(type, width, height, format, stride, image, - memory, memory_requirements.size, is_coherent); -} - -StagingTexture2DBuffer::StagingTexture2DBuffer(STAGING_BUFFER_TYPE type, u32 width, u32 height, - VkFormat format, u32 stride, VkBuffer buffer, - VkDeviceMemory memory, VkDeviceSize size, - bool coherent) - : StagingTexture2D(type, width, height, format, stride), m_buffer(buffer), m_memory(memory), - m_size(size), m_coherent(coherent) -{ -} - -StagingTexture2DBuffer::~StagingTexture2DBuffer() -{ - if (m_map_pointer) - Unmap(); - - g_command_buffer_mgr->DeferDeviceMemoryDestruction(m_memory); - g_command_buffer_mgr->DeferBufferDestruction(m_buffer); -} - -void StagingTexture2DBuffer::CopyFromImage(VkCommandBuffer command_buffer, VkImage image, - VkImageAspectFlags src_aspect, u32 x, u32 y, u32 width, - u32 height, u32 level, u32 layer) +void StagingTexture2D::CopyFromImage(VkCommandBuffer command_buffer, VkImage image, + VkImageAspectFlags src_aspect, u32 x, u32 y, u32 width, + u32 height, u32 level, u32 layer) { // Issue the image->buffer copy. VkBufferImageCopy image_copy = { - 0, // VkDeviceSize bufferOffset + y * m_row_stride + x * m_texel_size, // VkDeviceSize bufferOffset m_width, // uint32_t bufferRowLength 0, // uint32_t bufferImageHeight {src_aspect, level, layer, 1}, // VkImageSubresourceLayers imageSubresource @@ -382,42 +112,28 @@ void StagingTexture2DBuffer::CopyFromImage(VkCommandBuffer command_buffer, VkIma vkCmdCopyImageToBuffer(command_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_buffer, 1, &image_copy); - // Ensure the write has completed. - VkDeviceSize copy_size = m_row_stride * height; - Util::BufferMemoryBarrier(command_buffer, m_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, - VK_ACCESS_HOST_READ_BIT, 0, copy_size, VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_HOST_BIT); - - // If we're still mapped, invalidate the mapped range - if (m_map_pointer && !m_coherent) - { - VkMappedMemoryRange range = {VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, nullptr, m_memory, - m_map_offset, m_map_size}; - vkInvalidateMappedMemoryRanges(g_vulkan_context->GetDevice(), 1, &range); - } + // Flush CPU and GPU caches if not coherent mapping. + VkDeviceSize buffer_flush_offset = y * m_row_stride; + VkDeviceSize buffer_flush_size = height * m_row_stride; + FlushGPUCache(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + buffer_flush_offset, buffer_flush_size); + InvalidateCPUCache(buffer_flush_offset, buffer_flush_size); } -void StagingTexture2DBuffer::CopyToImage(VkCommandBuffer command_buffer, VkImage image, - VkImageAspectFlags dst_aspect, u32 x, u32 y, u32 width, - u32 height, u32 level, u32 layer) +void StagingTexture2D::CopyToImage(VkCommandBuffer command_buffer, VkImage image, + VkImageAspectFlags dst_aspect, u32 x, u32 y, u32 width, + u32 height, u32 level, u32 layer) { - // If we're still mapped, flush the mapped range - if (m_map_pointer && !m_coherent) - { - VkMappedMemoryRange range = {VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, nullptr, m_memory, - m_map_offset, m_map_size}; - vkFlushMappedMemoryRanges(g_vulkan_context->GetDevice(), 1, &range); - } + // Flush CPU and GPU caches if not coherent mapping. + VkDeviceSize buffer_flush_offset = y * m_row_stride; + VkDeviceSize buffer_flush_size = height * m_row_stride; + FlushCPUCache(buffer_flush_offset, buffer_flush_size); + InvalidateGPUCache(command_buffer, VK_ACCESS_HOST_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + buffer_flush_offset, buffer_flush_size); - // Ensure writes are visible to GPU. - VkDeviceSize copy_size = m_row_stride * height; - Util::BufferMemoryBarrier(command_buffer, m_buffer, VK_ACCESS_HOST_WRITE_BIT, - VK_ACCESS_TRANSFER_READ_BIT, 0, copy_size, VK_PIPELINE_STAGE_HOST_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT); - - // Issue the buffer->image copy + // Issue the buffer->image copy. VkBufferImageCopy image_copy = { - 0, // VkDeviceSize bufferOffset + y * m_row_stride + x * m_texel_size, // VkDeviceSize bufferOffset m_width, // uint32_t bufferRowLength 0, // uint32_t bufferImageHeight {dst_aspect, level, layer, 1}, // VkImageSubresourceLayers imageSubresource @@ -428,109 +144,21 @@ void StagingTexture2DBuffer::CopyToImage(VkCommandBuffer command_buffer, VkImage &image_copy); } -bool StagingTexture2DBuffer::Map(VkDeviceSize offset /* = 0 */, - VkDeviceSize size /* = VK_WHOLE_SIZE */) -{ - m_map_offset = offset; - if (size == VK_WHOLE_SIZE) - m_map_size = m_size - offset; - else - m_map_size = size; - - _assert_(!m_map_pointer); - _assert_(m_map_offset + m_map_size <= m_size); - - void* map_pointer; - VkResult res = vkMapMemory(g_vulkan_context->GetDevice(), m_memory, m_map_offset, m_map_size, 0, - &map_pointer); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkMapMemory failed: "); - return false; - } - - m_map_pointer = reinterpret_cast(map_pointer); - return true; -} - -void StagingTexture2DBuffer::Unmap() -{ - _assert_(m_map_pointer); - - vkUnmapMemory(g_vulkan_context->GetDevice(), m_memory); - m_map_pointer = nullptr; - m_map_offset = 0; - m_map_size = 0; -} - -std::unique_ptr -StagingTexture2DBuffer::Create(STAGING_BUFFER_TYPE type, u32 width, u32 height, VkFormat format) +std::unique_ptr StagingTexture2D::Create(STAGING_BUFFER_TYPE type, u32 width, + u32 height, VkFormat format) { // Assume tight packing. - u32 row_stride = Util::GetTexelSize(format) * width; - u32 buffer_size = row_stride * height; + u32 stride = Util::GetTexelSize(format) * width; + u32 size = stride * height; VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - VkBufferCreateInfo buffer_create_info = { - VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType - nullptr, // const void* pNext - 0, // VkBufferCreateFlags flags - buffer_size, // VkDeviceSize size - usage, // VkBufferUsageFlags usage - VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode - 0, // uint32_t queueFamilyIndexCount - nullptr // const uint32_t* pQueueFamilyIndices - }; + VkBuffer buffer; - VkResult res = - vkCreateBuffer(g_vulkan_context->GetDevice(), &buffer_create_info, nullptr, &buffer); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkCreateBuffer failed: "); - return nullptr; - } - - VkMemoryRequirements memory_requirements; - vkGetBufferMemoryRequirements(g_vulkan_context->GetDevice(), buffer, &memory_requirements); - - bool is_coherent; - u32 memory_type_index; - if (type == STAGING_BUFFER_TYPE_READBACK) - { - memory_type_index = - g_vulkan_context->GetReadbackMemoryType(memory_requirements.memoryTypeBits, &is_coherent); - } - else - { - memory_type_index = - g_vulkan_context->GetUploadMemoryType(memory_requirements.memoryTypeBits, &is_coherent); - } - - VkMemoryAllocateInfo memory_allocate_info = { - VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // VkStructureType sType - nullptr, // const void* pNext - memory_requirements.size, // VkDeviceSize allocationSize - memory_type_index // uint32_t memoryTypeIndex - }; VkDeviceMemory memory; - res = vkAllocateMemory(g_vulkan_context->GetDevice(), &memory_allocate_info, nullptr, &memory); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkAllocateMemory failed: "); - vkDestroyBuffer(g_vulkan_context->GetDevice(), buffer, nullptr); + bool coherent; + if (!AllocateBuffer(type, size, usage, &buffer, &memory, &coherent)) return nullptr; - } - res = vkBindBufferMemory(g_vulkan_context->GetDevice(), buffer, memory, 0); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkBindBufferMemory failed: "); - vkDestroyBuffer(g_vulkan_context->GetDevice(), buffer, nullptr); - vkFreeMemory(g_vulkan_context->GetDevice(), memory, nullptr); - return nullptr; - } - - return std::make_unique(type, width, height, format, row_stride, buffer, - memory, buffer_size, is_coherent); + return std::make_unique(type, buffer, memory, size, coherent, width, height, + format, stride); } - } // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/StagingTexture2D.h b/Source/Core/VideoBackends/Vulkan/StagingTexture2D.h index 9dfd0c0e04..74dbb8cf07 100644 --- a/Source/Core/VideoBackends/Vulkan/StagingTexture2D.h +++ b/Source/Core/VideoBackends/Vulkan/StagingTexture2D.h @@ -9,29 +9,26 @@ #include "Common/CommonTypes.h" #include "VideoBackends/Vulkan/Constants.h" +#include "VideoBackends/Vulkan/StagingBuffer.h" namespace Vulkan { -class StagingTexture2D +class StagingTexture2D final : public StagingBuffer { public: - StagingTexture2D(STAGING_BUFFER_TYPE type, u32 width, u32 height, VkFormat format, u32 stride); - virtual ~StagingTexture2D(); + StagingTexture2D(STAGING_BUFFER_TYPE type, VkBuffer buffer, VkDeviceMemory memory, + VkDeviceSize size, bool coherent, u32 width, u32 height, VkFormat format, + u32 stride); + ~StagingTexture2D(); - STAGING_BUFFER_TYPE GetType() const { return m_type; } u32 GetWidth() const { return m_width; } u32 GetHeight() const { return m_height; } VkFormat GetFormat() const { return m_format; } u32 GetRowStride() const { return m_row_stride; } u32 GetTexelSize() const { return m_texel_size; } - bool IsMapped() const { return m_map_pointer != nullptr; } - const char* GetMapPointer() const { return m_map_pointer; } - char* GetMapPointer() { return m_map_pointer; } - VkDeviceSize GetMapOffset() const { return m_map_offset; } - VkDeviceSize GetMapSize() const { return m_map_size; } + // Requires Map() to be called first. const char* GetRowPointer(u32 row) const { return m_map_pointer + row * m_row_stride; } char* GetRowPointer(u32 row) { return m_map_pointer + row * m_row_stride; } - // Requires Map() to be called first. void ReadTexel(u32 x, u32 y, void* data, size_t data_size) const; void WriteTexel(u32 x, u32 y, const void* data, size_t data_size); void ReadTexels(u32 x, u32 y, u32 width, u32 height, void* data, u32 data_stride) const; @@ -39,89 +36,23 @@ public: // Assumes that image is in TRANSFER_SRC layout. // Results are not ready until command_buffer has been executed. - virtual void CopyFromImage(VkCommandBuffer command_buffer, VkImage image, - VkImageAspectFlags src_aspect, u32 x, u32 y, u32 width, u32 height, - u32 level, u32 layer) = 0; + void CopyFromImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags src_aspect, + u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer); // Assumes that image is in TRANSFER_DST layout. // Buffer is not safe for re-use until after command_buffer has been executed. - virtual void CopyToImage(VkCommandBuffer command_buffer, VkImage image, - VkImageAspectFlags dst_aspect, u32 x, u32 y, u32 width, u32 height, - u32 level, u32 layer) = 0; - virtual bool Map(VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE) = 0; - virtual void Unmap() = 0; + void CopyToImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags dst_aspect, + u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer); // Creates the optimal format of image copy. static std::unique_ptr Create(STAGING_BUFFER_TYPE type, u32 width, u32 height, VkFormat format); protected: - STAGING_BUFFER_TYPE m_type; u32 m_width; u32 m_height; VkFormat m_format; u32 m_texel_size; u32 m_row_stride; - - char* m_map_pointer = nullptr; - VkDeviceSize m_map_offset = 0; - VkDeviceSize m_map_size = 0; -}; - -class StagingTexture2DLinear : public StagingTexture2D -{ -public: - StagingTexture2DLinear(STAGING_BUFFER_TYPE type, u32 width, u32 height, VkFormat format, - u32 stride, VkImage image, VkDeviceMemory memory, VkDeviceSize size, - bool coherent); - - ~StagingTexture2DLinear(); - - void CopyFromImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags src_aspect, - u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer) override; - - void CopyToImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags dst_aspect, - u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer) override; - - bool Map(VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE) override; - void Unmap() override; - - static std::unique_ptr Create(STAGING_BUFFER_TYPE type, u32 width, u32 height, - VkFormat format); - -private: - VkImage m_image; - VkDeviceMemory m_memory; - VkDeviceSize m_size; - VkImageLayout m_layout; - bool m_coherent; -}; - -class StagingTexture2DBuffer : public StagingTexture2D -{ -public: - StagingTexture2DBuffer(STAGING_BUFFER_TYPE type, u32 width, u32 height, VkFormat format, - u32 stride, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize size, - bool coherent); - - ~StagingTexture2DBuffer(); - - void CopyFromImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags src_aspect, - u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer) override; - - void CopyToImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags dst_aspect, - u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer) override; - - bool Map(VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE) override; - void Unmap() override; - - static std::unique_ptr Create(STAGING_BUFFER_TYPE type, u32 width, u32 height, - VkFormat format); - -private: - VkBuffer m_buffer; - VkDeviceMemory m_memory; - VkDeviceSize m_size; - bool m_coherent; }; }