mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 10:56:58 +02:00
Support layered PS5 image alias transitions
This commit is contained in:
@@ -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] = {};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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
@@ -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 {};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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, ®ion);
|
||||
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>(®ion, 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, ®ion);
|
||||
|
||||
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, ®ion);
|
||||
|
||||
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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user