Support layered PS5 image alias transitions

This commit is contained in:
nmzik
2026-07-16 21:56:09 +02:00
parent f743a48c03
commit ae86249765
17 changed files with 1740 additions and 814 deletions
+28 -13
View File
@@ -110,8 +110,15 @@ struct VideoOutVulkanImage: public VulkanImage {
};
struct DepthStencilVulkanImage: public VulkanImage {
struct AttachmentView {
uint32_t base_layer = 0;
uint32_t layer_count = 1;
VkImageView view = nullptr;
};
DepthStencilVulkanImage(): VulkanImage(VulkanImageType::DepthStencil) {}
bool compressed = false;
bool compressed = false;
std::mutex attachment_view_mutex;
std::vector<AttachmentView> attachment_views;
};
struct GpuTextureVulkanImage: public VulkanImage {
@@ -138,22 +145,30 @@ struct StorageTextureVulkanImage: public GpuTextureVulkanImage {
struct RenderTextureVulkanImage: public VulkanImage {
struct AttachmentView {
VkFormat format = VK_FORMAT_UNDEFINED;
uint32_t level = 0;
VkImageView view = nullptr;
VkFormat format = VK_FORMAT_UNDEFINED;
uint32_t level = 0;
uint32_t base_layer = 0;
uint32_t layer_count = 1;
VkImageView view = nullptr;
};
struct SampledView {
VkFormat format = VK_FORMAT_UNDEFINED;
uint32_t base_level = 0;
uint32_t level_count = 0;
int variant = VIEW_DEFAULT;
VkImageView view = nullptr;
VkFormat format = VK_FORMAT_UNDEFINED;
VkImageViewType type = VK_IMAGE_VIEW_TYPE_2D;
uint32_t base_level = 0;
uint32_t level_count = 0;
uint32_t base_layer = 0;
uint32_t layer_count = 1;
int variant = VIEW_DEFAULT;
VkImageView view = nullptr;
};
struct StorageView {
VkFormat format = VK_FORMAT_UNDEFINED;
uint32_t base_level = 0;
uint32_t level_count = 0;
VkImageView view = nullptr;
VkFormat format = VK_FORMAT_UNDEFINED;
VkImageViewType type = VK_IMAGE_VIEW_TYPE_2D;
uint32_t base_level = 0;
uint32_t level_count = 0;
uint32_t base_layer = 0;
uint32_t layer_count = 1;
VkImageView view = nullptr;
};
RenderTextureVulkanImage(): VulkanImage(VulkanImageType::RenderTexture) {}
VkImageView render_view[16] = {};
+141 -157
View File
@@ -28,7 +28,7 @@ namespace Libs::Graphics {
namespace {
thread_local const void* g_cache_lock_owner = nullptr;
constexpr uint32_t READBACK_MAX_RANGES = 256;
constexpr uint32_t READBACK_MAX_RANGES = 256;
constexpr uint64_t READBACK_COPY_ALIGNMENT = 64;
constexpr uint64_t READBACK_CAPACITY =
TRACKER_PAGE_SIZE + READBACK_MAX_RANGES * (READBACK_COPY_ALIGNMENT - 1);
@@ -42,24 +42,26 @@ constexpr bool ShouldLogCommandProcessorReadback(uint64_t serial) noexcept {
}
struct CommandProcessorReadbackState {
BufferCache* cache = nullptr;
PageFaultAccess access = PageFaultAccess::Unknown;
uint64_t vaddr = 0;
uint64_t size = 0;
uint64_t page = 0;
uint64_t serial = 0;
uint64_t dirty_bytes = 0;
BufferCache* cache = nullptr;
PageFaultAccess access = PageFaultAccess::Unknown;
uint64_t vaddr = 0;
uint64_t size = 0;
uint64_t page = 0;
uint64_t serial = 0;
uint64_t dirty_bytes = 0;
uint64_t packed_bytes = 0;
uint32_t dirty_count = 0;
uint32_t dirty_count = 0;
std::chrono::steady_clock::time_point started {};
bool installed = false;
};
thread_local CommandProcessorReadbackState g_command_processor_readback;
std::atomic<uint64_t> g_command_processor_readback_serial {0};
std::atomic<uint64_t> g_command_processor_readback_serial {0};
struct SharedVulkanBufferOwner {
explicit SharedVulkanBufferOwner(GraphicContext* context): ctx(context) { EXIT_IF(ctx == nullptr); }
explicit SharedVulkanBufferOwner(GraphicContext* context): ctx(context) {
EXIT_IF(ctx == nullptr);
}
~SharedVulkanBufferOwner() {
if (buffer.buffer != nullptr) {
@@ -68,7 +70,7 @@ struct SharedVulkanBufferOwner {
}
GraphicContext* ctx = nullptr;
VulkanBuffer buffer;
VulkanBuffer buffer;
};
std::shared_ptr<VulkanBuffer> MakeSharedVulkanBuffer(GraphicContext* ctx) {
@@ -203,8 +205,8 @@ struct BufferCache::CachedBuffer {
};
struct BufferCache::CommandProcessorReadbackResources {
GraphicContext* ctx = nullptr;
int queue = -1;
GraphicContext* ctx = nullptr;
int queue = -1;
VulkanBuffer buffer {};
void* mapped = nullptr;
std::unique_ptr<CommandBuffer> command;
@@ -257,8 +259,8 @@ struct BufferCache::ReadbackWorker {
}
auto current = state.load(std::memory_order_acquire);
while (current == State::InitRequested ||
(current == State::Claimed && (command == nullptr || mapped == nullptr ||
readback.buffer == nullptr))) {
(current == State::Claimed &&
(command == nullptr || mapped == nullptr || readback.buffer == nullptr))) {
state.wait(current, std::memory_order_acquire);
current = state.load(std::memory_order_acquire);
}
@@ -306,9 +308,8 @@ struct BufferCache::ReadbackWorker {
const bool submissions_prepaused_now = GraphicsRunSubmissionLockHeld();
const bool unsafe_gpu_lock = GraphicsRunGpuLockHeld() && !submissions_prepaused_now;
if (GraphicsRunIsCommandProcessorThread() || unsafe_gpu_lock || LabelInCallback() ||
g_cache_lock_owner != nullptr ||
ctx == nullptr || command == nullptr || mapped == nullptr ||
readback.buffer == nullptr) {
g_cache_lock_owner != nullptr || ctx == nullptr || command == nullptr ||
mapped == nullptr || readback.buffer == nullptr) {
EXIT("BufferCache: unsafe readback request context, command_thread=%d "
"submission_lock=%d "
"gpu_lock=%d label_callback=%d cache_lock=%p ctx=%p command=%p mapped=%p "
@@ -328,10 +329,10 @@ struct BufferCache::ReadbackWorker {
state.wait(expected, std::memory_order_acquire);
expected = State::Idle;
}
access = fault_access;
vaddr = fault_vaddr;
size = fault_size;
range_count = 0;
access = fault_access;
vaddr = fault_vaddr;
size = fault_size;
range_count = 0;
submissions_prepaused = submissions_prepaused_now;
state.store(State::Requested, std::memory_order_release);
state.notify_all();
@@ -368,7 +369,7 @@ struct BufferCache::ReadbackWorker {
for (uint32_t i = 0; i < range_count; i++) {
const auto& range = ranges[i];
Libs::LibKernel::Memory::WriteBacking(range.address, data.data() + range.offset,
range.size);
range.size);
}
if (!cache.m_memory_tracker.CompleteCpuFault(vaddr, size, access, true)) {
EXIT("BufferCache: failed to complete downloaded CPU fault, addr=0x%016" PRIx64
@@ -476,7 +477,7 @@ struct BufferCache::ReadbackWorker {
}
const auto page = vaddr & ~(TRACKER_PAGE_SIZE - 1);
FaultSafeCacheLock lock(&cache, cache.m_mutex);
auto it = cache.m_buffers.upper_bound(vaddr);
auto it = cache.m_buffers.upper_bound(vaddr);
if (it == cache.m_buffers.begin()) {
EXIT("BufferCache: readback address has no cached buffer, addr=0x%016" PRIx64 "\n",
vaddr);
@@ -564,10 +565,10 @@ struct BufferCache::ReadbackWorker {
VulkanBuffer readback {};
void* mapped = nullptr;
std::unique_ptr<CommandBuffer> command;
PageFaultAccess access = PageFaultAccess::Unknown;
uint64_t vaddr = 0;
uint64_t size = 0;
uint32_t range_count = 0;
PageFaultAccess access = PageFaultAccess::Unknown;
uint64_t vaddr = 0;
uint64_t size = 0;
uint32_t range_count = 0;
bool submissions_prepaused = false;
std::array<Range, MAX_RANGES> ranges {};
std::array<uint8_t, READBACK_CAPACITY> data {};
@@ -662,8 +663,8 @@ void BufferCache::RequestCommandProcessorReadback(PageFaultAccess access, uint64
}
const bool log_detail = ShouldLogCommandProcessorReadback(serial);
if (log_detail) {
LOGF("BufferCache: command-processor readback request serial=%" PRIu64
" addr=0x%016" PRIx64 " size=0x%016" PRIx64 " access=%u poll=%d\n",
LOGF("BufferCache: command-processor readback request serial=%" PRIu64 " addr=0x%016" PRIx64
" size=0x%016" PRIx64 " access=%u poll=%d\n",
serial, vaddr, size, static_cast<uint32_t>(access), GraphicsRunIsGuestMemoryPoll());
Log::Flush();
}
@@ -681,8 +682,8 @@ void BufferCache::RequestCommandProcessorReadback(PageFaultAccess access, uint64
g_cache_lock_owner = this;
m_mutex.Lock();
if (log_detail) {
LOGF("BufferCache: command-processor cache locked serial=%" PRIu64
" page=0x%016" PRIx64 "\n",
LOGF("BufferCache: command-processor cache locked serial=%" PRIu64 " page=0x%016" PRIx64
"\n",
serial, page);
Log::Flush();
}
@@ -715,12 +716,12 @@ void BufferCache::RequestCommandProcessorReadback(PageFaultAccess access, uint64
}
ValidateDirtyPages(m_gpu_modified_ranges, page, TRACKER_PAGE_SIZE, "command-processor");
if (m_cp_readback_resources == nullptr) {
auto resources = std::make_unique<CommandProcessorReadbackResources>();
resources->ctx = cached.ctx;
resources->buffer.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
auto resources = std::make_unique<CommandProcessorReadbackResources>();
resources->ctx = cached.ctx;
resources->buffer.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
resources->buffer.memory.property = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
VulkanCreateBuffer(cached.ctx, READBACK_CAPACITY, &resources->buffer);
VulkanMapMemory(cached.ctx, &resources->buffer.memory, &resources->mapped);
m_cp_readback_resources = std::move(resources);
@@ -739,7 +740,8 @@ void BufferCache::RequestCommandProcessorReadback(PageFaultAccess access, uint64
resources.command = std::make_unique<CommandBuffer>(static_cast<int>(queue));
resources.queue = static_cast<int>(queue);
}
if (resources.command->IsInvalid() || resources.command->GetQueue() != static_cast<int>(queue)) {
if (resources.command->IsInvalid() ||
resources.command->GetQueue() != static_cast<int>(queue)) {
EXIT("BufferCache: failed to initialize owning-queue readback command buffer, queue=%u\n",
queue);
}
@@ -750,18 +752,18 @@ void BufferCache::RequestCommandProcessorReadback(PageFaultAccess access, uint64
}
std::array<VkBufferCopy, ReadbackWorker::MAX_RANGES> copies {};
std::array<VkBufferMemoryBarrier, ReadbackWorker::MAX_RANGES> barriers {};
uint64_t dirty_bytes = 0;
uint64_t packed_bytes = 0;
uint32_t dirty_count = 0;
uint64_t dirty_bytes = 0;
uint64_t packed_bytes = 0;
uint32_t dirty_count = 0;
for (const auto& range: dirty) {
if ((range.address - cached.vaddr) % 4 != 0 || range.size % 4 != 0) {
EXIT("BufferCache: invalid command-processor dirty range, addr=0x%016" PRIx64
" size=0x%016" PRIx64 " page=0x%016" PRIx64 " packed=0x%016" PRIx64 "\n",
range.address, range.size, page, dirty_bytes);
}
copies[dirty_count] = {.srcOffset = range.address - cached.vaddr,
.dstOffset = packed_bytes,
.size = range.size};
copies[dirty_count] = {.srcOffset = range.address - cached.vaddr,
.dstOffset = packed_bytes,
.size = range.size};
auto& barrier = barriers[dirty_count];
barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
barrier.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT;
@@ -771,7 +773,7 @@ void BufferCache::RequestCommandProcessorReadback(PageFaultAccess access, uint64
barrier.buffer = cached.buffer->buffer;
barrier.offset = copies[dirty_count].srcOffset;
barrier.size = range.size;
const auto slot_size = AlignReadbackCopySize(range.size);
const auto slot_size = AlignReadbackCopySize(range.size);
if (packed_bytes > READBACK_CAPACITY || slot_size > READBACK_CAPACITY - packed_bytes) {
EXIT("BufferCache: aligned command-processor readback exceeds persistent capacity, "
"packed=%" PRIu64 " size=%" PRIu64 " capacity=%" PRIu64 "\n",
@@ -787,8 +789,7 @@ void BufferCache::RequestCommandProcessorReadback(PageFaultAccess access, uint64
const auto started = std::chrono::steady_clock::now();
if (log_detail) {
LOGF("BufferCache: command-processor readback begin serial=%" PRIu64
" queue=%u page=0x%016" PRIx64 " ranges=%u bytes=%" PRIu64
" packed=%" PRIu64 "\n",
" queue=%u page=0x%016" PRIx64 " ranges=%u bytes=%" PRIu64 " packed=%" PRIu64 "\n",
serial, queue, page, dirty_count, dirty_bytes, packed_bytes);
Log::Flush();
}
@@ -808,20 +809,19 @@ void BufferCache::RequestCommandProcessorReadback(PageFaultAccess access, uint64
}
for (uint32_t i = 0; i < dirty_count; i++) {
Libs::LibKernel::Memory::WriteBacking(
dirty[i].address,
static_cast<const uint8_t*>(resources.mapped) + copies[i].dstOffset, dirty[i].size);
dirty[i].address, static_cast<const uint8_t*>(resources.mapped) + copies[i].dstOffset,
dirty[i].size);
}
g_command_processor_readback = {
.cache = this,
.access = access,
.vaddr = vaddr,
.size = size,
.page = page,
.serial = serial,
.dirty_bytes = dirty_bytes,
.packed_bytes = packed_bytes,
.dirty_count = dirty_count,
.started = started};
g_command_processor_readback = {.cache = this,
.access = access,
.vaddr = vaddr,
.size = size,
.page = page,
.serial = serial,
.dirty_bytes = dirty_bytes,
.packed_bytes = packed_bytes,
.dirty_count = dirty_count,
.started = started};
}
bool BufferCache::CompleteCommandProcessorReadback(PageFaultAccess access, uint64_t vaddr,
@@ -866,13 +866,13 @@ bool BufferCache::ReleaseCommandProcessorReadback(PageFaultAccess access, uint64
const auto elapsed_micro = std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::steady_clock::now() - pending.started)
.count();
pending = {};
pending = {};
m_mutex.Unlock();
g_cache_lock_owner = nullptr;
GraphicsRunEndCommandProcessorReadback();
if (ShouldLogCommandProcessorReadback(serial)) {
LOGF("BufferCache: command-processor readback release serial=%" PRIu64
" page=0x%016" PRIx64 " ranges=%u bytes=%" PRIu64 " elapsed_us=%" PRIi64 "\n",
LOGF("BufferCache: command-processor readback release serial=%" PRIu64 " page=0x%016" PRIx64
" ranges=%u bytes=%" PRIu64 " elapsed_us=%" PRIi64 "\n",
serial, page, dirty_count, dirty_bytes, static_cast<int64_t>(elapsed_micro));
}
return true;
@@ -971,10 +971,10 @@ std::pair<VulkanBuffer*, uint64_t> BufferCache::ObtainBuffer(CommandBuffer* com
!m_texture_cache->IsMetaGpuModified(vaddr, size)) {
std::array<uint8_t, CACHING_PAGE_SIZE> guest_data;
if (Libs::LibKernel::Memory::TryReadBacking(vaddr, guest_data.data(), size)) {
VulkanBuffer* stream_buffer = nullptr;
VkDeviceSize stream_offset = 0;
VkDeviceSize stream_range = 0;
const auto stream_alignment = std::max<uint64_t>(ctx->StorageMinAlignment(), 16);
VulkanBuffer* stream_buffer = nullptr;
VkDeviceSize stream_offset = 0;
VkDeviceSize stream_range = 0;
const auto stream_alignment = std::max<uint64_t>(ctx->StorageMinAlignment(), 16);
if (UploadHostData(command, ctx, guest_data.data(), size, stream_alignment,
&stream_buffer, &stream_offset, &stream_range)) {
return {stream_buffer, stream_offset};
@@ -992,9 +992,9 @@ std::pair<VulkanBuffer*, uint64_t> BufferCache::ObtainBuffer(CommandBuffer* com
// performs the image ownership transition below.
if (m_texture_cache->HasPageOverlap(begin, end - begin) &&
m_texture_cache->HasRangeOverlap(vaddr, size)) {
const bool coherent_read =
is_read && !is_written && !m_memory_tracker.IsRegionGpuModified(vaddr, size) &&
!m_texture_cache->HasGpuModifiedRangeOverlap(vaddr, size);
const bool coherent_read = is_read && !is_written &&
!m_memory_tracker.IsRegionGpuModified(vaddr, size) &&
!m_texture_cache->HasGpuModifiedRangeOverlap(vaddr, size);
if (!coherent_read) {
if (!is_written || !is_formatted) {
EXIT("BufferCache: unsupported buffer/image alias, addr=0x%016" PRIx64
@@ -1041,7 +1041,7 @@ std::pair<VulkanBuffer*, uint64_t> BufferCache::ObtainBuffer(CommandBuffer* com
}
if (!overlaps.empty()) {
for (const auto overlap: overlaps) {
for (const auto& overlap: overlaps) {
auto& old = *overlap->second;
if (old.ctx != ctx || old.buffer == nullptr || old.buffer->buffer == nullptr) {
EXIT("BufferCache: invalid overlapping buffer owner, addr=0x%016" PRIx64
@@ -1071,10 +1071,10 @@ std::pair<VulkanBuffer*, uint64_t> BufferCache::ObtainBuffer(CommandBuffer* com
});
}
}
auto cached = std::make_unique<CachedBuffer>();
cached->vaddr = merged.address;
cached->size = merged.size;
cached->ctx = ctx;
auto cached = std::make_unique<CachedBuffer>();
cached->vaddr = merged.address;
cached->size = merged.size;
cached->ctx = ctx;
cached->buffer = MakeSharedVulkanBuffer(ctx);
cached->buffer->usage =
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
@@ -1086,7 +1086,7 @@ std::pair<VulkanBuffer*, uint64_t> BufferCache::ObtainBuffer(CommandBuffer* com
auto* vk_buffer = command->GetPool()->buffers[command->GetIndex()];
std::vector<VkBufferMemoryBarrier> before;
before.reserve(overlaps.size() + 1);
for (const auto overlap: overlaps) {
for (const auto& overlap: overlaps) {
VkBufferMemoryBarrier barrier {};
barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
barrier.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT;
@@ -1111,7 +1111,7 @@ std::pair<VulkanBuffer*, uint64_t> BufferCache::ObtainBuffer(CommandBuffer* com
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_DEPENDENCY_BY_REGION_BIT, 0,
nullptr, static_cast<uint32_t>(before.size()), before.data(), 0,
nullptr);
for (const auto overlap: overlaps) {
for (const auto& overlap: overlaps) {
const auto& old = *overlap->second;
const VkBufferCopy copy {
.srcOffset = 0, .dstOffset = old.vaddr - cached->vaddr, .size = old.size};
@@ -1129,7 +1129,7 @@ std::pair<VulkanBuffer*, uint64_t> BufferCache::ObtainBuffer(CommandBuffer* com
vkCmdPipelineBarrier(vk_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_DEPENDENCY_BY_REGION_BIT, 0,
nullptr, 1, &after, 0, nullptr);
for (const auto overlap: overlaps) {
for (const auto& overlap: overlaps) {
command->RetainResourceUntilFence(overlap->second->buffer);
m_buffers.erase(overlap);
}
@@ -1166,7 +1166,8 @@ bool BufferCache::UploadHostData(CommandBuffer* command, GraphicContext* ctx, co
if (command == nullptr || command->IsInvalid() || command->IsExecute() || ctx == nullptr) {
EXIT("BufferCache: host stream upload requires a recording command buffer\n");
}
return command->m_host_stream.Copy(ctx, src, size, alignment, out_buffer, out_offset, out_range);
return command->m_host_stream.Copy(ctx, src, size, alignment, out_buffer, out_offset,
out_range);
}
VulkanBuffer* BufferCache::ObtainNullBuffer(CommandBuffer* command, GraphicContext* ctx) {
@@ -1177,7 +1178,7 @@ VulkanBuffer* BufferCache::ObtainNullBuffer(CommandBuffer* command, GraphicConte
if (m_null_buffer == nullptr) {
// robustBufferAccess makes every fetch safe; TODO: Use a
// persistent 16-byte fallback when Vulkan null vertex descriptors are unavailable.
auto buffer = MakeSharedVulkanBuffer(ctx);
auto buffer = MakeSharedVulkanBuffer(ctx);
buffer->usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
buffer->memory.property = static_cast<uint32_t>(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) |
@@ -1207,57 +1208,28 @@ BufferImageCopySource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t
(vaddr & (TRACKER_PAGE_SIZE - 1)) == 0 && (size & (TRACKER_PAGE_SIZE - 1)) == 0);
}
FaultSafeCacheLock lock(this, m_mutex);
const bool cpu_dirty = m_memory_tracker.IsRegionCpuModified(vaddr, size);
auto use_coherent_guest_backing = [&](const CachedBuffer& overlap) {
const bool gpu_modified = m_memory_tracker.IsRegionGpuModified(vaddr, size);
const auto dirty_ranges = m_gpu_modified_ranges.Intersections(vaddr, size);
if (gpu_modified || !dirty_ranges.empty()) {
EXIT("BufferCache: GPU-modified image source requires a containing cached buffer, "
"addr=0x%016" PRIx64 " size=0x%016" PRIx64
" buffer=0x%016" PRIx64 "+0x%016" PRIx64 " tracker_dirty=%d byte_ranges=%zu\n",
vaddr, size, overlap.vaddr, overlap.size, gpu_modified, dirty_ranges.size());
const bool cpu_modified = m_memory_tracker.IsRegionCpuModified(vaddr, size);
const bool gpu_modified = m_memory_tracker.IsRegionGpuModified(vaddr, size);
const auto dirty_ranges = m_gpu_modified_ranges.Intersections(vaddr, size);
if (gpu_modified != !dirty_ranges.empty()) {
EXIT("BufferCache: image-source tracker and byte ownership disagree, addr=0x%016" PRIx64
" size=0x%016" PRIx64 " tracker_dirty=%d byte_ranges=%zu\n",
vaddr, size, gpu_modified, dirty_ranges.size());
}
auto find_owner = [&](uint64_t address, uint64_t bytes) noexcept -> CachedBuffer& {
auto owner = m_buffers.upper_bound(address);
if (owner == m_buffers.begin()) {
EXIT("BufferCache: GPU image-source bytes have no cached owner\n");
}
// A clean/CPU-current image range may coexist with a partial cached-buffer view because
// guest backing is authoritative. The PS5 detiler consumes that backing at the existing
// Tiler seam.
return BufferImageCopySource {nullptr, 0, vaddr, size, true, cpu_dirty};
--owner;
auto& cached = *owner->second;
const auto offset = address - cached.vaddr;
if (offset > cached.size || bytes > cached.size - offset || cached.ctx == nullptr ||
cached.buffer == nullptr || cached.buffer->buffer == nullptr) {
EXIT("BufferCache: GPU image-source bytes have no containing native buffer\n");
}
return cached;
};
auto it = m_buffers.upper_bound(vaddr);
if (it == m_buffers.begin()) {
if (it != m_buffers.end() &&
PageOverlaps(vaddr, size, it->second->vaddr, it->second->size)) {
return use_coherent_guest_backing(*it->second);
}
return {nullptr, 0, vaddr, size, true, cpu_dirty};
}
--it;
auto& cached = *it->second;
const auto offset = vaddr - cached.vaddr;
if (offset > cached.size || size > cached.size - offset) {
const auto next = std::next(it);
if (PageOverlaps(vaddr, size, cached.vaddr, cached.size)) {
return use_coherent_guest_backing(cached);
}
if (next != m_buffers.end() &&
PageOverlaps(vaddr, size, next->second->vaddr, next->second->size)) {
return use_coherent_guest_backing(*next->second);
}
return {nullptr, 0, vaddr, size, true, cpu_dirty};
}
if (cached.ctx == nullptr || cached.buffer == nullptr || cached.buffer->buffer == nullptr) {
EXIT("BufferCache: inconsistent image source, addr=0x%016" PRIx64 " size=0x%016" PRIx64
" buffer=0x%016" PRIx64 "+0x%016" PRIx64 " ctx=%p native=%p\n",
vaddr, size, cached.vaddr, cached.size, static_cast<const void*>(cached.ctx),
static_cast<const void*>(cached.buffer.get()));
}
const bool gpu_modified = m_memory_tracker.IsRegionGpuModified(vaddr, size);
const bool cpu_modified = cpu_dirty;
if (gpu_modified && cpu_modified) {
EXIT("BufferCache: image source is both CPU- and GPU-modified, addr=0x%016" PRIx64
" size=0x%016" PRIx64 "\n",
vaddr, size);
}
const auto ranges = m_gpu_modified_ranges.Intersections(vaddr, size);
if (gpu_modified) {
if (!GraphicsRunIsCommandProcessorThread() && !GraphicsRunSubmissionLockHeld() &&
!LabelInCallback()) {
@@ -1267,13 +1239,15 @@ BufferImageCopySource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t
vaddr, size, GraphicsRunIsCommandProcessorThread(),
GraphicsRunSubmissionLockHeld(), LabelInCallback());
}
if (ranges.empty()) {
EXIT("BufferCache: GPU-dirty image read has no dirty byte ranges, addr=0x%016" PRIx64
" size=0x%016" PRIx64 "\n",
vaddr, size);
std::vector<GraphicContext*> waited;
for (const auto& range: dirty_ranges) {
auto& owner = find_owner(range.address, range.size);
if (std::find(waited.begin(), waited.end(), owner.ctx) == waited.end()) {
VulkanDeviceWaitIdle(owner.ctx);
waited.push_back(owner.ctx);
}
}
VulkanDeviceWaitIdle(cached.ctx);
auto backing_writes = ReserveBackingWrites(m_page_manager, ranges);
auto backing_writes = ReserveBackingWrites(m_page_manager, dirty_ranges);
uint64_t downloaded = 0;
m_memory_tracker.ForEachDownloadRange<true>(
vaddr, size,
@@ -1288,9 +1262,10 @@ BufferImageCopySource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t
address, bytes);
}
for (const auto& range: dirty) {
const auto buffer_offset = range.address - cached.vaddr;
auto& owner = find_owner(range.address, range.size);
const auto buffer_offset = range.address - owner.vaddr;
std::vector<uint8_t> data(range.size);
UtilDownloadBuffer(cached.ctx, cached.buffer.get(), buffer_offset, data.data(),
UtilDownloadBuffer(owner.ctx, owner.buffer.get(), buffer_offset, data.data(),
range.size);
Libs::LibKernel::Memory::WriteBacking(range.address, data.data(), data.size());
downloaded += range.size;
@@ -1302,10 +1277,19 @@ BufferImageCopySource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t
vaddr, size);
}
m_gpu_modified_ranges.Subtract(vaddr, size);
} else if (!ranges.empty()) {
EXIT("BufferCache: CPU-current image source retains GPU-dirty bytes, addr=0x%016" PRIx64
" size=0x%016" PRIx64 "\n",
vaddr, size);
}
auto owner = m_buffers.upper_bound(vaddr);
if (owner == m_buffers.begin()) {
return {nullptr, 0, vaddr, size, true, cpu_modified};
}
--owner;
auto& cached = *owner->second;
const auto offset = vaddr - cached.vaddr;
if (offset > cached.size || size > cached.size - offset) {
return {nullptr, 0, vaddr, size, true, cpu_modified};
}
if (cached.ctx == nullptr || cached.buffer == nullptr || cached.buffer->buffer == nullptr) {
EXIT("BufferCache: containing image source is inconsistent\n");
}
if (cpu_modified) {
std::vector<std::pair<uint64_t, uint64_t>> uploads;
@@ -1334,7 +1318,7 @@ BufferImageCopySource BufferCache::ObtainBufferForImage(uint64_t vaddr, uint64_t
" size=0x%016" PRIx64 "\n",
vaddr, size);
}
return {cached.buffer.get(), offset, vaddr, size, true, cpu_dirty};
return {cached.buffer.get(), offset, vaddr, size, true, cpu_modified};
}
namespace {
@@ -1384,7 +1368,7 @@ void BufferCache::FillBuffer(CommandBuffer* command, GraphicContext* ctx, uint64
ValidateGpuAccess(vaddr, size, false, true);
{
std::lock_guard transaction(m_resource_mutex);
const bool image_overlap = m_texture_cache->HasPageOverlap(vaddr, size);
const bool image_overlap = m_texture_cache->HasRangeOverlap(vaddr, size);
const bool buffer_overlap = HasPageOverlap(vaddr, size);
const bool buffer_gpu_modified = IsRegionGpuModified(vaddr, size);
if (!buffer_overlap && !buffer_gpu_modified) {
@@ -1400,7 +1384,7 @@ void BufferCache::FillBuffer(CommandBuffer* command, GraphicContext* ctx, uint64
" size=0x%016" PRIx64 "\n",
vaddr, size);
}
if (m_texture_cache->HasMetaOverlap(vaddr, size)) {
if (m_texture_cache->HasMetaRangeOverlap(vaddr, size)) {
LOGF("BufferCache: GPU fill overlaps virtual metadata, addr=0x%016" PRIx64
" size=0x%016" PRIx64 "\n",
vaddr, size);
@@ -1439,15 +1423,15 @@ void BufferCache::CopyBuffer(CommandBuffer* command, GraphicContext* ctx, uint64
ValidateGpuAccess(dst_vaddr, size, false, true);
{
std::lock_guard transaction(m_resource_mutex);
const bool src_image = m_texture_cache->HasPageOverlap(src_vaddr, size);
const bool dst_image = m_texture_cache->HasPageOverlap(dst_vaddr, size);
const bool src_image_gpu = m_texture_cache->HasGpuModifiedRangeOverlap(src_vaddr, size);
const bool src_meta = m_texture_cache->HasMetaRangeOverlap(src_vaddr, size);
if (m_texture_cache->HasGpuTargetPageOverlap(src_vaddr, size)) {
EXIT("BufferCache: GPU copy aliases target pages, src=0x%016" PRIx64
" dst=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
src_vaddr, dst_vaddr, size);
}
if (!HasPageOverlap(dst_vaddr, size) && !IsRegionGpuModified(src_vaddr, size) &&
!IsRegionGpuModified(dst_vaddr, size)) {
!IsRegionGpuModified(dst_vaddr, size) && !src_image_gpu) {
if (m_texture_cache->IsMetaGpuModified(src_vaddr, size)) {
LOGF("BufferCache: host copy reads virtual metadata, src=0x%016" PRIx64
" size=0x%016" PRIx64 "\n",
@@ -1456,13 +1440,19 @@ void BufferCache::CopyBuffer(CommandBuffer* command, GraphicContext* ctx, uint64
"src=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
src_vaddr, size);
}
if (dst_image) {
if (m_texture_cache->HasRangeOverlap(dst_vaddr, size)) {
m_texture_cache->PrepareHostWrite(dst_vaddr, size);
}
std::memcpy(reinterpret_cast<void*>(dst_vaddr),
reinterpret_cast<const void*>(src_vaddr), size);
return;
}
if (src_image_gpu) {
EXIT("BufferCache: GPU copy source aliases GPU-modified image, src=0x%016" PRIx64
" dst=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
src_vaddr, dst_vaddr, size);
}
m_texture_cache->InvalidateMemoryFromGPU(dst_vaddr, size);
// A clean target destination is handled above like a protected host write. Target
// aliases that require an actual GPU buffer copy remain unsupported.
if (m_texture_cache->HasGpuTargetPageOverlap(dst_vaddr, size)) {
@@ -1470,13 +1460,7 @@ void BufferCache::CopyBuffer(CommandBuffer* command, GraphicContext* ctx, uint64
" dst=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
src_vaddr, dst_vaddr, size);
}
if (src_image || dst_image) {
EXIT("BufferCache: GPU copy requires image-buffer synchronization, src=0x%016" PRIx64
" dst=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
src_vaddr, dst_vaddr, size);
}
if (m_texture_cache->HasMetaOverlap(src_vaddr, size) ||
m_texture_cache->HasMetaOverlap(dst_vaddr, size)) {
if (src_meta || m_texture_cache->HasMetaRangeOverlap(dst_vaddr, size)) {
LOGF("BufferCache: GPU copy overlaps virtual metadata, src=0x%016" PRIx64
" dst=0x%016" PRIx64 " size=0x%016" PRIx64 "\n",
src_vaddr, dst_vaddr, size);
@@ -1606,7 +1590,7 @@ void BufferCache::SetTextureCache(TextureCache& texture_cache) {
m_texture_cache = &texture_cache;
}
//TODO: add LRU cache
// TODO: add LRU cache
void BufferCache::RegisterForDelete(VulkanBuffer* buffer) {
FaultSafeCacheLock lock(this, m_mutex);
@@ -315,9 +315,20 @@ void ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer* buffer, const H
}
const bool msaa_compat =
color_msaa_single_sample_compatible(rt.attrib.num_samples, rt.attrib.num_fragments);
if ((!msaa_compat && (rt.attrib.num_samples != 0 || rt.attrib.num_fragments != 0)) ||
rt.view.base_array_slice_index != 0 || render_target_slice_offset != 0) {
EXIT("multisampled or sliced render targets are unsupported\n");
if (!msaa_compat && (rt.attrib.num_samples != 0 || rt.attrib.num_fragments != 0)) {
EXIT("multisampled render targets are unsupported\n");
}
const auto view = ResolveTargetViewInfo(
rt.view.base_array_slice_index, rt.view.last_array_slice_index, render_target_slice_offset);
switch (view.type) {
case TargetViewType::Image2D: break;
case TargetViewType::Image2DArray:
EXIT("layered render-target views are unsupported: base=%u count=%u\n", view.base_layer,
view.layer_count);
case TargetViewType::Unsupported:
EXIT("invalid render-target view: base=%u last=%u draw_offset=%u\n",
rt.view.base_array_slice_index, rt.view.last_array_slice_index,
render_target_slice_offset);
}
const uint32_t levels = rt.attrib2.num_mip_levels + 1u;
if (levels == 0 || levels > 16 || rt.view.current_mip_level >= levels) {
@@ -409,15 +420,16 @@ void ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer* buffer, const H
} else {
size = static_cast<uint64_t>(pitch) * height * bytes_per_element;
}
if (size == 0 || size > TRACKER_ADDRESS_SIZE - rt.base.addr) {
if (size == 0 || size > UINT64_MAX / view.image_layers) {
EXIT("render-target memory footprint is invalid\n");
}
const auto backing_size = size * view.image_layers;
if (backing_size > TRACKER_ADDRESS_SIZE - rt.base.addr) {
EXIT("render-target backing range is invalid\n");
}
const auto effective_slice = rt.view.base_array_slice_index + render_target_slice_offset;
const auto effective_base = rt.base.addr + static_cast<uint64_t>(size) * effective_slice;
auto video_image = Presentation::DisplayBufferFind(effective_base, true);
bool render_to_texture = (video_image.image == nullptr);
auto video_image = Presentation::DisplayBufferFind(rt.base.addr, true);
bool render_to_texture = view.base_layer != 0 || video_image.image == nullptr;
if (!render_to_texture && (levels != 1 || rt.view.current_mip_level != 0)) {
EXIT("mipmapped display render targets are unsupported\n");
}
@@ -430,7 +442,7 @@ void ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer* buffer, const H
" extent=%ux%u view_mip=%u view_extent=%ux%u levels=%u pitch=%u"
" fmt=0x%08" PRIx32 " nfmt=0x%08" PRIx32 " order=0x%08" PRIx32
" tile=%s target=%s video_size=0x%016" PRIx64 " video_pitch=%" PRIu64 "\n",
rt_slot, effective_base, size, width, height, rt.view.current_mip_level,
rt_slot, rt.base.addr, backing_size, width, height, rt.view.current_mip_level,
view_extent.width, view_extent.height, levels, pitch, rt.info.format,
rt.info.channel_type, rt.info.channel_order, tile ? "tiled" : "linear",
render_to_texture ? "RenderTexture" : "DisplayBuffer", video_image.size,
@@ -443,8 +455,8 @@ void ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer* buffer, const H
(void)reuse_existing_render_texture;
RenderTargetInfo target {};
target.address = effective_base;
target.size = size;
target.address = rt.base.addr;
target.size = backing_size;
target.format = rt_format.format;
target.width = width;
target.height = height;
@@ -452,18 +464,20 @@ void ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer* buffer, const H
target.bytes_per_element = rt_format.bytes_per_element;
target.tile_mode = rt.attrib3.tile_mode;
target.levels = levels;
target.layers = view.image_layers;
auto* texture_cache = g_render_ctx->GetTextureCache();
auto* buffer_vulkan =
texture_cache->FindRenderTarget(buffer, g_render_ctx->GetGraphicCtx(), target);
r->type = RenderColorType::RenderTexture;
r->base_addr = effective_base;
r->base_addr = rt.base.addr;
r->vulkan_buffer = buffer_vulkan;
r->vulkan_view = texture_cache->GetRenderTargetAttachmentView(
g_render_ctx->GetGraphicCtx(), buffer_vulkan, target.format, rt.view.current_mip_level);
g_render_ctx->GetGraphicCtx(), buffer_vulkan, target.format, rt.view.current_mip_level,
view.base_layer, view.layer_count);
r->format = target.format;
r->extent = view_extent;
r->base_mip_level = rt.view.current_mip_level;
r->buffer_size = size;
r->buffer_size = backing_size;
} else {
const auto layout = static_cast<Prospero::ChannelLayout>(rt.info.format);
const auto type = static_cast<Prospero::ChannelType>(rt.info.channel_type);
@@ -489,7 +503,7 @@ void ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer* buffer, const H
EXIT_NOT_IMPLEMENTED(video_image.size < size);
EXIT_NOT_IMPLEMENTED(video_image.pitch != pitch);
r->type = RenderColorType::DisplayBuffer;
r->base_addr = effective_base;
r->base_addr = rt.base.addr;
r->vulkan_buffer = video_image.image;
r->vulkan_view = video_image.image->image_view[VulkanImage::VIEW_DEFAULT];
r->format = video_image.image->format;
+2 -2
View File
@@ -608,7 +608,7 @@ void CommandBuffer::BeginRenderPass(VulkanFramebuffer* framebuffer, RenderColorI
image_memory_barrier.subresourceRange.baseMipLevel = 0;
image_memory_barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
image_memory_barrier.subresourceRange.baseArrayLayer = 0;
image_memory_barrier.subresourceRange.layerCount = 1;
image_memory_barrier.subresourceRange.layerCount = colors[i].vulkan_buffer->layers;
vkCmdPipelineBarrier(buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, nullptr, 0,
@@ -646,7 +646,7 @@ void CommandBuffer::BeginRenderPass(VulkanFramebuffer* framebuffer, RenderColorI
image_memory_barrier.subresourceRange.baseMipLevel = 0;
image_memory_barrier.subresourceRange.levelCount = 1;
image_memory_barrier.subresourceRange.baseArrayLayer = 0;
image_memory_barrier.subresourceRange.layerCount = 1;
image_memory_barrier.subresourceRange.layerCount = depth->vulkan_buffer->layers;
vkCmdPipelineBarrier(
buffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
@@ -115,6 +115,16 @@ void ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer* buffer, const H
const bool msaa_compat = depth_msaa_single_sample_compatible(z.z_info.num_samples);
const bool htile_stencil_compat = depth_htile_stencil_acceleration_compatible(
has_stencil, has_htile, z.stencil_info.tile_stencil_disable);
const auto view = ResolveTargetViewInfo(z.depth_view.slice_start, z.depth_view.slice_max);
switch (view.type) {
case TargetViewType::Image2D: break;
case TargetViewType::Image2DArray:
DepthFatal("layered depth views are unsupported: base=%u count=%u", view.base_layer,
view.layer_count);
case TargetViewType::Unsupported:
DepthFatal("invalid depth view: base=%u last=%u", z.depth_view.slice_start,
z.depth_view.slice_max);
}
// Prospero defines the compression-disable bits as tile writeback policy. Vulkan attachments
// expose the same logical depth/stencil values regardless of the driver's backing compression.
if ((stencil_active && !has_stencil) || rc.resummarize_enable || rc.copy_centroid ||
@@ -123,8 +133,7 @@ void ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer* buffer, const H
z.stencil_info.partially_resident || z.z_info.plane_compression != 0 ||
(z.z_info.num_samples != 0 && !msaa_compat) || z.z_info.num_mip_levels != 0 ||
z.z_info.tile_mode_index != 0 || z.z_info.zrange_precision > 1 ||
z.depth_view.current_mip_level != 0 || z.depth_view.slice_start != 0 ||
z.depth_view.slice_max != 0 || z.depth_info.addr5_swizzle_mask != 0 ||
z.depth_view.current_mip_level != 0 || z.depth_info.addr5_swizzle_mask != 0 ||
z.depth_info.array_mode != 0 || z.depth_info.pipe_config != 0 ||
z.depth_info.bank_width != 0 || z.depth_info.bank_height != 0 ||
z.depth_info.macro_tile_aspect != 0 || z.depth_info.num_banks != 0 ||
@@ -172,6 +181,9 @@ void ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer* buffer, const H
if (z.htile_data_base_addr == 0 || (z.htile_data_base_addr & 0x7fffu) != 0) {
DepthFatal("invalid HTile metadata address");
}
if (z.depth_view.slice_max >= 32) {
DepthFatal("HTile clear tracking supports at most 32 slices");
}
} else if (z.htile_data_base_addr != 0) {
DepthFatal("HTile address without an enabled tile surface");
}
@@ -234,9 +246,6 @@ void ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer* buffer, const H
(has_htile != (htile_size.align == 32768 && htile_size.size != 0))) {
DepthFatal("unsupported depth/stencil/HTile footprint");
}
if (has_htile) {
g_render_ctx->GetTextureCache()->RegisterMeta(z.htile_data_base_addr, htile_size.size);
}
} else {
TileGetTextureTotalSize(guest_format, width, height, 1, pitch, 1,
Prospero::GpuEnumValue(Prospero::TileMode::kDepth), false,
@@ -253,16 +262,32 @@ void ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer* buffer, const H
z.pitch_height_valid ? 1u : 0u,
(static_cast<uint64_t>(z.slice_div64_minus1) + 1u) * 64u);
}
if (depth_size.size > UINT64_MAX / view.image_layers ||
stencil_size.size > UINT64_MAX / view.image_layers ||
htile_size.size > UINT64_MAX / view.image_layers) {
DepthFatal("layered depth footprint overflow");
}
const auto depth_backing_size = depth_size.size * view.image_layers;
const auto stencil_backing_size = stencil_size.size * view.image_layers;
const auto htile_backing_size = htile_size.size * view.image_layers;
if (depth_backing_size > TRACKER_ADDRESS_SIZE - z.z_read_base_addr ||
(has_stencil && stencil_backing_size > TRACKER_ADDRESS_SIZE - z.stencil_read_base_addr) ||
(has_htile && htile_backing_size > TRACKER_ADDRESS_SIZE - z.htile_data_base_addr)) {
DepthFatal("layered depth backing range is invalid");
}
r->htile = has_htile;
r->width = width;
r->height = height;
r->depth_buffer_size = depth_size.size;
r->depth_buffer_size = depth_backing_size;
r->depth_buffer_vaddr = z.z_read_base_addr;
r->stencil_buffer_size = has_stencil ? stencil_size.size : 0;
r->stencil_buffer_size = has_stencil ? stencil_backing_size : 0;
r->stencil_buffer_vaddr = has_stencil ? z.stencil_read_base_addr : 0;
r->htile_buffer_size = has_htile ? htile_size.size : 0;
r->htile_buffer_size = has_htile ? htile_backing_size : 0;
r->htile_buffer_vaddr = has_htile ? z.htile_data_base_addr : 0;
auto* cache = g_render_ctx->GetTextureCache();
if (has_htile) {
cache->RegisterMeta(r->htile_buffer_vaddr, r->htile_buffer_size, view.image_layers);
}
if (has_htile && rc.depth_clear_enable && !cache->ClearMeta(z.htile_data_base_addr)) {
DepthFatal("failed to acquire HTile metadata for a depth clear");
}
@@ -333,6 +358,7 @@ void ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer* buffer, const H
info.pitch = pitch;
info.bytes_per_element = bytes;
info.tile_mode = Prospero::GpuEnumValue(Prospero::TileMode::kDepth);
info.layers = view.image_layers;
info.depth_load_clear = r->depth_load_clear_enable;
info.stencil_load_clear = rc.stencil_clear_enable;
info.stencil_access =
@@ -343,6 +369,8 @@ void ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer* buffer, const H
info.stencil_htile_compressed =
has_stencil && has_htile && !z.stencil_info.tile_stencil_disable;
r->vulkan_buffer = cache->FindDepthTarget(buffer, g_render_ctx->GetGraphicCtx(), info);
r->vulkan_view = cache->GetDepthTargetAttachmentView(
g_render_ctx->GetGraphicCtx(), r->vulkan_buffer, view.base_layer, view.layer_count);
if (meta_clear && !cache->TouchMeta(z.htile_data_base_addr, z.depth_view.slice_start, false)) {
DepthFatal("failed to consume HTile clear state");
}
+54 -16
View File
@@ -238,12 +238,44 @@ static TextureVariant NativeTextureVariant(const ShaderRecompiler::IR::ImageReso
}
static bool IsSupportedSampledColorResource(const ShaderRecompiler::IR::ImageResource& resource) {
return resource.kind == ShaderRecompiler::IR::ResourceKind::Image &&
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D &&
bool supported_dimension = false;
switch (resource.dimension) {
case ShaderRecompiler::Decoder::ImageDimension::Dim2D:
case ShaderRecompiler::Decoder::ImageDimension::Dim2DArray:
supported_dimension = true;
break;
default: break;
}
return resource.kind == ShaderRecompiler::IR::ResourceKind::Image && supported_dimension &&
resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::None && resource.read &&
!resource.written && !resource.atomic && !resource.depth_compare;
}
struct TargetTextureViewInfo {
VkImageViewType type = VK_IMAGE_VIEW_TYPE_MAX_ENUM;
uint32_t base_layer = 0;
uint32_t layer_count = 0;
};
static TargetTextureViewInfo
ResolveTargetTextureView(const ShaderRecompiler::IR::ImageResource& resource,
Prospero::ImageType type, uint32_t base_layer, uint32_t image_layers) {
switch (type) {
case Prospero::ImageType::kColor2D:
return resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D &&
base_layer == 0 && image_layers == 1
? TargetTextureViewInfo {VK_IMAGE_VIEW_TYPE_2D, 0, 1}
: TargetTextureViewInfo {};
case Prospero::ImageType::kColor2DArray:
return resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DArray &&
base_layer < image_layers
? TargetTextureViewInfo {VK_IMAGE_VIEW_TYPE_2D_ARRAY, base_layer,
image_layers - base_layer}
: TargetTextureViewInfo {};
default: return {};
}
}
static bool IsSupportedSampledDepthFormat(VkFormat image_format, uint32_t guest_format,
VkFormat view_format) {
const bool d16 = image_format == VK_FORMAT_D16_UNORM &&
@@ -418,15 +450,17 @@ NativeTexture(uint64_t submit_id, CommandBuffer* command_buffer,
EXIT("unsupported texture mip view: base=%u last=%u levels=%u\n", base_level, last_level,
levels);
}
const auto view_levels = last_level - base_level + 1u;
const auto depth = static_cast<uint32_t>(descriptor.Depth()) + 1u;
const auto tile = descriptor.TileMode();
const auto format = descriptor.Format();
const auto view_format = TextureGetFormat(format, storage ? TextureFormatUsage::Storage
: TextureFormatUsage::Sampled);
const auto type = static_cast<Prospero::ImageType>(descriptor.Type());
const auto pitch = TileGetTexturePitch(format, width, levels, tile);
const auto swizzle = descriptor.DstSelXYZW();
const auto view_levels = last_level - base_level + 1u;
const auto depth = static_cast<uint32_t>(descriptor.Depth()) + 1u;
const auto tile = descriptor.TileMode();
const auto format = descriptor.Format();
const auto view_format = TextureGetFormat(format, storage ? TextureFormatUsage::Storage
: TextureFormatUsage::Sampled);
const auto type = static_cast<Prospero::ImageType>(descriptor.Type());
const auto target_view =
ResolveTargetTextureView(resource, type, descriptor.BaseArray5(), depth);
const auto pitch = TileGetTexturePitch(format, width, levels, tile);
const auto swizzle = descriptor.DstSelXYZW();
TileSizeAlign size;
TileGetTextureTotalSize(format, width, height, depth, pitch, levels, tile,
type == Prospero::ImageType::kColor3D, &size);
@@ -465,11 +499,13 @@ NativeTexture(uint64_t submit_id, CommandBuffer* command_buffer,
} else {
if (!(storage ? IsSupportedStorageImageResource(resource)
: IsSupportedSampledColorResource(resource)) ||
image->type != VulkanImageType::RenderTexture || image->layers != 1 ||
width != image->extent.width || height != image->extent.height || depth != 1 ||
image->type != VulkanImageType::RenderTexture || width != image->extent.width ||
height != image->extent.height ||
(storage ? levels != 1 || base_level != 0
: levels != image->mip_levels || base_level >= levels) ||
descriptor.BaseArray5() != 0 || type != Prospero::ImageType::kColor2D) {
target_view.type == VK_IMAGE_VIEW_TYPE_MAX_ENUM ||
target_view.base_layer >= image->layers ||
target_view.layer_count > image->layers - target_view.base_layer) {
EXIT("unsupported cached render-target image view: storage=%d resource=%u"
" image_type=%u layers=%u extent=%ux%u/%ux%u depth=%u"
" levels=%u/%u base_level=%u base_array=%u descriptor_type=%u\n",
@@ -485,12 +521,14 @@ NativeTexture(uint64_t submit_id, CommandBuffer* command_buffer,
image_view = g_render_ctx->GetTextureCache()->GetRenderTargetStorageView(
g_render_ctx->GetGraphicCtx(),
static_cast<RenderTextureVulkanImage*>(image), view_format, base_level,
view_levels);
view_levels, target_view.type, target_view.base_layer,
target_view.layer_count);
} else {
image_view = g_render_ctx->GetTextureCache()->GetRenderTargetSampledView(
g_render_ctx->GetGraphicCtx(),
static_cast<RenderTextureVulkanImage*>(image), view_format, view,
base_level, view_levels);
base_level, view_levels, target_view.type, target_view.base_layer,
target_view.layer_count);
}
}
if (image_view == nullptr && image->image_view[view] == nullptr) {
@@ -70,6 +70,7 @@ VulkanFramebuffer* FramebufferCache::CreateFramebuffer(RenderColorInfo* colors,
}
depth->format = VK_FORMAT_UNDEFINED;
depth->vulkan_buffer = nullptr;
depth->vulkan_view = nullptr;
depth->depth_test_enable = false;
depth->depth_write_enable = false;
depth->depth_bounds_test_enable = false;
@@ -97,6 +98,7 @@ VulkanFramebuffer* FramebufferCache::CreateFramebuffer(RenderColorInfo* colors,
}
if (color_match &&
f.depth_id == (with_depth ? depth->vulkan_buffer->memory.unique_id : 0) &&
f.depth_view == (with_depth ? depth->vulkan_view : nullptr) &&
f.depth_clear_enable == depth->depth_load_clear_enable &&
f.stencil_clear_enable == depth->stencil_clear_enable &&
f.depth_read_only == depth_read_only) {
@@ -237,7 +239,10 @@ VulkanFramebuffer* FramebufferCache::CreateFramebuffer(RenderColorInfo* colors,
views[i] = colors[i].vulkan_view;
}
if (with_depth) {
views[color_count] = depth->vulkan_buffer->image_view[VulkanImage::VIEW_DEFAULT];
if (depth->vulkan_view == nullptr) {
EXIT("Framebuffer: depth attachment view is missing\n");
}
views[color_count] = depth->vulkan_view;
}
VkFramebufferCreateInfo framebuffer_info {};
@@ -268,6 +273,7 @@ VulkanFramebuffer* FramebufferCache::CreateFramebuffer(RenderColorInfo* colors,
fnew.color_layout[i] = color_layout[i];
}
fnew.depth_id = (with_depth ? depth->vulkan_buffer->memory.unique_id : 0);
fnew.depth_view = (with_depth ? depth->vulkan_view : nullptr);
fnew.depth_clear_enable = depth->depth_load_clear_enable;
fnew.stencil_clear_enable = depth->stencil_clear_enable;
fnew.depth_read_only = depth_read_only;
@@ -63,6 +63,7 @@ private:
uint64_t image_id[RENDER_COLOR_ATTACHMENTS_MAX] = {};
VkImageView color_view[RENDER_COLOR_ATTACHMENTS_MAX] = {};
uint64_t depth_id = 0;
VkImageView depth_view = nullptr;
bool color_clear_enable[RENDER_COLOR_ATTACHMENTS_MAX] = {};
VkImageLayout color_layout[RENDER_COLOR_ATTACHMENTS_MAX] = {};
bool depth_clear_enable = false;
+142 -35
View File
@@ -39,6 +39,7 @@ struct RenderTargetInfo {
uint32_t bytes_per_element = 0;
uint32_t tile_mode = 0;
uint32_t levels = 1;
uint32_t layers = 1;
};
// Common image-to-buffer copy description. Storage images and render targets keep distinct
@@ -70,22 +71,23 @@ MakeColorImageTransferInfo(const RenderTargetInfo& info) noexcept {
}
struct DepthTargetInfo {
uint64_t address = 0;
uint64_t size = 0;
uint64_t stencil_address = 0;
uint64_t stencil_size = 0;
uint64_t htile_address = 0;
uint64_t htile_size = 0;
VkFormat format = VK_FORMAT_UNDEFINED;
uint32_t guest_format = 0;
uint32_t width = 0;
uint32_t height = 0;
uint32_t pitch = 0;
uint32_t bytes_per_element = 0;
uint32_t tile_mode = 0;
bool depth_load_clear = false;
bool stencil_load_clear = false;
bool stencil_access = false;
uint64_t address = 0;
uint64_t size = 0;
uint64_t stencil_address = 0;
uint64_t stencil_size = 0;
uint64_t htile_address = 0;
uint64_t htile_size = 0;
VkFormat format = VK_FORMAT_UNDEFINED;
uint32_t guest_format = 0;
uint32_t width = 0;
uint32_t height = 0;
uint32_t pitch = 0;
uint32_t bytes_per_element = 0;
uint32_t tile_mode = 0;
uint32_t layers = 1;
bool depth_load_clear = false;
bool stencil_load_clear = false;
bool stencil_access = false;
bool stencil_htile_compressed = false;
};
@@ -163,25 +165,29 @@ struct VideoOutPixelFormatInfo {
}
[[nodiscard]] inline bool IsSupportedVideoOutFormat(uint32_t guest_format, VkFormat format) {
const bool rgba8 = guest_format == Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8Srgb) &&
(format == VK_FORMAT_R8G8B8A8_SRGB || format == VK_FORMAT_B8G8R8A8_SRGB);
const bool rgb10 = guest_format == Prospero::GpuEnumValue(Prospero::BufferFormat::k10_10_10_2UNorm) &&
(format == VK_FORMAT_A2B10G10R10_UNORM_PACK32 ||
format == VK_FORMAT_A2R10G10B10_UNORM_PACK32);
const bool rgba8 =
guest_format == Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8Srgb) &&
(format == VK_FORMAT_R8G8B8A8_SRGB || format == VK_FORMAT_B8G8R8A8_SRGB);
const bool rgb10 =
guest_format == Prospero::GpuEnumValue(Prospero::BufferFormat::k10_10_10_2UNorm) &&
(format == VK_FORMAT_A2B10G10R10_UNORM_PACK32 ||
format == VK_FORMAT_A2R10G10B10_UNORM_PACK32);
return rgba8 || rgb10;
}
enum class DepthOverlap : uint8_t { None, RetireSampled, Unsupported };
enum class DepthOverlap : uint8_t { None, RetireSampled, ExpandTarget, Unsupported };
enum class DepthTransitionSource : uint8_t { None, Guest, Native };
enum class RenderTargetOverlap : uint8_t {
None,
RetireSampled,
PreserveStorage,
ExpandTarget,
RetireTarget,
Unsupported
};
enum class SampledOverlap : uint8_t { None, ReadOnlyAlias, Unsupported };
enum class StorageSampledOverlap : uint8_t { None, ExactImage, Unsupported };
enum class StorageImageOverlap : uint8_t { None, RetireSampled, PageNeighbor, Unsupported };
enum class HostWriteOverlap : uint8_t { None, InvalidateImage, Unsupported };
enum class BufferImageAlias : uint8_t { None, VideoOutWrite, RenderTargetWrite, Unsupported };
enum class MetaImageOverlap : uint8_t { RetainSampled, RetireTarget, Unsupported };
@@ -295,14 +301,15 @@ SelectDepthTransitionSource(bool depth_load_clear, bool sampled_native_available
uint64_t address, uint64_t size) {
const bool d32 =
target.format == VK_FORMAT_D32_SFLOAT || target.format == VK_FORMAT_D32_SFLOAT_S8_UINT;
return address == target.address && size == target.size && d32 &&
return address == target.address && size == target.size && target.layers == 1 && d32 &&
target.guest_format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float) &&
target.bytes_per_element == 4 && target.tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kDepth) &&
target.bytes_per_element == 4 &&
target.tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kDepth) &&
target.htile_address == 0 && target.htile_size == 0;
}
[[nodiscard]] inline bool CanLoadStencilAttachment(const DepthTargetInfo& target,
bool stencil_initialized) {
bool stencil_initialized) {
const bool has_stencil = target.stencil_address != 0 || target.stencil_size != 0;
return !has_stencil || !target.stencil_access || target.stencil_load_clear ||
stencil_initialized;
@@ -448,11 +455,12 @@ ClassifyBufferImageAlias(uint64_t buffer_address, uint64_t buffer_size, uint64_t
depth.format == VK_FORMAT_D32_SFLOAT && depth.bytes_per_element == 4));
const bool exact_depth_load =
!has_stencil && !depth.depth_load_clear && sampled.address == depth.address &&
sampled.size == depth.size && sampled.width == depth.width && sampled.height == depth.height &&
sampled.pitch == depth.pitch && sampled.base_level == 0 && sampled.levels == 1 &&
sampled.view_levels == 1 && sampled.tile == depth.tile_mode && sampled.depth == 1 &&
sampled.size == depth.size && sampled.width == depth.width &&
sampled.height == depth.height && sampled.pitch == depth.pitch && sampled.base_level == 0 &&
sampled.levels == 1 && sampled.view_levels == 1 && sampled.tile == depth.tile_mode &&
sampled.depth == 1 &&
sampled.type == Prospero::GpuEnumValue(Prospero::ImageType::kColor2D) &&
sampled.base_array == 0 && exact_depth_format;
depth.layers == 1 && sampled.base_array == 0 && exact_depth_format;
if (!sampled_gpu_modified && exact_depth_load) {
return DepthOverlap::RetireSampled;
}
@@ -480,10 +488,11 @@ ClassifyRenderTargetOverlap(const ImageInfo& sampled, bool sampled_gpu_modified,
: RenderTargetOverlap::Unsupported;
}
[[nodiscard]] inline RenderTargetOverlap ClassifyStorageRenderTargetOverlap(
const ImageInfo& storage, VkFormat storage_format, bool storage_gpu_modified,
bool storage_buffer_modified, bool storage_cpu_dirty, bool same_context,
const RenderTargetInfo& target) {
[[nodiscard]] inline RenderTargetOverlap
ClassifyStorageRenderTargetOverlap(const ImageInfo& storage, VkFormat storage_format,
bool storage_gpu_modified, bool storage_buffer_modified,
bool storage_cpu_dirty, bool same_context,
const RenderTargetInfo& target) {
if (!ImagePageRangesOverlap(storage.address, storage.size, target.address, target.size)) {
return RenderTargetOverlap::None;
}
@@ -494,7 +503,7 @@ ClassifyRenderTargetOverlap(const ImageInfo& sampled, bool sampled_gpu_modified,
storage.base_level == 0 && storage.levels == 1 && storage.view_levels == 1 &&
storage.tile == target.tile_mode && storage.depth == 1 &&
storage.type == Prospero::GpuEnumValue(Prospero::ImageType::kColor2D) &&
storage.base_array == 0 && target.levels == 1;
storage.base_array == 0 && target.levels == 1 && target.layers == 1;
return exact_native_image && storage_gpu_modified && !storage_buffer_modified &&
!storage_cpu_dirty && same_context
? RenderTargetOverlap::PreserveStorage
@@ -521,9 +530,85 @@ ClassifyRenderTargetOverlap(const ImageInfo& sampled, bool sampled_gpu_modified,
cached.pitch == requested.pitch &&
cached.bytes_per_element == requested.bytes_per_element &&
cached.tile_mode == requested.tile_mode && cached.levels == requested.levels &&
cached.layers == requested.layers &&
IsRgba8SrgbReinterpretation(cached.format, requested.format);
}
[[nodiscard]] inline RenderTargetOverlap
ClassifySampledRenderTargetOverlap(const ImageInfo& sampled, const RenderTargetInfo& target,
bool target_buffer_modified, bool same_context) {
if (!ImagePageRangesOverlap(sampled.address, sampled.size, target.address, target.size)) {
return RenderTargetOverlap::None;
}
return same_context && !target_buffer_modified ? RenderTargetOverlap::RetireTarget
: RenderTargetOverlap::Unsupported;
}
[[nodiscard]] inline StorageImageOverlap
ClassifyStorageImageOverlap(uint64_t requested_address, uint64_t requested_size,
uint64_t cached_address, uint64_t cached_size, bool sampled,
bool same_context, bool gpu_modified, bool buffer_modified,
bool tracker_gpu_modified) {
if (!ImagePageRangesOverlap(requested_address, requested_size, cached_address, cached_size)) {
return StorageImageOverlap::None;
}
if (!ImageRangeOverlaps(requested_address, requested_size, cached_address, cached_size)) {
return StorageImageOverlap::PageNeighbor;
}
return sampled && same_context && !gpu_modified && !buffer_modified && !tracker_gpu_modified
? StorageImageOverlap::RetireSampled
: StorageImageOverlap::Unsupported;
}
[[nodiscard]] inline constexpr bool LayeredBackingContains(uint64_t container_size,
uint32_t container_layers,
uint64_t view_size,
uint32_t view_layers) {
return container_layers >= view_layers && container_layers != 0 && view_layers != 0 &&
container_size % container_layers == 0 && view_size % view_layers == 0 &&
container_size / container_layers == view_size / view_layers;
}
[[nodiscard]] inline constexpr bool
LayeredPlaneContains(uint64_t container_address, uint64_t container_size, uint32_t container_layers,
uint64_t view_address, uint64_t view_size, uint32_t view_layers) {
return container_address == 0 ? view_address == 0 && view_size == 0
: view_address == container_address &&
LayeredBackingContains(container_size, container_layers,
view_size, view_layers);
}
[[nodiscard]] inline bool
IsCompatibleRenderTargetBacking(const RenderTargetInfo& cached,
const RenderTargetInfo& requested) noexcept {
return cached.address == requested.address &&
LayeredBackingContains(cached.size, cached.layers, requested.size, requested.layers) &&
cached.width == requested.width && cached.height == requested.height &&
cached.pitch == requested.pitch &&
cached.bytes_per_element == requested.bytes_per_element &&
cached.tile_mode == requested.tile_mode && cached.levels == requested.levels &&
(cached.format == requested.format ||
IsRgba8SrgbReinterpretation(cached.format, requested.format));
}
[[nodiscard]] inline bool
IsCompatibleDepthTargetBacking(const DepthTargetInfo& cached,
const DepthTargetInfo& requested) noexcept {
return cached.address == requested.address &&
LayeredBackingContains(cached.size, cached.layers, requested.size, requested.layers) &&
LayeredPlaneContains(cached.stencil_address, cached.stencil_size, cached.layers,
requested.stencil_address, requested.stencil_size,
requested.layers) &&
LayeredPlaneContains(cached.htile_address, cached.htile_size, cached.layers,
requested.htile_address, requested.htile_size, requested.layers) &&
cached.format == requested.format && cached.guest_format == requested.guest_format &&
cached.width == requested.width && cached.height == requested.height &&
cached.pitch == requested.pitch &&
cached.bytes_per_element == requested.bytes_per_element &&
cached.tile_mode == requested.tile_mode &&
cached.stencil_htile_compressed == requested.stencil_htile_compressed;
}
[[nodiscard]] inline RenderTargetOverlap
ClassifyRenderTargetOverlap(const RenderTargetInfo& cached, bool cached_gpu_modified,
bool cached_buffer_modified, bool same_context,
@@ -531,8 +616,15 @@ ClassifyRenderTargetOverlap(const RenderTargetInfo& cached, bool cached_gpu_modi
if (!ImagePageRangesOverlap(cached.address, cached.size, requested.address, requested.size)) {
return RenderTargetOverlap::None;
}
const bool expand = requested.layers > cached.layers &&
IsCompatibleRenderTargetBacking(requested, cached) &&
cached.format == requested.format;
if (expand && cached_gpu_modified && !cached_buffer_modified && same_context) {
return RenderTargetOverlap::ExpandTarget;
}
// For an equal-address allocation-pool entry, a changed block raster or block size is a new
// image allocation, not a view of the old image. In Kyty we can only retire an already-published target with page-isolated storage :)
// image allocation, not a view of the old image. In Kyty we can only retire an
// already-published target with page-isolated storage :)
const bool page_isolated =
cached.address % TRACKER_PAGE_SIZE == 0 && cached.size % TRACKER_PAGE_SIZE == 0 &&
requested.address % TRACKER_PAGE_SIZE == 0 && requested.size % TRACKER_PAGE_SIZE == 0;
@@ -545,6 +637,21 @@ ClassifyRenderTargetOverlap(const RenderTargetInfo& cached, bool cached_gpu_modi
: RenderTargetOverlap::Unsupported;
}
[[nodiscard]] inline DepthOverlap ClassifyDepthTargetOverlap(const DepthTargetInfo& cached,
bool cached_gpu_modified,
bool cached_buffer_modified,
bool same_context,
const DepthTargetInfo& requested) {
if (!ImagePageRangesOverlap(cached.address, cached.size, requested.address, requested.size)) {
return DepthOverlap::None;
}
const bool expand =
requested.layers > cached.layers && IsCompatibleDepthTargetBacking(requested, cached);
return expand && cached_gpu_modified && !cached_buffer_modified && same_context
? DepthOverlap::ExpandTarget
: DepthOverlap::Unsupported;
}
} // namespace Libs::Graphics
#endif // EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_IMAGEINFO_H_
+21 -2
View File
@@ -40,6 +40,24 @@ inline constexpr bool color_msaa_single_sample_compatible(uint32_t encoded_sampl
depth_msaa_single_sample_compatible(encoded_fragments);
}
enum class TargetViewType : uint8_t { Image2D, Image2DArray, Unsupported };
struct TargetViewInfo {
TargetViewType type = TargetViewType::Unsupported;
uint32_t base_layer = 0;
uint32_t layer_count = 0;
uint32_t image_layers = 0;
};
inline constexpr TargetViewInfo ResolveTargetViewInfo(uint32_t base_layer, uint32_t last_layer,
uint32_t draw_layer_offset = 0) {
if (base_layer > last_layer || draw_layer_offset != 0) {
return {};
}
return {base_layer == last_layer ? TargetViewType::Image2D : TargetViewType::Image2DArray,
base_layer, last_layer - base_layer + 1u, last_layer + 1u};
}
inline constexpr bool depth_htile_stencil_acceleration_compatible(bool has_stencil, bool has_htile,
bool acceleration_disabled) {
return acceleration_disabled || (has_stencil && has_htile);
@@ -147,8 +165,8 @@ static_assert(std::is_trivially_copyable_v<PipelineDynamicParameters>);
static_assert(std::is_standard_layout_v<PipelineDynamicParameters>);
static_assert(alignof(PipelineDynamicParameters) == 1);
static_assert(sizeof(PipelineDynamicParameters) ==
sizeof(bool) + sizeof(float[3]) + sizeof(float[3]) + sizeof(int[4]) +
sizeof(float) + sizeof(uint32_t) + sizeof(bool[RENDER_COLOR_ATTACHMENTS_MAX]) +
sizeof(bool) + sizeof(float[3]) + sizeof(float[3]) + sizeof(int[4]) + sizeof(float) +
sizeof(uint32_t) + sizeof(bool[RENDER_COLOR_ATTACHMENTS_MAX]) +
sizeof(PipelineStencilDynamicState) * 2);
struct RenderDepthInfo {
@@ -183,6 +201,7 @@ struct RenderDepthInfo {
PipelineStencilDynamicState stencil_dynamic_front;
PipelineStencilDynamicState stencil_dynamic_back;
DepthStencilVulkanImage* vulkan_buffer = nullptr;
VkImageView vulkan_view = nullptr;
uint64_t vaddr[3] = {};
uint64_t size[3] = {};
int vaddr_num = 0;
File diff suppressed because it is too large Load Diff
+32 -16
View File
@@ -31,7 +31,8 @@ class ResourceMutex;
class TextureCache {
public:
TextureCache(PageManager& page_manager, BufferCache& buffer_cache, ResourceMutex& resource_mutex);
TextureCache(PageManager& page_manager, BufferCache& buffer_cache,
ResourceMutex& resource_mutex);
~TextureCache();
KYTY_CLASS_NO_COPY(TextureCache);
@@ -61,31 +62,41 @@ public:
uint64_t vaddr, uint64_t size);
[[nodiscard]] VkImageView GetRenderTargetAttachmentView(GraphicContext* ctx,
RenderTextureVulkanImage* image,
VkFormat format, uint32_t level);
VkFormat format, uint32_t level,
uint32_t base_layer,
uint32_t layer_count);
[[nodiscard]] VkImageView GetDepthTargetAttachmentView(GraphicContext* ctx,
DepthStencilVulkanImage* image,
uint32_t base_layer,
uint32_t layer_count);
[[nodiscard]] VkImageView GetRenderTargetSampledView(GraphicContext* ctx,
RenderTextureVulkanImage* image,
VkFormat view_format, int variant,
uint32_t base_level, uint32_t level_count);
uint32_t base_level, uint32_t level_count,
VkImageViewType type, uint32_t base_layer,
uint32_t layer_count);
[[nodiscard]] VkImageView GetRenderTargetStorageView(GraphicContext* ctx,
RenderTextureVulkanImage* image,
VkFormat view_format, uint32_t base_level,
uint32_t level_count);
uint32_t level_count, VkImageViewType type,
uint32_t base_layer, uint32_t layer_count);
[[nodiscard]] VkImageView GetStorageTextureSampledView(GraphicContext* ctx,
StorageTextureVulkanImage* image,
const ImageInfo& info);
[[nodiscard]] DepthStencilVulkanImage* FindDepthTargetByRange(uint64_t vaddr, uint64_t size);
[[nodiscard]] bool HasPageOverlap(uint64_t vaddr, uint64_t size);
[[nodiscard]] bool HasRangeOverlap(uint64_t vaddr, uint64_t size);
[[nodiscard]] bool HasGpuModifiedRangeOverlap(uint64_t vaddr, uint64_t size);
[[nodiscard]] bool HasGpuTargetPageOverlap(uint64_t vaddr, uint64_t size);
void RegisterMeta(uint64_t vaddr, uint64_t size);
[[nodiscard]] bool IsMeta(uint64_t vaddr);
[[nodiscard]] bool IsMetaRange(uint64_t vaddr, uint64_t size);
[[nodiscard]] bool HasMetaOverlap(uint64_t vaddr, uint64_t size);
[[nodiscard]] bool IsMetaGpuModified(uint64_t vaddr, uint64_t size);
[[nodiscard]] bool IsMetaCleared(uint64_t vaddr, uint32_t slice);
[[nodiscard]] bool ClearMeta(uint64_t vaddr);
[[nodiscard]] bool TouchMeta(uint64_t vaddr, uint32_t slice, bool is_clear);
[[nodiscard]] bool HasGpuModifiedRangeOverlap(uint64_t vaddr, uint64_t size);
[[nodiscard]] bool HasGpuTargetPageOverlap(uint64_t vaddr, uint64_t size);
void RegisterMeta(uint64_t vaddr, uint64_t size, uint32_t layers = 1);
[[nodiscard]] bool IsMeta(uint64_t vaddr);
[[nodiscard]] bool IsMetaRange(uint64_t vaddr, uint64_t size);
[[nodiscard]] bool HasMetaRangeOverlap(uint64_t vaddr, uint64_t size);
[[nodiscard]] bool HasMetaOverlap(uint64_t vaddr, uint64_t size);
[[nodiscard]] bool IsMetaGpuModified(uint64_t vaddr, uint64_t size);
[[nodiscard]] bool IsMetaCleared(uint64_t vaddr, uint32_t slice);
[[nodiscard]] bool ClearMeta(uint64_t vaddr);
[[nodiscard]] bool TouchMeta(uint64_t vaddr, uint32_t slice, bool is_clear);
[[nodiscard]] bool InvalidateMemory(PageFaultAccess access, uint64_t vaddr, uint64_t size,
PageFaultPhase phase) noexcept;
void UnmapMemory(uint64_t vaddr, uint64_t size);
@@ -106,6 +117,7 @@ private:
struct ReadbackWorker;
struct MetaDataInfo {
uint64_t size = 0;
uint32_t layers = 1;
uint32_t clear_mask = 0;
bool gpu_modified = false;
};
@@ -115,9 +127,13 @@ private:
uint64_t size) const;
void RequireNoMetaOverlapLocked(uint64_t vaddr, uint64_t size) const;
void MarkSampledAliasesCpuDirtyLocked(uint64_t vaddr, uint64_t size);
void RetireSampledTargetAliases(GraphicContext* ctx, const ImageInfo& requested);
void RetireStoragePageNeighbors(GraphicContext* ctx, const ImageInfo& requested);
void RequireRetirementIsolation(const std::vector<CachedImage*>& retire, const char* operation,
uint64_t address, uint64_t size) const;
void RetireImages(const std::vector<CachedImage*>& retire,
const CachedImage* native_storage_source = nullptr);
void SynchronizeRenderTargetToBufferLocked(CachedImage& cached);
const CachedImage* native_image_source = nullptr);
void SynchronizeRenderTargetToBufferLocked(CachedImage& cached);
Common::Mutex m_dummy_mutex;
std::array<VulkanImage*, 4> m_dummy_sampled_textures {};
+81 -34
View File
@@ -8,11 +8,12 @@
#include "graphics/host_gpu/renderer/image.h"
#include "graphics/host_gpu/utils.h"
#include <vector>
namespace Libs::Graphics {
void Tiler::DetileImage(GraphicContext* ctx, GpuTextureVulkanImage* image,
const ImageInfo& info, const BufferImageCopySource& source,
bool refresh, bool storage) const {
void Tiler::DetileImage(GraphicContext* ctx, GpuTextureVulkanImage* image, const ImageInfo& info,
const BufferImageCopySource& source, bool refresh, bool storage) const {
if (ctx == nullptr || image == nullptr || info.address == 0 || info.size == 0 ||
info.width == 0 || info.height == 0 || info.depth == 0 || info.levels == 0 ||
info.levels >= 16 || !source.cpu_current || source.address != info.address ||
@@ -50,16 +51,19 @@ void Tiler::DetileImage(GraphicContext* ctx, GpuTextureVulkanImage* image,
}
void Tiler::DetileImage(GraphicContext* ctx, DepthStencilVulkanImage* image,
const DepthTargetInfo& info, const BufferImageCopySource& source,
bool refresh) const {
const bool d16 = info.guest_format == Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm) &&
info.format == VK_FORMAT_D16_UNORM && info.bytes_per_element == 2;
const DepthTargetInfo& info, const BufferImageCopySource& source,
bool refresh, uint32_t base_layer) const {
const bool d16 =
info.guest_format == Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm) &&
info.format == VK_FORMAT_D16_UNORM && info.bytes_per_element == 2;
const bool d32 =
info.guest_format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float) &&
(info.format == VK_FORMAT_D32_SFLOAT || info.format == VK_FORMAT_D32_SFLOAT_S8_UINT) &&
info.bytes_per_element == 4;
if (ctx == nullptr || image == nullptr || info.address == 0 || info.size == 0 ||
info.width == 0 || info.height == 0 || info.pitch < info.width ||
info.width == 0 || info.height == 0 || info.pitch < info.width || info.layers == 0 ||
info.size % info.layers != 0 || base_layer > image->layers ||
info.layers > image->layers - base_layer || info.size > UINT32_MAX ||
info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kDepth) || (!d16 && !d32) ||
!source.cpu_current || source.address != info.address || source.size != info.size ||
(source.buffer == nullptr && source.offset != 0)) {
@@ -76,38 +80,55 @@ void Tiler::DetileImage(GraphicContext* ctx, DepthStencilVulkanImage* image,
if (refresh) {
VulkanDeviceWaitIdle(ctx);
}
UtilScratchBuffer linear(info.size);
TileConvertTiledToLinearDepth(linear.Data(), reinterpret_cast<const void*>(source.address),
info.guest_format, info.width, info.height, info.pitch,
info.size);
UtilFillImage(ctx, image, linear.Data(), info.size, info.pitch, VK_IMAGE_ASPECT_DEPTH_BIT);
const auto slice_size = info.size / info.layers;
UtilScratchBuffer linear(info.size);
std::vector<BufferImageCopy> regions;
regions.reserve(info.layers);
for (uint32_t layer = 0; layer < info.layers; layer++) {
auto* linear_slice = static_cast<uint8_t*>(linear.Data()) + slice_size * layer;
auto* guest_slice = reinterpret_cast<const uint8_t*>(source.address) + slice_size * layer;
TileConvertTiledToLinearDepth(linear_slice, guest_slice, info.guest_format, info.width,
info.height, info.pitch, slice_size);
BufferImageCopy region {};
region.offset = static_cast<uint32_t>(slice_size * layer);
region.pitch = info.pitch;
region.width = info.width;
region.height = info.height;
region.dst_layer = base_layer + layer;
region.aspect = VK_IMAGE_ASPECT_DEPTH_BIT;
regions.push_back(region);
}
UtilFillImage(ctx, image, linear.Data(), info.size, regions,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
}
void Tiler::DetileStencil(GraphicContext* ctx, DepthStencilVulkanImage* image,
const DepthTargetInfo& info, const BufferImageCopySource& source,
bool refresh) const {
bool refresh, uint32_t base_layer) const {
TileSizeAlign stencil_size {};
TileSizeAlign htile_size {};
TileSizeAlign depth_size {};
const auto stencil_format = Prospero::GpuEnumValue(Prospero::BufferFormat::k8UInt);
const auto stencil_pitch = TileGetTexturePitch(
stencil_format, info.width, 1, Prospero::GpuEnumValue(Prospero::TileMode::kDepth));
const bool supported_layout =
const auto stencil_format = Prospero::GpuEnumValue(Prospero::BufferFormat::k8UInt);
const auto stencil_pitch = TileGetTexturePitch(
stencil_format, info.width, 1, Prospero::GpuEnumValue(Prospero::TileMode::kDepth));
const bool supported_layout =
TileGetDepthSize(info.width, info.height, 0,
Prospero::GpuEnumValue(Prospero::DepthFormat::kZ32F),
Prospero::GpuEnumValue(Prospero::StencilFormat::k8UInt),
info.htile_address != 0, &stencil_size, &htile_size, &depth_size) &&
stencil_size.size == info.stencil_size && stencil_size.align == 65536 &&
info.layers != 0 && info.stencil_size % info.layers == 0 &&
stencil_size.size == info.stencil_size / info.layers && stencil_size.align == 65536 &&
stencil_pitch != 0;
if (ctx == nullptr || image == nullptr || info.address == 0 || info.size == 0 ||
info.stencil_address == 0 || info.stencil_size == 0 || info.width == 0 ||
info.height == 0 || info.format != VK_FORMAT_D32_SFLOAT_S8_UINT ||
info.guest_format != Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float) ||
info.bytes_per_element != 4 ||
info.bytes_per_element != 4 || base_layer > image->layers ||
info.layers > image->layers - base_layer || info.stencil_size > UINT32_MAX ||
info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kDepth) ||
info.stencil_htile_compressed || !supported_layout || !source.cpu_current ||
source.address != info.stencil_address ||
source.size != info.stencil_size || (source.buffer == nullptr && source.offset != 0)) {
source.address != info.stencil_address || source.size != info.stencil_size ||
(source.buffer == nullptr && source.offset != 0)) {
EXIT("Tiler: unsupported stencil detile, ctx=%p image=%p source_buffer=%p "
"source=0x%016" PRIx64 "+0x%016" PRIx64 " offset=0x%016" PRIx64
" current=%d stencil=0x%016" PRIx64 "+0x%016" PRIx64
@@ -122,12 +143,26 @@ void Tiler::DetileStencil(GraphicContext* ctx, DepthStencilVulkanImage* image,
if (refresh) {
VulkanDeviceWaitIdle(ctx);
}
UtilScratchBuffer linear(info.stencil_size);
TileConvertTiledToLinearDepth(linear.Data(), reinterpret_cast<const void*>(source.address),
stencil_format, info.width, info.height, stencil_pitch,
info.stencil_size);
UtilFillImage(ctx, image, linear.Data(), info.stencil_size, stencil_pitch,
VK_IMAGE_ASPECT_STENCIL_BIT);
const auto slice_size = info.stencil_size / info.layers;
UtilScratchBuffer linear(info.stencil_size);
std::vector<BufferImageCopy> regions;
regions.reserve(info.layers);
for (uint32_t layer = 0; layer < info.layers; layer++) {
auto* linear_slice = static_cast<uint8_t*>(linear.Data()) + slice_size * layer;
auto* guest_slice = reinterpret_cast<const uint8_t*>(source.address) + slice_size * layer;
TileConvertTiledToLinearDepth(linear_slice, guest_slice, stencil_format, info.width,
info.height, stencil_pitch, slice_size);
BufferImageCopy region {};
region.offset = static_cast<uint32_t>(slice_size * layer);
region.pitch = stencil_pitch;
region.width = info.width;
region.height = info.height;
region.dst_layer = base_layer + layer;
region.aspect = VK_IMAGE_ASPECT_STENCIL_BIT;
regions.push_back(region);
}
UtilFillImage(ctx, image, linear.Data(), info.stencil_size, regions,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
}
void Tiler::TileImage(void* dst, const void* src, const RenderTargetInfo& info) const {
@@ -135,7 +170,8 @@ void Tiler::TileImage(void* dst, const void* src, const RenderTargetInfo& info)
info.bytes_per_element == 4 || info.bytes_per_element == 8;
if (dst == nullptr || src == nullptr || info.address == 0 || info.size == 0 ||
info.width == 0 || info.height == 0 || info.pitch < info.width ||
info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget) || info.levels != 1 ||
info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget) ||
info.levels != 1 || info.layers == 0 || info.size % info.layers != 0 ||
!supported_element) {
EXIT("Tiler: unsupported render-target tile, dst=%p src=%p "
"addr=0x%016" PRIx64 "+0x%016" PRIx64
@@ -143,8 +179,13 @@ void Tiler::TileImage(void* dst, const void* src, const RenderTargetInfo& info)
dst, src, info.address, info.size, info.width, info.height, info.pitch, info.levels,
info.tile_mode, info.bytes_per_element);
}
TileConvertLinearToTiledRenderTarget(dst, src, info.width, info.height, info.pitch,
info.bytes_per_element, info.size);
const auto slice_size = info.size / info.layers;
for (uint32_t layer = 0; layer < info.layers; layer++) {
auto* guest_slice = static_cast<uint8_t*>(dst) + slice_size * layer;
auto* linear_slice = static_cast<const uint8_t*>(src) + slice_size * layer;
TileConvertLinearToTiledRenderTarget(guest_slice, linear_slice, info.width, info.height,
info.pitch, info.bytes_per_element, slice_size);
}
}
void Tiler::TileImage(void* dst, const void* src, const DepthTargetInfo& info) const {
@@ -156,7 +197,8 @@ void Tiler::TileImage(void* dst, const void* src, const DepthTargetInfo& info) c
if (dst == nullptr || src == nullptr || info.address == 0 || info.size == 0 ||
info.stencil_address != 0 || info.stencil_size != 0 || info.width == 0 ||
info.height == 0 || info.pitch < info.width ||
info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kDepth) || !supported_format) {
info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kDepth) || info.layers == 0 ||
info.size % info.layers != 0 || !supported_format) {
EXIT("Tiler: unsupported depth-target tile, dst=%p src=%p "
"depth=0x%016" PRIx64 "+0x%016" PRIx64 " stencil=0x%016" PRIx64 "+0x%016" PRIx64
" extent=%ux%u pitch=%u tile=%u format=%d guest_format=%u bpe=%u\n",
@@ -164,8 +206,13 @@ void Tiler::TileImage(void* dst, const void* src, const DepthTargetInfo& info) c
info.height, info.pitch, info.tile_mode, static_cast<int>(info.format),
info.guest_format, info.bytes_per_element);
}
TileConvertLinearToTiledDepth(dst, src, info.guest_format, info.width, info.height, info.pitch,
info.size);
const auto slice_size = info.size / info.layers;
for (uint32_t layer = 0; layer < info.layers; layer++) {
auto* guest_slice = static_cast<uint8_t*>(dst) + slice_size * layer;
auto* linear_slice = static_cast<const uint8_t*>(src) + slice_size * layer;
TileConvertLinearToTiledDepth(guest_slice, linear_slice, info.guest_format, info.width,
info.height, info.pitch, slice_size);
}
}
} // namespace Libs::Graphics
+3 -3
View File
@@ -19,11 +19,11 @@ public:
void DetileImage(GraphicContext* ctx, GpuTextureVulkanImage* image, const ImageInfo& info,
const BufferImageCopySource& source, bool refresh, bool storage) const;
void DetileImage(GraphicContext* ctx, DepthStencilVulkanImage* image,
const DepthTargetInfo& info, const BufferImageCopySource& source,
bool refresh) const;
const DepthTargetInfo& info, const BufferImageCopySource& source, bool refresh,
uint32_t base_layer = 0) const;
void DetileStencil(GraphicContext* ctx, DepthStencilVulkanImage* image,
const DepthTargetInfo& info, const BufferImageCopySource& source,
bool refresh) const;
bool refresh, uint32_t base_layer = 0) const;
void TileImage(void* dst, const void* src, const RenderTargetInfo& info) const;
void TileImage(void* dst, const void* src, const DepthTargetInfo& info) const;
};
+91 -38
View File
@@ -251,38 +251,58 @@ ConvertBufferImageCopies(std::span<const BufferImageCopy> regions) {
for (size_t i = 0; i < regions.size(); i++) {
const auto& r = regions[i];
vk_regions[i] =
MakeBufferImageCopy(r.offset, r.pitch, VK_IMAGE_ASPECT_COLOR_BIT, r.dst_level,
r.dst_layer, {r.dst_x, r.dst_y, r.dst_z},
{r.width, (r.copy_height != 0 ? r.copy_height : r.height), 1});
vk_regions[i] = MakeBufferImageCopy(
r.offset, r.pitch, r.aspect, r.dst_level, r.dst_layer, {r.dst_x, r.dst_y, r.dst_z},
{r.width, (r.copy_height != 0 ? r.copy_height : r.height), 1});
}
return vk_regions;
}
[[nodiscard]] static std::span<const VkBufferImageCopy>
ConvertImageBufferCopies(std::span<const ImageBufferCopy> regions) {
static thread_local std::vector<VkBufferImageCopy> vk_regions;
vk_regions.resize(regions.size());
for (size_t i = 0; i < regions.size(); i++) {
const auto& r = regions[i];
vk_regions[i] = MakeBufferImageCopy(
r.offset, r.pitch, r.aspect, r.src_level, r.src_layer, {r.src_x, r.src_y, r.src_z},
{r.width, r.copy_height != 0 ? r.copy_height : r.height, 1});
}
return vk_regions;
}
static void RecordImageToBuffer(CommandBuffer& command, VulkanImage& src_image,
VulkanBuffer& dst_buffer, uint32_t dst_pitch,
VkImageLayout final_layout, VkImageAspectFlags aspect) {
auto* vk_command = command.GetPool()->buffers[command.GetIndex()];
const auto transition_aspects = GetTransferAspects(src_image, aspect);
SetImageLayout(vk_command, &src_image, 0, VK_REMAINING_MIP_LEVELS, transition_aspects,
src_image.layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
const auto region = MakeBufferImageCopy(0, dst_pitch, aspect, 0, 0, {0, 0, 0},
{src_image.extent.width, src_image.extent.height, 1});
VulkanBuffer& dst_buffer, std::span<const ImageBufferCopy> regions,
VkImageLayout final_layout) {
auto* vk_command = command.GetPool()->buffers[command.GetIndex()];
VkImageAspectFlags aspects = 0;
for (const auto& region: regions) {
aspects |= GetTransferAspects(src_image, region.aspect);
}
SetImageLayout(vk_command, &src_image, 0, VK_REMAINING_MIP_LEVELS, aspects, src_image.layout,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
SetBufferMemoryBarrier(vk_command, dst_buffer.buffer, 0, VK_WHOLE_SIZE,
VK_ACCESS_MEMORY_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
const auto copies = ConvertImageBufferCopies(regions);
vkCmdCopyImageToBuffer(vk_command, src_image.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
dst_buffer.buffer, 1, &region);
dst_buffer.buffer, static_cast<uint32_t>(copies.size()), copies.data());
SetBufferMemoryBarrier(vk_command, dst_buffer.buffer, 0, VK_WHOLE_SIZE,
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT);
SetImageLayout(vk_command, &src_image, 0, VK_REMAINING_MIP_LEVELS, transition_aspects,
SetImageLayout(vk_command, &src_image, 0, VK_REMAINING_MIP_LEVELS, aspects,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, final_layout);
}
static void RecordImageToBuffer(CommandBuffer& command, VulkanImage& src_image,
VulkanBuffer& dst_buffer, uint32_t dst_pitch,
VkImageLayout final_layout, VkImageAspectFlags aspect) {
const ImageBufferCopy region {
0, dst_pitch, 0, src_image.extent.width, src_image.extent.height, 0, 0, 0, 0, 0, aspect};
RecordImageToBuffer(command, src_image, dst_buffer,
std::span<const ImageBufferCopy>(&region, 1), final_layout);
}
class ReusableStagingBuffer {
public:
ReusableStagingBuffer() = default;
@@ -328,10 +348,13 @@ public:
uint64_t size, std::span<const BufferImageCopy> regions,
uint64_t dst_layout) {
RecordUpload<false>(ctx, src_data, size, [&](CommandBuffer* command, VkCommandBuffer) {
VkImageAspectFlags transition_aspects = 0;
for (const auto& region: regions) {
transition_aspects |= GetTransferAspects(*dst_image, region.aspect);
}
RecordBufferToImageCopy(*command, m_buffer, *dst_image,
ConvertBufferImageCopies(regions), VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_UNDEFINED,
static_cast<VkImageLayout>(dst_layout));
ConvertBufferImageCopies(regions), transition_aspects,
dst_image->layout, static_cast<VkImageLayout>(dst_layout));
});
}
@@ -349,6 +372,18 @@ public:
std::memcpy(dst_data, m_mapped_data, size);
}
void DownloadFromImage(GraphicContext* ctx, void* dst_data, uint64_t size,
std::span<const ImageBufferCopy> regions, VulkanImage* src_image,
uint64_t src_layout) {
Common::LockGuard lock(m_mutex);
EnsureBuffer(ctx, size, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
ExecuteImmediateUtilCommands([&](CommandBuffer* command, VkCommandBuffer) {
RecordImageToBuffer(*command, *src_image, m_buffer, regions,
static_cast<VkImageLayout>(src_layout));
});
std::memcpy(dst_data, m_mapped_data, size);
}
private:
template <bool WaitIdle, typename Recorder>
void RecordUpload(GraphicContext* ctx, const void* src_data, uint64_t size,
@@ -583,7 +618,8 @@ static void SetImageLayout(VkCommandBuffer buffer, VulkanImage* dst_image, uint3
static bool CopyBlockImageToCompressedImage(VkCommandBuffer vk_buffer, const ImageImageCopy& r,
VulkanImage* dst_image) {
if (!UtilIsBcFormat(dst_image->format)) {
if (r.src_aspect != VK_IMAGE_ASPECT_COLOR_BIT || r.dst_aspect != VK_IMAGE_ASPECT_COLOR_BIT ||
!UtilIsBcFormat(dst_image->format)) {
return false;
}
@@ -637,8 +673,10 @@ void UtilImageToImage(CommandBuffer* buffer, std::span<const ImageImageCopy> reg
VulkanImage* dst_image, uint64_t dst_layout) {
auto* vk_buffer = buffer->GetPool()->buffers[buffer->GetIndex()];
bool same_image_copy = false;
bool same_image_copy = false;
VkImageAspectFlags dst_transition_aspects = 0;
for (const auto& r: regions) {
dst_transition_aspects |= GetTransferAspects(*dst_image, r.dst_aspect);
if (r.src_image == dst_image) {
same_image_copy = true;
break;
@@ -646,18 +684,18 @@ void UtilImageToImage(CommandBuffer* buffer, std::span<const ImageImageCopy> reg
}
if (same_image_copy) {
SetImageLayout(vk_buffer, dst_image, 0, VK_REMAINING_MIP_LEVELS, VK_IMAGE_ASPECT_COLOR_BIT,
SetImageLayout(vk_buffer, dst_image, 0, VK_REMAINING_MIP_LEVELS, dst_transition_aspects,
dst_image->layout, VK_IMAGE_LAYOUT_GENERAL);
for (const auto& r: regions) {
VkImageCopy region;
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.srcSubresource.aspectMask = r.src_aspect;
region.srcSubresource.mipLevel = r.src_level;
region.srcSubresource.baseArrayLayer = r.src_layer;
region.srcSubresource.layerCount = 1;
region.srcOffset = {r.src_x, r.src_y, r.src_z};
region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.dstSubresource.aspectMask = r.dst_aspect;
region.dstSubresource.mipLevel = r.dst_level;
region.dstSubresource.baseArrayLayer = r.dst_layer;
region.dstSubresource.layerCount = 1;
@@ -668,8 +706,9 @@ void UtilImageToImage(CommandBuffer* buffer, std::span<const ImageImageCopy> reg
auto restore_layout = VK_IMAGE_LAYOUT_GENERAL;
if (r.src_image != dst_image) {
restore_layout = r.src_image->layout;
SetImageLayout(vk_buffer, r.src_image, r.src_level, 1, VK_IMAGE_ASPECT_COLOR_BIT,
restore_layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
SetImageLayout(vk_buffer, r.src_image, r.src_level, 1,
GetTransferAspects(*r.src_image, r.src_aspect), restore_layout,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
src_layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
}
@@ -677,51 +716,56 @@ void UtilImageToImage(CommandBuffer* buffer, std::span<const ImageImageCopy> reg
VK_IMAGE_LAYOUT_GENERAL, 1, &region);
if (r.src_image != dst_image) {
SetImageLayout(vk_buffer, r.src_image, r.src_level, 1, VK_IMAGE_ASPECT_COLOR_BIT,
SetImageLayout(vk_buffer, r.src_image, r.src_level, 1,
GetTransferAspects(*r.src_image, r.src_aspect),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, restore_layout);
}
}
SetImageLayout(vk_buffer, dst_image, 0, VK_REMAINING_MIP_LEVELS, VK_IMAGE_ASPECT_COLOR_BIT,
SetImageLayout(vk_buffer, dst_image, 0, VK_REMAINING_MIP_LEVELS, dst_transition_aspects,
VK_IMAGE_LAYOUT_GENERAL, static_cast<VkImageLayout>(dst_layout));
return;
}
SetImageLayout(vk_buffer, dst_image, 0, VK_REMAINING_MIP_LEVELS, VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
SetImageLayout(vk_buffer, dst_image, 0, VK_REMAINING_MIP_LEVELS, dst_transition_aspects,
dst_image->layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
for (const auto& r: regions) {
VkImageCopy region;
auto src_layout = r.src_image->layout;
if (CopyBlockImageToCompressedImage(vk_buffer, r, dst_image)) {
if (r.src_aspect == VK_IMAGE_ASPECT_COLOR_BIT &&
r.dst_aspect == VK_IMAGE_ASPECT_COLOR_BIT &&
CopyBlockImageToCompressedImage(vk_buffer, r, dst_image)) {
continue;
}
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.srcSubresource.aspectMask = r.src_aspect;
region.srcSubresource.mipLevel = r.src_level;
region.srcSubresource.baseArrayLayer = r.src_layer;
region.srcSubresource.layerCount = 1;
region.srcOffset = {r.src_x, r.src_y, r.src_z};
region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.dstSubresource.aspectMask = r.dst_aspect;
region.dstSubresource.mipLevel = r.dst_level;
region.dstSubresource.baseArrayLayer = r.dst_layer;
region.dstSubresource.layerCount = 1;
region.dstOffset = {r.dst_x, r.dst_y, r.dst_z};
region.extent = {r.width, r.height, 1};
SetImageLayout(vk_buffer, r.src_image, r.src_level, 1, VK_IMAGE_ASPECT_COLOR_BIT,
src_layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
SetImageLayout(vk_buffer, r.src_image, r.src_level, 1,
GetTransferAspects(*r.src_image, r.src_aspect), src_layout,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
vkCmdCopyImage(vk_buffer, r.src_image->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
dst_image->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
SetImageLayout(vk_buffer, r.src_image, r.src_level, 1, VK_IMAGE_ASPECT_COLOR_BIT,
SetImageLayout(vk_buffer, r.src_image, r.src_level, 1,
GetTransferAspects(*r.src_image, r.src_aspect),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, src_layout);
}
SetImageLayout(vk_buffer, dst_image, 0, VK_REMAINING_MIP_LEVELS, VK_IMAGE_ASPECT_COLOR_BIT,
SetImageLayout(vk_buffer, dst_image, 0, VK_REMAINING_MIP_LEVELS, dst_transition_aspects,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, static_cast<VkImageLayout>(dst_layout));
}
@@ -954,6 +998,15 @@ void UtilFillBuffer(GraphicContext* ctx, void* dst_data, uint64_t size, uint32_t
->DownloadFromImage(ctx, dst_data, size, dst_pitch, src_image, src_layout, aspect);
}
void UtilFillBuffer(GraphicContext* ctx, void* dst_data, uint64_t size,
std::span<const ImageBufferCopy> regions, VulkanImage* src_image,
uint64_t src_layout) {
KYTY_PROFILER_FUNCTION();
EXIT_IF(size == 0 || regions.empty());
GetStagingBuffer(StagingBufferType::ReadBack)
->DownloadFromImage(ctx, dst_data, size, regions, src_image, src_layout);
}
void UtilSetImageLayoutOptimal(VulkanImage* image) {
ExecuteImmediateUtilCommands([&](CommandBuffer*, VkCommandBuffer vk_command) {
SetImageLayout(vk_command, image, 0, VK_REMAINING_MIP_LEVELS, VK_IMAGE_ASPECT_COLOR_BIT,
+43 -23
View File
@@ -19,32 +19,49 @@ struct DepthStencilVulkanImage;
struct VulkanSwapchain;
struct BufferImageCopy {
uint32_t offset;
uint32_t pitch;
uint32_t dst_level;
uint32_t width;
uint32_t height;
uint32_t copy_height = 0;
uint32_t dst_layer = 0;
int dst_x;
int dst_y;
int dst_z = 0;
uint32_t offset;
uint32_t pitch;
uint32_t dst_level;
uint32_t width;
uint32_t height;
uint32_t copy_height = 0;
uint32_t dst_layer = 0;
int dst_x;
int dst_y;
int dst_z = 0;
VkImageAspectFlags aspect = VK_IMAGE_ASPECT_COLOR_BIT;
};
struct ImageBufferCopy {
uint32_t offset;
uint32_t pitch;
uint32_t src_level;
uint32_t width;
uint32_t height;
uint32_t copy_height = 0;
uint32_t src_layer = 0;
int src_x;
int src_y;
int src_z = 0;
VkImageAspectFlags aspect = VK_IMAGE_ASPECT_COLOR_BIT;
};
struct ImageImageCopy {
VulkanImage* src_image;
uint32_t src_level;
uint32_t dst_level;
uint32_t width;
uint32_t height;
uint32_t src_layer = 0;
uint32_t dst_layer = 0;
int src_x;
int src_y;
int src_z = 0;
int dst_x;
int dst_y;
int dst_z = 0;
VulkanImage* src_image;
uint32_t src_level;
uint32_t dst_level;
uint32_t width;
uint32_t height;
uint32_t src_layer = 0;
uint32_t dst_layer = 0;
VkImageAspectFlags src_aspect = VK_IMAGE_ASPECT_COLOR_BIT;
VkImageAspectFlags dst_aspect = VK_IMAGE_ASPECT_COLOR_BIT;
int src_x;
int src_y;
int src_z = 0;
int dst_x;
int dst_y;
int dst_z = 0;
};
enum class StagingBufferType { Texture, Vertex, ReadBack };
@@ -79,6 +96,9 @@ void UtilFillImage(GraphicContext* ctx, std::span<const ImageImageCopy> regions,
void UtilFillBuffer(GraphicContext* ctx, void* dst_data, uint64_t size, uint32_t dst_pitch,
VulkanImage* src_image, uint64_t src_layout,
VkImageAspectFlags aspect = VK_IMAGE_ASPECT_COLOR_BIT);
void UtilFillBuffer(GraphicContext* ctx, void* dst_data, uint64_t size,
std::span<const ImageBufferCopy> regions, VulkanImage* src_image,
uint64_t src_layout);
void UtilUploadBuffer(GraphicContext* ctx, StagingBufferType type, VulkanBuffer* dst_buffer,
uint64_t dst_offset, const void* src_data, uint64_t size);
void UtilCopyBuffer(VulkanBuffer* src_buffer, VulkanBuffer* dst_buffer, uint64_t size);