graphics: support depth-tiled color targets

This commit is contained in:
nmzik 2026-08-16 01:46:50 +02:00
parent 484433b180
commit 4f1bc505cc
3 changed files with 159 additions and 20 deletions

View File

@ -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;
}

View File

@ -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<uint32_t>(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 =

View File

@ -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<void *>(base);
Require(name, "direct mapping",
Libs::LibKernel::Memory::KernelMapDirectMemory(
&mapped, allocation_size, 0x3, 0x10, direct_offset,
allocation_alignment) == 0 &&
mapped == reinterpret_cast<void *>(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<uint32_t> 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<uint8_t> 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<uint32_t> 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();