mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-30 01:29:42 -06:00
Merge pull request #6193 from stenzek/readbacks
Abstract Staging Textures - VideoCommon interface for texture readbacks/uploads
This commit is contained in:
@ -11,7 +11,6 @@ set(SRCS
|
||||
ShaderCompiler.cpp
|
||||
StateTracker.cpp
|
||||
StagingBuffer.cpp
|
||||
StagingTexture2D.cpp
|
||||
StreamBuffer.cpp
|
||||
SwapChain.cpp
|
||||
Texture2D.cpp
|
||||
|
@ -371,6 +371,12 @@ void CommandBufferManager::OnCommandBufferExecuted(size_t index)
|
||||
FrameResources& resources = m_frame_resources[index];
|
||||
|
||||
// Fire fence tracking callbacks.
|
||||
for (auto iter = m_fence_point_callbacks.begin(); iter != m_fence_point_callbacks.end();)
|
||||
{
|
||||
auto backup_iter = iter++;
|
||||
backup_iter->second.second(resources.fence);
|
||||
}
|
||||
|
||||
for (const auto& iter : m_fence_point_callbacks)
|
||||
iter.second.second(resources.fence);
|
||||
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
#include "VideoBackends/Vulkan/CommandBufferManager.h"
|
||||
#include "VideoBackends/Vulkan/ObjectCache.h"
|
||||
#include "VideoBackends/Vulkan/StagingTexture2D.h"
|
||||
#include "VideoBackends/Vulkan/StateTracker.h"
|
||||
#include "VideoBackends/Vulkan/StreamBuffer.h"
|
||||
#include "VideoBackends/Vulkan/Texture2D.h"
|
||||
@ -698,7 +697,7 @@ u32 FramebufferManager::PeekEFBColor(u32 x, u32 y)
|
||||
return 0;
|
||||
|
||||
u32 value;
|
||||
m_color_readback_texture->ReadTexel(x, y, &value, sizeof(value));
|
||||
m_color_readback_texture->ReadTexel(x, y, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
@ -711,7 +710,6 @@ bool FramebufferManager::PopulateColorReadbackTexture()
|
||||
// Issue a copy from framebuffer -> copy texture if we have >1xIR or MSAA on.
|
||||
VkRect2D src_region = {{0, 0}, {GetEFBWidth(), GetEFBHeight()}};
|
||||
Texture2D* src_texture = m_efb_color_texture.get();
|
||||
VkImageAspectFlags src_aspect = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
if (GetEFBSamples() > 1)
|
||||
src_texture = ResolveEFBColorTexture(src_region);
|
||||
|
||||
@ -750,9 +748,9 @@ bool FramebufferManager::PopulateColorReadbackTexture()
|
||||
// Copy from EFB or copy texture to staging texture.
|
||||
src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||
m_color_readback_texture->CopyFromImage(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
src_texture->GetImage(), src_aspect, 0, 0, EFB_WIDTH,
|
||||
EFB_HEIGHT, 0, 0);
|
||||
static_cast<VKStagingTexture*>(m_color_readback_texture.get())
|
||||
->CopyFromTexture(src_texture, m_color_readback_texture->GetConfig().GetRect(), 0, 0,
|
||||
m_color_readback_texture->GetConfig().GetRect());
|
||||
|
||||
// Restore original layout if we used the EFB as a source.
|
||||
if (src_texture == m_efb_color_texture.get())
|
||||
@ -762,12 +760,7 @@ bool FramebufferManager::PopulateColorReadbackTexture()
|
||||
}
|
||||
|
||||
// Wait until the copy is complete.
|
||||
Util::ExecuteCurrentCommandsAndRestoreState(false, true);
|
||||
|
||||
// Map to host memory.
|
||||
if (!m_color_readback_texture->IsMapped() && !m_color_readback_texture->Map())
|
||||
return false;
|
||||
|
||||
m_color_readback_texture->Flush();
|
||||
m_color_readback_texture_valid = true;
|
||||
return true;
|
||||
}
|
||||
@ -778,7 +771,7 @@ float FramebufferManager::PeekEFBDepth(u32 x, u32 y)
|
||||
return 0.0f;
|
||||
|
||||
float value;
|
||||
m_depth_readback_texture->ReadTexel(x, y, &value, sizeof(value));
|
||||
m_depth_readback_texture->ReadTexel(x, y, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
@ -791,12 +784,10 @@ bool FramebufferManager::PopulateDepthReadbackTexture()
|
||||
// Issue a copy from framebuffer -> copy texture if we have >1xIR or MSAA on.
|
||||
VkRect2D src_region = {{0, 0}, {GetEFBWidth(), GetEFBHeight()}};
|
||||
Texture2D* src_texture = m_efb_depth_texture.get();
|
||||
VkImageAspectFlags src_aspect = VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
if (GetEFBSamples() > 1)
|
||||
{
|
||||
// EFB depth resolves are written out as color textures
|
||||
src_texture = ResolveEFBDepthTexture(src_region);
|
||||
src_aspect = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
}
|
||||
if (GetEFBWidth() != EFB_WIDTH || GetEFBHeight() != EFB_HEIGHT)
|
||||
{
|
||||
@ -828,15 +819,14 @@ bool FramebufferManager::PopulateDepthReadbackTexture()
|
||||
|
||||
// Use this as a source texture now.
|
||||
src_texture = m_depth_copy_texture.get();
|
||||
src_aspect = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
}
|
||||
|
||||
// Copy from EFB or copy texture to staging texture.
|
||||
src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||
m_depth_readback_texture->CopyFromImage(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
src_texture->GetImage(), src_aspect, 0, 0, EFB_WIDTH,
|
||||
EFB_HEIGHT, 0, 0);
|
||||
static_cast<VKStagingTexture*>(m_depth_readback_texture.get())
|
||||
->CopyFromTexture(src_texture, m_depth_readback_texture->GetConfig().GetRect(), 0, 0,
|
||||
m_depth_readback_texture->GetConfig().GetRect());
|
||||
|
||||
// Restore original layout if we used the EFB as a source.
|
||||
if (src_texture == m_efb_depth_texture.get())
|
||||
@ -846,12 +836,7 @@ bool FramebufferManager::PopulateDepthReadbackTexture()
|
||||
}
|
||||
|
||||
// Wait until the copy is complete.
|
||||
Util::ExecuteCurrentCommandsAndRestoreState(false, true);
|
||||
|
||||
// Map to host memory.
|
||||
if (!m_depth_readback_texture->IsMapped() && !m_depth_readback_texture->Map())
|
||||
return false;
|
||||
|
||||
m_depth_readback_texture->Flush();
|
||||
m_depth_readback_texture_valid = true;
|
||||
return true;
|
||||
}
|
||||
@ -1011,32 +996,27 @@ bool FramebufferManager::CreateReadbackTextures()
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
|
||||
|
||||
m_color_readback_texture = StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, EFB_WIDTH,
|
||||
EFB_HEIGHT, EFB_COLOR_TEXTURE_FORMAT);
|
||||
if (!m_color_copy_texture || !m_color_readback_texture)
|
||||
{
|
||||
ERROR_LOG(VIDEO, "Failed to create EFB color readback texture");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_depth_copy_texture =
|
||||
Texture2D::Create(EFB_WIDTH, EFB_HEIGHT, 1, 1, EFB_DEPTH_AS_COLOR_TEXTURE_FORMAT,
|
||||
VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
|
||||
|
||||
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)
|
||||
if (!m_color_copy_texture || !m_depth_copy_texture)
|
||||
{
|
||||
ERROR_LOG(VIDEO, "Failed to create EFB depth readback texture");
|
||||
ERROR_LOG(VIDEO, "Failed to create EFB copy textures");
|
||||
return false;
|
||||
}
|
||||
|
||||
// With Vulkan, we can leave these textures mapped and use invalidate/flush calls instead.
|
||||
if (!m_color_readback_texture->Map() || !m_depth_readback_texture->Map())
|
||||
TextureConfig readback_texture_config(EFB_WIDTH, EFB_HEIGHT, 1, 1, AbstractTextureFormat::RGBA8,
|
||||
false);
|
||||
m_color_readback_texture =
|
||||
g_renderer->CreateStagingTexture(StagingTextureType::Mutable, readback_texture_config);
|
||||
m_depth_readback_texture =
|
||||
g_renderer->CreateStagingTexture(StagingTextureType::Mutable, readback_texture_config);
|
||||
if (!m_color_readback_texture || !m_depth_readback_texture)
|
||||
{
|
||||
ERROR_LOG(VIDEO, "Failed to map EFB readback textures");
|
||||
ERROR_LOG(VIDEO, "Failed to create EFB readback textures");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1113,7 +1093,7 @@ void FramebufferManager::PokeEFBColor(u32 x, u32 y, u32 color)
|
||||
|
||||
// Update the peek cache if it's valid, since we know the color of the pixel now.
|
||||
if (m_color_readback_texture_valid)
|
||||
m_color_readback_texture->WriteTexel(x, y, &color, sizeof(color));
|
||||
m_color_readback_texture->WriteTexel(x, y, &color);
|
||||
}
|
||||
|
||||
void FramebufferManager::PokeEFBDepth(u32 x, u32 y, float depth)
|
||||
@ -1126,7 +1106,7 @@ void FramebufferManager::PokeEFBDepth(u32 x, u32 y, float depth)
|
||||
|
||||
// Update the peek cache if it's valid, since we know the color of the pixel now.
|
||||
if (m_depth_readback_texture_valid)
|
||||
m_depth_readback_texture->WriteTexel(x, y, &depth, sizeof(depth));
|
||||
m_depth_readback_texture->WriteTexel(x, y, &depth);
|
||||
}
|
||||
|
||||
void FramebufferManager::CreatePokeVertices(std::vector<EFBPokeVertex>* destination_list, u32 x,
|
||||
|
@ -13,9 +13,10 @@
|
||||
#include "VideoCommon/FramebufferManagerBase.h"
|
||||
#include "VideoCommon/RenderState.h"
|
||||
|
||||
class AbstractStagingTexture;
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class StagingTexture2D;
|
||||
class StateTracker;
|
||||
class StreamBuffer;
|
||||
class Texture2D;
|
||||
@ -138,8 +139,8 @@ private:
|
||||
VkFramebuffer m_depth_copy_framebuffer = VK_NULL_HANDLE;
|
||||
|
||||
// CPU-side EFB readback texture
|
||||
std::unique_ptr<StagingTexture2D> m_color_readback_texture;
|
||||
std::unique_ptr<StagingTexture2D> m_depth_readback_texture;
|
||||
std::unique_ptr<AbstractStagingTexture> m_color_readback_texture;
|
||||
std::unique_ptr<AbstractStagingTexture> m_depth_readback_texture;
|
||||
bool m_color_readback_texture_valid = false;
|
||||
bool m_depth_readback_texture_valid = false;
|
||||
|
||||
|
@ -159,6 +159,17 @@ void Renderer::DestroySemaphores()
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<AbstractTexture> Renderer::CreateTexture(const TextureConfig& config)
|
||||
{
|
||||
return VKTexture::Create(config);
|
||||
}
|
||||
|
||||
std::unique_ptr<AbstractStagingTexture> Renderer::CreateStagingTexture(StagingTextureType type,
|
||||
const TextureConfig& config)
|
||||
{
|
||||
return VKStagingTexture::Create(type, config);
|
||||
}
|
||||
|
||||
void Renderer::RenderText(const std::string& text, int left, int top, u32 color)
|
||||
{
|
||||
u32 backbuffer_width = m_swap_chain->GetWidth();
|
||||
|
@ -32,6 +32,10 @@ public:
|
||||
|
||||
static Renderer* GetInstance();
|
||||
|
||||
std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) override;
|
||||
std::unique_ptr<AbstractStagingTexture>
|
||||
CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override;
|
||||
|
||||
SwapChain* GetSwapChain() const { return m_swap_chain.get(); }
|
||||
BoundingBox* GetBoundingBox() const { return m_bounding_box.get(); }
|
||||
bool Initialize();
|
||||
|
@ -59,11 +59,11 @@ public:
|
||||
static std::unique_ptr<StagingBuffer> Create(STAGING_BUFFER_TYPE type, VkDeviceSize size,
|
||||
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);
|
||||
|
||||
protected:
|
||||
STAGING_BUFFER_TYPE m_type;
|
||||
VkBuffer m_buffer;
|
||||
VkDeviceMemory m_memory;
|
||||
|
@ -1,164 +0,0 @@
|
||||
// Copyright 2016 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#include "Common/Assert.h"
|
||||
|
||||
#include "VideoBackends/Vulkan/CommandBufferManager.h"
|
||||
#include "VideoBackends/Vulkan/StagingTexture2D.h"
|
||||
#include "VideoBackends/Vulkan/Util.h"
|
||||
#include "VideoBackends/Vulkan/VulkanContext.h"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
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()
|
||||
{
|
||||
}
|
||||
|
||||
void StagingTexture2D::ReadTexel(u32 x, u32 y, void* data, size_t data_size) const
|
||||
{
|
||||
_assert_(data_size >= m_texel_size);
|
||||
|
||||
VkDeviceSize offset = y * m_row_stride + x * m_texel_size;
|
||||
VkDeviceSize map_offset = offset - m_map_offset;
|
||||
_assert_(offset >= m_map_offset && (map_offset + m_texel_size) <= (m_map_offset + m_map_size));
|
||||
|
||||
const char* ptr = m_map_pointer + map_offset;
|
||||
memcpy(data, ptr, data_size);
|
||||
}
|
||||
|
||||
void StagingTexture2D::WriteTexel(u32 x, u32 y, const void* data, size_t data_size)
|
||||
{
|
||||
_assert_(data_size >= m_texel_size);
|
||||
|
||||
VkDeviceSize offset = y * m_row_stride + x * m_texel_size;
|
||||
VkDeviceSize map_offset = offset - m_map_offset;
|
||||
_assert_(offset >= m_map_offset && (map_offset + m_texel_size) <= (m_map_offset + m_map_size));
|
||||
|
||||
char* ptr = m_map_pointer + map_offset;
|
||||
memcpy(ptr, data, data_size);
|
||||
}
|
||||
|
||||
void StagingTexture2D::ReadTexels(u32 x, u32 y, u32 width, u32 height, void* data,
|
||||
u32 data_stride) const
|
||||
{
|
||||
const char* src_ptr = GetRowPointer(y);
|
||||
|
||||
// Optimal path: same dimensions, same stride.
|
||||
_assert_((x + width) <= m_width && (y + height) <= m_height);
|
||||
if (x == 0 && width == m_width && m_row_stride == data_stride)
|
||||
{
|
||||
memcpy(data, src_ptr, m_row_stride * height);
|
||||
return;
|
||||
}
|
||||
|
||||
u32 copy_size = std::min(width * m_texel_size, data_stride);
|
||||
char* dst_ptr = reinterpret_cast<char*>(data);
|
||||
for (u32 row = 0; row < height; row++)
|
||||
{
|
||||
memcpy(dst_ptr, src_ptr + (x * m_texel_size), copy_size);
|
||||
src_ptr += m_row_stride;
|
||||
dst_ptr += data_stride;
|
||||
}
|
||||
}
|
||||
|
||||
void StagingTexture2D::WriteTexels(u32 x, u32 y, u32 width, u32 height, const void* data,
|
||||
u32 data_stride)
|
||||
{
|
||||
char* dst_ptr = GetRowPointer(y);
|
||||
|
||||
// Optimal path: same dimensions, same stride.
|
||||
_assert_((x + width) <= m_width && (y + height) <= m_height);
|
||||
if (x == 0 && width == m_width && m_row_stride == data_stride)
|
||||
{
|
||||
memcpy(dst_ptr, data, m_row_stride * height);
|
||||
return;
|
||||
}
|
||||
|
||||
u32 copy_size = std::min(width * m_texel_size, data_stride);
|
||||
const char* src_ptr = reinterpret_cast<const char*>(data);
|
||||
for (u32 row = 0; row < height; row++)
|
||||
{
|
||||
memcpy(dst_ptr + (x * m_texel_size), src_ptr, copy_size);
|
||||
dst_ptr += m_row_stride;
|
||||
src_ptr += data_stride;
|
||||
}
|
||||
}
|
||||
|
||||
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 = {
|
||||
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
|
||||
{static_cast<s32>(x), static_cast<s32>(y), 0}, // VkOffset3D imageOffset
|
||||
{width, height, 1} // VkExtent3D imageExtent
|
||||
};
|
||||
vkCmdCopyImageToBuffer(command_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_buffer, 1,
|
||||
&image_copy);
|
||||
|
||||
// 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 StagingTexture2D::CopyToImage(VkCommandBuffer command_buffer, VkImage image,
|
||||
VkImageAspectFlags dst_aspect, u32 x, u32 y, u32 width,
|
||||
u32 height, u32 level, u32 layer)
|
||||
{
|
||||
// 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);
|
||||
|
||||
// Issue the buffer->image copy.
|
||||
VkBufferImageCopy image_copy = {
|
||||
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
|
||||
{static_cast<s32>(x), static_cast<s32>(y), 0}, // VkOffset3D imageOffset
|
||||
{width, height, 1} // VkExtent3D imageExtent
|
||||
};
|
||||
vkCmdCopyBufferToImage(command_buffer, m_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
|
||||
&image_copy);
|
||||
}
|
||||
|
||||
std::unique_ptr<StagingTexture2D> StagingTexture2D::Create(STAGING_BUFFER_TYPE type, u32 width,
|
||||
u32 height, VkFormat format)
|
||||
{
|
||||
// Assume tight packing.
|
||||
u32 stride = Util::GetTexelSize(format) * width;
|
||||
u32 size = stride * height;
|
||||
VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
|
||||
VkBuffer buffer;
|
||||
VkDeviceMemory memory;
|
||||
bool coherent;
|
||||
if (!AllocateBuffer(type, size, usage, &buffer, &memory, &coherent))
|
||||
return nullptr;
|
||||
|
||||
return std::make_unique<StagingTexture2D>(type, buffer, memory, size, coherent, width, height,
|
||||
format, stride);
|
||||
}
|
||||
} // namespace Vulkan
|
@ -1,58 +0,0 @@
|
||||
// Copyright 2016 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "VideoBackends/Vulkan/Constants.h"
|
||||
#include "VideoBackends/Vulkan/StagingBuffer.h"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class StagingTexture2D final : public StagingBuffer
|
||||
{
|
||||
public:
|
||||
StagingTexture2D(STAGING_BUFFER_TYPE type, VkBuffer buffer, VkDeviceMemory memory,
|
||||
VkDeviceSize size, bool coherent, u32 width, u32 height, VkFormat format,
|
||||
u32 stride);
|
||||
~StagingTexture2D();
|
||||
|
||||
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; }
|
||||
// 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; }
|
||||
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;
|
||||
void WriteTexels(u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride);
|
||||
|
||||
// Assumes that image is in TRANSFER_SRC layout.
|
||||
// Results are not ready until command_buffer has been executed.
|
||||
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.
|
||||
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<StagingTexture2D> Create(STAGING_BUFFER_TYPE type, u32 width, u32 height,
|
||||
VkFormat format);
|
||||
|
||||
protected:
|
||||
u32 m_width;
|
||||
u32 m_height;
|
||||
VkFormat m_format;
|
||||
u32 m_texel_size;
|
||||
u32 m_row_stride;
|
||||
};
|
||||
}
|
@ -178,11 +178,6 @@ void TextureCache::DecodeTextureOnGPU(TCacheEntry* entry, u32 dst_level, const u
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<AbstractTexture> TextureCache::CreateTexture(const TextureConfig& config)
|
||||
{
|
||||
return VKTexture::Create(config);
|
||||
}
|
||||
|
||||
bool TextureCache::CreateRenderPasses()
|
||||
{
|
||||
static constexpr VkAttachmentDescription update_attachment = {
|
||||
|
@ -31,8 +31,6 @@ public:
|
||||
bool CompileShaders() override;
|
||||
void DeleteShaders() override;
|
||||
|
||||
std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) override;
|
||||
|
||||
void ConvertTexture(TCacheEntry* destination, TCacheEntry* source, const void* palette,
|
||||
TLUTFormat format) override;
|
||||
|
||||
|
@ -19,7 +19,6 @@
|
||||
#include "VideoBackends/Vulkan/CommandBufferManager.h"
|
||||
#include "VideoBackends/Vulkan/FramebufferManager.h"
|
||||
#include "VideoBackends/Vulkan/ObjectCache.h"
|
||||
#include "VideoBackends/Vulkan/StagingTexture2D.h"
|
||||
#include "VideoBackends/Vulkan/StateTracker.h"
|
||||
#include "VideoBackends/Vulkan/StreamBuffer.h"
|
||||
#include "VideoBackends/Vulkan/Texture2D.h"
|
||||
@ -67,9 +66,6 @@ TextureConverter::~TextureConverter()
|
||||
if (m_encoding_render_pass != VK_NULL_HANDLE)
|
||||
vkDestroyRenderPass(g_vulkan_context->GetDevice(), m_encoding_render_pass, nullptr);
|
||||
|
||||
if (m_encoding_render_framebuffer != VK_NULL_HANDLE)
|
||||
vkDestroyFramebuffer(g_vulkan_context->GetDevice(), m_encoding_render_framebuffer, nullptr);
|
||||
|
||||
for (auto& it : m_encoding_shaders)
|
||||
vkDestroyShaderModule(g_vulkan_context->GetDevice(), it.second, nullptr);
|
||||
|
||||
@ -111,12 +107,6 @@ bool TextureConverter::Initialize()
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CreateEncodingDownloadTexture())
|
||||
{
|
||||
PanicAlert("Failed to create download texture");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CreateDecodingTexture())
|
||||
{
|
||||
PanicAlert("Failed to create decoding texture");
|
||||
@ -245,8 +235,10 @@ void TextureConverter::EncodeTextureToMemory(VkImageView src_texture, u8* dest_p
|
||||
// Can't do our own draw within a render pass.
|
||||
StateTracker::GetInstance()->EndRenderPass();
|
||||
|
||||
m_encoding_render_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
static_cast<VKTexture*>(m_encoding_render_texture.get())
|
||||
->GetRawTexIdentifier()
|
||||
->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
|
||||
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_PUSH_CONSTANT),
|
||||
@ -276,23 +268,15 @@ void TextureConverter::EncodeTextureToMemory(VkImageView src_texture, u8* dest_p
|
||||
render_height);
|
||||
|
||||
VkRect2D render_region = {{0, 0}, {render_width, render_height}};
|
||||
draw.BeginRenderPass(m_encoding_render_framebuffer, render_region);
|
||||
draw.BeginRenderPass(static_cast<VKTexture*>(m_encoding_render_texture.get())->GetFramebuffer(),
|
||||
render_region);
|
||||
draw.DrawWithoutVertexBuffer(4);
|
||||
draw.EndRenderPass();
|
||||
|
||||
// Transition the image before copying
|
||||
m_encoding_render_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||
m_encoding_download_texture->CopyFromImage(
|
||||
g_command_buffer_mgr->GetCurrentCommandBuffer(), m_encoding_render_texture->GetImage(),
|
||||
VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, render_width, render_height, 0, 0);
|
||||
|
||||
// Block until the GPU has finished copying to the staging texture.
|
||||
Util::ExecuteCurrentCommandsAndRestoreState(false, true);
|
||||
|
||||
// Copy from staging texture to the final destination, adjusting pitch if necessary.
|
||||
m_encoding_download_texture->ReadTexels(0, 0, render_width, render_height, dest_ptr,
|
||||
memory_stride);
|
||||
MathUtil::Rectangle<int> copy_rect(0, 0, render_width, render_height);
|
||||
m_encoding_readback_texture->CopyFromTexture(m_encoding_render_texture.get(), copy_rect, 0, 0,
|
||||
copy_rect);
|
||||
m_encoding_readback_texture->ReadTexels(copy_rect, dest_ptr, memory_stride);
|
||||
}
|
||||
|
||||
void TextureConverter::EncodeTextureToMemoryYUYV(void* dst_ptr, u32 dst_width, u32 dst_stride,
|
||||
@ -304,8 +288,9 @@ void TextureConverter::EncodeTextureToMemoryYUYV(void* dst_ptr, u32 dst_width, u
|
||||
// Borrow framebuffer from EFB2RAM encoder.
|
||||
VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer();
|
||||
src_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
m_encoding_render_texture->TransitionToLayout(command_buffer,
|
||||
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
static_cast<VKTexture*>(m_encoding_render_texture.get())
|
||||
->GetRawTexIdentifier()
|
||||
->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
|
||||
// Use fragment shader to convert RGBA to YUYV.
|
||||
// Use linear sampler for downscaling. This texture is in BGRA order, so the data is already in
|
||||
@ -317,7 +302,8 @@ void TextureConverter::EncodeTextureToMemoryYUYV(void* dst_ptr, u32 dst_width, u
|
||||
m_encoding_render_pass, g_shader_cache->GetPassthroughVertexShader(),
|
||||
VK_NULL_HANDLE, m_rgb_to_yuyv_shader);
|
||||
VkRect2D region = {{0, 0}, {output_width, dst_height}};
|
||||
draw.BeginRenderPass(m_encoding_render_framebuffer, region);
|
||||
draw.BeginRenderPass(static_cast<VKTexture*>(m_encoding_render_texture.get())->GetFramebuffer(),
|
||||
region);
|
||||
draw.SetPSSampler(0, src_texture->GetView(), g_object_cache->GetLinearSampler());
|
||||
draw.DrawQuad(0, 0, static_cast<int>(output_width), static_cast<int>(dst_height), src_rect.left,
|
||||
src_rect.top, 0, src_rect.GetWidth(), src_rect.GetHeight(),
|
||||
@ -325,18 +311,11 @@ void TextureConverter::EncodeTextureToMemoryYUYV(void* dst_ptr, u32 dst_width, u
|
||||
static_cast<int>(src_texture->GetHeight()));
|
||||
draw.EndRenderPass();
|
||||
|
||||
// Render pass transitions to TRANSFER_SRC.
|
||||
m_encoding_render_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||
|
||||
// Copy from encoding texture to download buffer.
|
||||
m_encoding_download_texture->CopyFromImage(command_buffer, m_encoding_render_texture->GetImage(),
|
||||
VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, output_width,
|
||||
dst_height, 0, 0);
|
||||
Util::ExecuteCurrentCommandsAndRestoreState(false, true);
|
||||
|
||||
// Finally, copy to guest memory. This may have a different stride.
|
||||
m_encoding_download_texture->ReadTexels(0, 0, output_width, dst_height, dst_ptr, dst_stride);
|
||||
MathUtil::Rectangle<int> copy_rect(0, 0, output_width, dst_height);
|
||||
m_encoding_readback_texture->CopyFromTexture(m_encoding_render_texture.get(), copy_rect, 0, 0,
|
||||
copy_rect);
|
||||
m_encoding_readback_texture->ReadTexels(copy_rect, dst_ptr, dst_stride);
|
||||
}
|
||||
|
||||
void TextureConverter::DecodeYUYVTextureFromMemory(VKTexture* dst_texture, const void* src_ptr,
|
||||
@ -734,10 +713,10 @@ VkShaderModule TextureConverter::GetEncodingShader(const EFBCopyParams& params)
|
||||
bool TextureConverter::CreateEncodingRenderPass()
|
||||
{
|
||||
VkAttachmentDescription attachments[] = {
|
||||
{0, ENCODING_TEXTURE_FORMAT, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||
VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||
VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}};
|
||||
{0, Util::GetVkFormatForHostTextureFormat(ENCODING_TEXTURE_FORMAT), VK_SAMPLE_COUNT_1_BIT,
|
||||
VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_STORE,
|
||||
VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}};
|
||||
|
||||
VkAttachmentReference color_attachment_references[] = {
|
||||
{0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}};
|
||||
@ -769,43 +748,14 @@ bool TextureConverter::CreateEncodingRenderPass()
|
||||
|
||||
bool TextureConverter::CreateEncodingTexture()
|
||||
{
|
||||
m_encoding_render_texture = Texture2D::Create(
|
||||
ENCODING_TEXTURE_WIDTH, ENCODING_TEXTURE_HEIGHT, 1, 1, ENCODING_TEXTURE_FORMAT,
|
||||
VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
|
||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
|
||||
if (!m_encoding_render_texture)
|
||||
return false;
|
||||
TextureConfig config(ENCODING_TEXTURE_WIDTH, ENCODING_TEXTURE_HEIGHT, 1, 1,
|
||||
ENCODING_TEXTURE_FORMAT, true);
|
||||
|
||||
VkImageView framebuffer_attachments[] = {m_encoding_render_texture->GetView()};
|
||||
VkFramebufferCreateInfo framebuffer_info = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||||
nullptr,
|
||||
0,
|
||||
m_encoding_render_pass,
|
||||
static_cast<u32>(ArraySize(framebuffer_attachments)),
|
||||
framebuffer_attachments,
|
||||
m_encoding_render_texture->GetWidth(),
|
||||
m_encoding_render_texture->GetHeight(),
|
||||
m_encoding_render_texture->GetLayers()};
|
||||
m_encoding_render_texture = g_renderer->CreateTexture(config);
|
||||
m_encoding_readback_texture =
|
||||
g_renderer->CreateStagingTexture(StagingTextureType::Readback, config);
|
||||
|
||||
VkResult res = vkCreateFramebuffer(g_vulkan_context->GetDevice(), &framebuffer_info, nullptr,
|
||||
&m_encoding_render_framebuffer);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateFramebuffer failed: ");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextureConverter::CreateEncodingDownloadTexture()
|
||||
{
|
||||
m_encoding_download_texture =
|
||||
StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, ENCODING_TEXTURE_WIDTH,
|
||||
ENCODING_TEXTURE_HEIGHT, ENCODING_TEXTURE_FORMAT);
|
||||
|
||||
return m_encoding_download_texture && m_encoding_download_texture->Map();
|
||||
return m_encoding_render_texture && m_encoding_readback_texture;
|
||||
}
|
||||
|
||||
bool TextureConverter::CreateDecodingTexture()
|
||||
|
@ -16,6 +16,9 @@
|
||||
#include "VideoCommon/TextureDecoder.h"
|
||||
#include "VideoCommon/VideoCommon.h"
|
||||
|
||||
class AbstractTexture;
|
||||
class AbstractStagingTexture;
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class StagingTexture2D;
|
||||
@ -58,7 +61,7 @@ public:
|
||||
private:
|
||||
static const u32 ENCODING_TEXTURE_WIDTH = EFB_WIDTH * 4;
|
||||
static const u32 ENCODING_TEXTURE_HEIGHT = 1024;
|
||||
static const VkFormat ENCODING_TEXTURE_FORMAT = VK_FORMAT_B8G8R8A8_UNORM;
|
||||
static const AbstractTextureFormat ENCODING_TEXTURE_FORMAT = AbstractTextureFormat::BGRA8;
|
||||
static const size_t NUM_PALETTE_CONVERSION_SHADERS = 3;
|
||||
|
||||
// Maximum size of a texture based on BP registers.
|
||||
@ -75,8 +78,6 @@ private:
|
||||
|
||||
bool CreateEncodingRenderPass();
|
||||
bool CreateEncodingTexture();
|
||||
bool CreateEncodingDownloadTexture();
|
||||
|
||||
bool CreateDecodingTexture();
|
||||
|
||||
bool CompileYUYVConversionShaders();
|
||||
@ -106,10 +107,9 @@ private:
|
||||
|
||||
// Texture encoding - RGBA8->GX format in memory
|
||||
std::map<EFBCopyParams, VkShaderModule> m_encoding_shaders;
|
||||
std::unique_ptr<AbstractTexture> m_encoding_render_texture;
|
||||
std::unique_ptr<AbstractStagingTexture> m_encoding_readback_texture;
|
||||
VkRenderPass m_encoding_render_pass = VK_NULL_HANDLE;
|
||||
std::unique_ptr<Texture2D> m_encoding_render_texture;
|
||||
VkFramebuffer m_encoding_render_framebuffer = VK_NULL_HANDLE;
|
||||
std::unique_ptr<StagingTexture2D> m_encoding_download_texture;
|
||||
|
||||
// Texture decoding - GX format in memory->RGBA8
|
||||
struct TextureDecodingPipeline
|
||||
|
@ -107,7 +107,13 @@ VkFormat GetVkFormatForHostTextureFormat(AbstractTextureFormat format)
|
||||
return VK_FORMAT_BC7_UNORM_BLOCK;
|
||||
|
||||
case AbstractTextureFormat::RGBA8:
|
||||
return VK_FORMAT_R8G8B8A8_UNORM;
|
||||
|
||||
case AbstractTextureFormat::BGRA8:
|
||||
return VK_FORMAT_B8G8R8A8_UNORM;
|
||||
|
||||
default:
|
||||
PanicAlert("Unhandled texture format.");
|
||||
return VK_FORMAT_R8G8B8A8_UNORM;
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
#include "VideoBackends/Vulkan/CommandBufferManager.h"
|
||||
#include "VideoBackends/Vulkan/FramebufferManager.h"
|
||||
#include "VideoBackends/Vulkan/StagingTexture2D.h"
|
||||
#include "VideoBackends/Vulkan/StagingBuffer.h"
|
||||
#include "VideoBackends/Vulkan/StateTracker.h"
|
||||
#include "VideoBackends/Vulkan/Texture2D.h"
|
||||
#include "VideoBackends/Vulkan/Util.h"
|
||||
@ -113,60 +113,13 @@ void VKTexture::Bind(unsigned int stage)
|
||||
StateTracker::GetInstance()->SetTexture(stage, m_texture->GetView());
|
||||
}
|
||||
|
||||
std::optional<AbstractTexture::RawTextureInfo> VKTexture::MapFullImpl()
|
||||
void VKTexture::CopyRectangleFromTexture(const AbstractTexture* src,
|
||||
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
|
||||
u32 src_level, const MathUtil::Rectangle<int>& dst_rect,
|
||||
u32 dst_layer, u32 dst_level)
|
||||
{
|
||||
// No support for optimization of full copy
|
||||
return MapRegionImpl(0, 0, 0, m_config.width, m_config.height);
|
||||
}
|
||||
Texture2D* src_texture = static_cast<const VKTexture*>(src)->GetRawTexIdentifier();
|
||||
|
||||
std::optional<AbstractTexture::RawTextureInfo> VKTexture::MapRegionImpl(u32 level, u32 x, u32 y,
|
||||
u32 width, u32 height)
|
||||
{
|
||||
m_staging_texture = StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, width, height,
|
||||
TEXTURECACHE_TEXTURE_FORMAT);
|
||||
|
||||
// Transition image to transfer source, and invalidate the current state,
|
||||
// since we'll be executing the command buffer.
|
||||
m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||
StateTracker::GetInstance()->EndRenderPass();
|
||||
|
||||
// Copy to download buffer.
|
||||
m_staging_texture->CopyFromImage(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
m_texture->GetImage(), VK_IMAGE_ASPECT_COLOR_BIT, x, y, width,
|
||||
height, level, 0);
|
||||
|
||||
// Restore original state of texture.
|
||||
m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
|
||||
// Block until the GPU has finished copying to the staging texture.
|
||||
Util::ExecuteCurrentCommandsAndRestoreState(false, true);
|
||||
|
||||
// Map the staging texture so we can copy the contents out.
|
||||
if (!m_staging_texture->Map())
|
||||
{
|
||||
PanicAlert("Failed to map staging texture");
|
||||
return {};
|
||||
}
|
||||
|
||||
return AbstractTexture::RawTextureInfo{reinterpret_cast<u8*>(m_staging_texture->GetMapPointer()),
|
||||
static_cast<u32>(m_staging_texture->GetRowStride()), width,
|
||||
height};
|
||||
}
|
||||
|
||||
void VKTexture::Unmap()
|
||||
{
|
||||
if (!m_staging_texture)
|
||||
return;
|
||||
|
||||
m_staging_texture->Unmap();
|
||||
}
|
||||
|
||||
void VKTexture::CopyTextureRectangle(const MathUtil::Rectangle<int>& dst_rect,
|
||||
Texture2D* src_texture,
|
||||
const MathUtil::Rectangle<int>& src_rect)
|
||||
{
|
||||
_assert_msg_(VIDEO, static_cast<u32>(src_rect.GetWidth()) <= src_texture->GetWidth() &&
|
||||
static_cast<u32>(src_rect.GetHeight()) <= src_texture->GetHeight(),
|
||||
"Source rect is too large for CopyRectangleFromTexture");
|
||||
@ -176,15 +129,11 @@ void VKTexture::CopyTextureRectangle(const MathUtil::Rectangle<int>& dst_rect,
|
||||
"Dest rect is too large for CopyRectangleFromTexture");
|
||||
|
||||
VkImageCopy image_copy = {
|
||||
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 0,
|
||||
src_texture->GetLayers()}, // VkImageSubresourceLayers srcSubresource
|
||||
{src_rect.left, src_rect.top, 0}, // VkOffset3D srcOffset
|
||||
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, // VkImageSubresourceLayers dstSubresource
|
||||
m_config.layers},
|
||||
{dst_rect.left, dst_rect.top, 0}, // VkOffset3D dstOffset
|
||||
{static_cast<uint32_t>(src_rect.GetWidth()), static_cast<uint32_t>(src_rect.GetHeight()),
|
||||
1} // VkExtent3D extent
|
||||
};
|
||||
{VK_IMAGE_ASPECT_COLOR_BIT, src_level, src_layer, src_texture->GetLayers()},
|
||||
{src_rect.left, src_rect.top, 0},
|
||||
{VK_IMAGE_ASPECT_COLOR_BIT, dst_level, dst_layer, m_config.layers},
|
||||
{dst_rect.left, dst_rect.top, 0},
|
||||
{static_cast<uint32_t>(src_rect.GetWidth()), static_cast<uint32_t>(src_rect.GetHeight()), 1}};
|
||||
|
||||
// Must be called outside of a render pass.
|
||||
StateTracker::GetInstance()->EndRenderPass();
|
||||
@ -197,12 +146,20 @@ void VKTexture::CopyTextureRectangle(const MathUtil::Rectangle<int>& dst_rect,
|
||||
vkCmdCopyImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), src_texture->GetImage(),
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_texture->GetImage(),
|
||||
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::ScaleTextureRectangle(const MathUtil::Rectangle<int>& dst_rect,
|
||||
Texture2D* src_texture,
|
||||
const MathUtil::Rectangle<int>& src_rect)
|
||||
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();
|
||||
@ -235,27 +192,10 @@ void VKTexture::ScaleTextureRectangle(const MathUtil::Rectangle<int>& dst_rect,
|
||||
static_cast<int>(src_texture->GetWidth()),
|
||||
static_cast<int>(src_texture->GetHeight()));
|
||||
draw.EndRenderPass();
|
||||
}
|
||||
|
||||
void VKTexture::CopyRectangleFromTexture(const AbstractTexture* source,
|
||||
const MathUtil::Rectangle<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& dstrect)
|
||||
{
|
||||
auto* raw_source_texture = static_cast<const VKTexture*>(source)->GetRawTexIdentifier();
|
||||
CopyRectangleFromTexture(raw_source_texture, srcrect, dstrect);
|
||||
}
|
||||
|
||||
void VKTexture::CopyRectangleFromTexture(Texture2D* source, const MathUtil::Rectangle<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& dstrect)
|
||||
{
|
||||
if (srcrect.GetWidth() == dstrect.GetWidth() && srcrect.GetHeight() == dstrect.GetHeight())
|
||||
CopyTextureRectangle(dstrect, source, srcrect);
|
||||
else
|
||||
ScaleTextureRectangle(dstrect, source, srcrect);
|
||||
|
||||
// Ensure both textures remain in the SHADER_READ_ONLY layout so they can be bound.
|
||||
source->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
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);
|
||||
}
|
||||
@ -291,7 +231,7 @@ void VKTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8*
|
||||
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 = CalculateHostTextureLevelPitch(m_config.format, row_length);
|
||||
size_t source_pitch = CalculateStrideForFormat(m_config.format, row_length);
|
||||
size_t upload_size = source_pitch * num_rows;
|
||||
std::unique_ptr<StagingBuffer> temp_buffer;
|
||||
VkBuffer upload_buffer;
|
||||
@ -356,4 +296,224 @@ void VKTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8*
|
||||
}
|
||||
}
|
||||
|
||||
VKStagingTexture::VKStagingTexture(StagingTextureType type, const TextureConfig& config,
|
||||
std::unique_ptr<StagingBuffer> buffer)
|
||||
: AbstractStagingTexture(type, config), m_staging_buffer(std::move(buffer))
|
||||
{
|
||||
}
|
||||
|
||||
VKStagingTexture::~VKStagingTexture()
|
||||
{
|
||||
if (m_needs_flush)
|
||||
VKStagingTexture::Flush();
|
||||
}
|
||||
|
||||
std::unique_ptr<VKStagingTexture> VKStagingTexture::Create(StagingTextureType type,
|
||||
const TextureConfig& config)
|
||||
{
|
||||
size_t stride = config.GetStride();
|
||||
size_t buffer_size = stride * static_cast<size_t>(config.height);
|
||||
|
||||
STAGING_BUFFER_TYPE buffer_type;
|
||||
VkImageUsageFlags buffer_usage;
|
||||
if (type == StagingTextureType::Readback)
|
||||
{
|
||||
buffer_type = STAGING_BUFFER_TYPE_READBACK;
|
||||
buffer_usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
}
|
||||
else if (type == StagingTextureType::Upload)
|
||||
{
|
||||
buffer_type = STAGING_BUFFER_TYPE_UPLOAD;
|
||||
buffer_usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer_type = STAGING_BUFFER_TYPE_READBACK;
|
||||
buffer_usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
}
|
||||
|
||||
VkBuffer buffer;
|
||||
VkDeviceMemory memory;
|
||||
bool coherent;
|
||||
if (!StagingBuffer::AllocateBuffer(buffer_type, buffer_size, buffer_usage, &buffer, &memory,
|
||||
&coherent))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<StagingBuffer> staging_buffer =
|
||||
std::make_unique<StagingBuffer>(buffer_type, buffer, memory, buffer_size, coherent);
|
||||
std::unique_ptr<VKStagingTexture> staging_tex = std::unique_ptr<VKStagingTexture>(
|
||||
new VKStagingTexture(type, config, std::move(staging_buffer)));
|
||||
|
||||
// Use persistent mapping.
|
||||
if (!staging_tex->m_staging_buffer->Map())
|
||||
return nullptr;
|
||||
staging_tex->m_map_pointer = staging_tex->m_staging_buffer->GetMapPointer();
|
||||
staging_tex->m_map_stride = stride;
|
||||
return staging_tex;
|
||||
}
|
||||
|
||||
void VKStagingTexture::CopyFromTexture(const AbstractTexture* src,
|
||||
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
|
||||
u32 src_level, const MathUtil::Rectangle<int>& dst_rect)
|
||||
{
|
||||
_assert_(m_type == StagingTextureType::Readback);
|
||||
_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_(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);
|
||||
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);
|
||||
|
||||
// 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;
|
||||
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);
|
||||
image_copy.bufferRowLength = static_cast<u32>(m_config.width);
|
||||
image_copy.bufferImageHeight = 0;
|
||||
image_copy.imageSubresource = {aspect, src_level, src_layer, 1};
|
||||
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(),
|
||||
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);
|
||||
|
||||
m_needs_flush = true;
|
||||
g_command_buffer_mgr->AddFencePointCallback(this,
|
||||
[this](VkCommandBuffer buf, VkFence fence) {
|
||||
_assert_(m_needs_flush);
|
||||
m_flush_fence = fence;
|
||||
},
|
||||
[this](VkFence fence) {
|
||||
m_flush_fence = VK_NULL_HANDLE;
|
||||
m_needs_flush = false;
|
||||
g_command_buffer_mgr->RemoveFencePointCallback(
|
||||
this);
|
||||
});
|
||||
}
|
||||
|
||||
void VKStagingTexture::CopyToTexture(const MathUtil::Rectangle<int>& src_rect, AbstractTexture* dst,
|
||||
const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer,
|
||||
u32 dst_level)
|
||||
{
|
||||
_assert_(m_type == StagingTextureType::Upload);
|
||||
_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);
|
||||
|
||||
if (m_needs_flush)
|
||||
{
|
||||
// Drop copy before reusing it.
|
||||
g_command_buffer_mgr->RemoveFencePointCallback(this);
|
||||
m_flush_fence = VK_NULL_HANDLE;
|
||||
m_needs_flush = false;
|
||||
}
|
||||
|
||||
// Flush caches before copying.
|
||||
m_staging_buffer->FlushCPUCache();
|
||||
|
||||
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);
|
||||
|
||||
// Issue the image->buffer copy, but delay it for now.
|
||||
VkBufferImageCopy image_copy = {};
|
||||
image_copy.bufferOffset =
|
||||
static_cast<VkDeviceSize>(static_cast<size_t>(src_rect.top) * m_config.GetStride() +
|
||||
static_cast<size_t>(src_rect.left) * m_texel_size);
|
||||
image_copy.bufferRowLength = static_cast<u32>(m_config.width);
|
||||
image_copy.bufferImageHeight = 0;
|
||||
image_copy.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, dst_level, dst_layer, 1};
|
||||
image_copy.imageOffset = {dst_rect.left, dst_rect.top, 0};
|
||||
image_copy.imageExtent = {static_cast<u32>(dst_rect.GetWidth()),
|
||||
static_cast<u32>(dst_rect.GetHeight()), 1u};
|
||||
vkCmdCopyBufferToImage(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
m_staging_buffer->GetBuffer(), dst_tex->GetImage(),
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy);
|
||||
|
||||
// Restore old source texture layout.
|
||||
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);
|
||||
m_flush_fence = fence;
|
||||
},
|
||||
[this](VkFence fence) {
|
||||
m_flush_fence = VK_NULL_HANDLE;
|
||||
m_needs_flush = false;
|
||||
g_command_buffer_mgr->RemoveFencePointCallback(
|
||||
this);
|
||||
});
|
||||
}
|
||||
|
||||
bool VKStagingTexture::Map()
|
||||
{
|
||||
// Always mapped.
|
||||
return true;
|
||||
}
|
||||
|
||||
void VKStagingTexture::Unmap()
|
||||
{
|
||||
// Always mapped.
|
||||
}
|
||||
|
||||
void VKStagingTexture::Flush()
|
||||
{
|
||||
if (!m_needs_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)
|
||||
{
|
||||
// WaitForFence should fire the callback.
|
||||
g_command_buffer_mgr->WaitForFence(m_flush_fence);
|
||||
}
|
||||
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);
|
||||
}
|
||||
m_needs_flush = false;
|
||||
|
||||
// For readback textures, invalidate the CPU cache as there is new data there.
|
||||
if (m_type == StagingTextureType::Readback)
|
||||
m_staging_buffer->InvalidateCPUCache();
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
|
@ -7,10 +7,12 @@
|
||||
#include <memory>
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
#include "VideoCommon/AbstractStagingTexture.h"
|
||||
#include "VideoCommon/AbstractTexture.h"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class StagingBuffer;
|
||||
class Texture2D;
|
||||
|
||||
class VKTexture final : public AbstractTexture
|
||||
@ -20,13 +22,16 @@ public:
|
||||
~VKTexture();
|
||||
|
||||
void Bind(unsigned int stage) override;
|
||||
void Unmap() override;
|
||||
|
||||
void CopyRectangleFromTexture(const AbstractTexture* source,
|
||||
const MathUtil::Rectangle<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& dstrect) override;
|
||||
void CopyRectangleFromTexture(Texture2D* source, const MathUtil::Rectangle<int>& srcrect,
|
||||
const MathUtil::Rectangle<int>& dstrect);
|
||||
void CopyRectangleFromTexture(const AbstractTexture* src,
|
||||
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
|
||||
u32 src_level, const MathUtil::Rectangle<int>& dst_rect,
|
||||
u32 dst_layer, u32 dst_level) override;
|
||||
void ScaleRectangleFromTexture(const AbstractTexture* source,
|
||||
const MathUtil::Rectangle<int>& src_rect,
|
||||
const MathUtil::Rectangle<int>& dst_rect);
|
||||
void ScaleRectangleFromTexture(Texture2D* src_texture, const MathUtil::Rectangle<int>& src_rect,
|
||||
const MathUtil::Rectangle<int>& dst_rect);
|
||||
void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer,
|
||||
size_t buffer_size) override;
|
||||
|
||||
@ -39,21 +44,41 @@ private:
|
||||
VKTexture(const TextureConfig& tex_config, std::unique_ptr<Texture2D> texture,
|
||||
VkFramebuffer framebuffer);
|
||||
|
||||
// Copies the contents of a texture using vkCmdCopyImage
|
||||
void CopyTextureRectangle(const MathUtil::Rectangle<int>& dst_rect, Texture2D* src_texture,
|
||||
const MathUtil::Rectangle<int>& src_rect);
|
||||
|
||||
// Copies (and optionally scales) the contents of a texture using a framgent shader.
|
||||
void ScaleTextureRectangle(const MathUtil::Rectangle<int>& dst_rect, Texture2D* src_texture,
|
||||
const MathUtil::Rectangle<int>& src_rect);
|
||||
|
||||
std::optional<RawTextureInfo> MapFullImpl() override;
|
||||
std::optional<RawTextureInfo> MapRegionImpl(u32 level, u32 x, u32 y, u32 width,
|
||||
u32 height) override;
|
||||
|
||||
std::unique_ptr<Texture2D> m_texture;
|
||||
std::unique_ptr<StagingTexture2D> m_staging_texture;
|
||||
VkFramebuffer m_framebuffer;
|
||||
};
|
||||
|
||||
class VKStagingTexture final : public AbstractStagingTexture
|
||||
{
|
||||
public:
|
||||
VKStagingTexture() = delete;
|
||||
~VKStagingTexture();
|
||||
|
||||
void CopyFromTexture(const AbstractTexture* src, const MathUtil::Rectangle<int>& src_rect,
|
||||
u32 src_layer, u32 src_level,
|
||||
const MathUtil::Rectangle<int>& dst_rect) override;
|
||||
void CopyToTexture(const MathUtil::Rectangle<int>& src_rect, AbstractTexture* dst,
|
||||
const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer,
|
||||
u32 dst_level) override;
|
||||
|
||||
bool Map() override;
|
||||
void Unmap() override;
|
||||
void Flush() override;
|
||||
|
||||
// This overload is provided for compatibility as we dropped StagingTexture2D.
|
||||
// For now, FramebufferManager relies on them. But we can drop it once we move that to common.
|
||||
void CopyFromTexture(Texture2D* src, const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
|
||||
u32 src_level, const MathUtil::Rectangle<int>& dst_rect);
|
||||
|
||||
static std::unique_ptr<VKStagingTexture> Create(StagingTextureType type,
|
||||
const TextureConfig& config);
|
||||
|
||||
private:
|
||||
VKStagingTexture(StagingTextureType type, const TextureConfig& config,
|
||||
std::unique_ptr<StagingBuffer> buffer);
|
||||
|
||||
std::unique_ptr<StagingBuffer> m_staging_buffer;
|
||||
VkFence m_flush_fence = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
@ -46,7 +46,6 @@
|
||||
<ClCompile Include="PerfQuery.cpp" />
|
||||
<ClCompile Include="RasterFont.cpp" />
|
||||
<ClCompile Include="StagingBuffer.cpp" />
|
||||
<ClCompile Include="StagingTexture2D.cpp" />
|
||||
<ClCompile Include="Util.cpp" />
|
||||
<ClCompile Include="VertexFormat.cpp" />
|
||||
<ClCompile Include="ObjectCache.cpp" />
|
||||
@ -72,7 +71,6 @@
|
||||
<ClInclude Include="TextureConverter.h" />
|
||||
<ClInclude Include="RasterFont.h" />
|
||||
<ClInclude Include="StagingBuffer.h" />
|
||||
<ClInclude Include="StagingTexture2D.h" />
|
||||
<ClInclude Include="Util.h" />
|
||||
<ClInclude Include="VertexFormat.h" />
|
||||
<ClInclude Include="PerfQuery.h" />
|
||||
|
Reference in New Issue
Block a user