mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 00:12:40 +00:00
fix: render-target aliases were synchronously read back and copied through host memory twice
This commit is contained in:
1 parent
42f634a751
commit
728b5b7d01
6 files changed
+116
-51
No files matched your search
@@ -49,14 +49,15 @@ struct GraphicContext: public VulkanInstance {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct VulkanMemory {
|
struct VulkanMemory {
|
||||||
vk::MemoryRequirements requirements = {};
|
vk::MemoryRequirements requirements = {};
|
||||||
vk::MemoryPropertyFlags property = {};
|
vk::MemoryPropertyFlags property = {};
|
||||||
vk::DeviceMemory memory = nullptr;
|
vk::MemoryPropertyFlags preferred_property = {};
|
||||||
VmaAllocation allocation = nullptr;
|
vk::DeviceMemory memory = nullptr;
|
||||||
VmaAllocationInfo allocation_info = {};
|
VmaAllocation allocation = nullptr;
|
||||||
vk::DeviceSize offset = 0;
|
VmaAllocationInfo allocation_info = {};
|
||||||
uint32_t type = 0;
|
vk::DeviceSize offset = 0;
|
||||||
uint64_t unique_id = 0;
|
uint32_t type = 0;
|
||||||
|
uint64_t unique_id = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class VulkanImageType {
|
enum class VulkanImageType {
|
||||||
|
|||||||
@@ -3015,11 +3015,9 @@ void TextureCache::SynchronizeColorImageToBufferLocked(CachedImage& cached, uint
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This is the CPU Tiler backend for the image-to-buffer synchronization seam.
|
// This is the CPU Tiler backend for the image-to-buffer synchronization seam.
|
||||||
// The vectors and Vulkan staging allocation retain capacity; a future PS5 GPU tiler can replace
|
// The guest vector and Vulkan staging allocation retain capacity; a future PS5 GPU tiler can
|
||||||
// this block without changing alias classification or ownership transitions.
|
// replace this block without changing alias classification or ownership transitions.
|
||||||
Transfer::WaitForGraphicsIdle(cached.ctx);
|
Transfer::WaitForGraphicsIdle(cached.ctx);
|
||||||
m_buffer_transition_linear.resize(target.size);
|
|
||||||
std::fill(m_buffer_transition_linear.begin(), m_buffer_transition_linear.end(), 0);
|
|
||||||
std::vector<ImageBufferCopy> regions;
|
std::vector<ImageBufferCopy> regions;
|
||||||
if (storage) {
|
if (storage) {
|
||||||
auto layout = TextureCalcUploadLayout(
|
auto layout = TextureCalcUploadLayout(
|
||||||
@@ -3040,24 +3038,23 @@ void TextureCache::SynchronizeColorImageToBufferLocked(CachedImage& cached, uint
|
|||||||
regions = Transfer::MakeLayeredImageBufferCopies(target.layers, slice_size, target.pitch,
|
regions = Transfer::MakeLayeredImageBufferCopies(target.layers, slice_size, target.pitch,
|
||||||
target.width, target.height);
|
target.width, target.height);
|
||||||
}
|
}
|
||||||
Transfer::DownloadImage(cached.ctx, m_buffer_transition_linear.data(), target.size, regions,
|
Transfer::ProcessDownloadedImage(
|
||||||
cached.image, cached.image->layout);
|
cached.ctx, target.size, regions, cached.image, cached.image->layout,
|
||||||
if (storage) {
|
[&](std::span<const uint8_t> linear) {
|
||||||
m_buffer_transition_guest.resize(target.size);
|
if (storage) {
|
||||||
m_tiler.TileImage(m_buffer_transition_guest.data(), m_buffer_transition_linear.data(),
|
m_buffer_transition_guest.resize(target.size);
|
||||||
cached.info);
|
m_tiler.TileImage(m_buffer_transition_guest.data(), linear.data(), cached.info);
|
||||||
Libs::LibKernel::Memory::WriteBacking(target.address, m_buffer_transition_guest.data(),
|
Libs::LibKernel::Memory::WriteBacking(
|
||||||
target.size);
|
target.address, m_buffer_transition_guest.data(), target.size);
|
||||||
} else if (tiled) {
|
} else if (tiled) {
|
||||||
m_buffer_transition_guest.resize(target.size);
|
m_buffer_transition_guest.resize(target.size);
|
||||||
m_tiler.TileImage(m_buffer_transition_guest.data(), m_buffer_transition_linear.data(),
|
m_tiler.TileImage(m_buffer_transition_guest.data(), linear.data(), target);
|
||||||
target);
|
Libs::LibKernel::Memory::WriteBacking(
|
||||||
Libs::LibKernel::Memory::WriteBacking(target.address, m_buffer_transition_guest.data(),
|
target.address, m_buffer_transition_guest.data(), target.size);
|
||||||
target.size);
|
} else {
|
||||||
} else {
|
Libs::LibKernel::Memory::WriteBacking(target.address, linear.data(), target.size);
|
||||||
Libs::LibKernel::Memory::WriteBacking(target.address, m_buffer_transition_linear.data(),
|
}
|
||||||
target.size);
|
});
|
||||||
}
|
|
||||||
m_memory_tracker.ForEachDownloadRange<true>(target.address, target.size,
|
m_memory_tracker.ForEachDownloadRange<true>(target.address, target.size,
|
||||||
[](uint64_t, uint64_t) noexcept {});
|
[](uint64_t, uint64_t) noexcept {});
|
||||||
// Only the impending buffer-write range needs publication into BufferCache ownership. The
|
// Only the impending buffer-write range needs publication into BufferCache ownership. The
|
||||||
@@ -3104,16 +3101,16 @@ void TextureCache::SynchronizeDepthImageToBufferLocked(CachedImage& cached, uint
|
|||||||
has_stencil, has_htile, cached.gpu_modified, cached.buffer_modified);
|
has_stencil, has_htile, cached.gpu_modified, cached.buffer_modified);
|
||||||
}
|
}
|
||||||
Transfer::WaitForGraphicsIdle(cached.ctx);
|
Transfer::WaitForGraphicsIdle(cached.ctx);
|
||||||
m_buffer_transition_linear.resize(info.size);
|
|
||||||
std::fill(m_buffer_transition_linear.begin(), m_buffer_transition_linear.end(), 0);
|
|
||||||
const auto regions = Transfer::MakeLayeredImageBufferCopies(
|
const auto regions = Transfer::MakeLayeredImageBufferCopies(
|
||||||
1, info.size, info.pitch, info.width, info.height, vk::ImageAspectFlagBits::eDepth);
|
1, info.size, info.pitch, info.width, info.height, vk::ImageAspectFlagBits::eDepth);
|
||||||
Transfer::DownloadImage(cached.ctx, m_buffer_transition_linear.data(), info.size, regions,
|
Transfer::ProcessDownloadedImage(
|
||||||
cached.image, cached.image->layout);
|
cached.ctx, info.size, regions, cached.image, cached.image->layout,
|
||||||
m_buffer_transition_guest.resize(info.size);
|
[&](std::span<const uint8_t> linear) {
|
||||||
m_tiler.TileImage(m_buffer_transition_guest.data(), m_buffer_transition_linear.data(), info);
|
m_buffer_transition_guest.resize(info.size);
|
||||||
Libs::LibKernel::Memory::WriteBacking(info.address, m_buffer_transition_guest.data(),
|
m_tiler.TileImage(m_buffer_transition_guest.data(), linear.data(), info);
|
||||||
info.size);
|
Libs::LibKernel::Memory::WriteBacking(info.address, m_buffer_transition_guest.data(),
|
||||||
|
info.size);
|
||||||
|
});
|
||||||
m_memory_tracker.ForEachDownloadRange<true>(info.address, info.size,
|
m_memory_tracker.ForEachDownloadRange<true>(info.address, info.size,
|
||||||
[](uint64_t, uint64_t) noexcept {});
|
[](uint64_t, uint64_t) noexcept {});
|
||||||
m_buffer_cache.PublishImageBacking(write_address, write_size);
|
m_buffer_cache.PublishImageBacking(write_address, write_size);
|
||||||
|
|||||||
@@ -176,7 +176,6 @@ private:
|
|||||||
ImageOwnerIndex m_image_owner_index;
|
ImageOwnerIndex m_image_owner_index;
|
||||||
std::map<uint64_t, MetaDataInfo> m_surface_metas;
|
std::map<uint64_t, MetaDataInfo> m_surface_metas;
|
||||||
std::unique_ptr<ReadbackWorker> m_readback;
|
std::unique_ptr<ReadbackWorker> m_readback;
|
||||||
std::vector<uint8_t> m_buffer_transition_linear;
|
|
||||||
std::vector<uint8_t> m_buffer_transition_guest;
|
std::vector<uint8_t> m_buffer_transition_guest;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -351,12 +351,28 @@ public:
|
|||||||
void DownloadFromImage(GraphicContext* ctx, void* dst_data, uint64_t size,
|
void DownloadFromImage(GraphicContext* ctx, void* dst_data, uint64_t size,
|
||||||
std::span<const ImageBufferCopy> regions, VulkanImage* src_image,
|
std::span<const ImageBufferCopy> regions, VulkanImage* src_image,
|
||||||
vk::ImageLayout src_layout) {
|
vk::ImageLayout src_layout) {
|
||||||
Common::LockGuard lock(m_mutex);
|
WithDownloadedImage(ctx, size, regions, src_image, src_layout,
|
||||||
EnsureBuffer(ctx, size, vk::BufferUsageFlagBits::eTransferDst);
|
[&](std::span<const uint8_t> data) {
|
||||||
ExecuteImmediateCommands([&](CommandBuffer* command, vk::CommandBuffer) {
|
std::memcpy(dst_data, data.data(), data.size());
|
||||||
RecordImageToBuffer(*command, *src_image, m_buffer, regions, src_layout);
|
});
|
||||||
});
|
}
|
||||||
std::memcpy(dst_data, m_mapped_data, size);
|
|
||||||
|
void ProcessDownloadedImage(GraphicContext* ctx, uint64_t size,
|
||||||
|
std::span<const ImageBufferCopy> regions,
|
||||||
|
VulkanImage* src_image, vk::ImageLayout src_layout,
|
||||||
|
const DownloadedImageConsumer& consumer) {
|
||||||
|
WithDownloadedImage(ctx, size, regions, src_image, src_layout,
|
||||||
|
[&](std::span<const uint8_t> data) {
|
||||||
|
if (m_host_cached) {
|
||||||
|
consumer(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Scattered CPU tiler reads can be much slower from uncached mapped
|
||||||
|
// memory. Preserve the original sequential-copy path as a fallback.
|
||||||
|
m_cached_readback.resize(data.size());
|
||||||
|
std::memcpy(m_cached_readback.data(), data.data(), data.size());
|
||||||
|
consumer(m_cached_readback);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Release(GraphicContext* ctx) {
|
void Release(GraphicContext* ctx) {
|
||||||
@@ -366,11 +382,26 @@ public:
|
|||||||
VulkanUnmapMemory(ctx, &m_buffer.memory);
|
VulkanUnmapMemory(ctx, &m_buffer.memory);
|
||||||
VulkanDeleteBuffer(ctx, &m_buffer);
|
VulkanDeleteBuffer(ctx, &m_buffer);
|
||||||
}
|
}
|
||||||
m_capacity = 0;
|
m_capacity = 0;
|
||||||
m_mapped_data = nullptr;
|
m_mapped_data = nullptr;
|
||||||
|
m_host_cached = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
template <typename Consumer>
|
||||||
|
void WithDownloadedImage(GraphicContext* ctx, uint64_t size,
|
||||||
|
std::span<const ImageBufferCopy> regions,
|
||||||
|
VulkanImage* src_image, vk::ImageLayout src_layout,
|
||||||
|
const Consumer& consumer) {
|
||||||
|
Common::LockGuard lock(m_mutex);
|
||||||
|
EnsureBuffer(ctx, size, vk::BufferUsageFlagBits::eTransferDst);
|
||||||
|
ExecuteImmediateCommands([&](CommandBuffer* command, vk::CommandBuffer) {
|
||||||
|
RecordImageToBuffer(*command, *src_image, m_buffer, regions, src_layout);
|
||||||
|
});
|
||||||
|
consumer(std::span<const uint8_t>(static_cast<const uint8_t*>(m_mapped_data),
|
||||||
|
static_cast<size_t>(size)));
|
||||||
|
}
|
||||||
|
|
||||||
template <bool WaitIdle, typename Recorder>
|
template <bool WaitIdle, typename Recorder>
|
||||||
void RecordUpload(GraphicContext* ctx, const void* src_data, uint64_t size,
|
void RecordUpload(GraphicContext* ctx, const void* src_data, uint64_t size,
|
||||||
const Recorder& recorder) {
|
const Recorder& recorder) {
|
||||||
@@ -400,17 +431,30 @@ private:
|
|||||||
m_buffer.usage = usage;
|
m_buffer.usage = usage;
|
||||||
m_buffer.memory.property = vk::MemoryPropertyFlagBits::eHostVisible |
|
m_buffer.memory.property = vk::MemoryPropertyFlagBits::eHostVisible |
|
||||||
vk::MemoryPropertyFlagBits::eHostCoherent;
|
vk::MemoryPropertyFlagBits::eHostCoherent;
|
||||||
|
// CPU readback benefits from cached host memory. Keep it optional so Vulkan devices
|
||||||
|
// without a coherent cached type can fall back to the original sequential-copy path.
|
||||||
|
m_buffer.memory.preferred_property =
|
||||||
|
usage & vk::BufferUsageFlagBits::eTransferDst
|
||||||
|
? vk::MemoryPropertyFlags(vk::MemoryPropertyFlagBits::eHostCached)
|
||||||
|
: vk::MemoryPropertyFlags {};
|
||||||
VulkanCreateBuffer(ctx, size, &m_buffer);
|
VulkanCreateBuffer(ctx, size, &m_buffer);
|
||||||
m_capacity = size;
|
m_capacity = size;
|
||||||
|
const auto& properties = ctx->GetPhysicalDeviceMemoryProperties();
|
||||||
|
EXIT_IF(m_buffer.memory.type >= properties.memoryTypeCount);
|
||||||
|
m_host_cached =
|
||||||
|
static_cast<bool>(properties.memoryTypes[m_buffer.memory.type].propertyFlags &
|
||||||
|
vk::MemoryPropertyFlagBits::eHostCached);
|
||||||
|
|
||||||
VulkanMapMemory(ctx, &m_buffer.memory, &m_mapped_data);
|
VulkanMapMemory(ctx, &m_buffer.memory, &m_mapped_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::Mutex m_mutex;
|
Common::Mutex m_mutex;
|
||||||
VulkanBuffer m_buffer;
|
VulkanBuffer m_buffer;
|
||||||
uint64_t m_capacity = 0;
|
uint64_t m_capacity = 0;
|
||||||
void* m_mapped_data = nullptr;
|
void* m_mapped_data = nullptr;
|
||||||
|
bool m_host_cached = false;
|
||||||
|
std::vector<uint8_t> m_cached_readback;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Do not replace with one
|
// Do not replace with one
|
||||||
@@ -927,6 +971,16 @@ void DownloadImage(GraphicContext* ctx, void* dst_data, uint64_t size,
|
|||||||
->DownloadFromImage(ctx, dst_data, size, regions, src_image, src_layout);
|
->DownloadFromImage(ctx, dst_data, size, regions, src_image, src_layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProcessDownloadedImage(GraphicContext* ctx, uint64_t size,
|
||||||
|
std::span<const ImageBufferCopy> regions, VulkanImage* src_image,
|
||||||
|
vk::ImageLayout src_layout,
|
||||||
|
const DownloadedImageConsumer& consumer) {
|
||||||
|
KYTY_PROFILER_FUNCTION();
|
||||||
|
EXIT_IF(size == 0 || regions.empty() || !consumer);
|
||||||
|
GetStagingBuffer(StagingBufferType::ReadBack)
|
||||||
|
->ProcessDownloadedImage(ctx, size, regions, src_image, src_layout, consumer);
|
||||||
|
}
|
||||||
|
|
||||||
void UploadImage(GraphicContext* ctx, VulkanImage* image, const void* src_data, uint64_t size,
|
void UploadImage(GraphicContext* ctx, VulkanImage* image, const void* src_data, uint64_t size,
|
||||||
std::span<const BufferImageCopy> regions, vk::ImageLayout dst_layout) {
|
std::span<const BufferImageCopy> regions, vk::ImageLayout dst_layout) {
|
||||||
EXIT_IF(size == 0 || regions.empty());
|
EXIT_IF(size == 0 || regions.empty());
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
#include "graphics/host_gpu/vulkanCommon.h"
|
#include "graphics/host_gpu/vulkanCommon.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -73,6 +74,8 @@ MakeLayeredImageBufferCopies(uint32_t layers, uint64_t slice_size, uint32_t pitc
|
|||||||
|
|
||||||
enum class StagingBufferType { Texture, Vertex, ReadBack };
|
enum class StagingBufferType { Texture, Vertex, ReadBack };
|
||||||
|
|
||||||
|
using DownloadedImageConsumer = std::function<void(std::span<const uint8_t>)>;
|
||||||
|
|
||||||
class ScratchBuffer {
|
class ScratchBuffer {
|
||||||
public:
|
public:
|
||||||
explicit ScratchBuffer(uint64_t size);
|
explicit ScratchBuffer(uint64_t size);
|
||||||
@@ -107,6 +110,13 @@ void DownloadImage(GraphicContext* ctx, void* dst_data, uint64_t size, uint32_t
|
|||||||
void DownloadImage(GraphicContext* ctx, void* dst_data, uint64_t size,
|
void DownloadImage(GraphicContext* ctx, void* dst_data, uint64_t size,
|
||||||
std::span<const ImageBufferCopy> regions, VulkanImage* src_image,
|
std::span<const ImageBufferCopy> regions, VulkanImage* src_image,
|
||||||
vk::ImageLayout src_layout);
|
vk::ImageLayout src_layout);
|
||||||
|
// Invokes consumer synchronously after the image copy fence while the readback staging buffer is
|
||||||
|
// reserved. The supplied span is valid only for the call; the consumer must not retain it or
|
||||||
|
// re-enter Transfer.
|
||||||
|
void ProcessDownloadedImage(GraphicContext* ctx, uint64_t size,
|
||||||
|
std::span<const ImageBufferCopy> regions, VulkanImage* src_image,
|
||||||
|
vk::ImageLayout src_layout,
|
||||||
|
const DownloadedImageConsumer& consumer);
|
||||||
void UploadBuffer(GraphicContext* ctx, StagingBufferType type, VulkanBuffer* dst_buffer,
|
void UploadBuffer(GraphicContext* ctx, StagingBufferType type, VulkanBuffer* dst_buffer,
|
||||||
uint64_t dst_offset, const void* src_data, uint64_t size);
|
uint64_t dst_offset, const void* src_data, uint64_t size);
|
||||||
void CopyBuffer(VulkanBuffer* src_buffer, VulkanBuffer* dst_buffer, uint64_t size);
|
void CopyBuffer(VulkanBuffer* src_buffer, VulkanBuffer* dst_buffer, uint64_t size);
|
||||||
|
|||||||
@@ -127,6 +127,8 @@ void VulkanCreateBuffer(GraphicContext* ctx, uint64_t size, VulkanBuffer* buffer
|
|||||||
VmaAllocationCreateInfo alloc_info {};
|
VmaAllocationCreateInfo alloc_info {};
|
||||||
alloc_info.requiredFlags =
|
alloc_info.requiredFlags =
|
||||||
static_cast<vk::MemoryPropertyFlags::MaskType>(buffer->memory.property);
|
static_cast<vk::MemoryPropertyFlags::MaskType>(buffer->memory.property);
|
||||||
|
alloc_info.preferredFlags =
|
||||||
|
static_cast<vk::MemoryPropertyFlags::MaskType>(buffer->memory.preferred_property);
|
||||||
|
|
||||||
vk::Buffer::CType native_buffer = VK_NULL_HANDLE;
|
vk::Buffer::CType native_buffer = VK_NULL_HANDLE;
|
||||||
const auto result = static_cast<vk::Result>(vmaCreateBuffer(
|
const auto result = static_cast<vk::Result>(vmaCreateBuffer(
|
||||||
@@ -170,6 +172,8 @@ bool VulkanCreateImage(GraphicContext* ctx, const vk::ImageCreateInfo& image_inf
|
|||||||
auto& memory = image->memory;
|
auto& memory = image->memory;
|
||||||
VmaAllocationCreateInfo alloc_info {};
|
VmaAllocationCreateInfo alloc_info {};
|
||||||
alloc_info.requiredFlags = static_cast<vk::MemoryPropertyFlags::MaskType>(memory.property);
|
alloc_info.requiredFlags = static_cast<vk::MemoryPropertyFlags::MaskType>(memory.property);
|
||||||
|
alloc_info.preferredFlags =
|
||||||
|
static_cast<vk::MemoryPropertyFlags::MaskType>(memory.preferred_property);
|
||||||
|
|
||||||
vk::Image::CType native_image = VK_NULL_HANDLE;
|
vk::Image::CType native_image = VK_NULL_HANDLE;
|
||||||
const auto result = static_cast<vk::Result>(vmaCreateImage(
|
const auto result = static_cast<vk::Result>(vmaCreateImage(
|
||||||
|
|||||||
Reference in new issue
Block a user