From 4f1bc505cc845e468e635bd0186a520e606ce659 Mon Sep 17 00:00:00 2001 From: nmzik Date: Sun, 16 Aug 2026 01:46:50 +0200 Subject: [PATCH] graphics: support depth-tiled color targets --- .../host_gpu/renderer/cache/textureCache.cpp | 4 +- .../host_gpu/renderer/colorRenderTarget.cpp | 41 +++--- tests/ShaderRecompilerComputeTests.cpp | 134 ++++++++++++++++++ 3 files changed, 159 insertions(+), 20 deletions(-) diff --git a/src/graphics/host_gpu/renderer/cache/textureCache.cpp b/src/graphics/host_gpu/renderer/cache/textureCache.cpp index 5c59348f..4d2222a2 100644 --- a/src/graphics/host_gpu/renderer/cache/textureCache.cpp +++ b/src/graphics/host_gpu/renderer/cache/textureCache.cpp @@ -924,7 +924,7 @@ TextureCache::BuildColorTransfer(const Image& image, BindingType binding, EXIT("TextureCache: invalid color-attachment upload\n"); } format = ImageOps::RenderTargetTransferFormat(info.bytes_per_block); - allow_depth_tile = false; + allow_depth_tile = true; plan.swap_bgra16 = info.bgra16; owner = "RenderTarget"; break; @@ -950,7 +950,7 @@ TextureCache::BuildColorTransfer(const Image& image, BindingType binding, format = binding == BindingType::RenderTarget ? ImageOps::RenderTargetTransferFormat(info.bytes_per_block) : info.guest_format; - allow_depth_tile = binding == BindingType::Storage; + allow_depth_tile = binding == BindingType::Storage || binding == BindingType::RenderTarget; plan.swap_bgra16 = info.bgra16; } diff --git a/src/graphics/host_gpu/renderer/colorRenderTarget.cpp b/src/graphics/host_gpu/renderer/colorRenderTarget.cpp index be89a22f..7b67e390 100644 --- a/src/graphics/host_gpu/renderer/colorRenderTarget.cpp +++ b/src/graphics/host_gpu/renderer/colorRenderTarget.cpp @@ -132,10 +132,12 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB } const uint32_t depth = volume ? rt.attrib3.depth + 1u : 1u; const bool standard64 = rt.attrib3.tile_mode == Prospero::TileMode::kStandard64KB; + const bool depth_tile = rt.attrib3.tile_mode == Prospero::TileMode::kDepth; switch (rt.attrib3.tile_mode) { case Prospero::TileMode::kLinear: case Prospero::TileMode::kStandard64KB: + case Prospero::TileMode::kDepth: case Prospero::TileMode::kRenderTarget: tile = !RenderIsColorTileModeLinear(rt.attrib3.tile_mode); break; @@ -147,6 +149,9 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB if (samples > 1 && (!tile || levels != 1)) { EXIT("multisampled render targets require a single-mip tiled surface\n"); } + if (depth_tile && samples != 1) { + EXIT("depth-tiled color render targets do not support multisampling\n"); + } width = rt.attrib2.width + 1; height = rt.attrib2.height + 1; @@ -157,31 +162,31 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB EXIT("render-target format has no valid element size\n"); } const auto transfer_format = ImageOps::RenderTargetTransferFormat(bytes_per_element); - if (standard64 && + if ((standard64 || depth_tile) && (rt.attrib3.dimension != 1 || rt.attrib3.depth != 0 || levels != 1 || rt.view.current_mip_level != 0 || view.base_layer != 0 || view.image_layers != 1 || - samples != 1 || bytes_per_element != 4 || rt.pitch.pitch_div8_minus1 != 0 || - (rt.base.addr & 0xffffu) != 0 || rt.info.fmask_compression_enable || - rt.info.fmask_data_compression_disable || rt.info.fmask_one_frag_mode || - rt.info.cmask_fast_clear_enable || rt.info.dcc_compression_enable || - rt.info.cmask_is_linear != 0 || rt.info.cmask_addr_type != 0 || rt.info.alt_tile_mode || - rt.cmask.addr != 0 || rt.fmask.addr != 0 || rt.dcc_addr.addr != 0 || - rt.dcc.data_write_on_dcc_clear_to_reg)) { - EXIT("unsupported Standard64KB render target: addr=0x%016" PRIx64 + samples != 1 || (standard64 && bytes_per_element != 4) || + rt.pitch.pitch_div8_minus1 != 0 || (rt.base.addr & 0xffffu) != 0 || + rt.info.fmask_compression_enable || rt.info.fmask_data_compression_disable || + rt.info.fmask_one_frag_mode || rt.info.cmask_fast_clear_enable || + rt.info.dcc_compression_enable || rt.info.cmask_is_linear != 0 || + rt.info.cmask_addr_type != 0 || rt.info.alt_tile_mode || rt.cmask.addr != 0 || + rt.fmask.addr != 0 || rt.dcc_addr.addr != 0 || rt.dcc.data_write_on_dcc_clear_to_reg)) { + EXIT("unsupported texture-tiled render target: addr=0x%016" PRIx64 " tile=%u" " dimension=%u depth=%u levels=%u layer=%u/%u samples=%u fragments=%u bpe=%u" " cmask=0x%016" PRIx64 " fmask=0x%016" PRIx64 " dcc=0x%016" PRIx64 "\n", - rt.base.addr, rt.attrib3.dimension, rt.attrib3.depth, levels, view.base_layer, - view.image_layers, rt.attrib.num_samples, rt.attrib.num_fragments, bytes_per_element, - rt.cmask.addr, rt.fmask.addr, rt.dcc_addr.addr); + rt.base.addr, static_cast(rt.attrib3.tile_mode), rt.attrib3.dimension, + rt.attrib3.depth, levels, view.base_layer, view.image_layers, rt.attrib.num_samples, + rt.attrib.num_fragments, bytes_per_element, rt.cmask.addr, rt.fmask.addr, + rt.dcc_addr.addr); } if (rt.pitch.pitch_div8_minus1 != 0) { pitch = (rt.pitch.pitch_div8_minus1 + 1u) << 3u; } else if (tile) { if (volume) { pitch = TileGetTexturePitch(transfer_format, width, rt.attrib3.tile_mode); - } else if (standard64) { - pitch = - TileGetTexturePitch(Prospero::BufferFormat::k32Float, width, rt.attrib3.tile_mode); + } else if (standard64 || depth_tile) { + pitch = TileGetTexturePitch(transfer_format, width, rt.attrib3.tile_mode); } else { pitch = TileGetRenderTargetPitch(width, bytes_per_element, rt.attrib.num_fragments); } @@ -214,9 +219,9 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, RenderCommandB } else if (tile) { TileSizeAlign layout {}; bool valid_layout = false; - if (standard64) { - TileGetTextureSize(Prospero::BufferFormat::k32Float, width, height, levels, - rt.attrib3.tile_mode, &layout, mip_sizes, mip_padded); + if (standard64 || depth_tile) { + TileGetTextureSize(transfer_format, width, height, levels, rt.attrib3.tile_mode, + &layout, mip_sizes, mip_padded); valid_layout = layout.size != 0 && layout.align == 65536; } else { valid_layout = diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 3312eede..67549fc2 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -6216,6 +6216,138 @@ public: std::printf("[gpu] %-32s ok\n", name); } + void CheckRenderExecutorColorDepthTileDiscovery() { + constexpr const char *name = "RenderExecutorColorDepthTile"; + constexpr uintptr_t base = 0x0000000203d00000ull; + constexpr uint64_t allocation_size = 0x10000; + constexpr uint64_t allocation_alignment = 0x10000; + EnsureRuntimeContext(); + + int64_t direct_offset = -1; + Require(name, "direct allocation", + Libs::LibKernel::Memory::KernelAllocateDirectMemory( + 0, Libs::LibKernel::Memory::KernelGetDirectMemorySize(), + allocation_size, allocation_alignment, 0, &direct_offset) == 0, + "depth-tiled color allocation failed"); + void *mapped = reinterpret_cast(base); + Require(name, "direct mapping", + Libs::LibKernel::Memory::KernelMapDirectMemory( + &mapped, allocation_size, 0x3, 0x10, direct_offset, + allocation_alignment) == 0 && + mapped == reinterpret_cast(base), + "depth-tiled color mapping failed"); + constexpr uint32_t width = 64; + constexpr uint32_t height = 64; + TileTextureBlockLayout tile_layout{}; + Require(name, "tile layout", + TileGetTextureBlockLayout(Prospero::BufferFormat::k32Float, + Prospero::TileMode::kDepth, false, + tile_layout), + "depth-tiled color block layout is unavailable"); + uint32_t block_xor = 0; + Require(name, "block xor", + TileGetBlockXor(tile_layout.block, 0, 0, 0, block_xor), + "depth-tiled color block XOR is unavailable"); + std::vector expected(allocation_size / sizeof(uint32_t)); + for (uint32_t y = 0; y < height; ++y) { + for (uint32_t x = 0; x < width; ++x) { + uint32_t offset = 0; + Require(name, "texel offset", + TileGetBlockOffset(tile_layout.block, x, y, 0, offset), + "depth-tiled color texel offset is unavailable"); + const auto index = (offset ^ block_xor) / sizeof(uint32_t); + Require(name, "texel address", index < expected.size(), + "depth-tiled color texel address exceeds its backing"); + expected[index] = 0x3f000000u + y * width + x; + } + } + std::memcpy(mapped, expected.data(), allocation_size); + + { + RenderContext context(m_runtime_context); + auto &scheduler = context.GetCommandScheduler(); + HW::Context registers{}; + HW::UserConfig user_config{}; + HW::Shader shaders{}; + registers.SetColorBase(0, {.addr = base}); + registers.SetColorInfo( + 0, {.format = Prospero::ChannelLayout::k32, + .channel_type = Prospero::ChannelType::kFloat, + .channel_order = Prospero::ChannelOrder::kStandard}); + registers.SetColorAttrib2(0, {.height = 63, .width = 63}); + registers.SetColorAttrib3( + 0, {.tile_mode = Prospero::TileMode::kDepth, + .dimension = 1, + .cmask_pipe_aligned = true, + .dcc_pipe_aligned = true}); + registers.SetRenderTargetMask(0x0f); + scheduler.Begin(registers, user_config, shaders); + + auto &resources = context.GetGpuResources(); + auto &texture_cache = resources.GetTextureCache(); + auto &executor = context.GetRenderExecutor(); + resources.MapMemory(base, allocation_size); + + RenderColorInfo color{}; + RenderExecutorTestAccess::ResolveRenderColorTarget( + executor, 1, scheduler.Current(), color, 0); + const auto attachment = + texture_cache.FindRenderTarget(color.image_id, color.desc); + const auto &image = texture_cache.GetImage(color.image_id); + Require(name, "captured target", + color.image_id && attachment != nullptr && + color.desc.info.tile_mode == Prospero::TileMode::kDepth && + color.desc.info.pitch == 128 && + color.desc.info.data.size == allocation_size && + color.desc.info.mip_layout[0].size == allocation_size && + image.usage.render_target && image.IsGpuModified(), + "supported depth-tiled color target was not resolved"); + scheduler.FinishCurrent(); + scheduler.DrainPriorityOperations(); + std::vector cleared(allocation_size); + Require(name, "clear guest backing", + Libs::LibKernel::Memory::TryWriteBacking( + base, cleared.data(), allocation_size), + "depth-tiled color backing could not be cleared"); + Require(name, "readback queue", + TextureCacheTestAccess::TryDownload(texture_cache, color.image_id), + "depth-tiled color target could not be queued for readback"); + + RenderExecutorTestAccess::ResetBindings(executor); + scheduler.Finish(); + scheduler.DrainPriorityOperations(); + std::vector observed(expected.size()); + Require(name, "readback backing", + Libs::LibKernel::Memory::TryReadBacking( + base, observed.data(), allocation_size), + "depth-tiled color readback is unavailable"); + bool recovered = true; + for (uint32_t y = 0; y < height; ++y) { + for (uint32_t x = 0; x < width; ++x) { + uint32_t offset = 0; + const auto valid = + TileGetBlockOffset(tile_layout.block, x, y, 0, offset); + const auto index = (offset ^ block_xor) / sizeof(uint32_t); + recovered &= valid && index < observed.size() && + observed[index] == expected[index]; + } + } + Require(name, "round trip", recovered, + "depth-tiled color upload/readback changed active texels"); + resources.UnmapMemory(base, allocation_size); + scheduler.Finish(); + } + + Require(name, "unmap direct backing", + Libs::LibKernel::Memory::KernelMunmap(base, allocation_size) == 0, + "depth-tiled color mapping release failed"); + Require(name, "release direct backing", + Libs::LibKernel::Memory::KernelReleaseDirectMemory( + direct_offset, allocation_size) == 0, + "depth-tiled color allocation release failed"); + std::printf("[gpu] %-32s ok\n", name); + } + void CheckRenderExecutorStencilBindingDiscovery() { constexpr const char *name = "RenderExecutorStencilBindingDiscovery"; constexpr uintptr_t base = 0x0000000203600000ull; @@ -20414,6 +20546,7 @@ int main(int argc, char **argv) { CheckDynamicRenderingState(); VulkanHarness vulkan; vulkan.CheckRenderExecutorColorVolumeDiscovery(); + vulkan.CheckRenderExecutorColorDepthTileDiscovery(); vulkan.CheckRenderExecutorStencilBindingDiscovery(); vulkan.CheckUnifiedTextureCacheFlow(); vulkan.CheckBgra16Readback(); @@ -20559,6 +20692,7 @@ int main(int argc, char **argv) { vulkan.CheckGpuTilerCpuParity(); #if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS vulkan.CheckRenderExecutorColorVolumeDiscovery(); + vulkan.CheckRenderExecutorColorDepthTileDiscovery(); vulkan.CheckRenderExecutorStencilBindingDiscovery(); vulkan.CheckUnifiedTextureCacheFlow(); vulkan.CheckBgra16Readback();