graphics: materialize CMASK clears for multisampled targets (PPSA13794)

This commit is contained in:
nmzik
2026-10-18 02:35:54 +02:00
parent c3e7ec8285
commit fe3857a171
6 changed files with 56 additions and 39 deletions
+45 -17
View File
@@ -390,7 +390,6 @@ struct TextureCacheTestAccess {
static void RegisterHtileMeta(TextureCache &cache, uint64_t address) {
std::lock_guard lock(cache.m_lock);
auto &metadata = cache.m_surface_metas[address];
metadata.type = TextureCache::MetaDataInfo::Type::HTile;
metadata.clear_mask.reset();
}
@@ -10487,22 +10486,34 @@ public:
vk::Extent3D extent = {1, 1, 1}, uint32_t layer = 0,
uint32_t mip = 0) {
auto &scheduler = context.GetCommandScheduler();
auto &image = context.GetTextureCache().GetImage(id);
const auto bytes = image.info.bytes_per_block * extent.width *
auto *image = &context.GetTextureCache().GetImage(id);
std::unique_ptr<Libs::Graphics::Image> resolved;
if (image->backing.samples > 1) {
auto info = image->info;
info.data = {};
info.metadata = {};
info.samples = 1;
info.resources = {1, 1};
resolved = std::make_unique<Libs::Graphics::Image>(m_runtime_context, scheduler, info);
resolved->Resolve(*image, {mip, 1, layer, 1}, {});
image = resolved.get();
layer = mip = 0;
}
const auto bytes = image->info.bytes_per_block * extent.width *
extent.height * extent.depth;
auto probe = CreateHostBuffer(name, bytes,
vk::BufferUsageFlagBits::eTransferDst, {});
scheduler.Current().EndRendering();
image.Transit(vk::ImageLayout::eTransferSrcOptimal,
image->Transit(vk::ImageLayout::eTransferSrcOptimal,
vk::AccessFlagBits2::eTransferRead, {},
scheduler.Current().Handle());
vk::BufferImageCopy copy{};
copy.imageSubresource = {image.info.IsDepth() ? vk::ImageAspectFlagBits::eDepth
copy.imageSubresource = {image->info.IsDepth() ? vk::ImageAspectFlagBits::eDepth
: vk::ImageAspectFlagBits::eColor, mip, layer, 1};
copy.imageOffset = offset;
copy.imageExtent = extent;
scheduler.Current().Handle().copyImageToBuffer(
image.backing.image, vk::ImageLayout::eTransferSrcOptimal,
image->backing.image, vk::ImageLayout::eTransferSrcOptimal,
probe.buffer, 1, &copy);
vk::BufferMemoryBarrier barrier{};
barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
@@ -11509,7 +11520,7 @@ public:
clear.float32[0] == 1.0f && clear.float32[1] == 1.0f,
"a repeated DWORD fill was decoded as a two-register clear");
constexpr uintptr_t base = 0x0000000204100000ull;
constexpr uint64_t allocation_size = 0x400000;
constexpr uint64_t allocation_size = 0x1000000;
constexpr uint64_t allocation_alignment = 0x10000;
struct FillCase {
uint32_t fill;
@@ -11521,6 +11532,7 @@ public:
Prospero::ChannelType type = Prospero::ChannelType::kFloat;
uint32_t clear_word = 0;
bool eliminate_fast_clear = false;
uint32_t samples_log2 = 0;
};
constexpr std::array cases{
FillCase{0x40404040u, {0, 0x3c000000u}},
@@ -11552,6 +11564,15 @@ public:
FillCase{.fill = 0x20202020u, .texel = {0x0000ffffu},
.layout = Prospero::ChannelLayout::k16_16,
.type = Prospero::ChannelType::kUNorm, .clear_word = 0x0000ffffu},
FillCase{.fill = 0, .texel = {0x804020ffu, 0}, .cmask = true,
.layout = Prospero::ChannelLayout::k8_8_8_8,
.type = Prospero::ChannelType::kUNorm, .samples_log2 = 2},
FillCase{.fill = 0, .texel = {0x804020ffu, 0}, .cmask = true,
.layout = Prospero::ChannelLayout::k8_8_8_8,
.type = Prospero::ChannelType::kUNorm, .eliminate_fast_clear = true, .samples_log2 = 2},
FillCase{.fill = 0, .texel = {0, 0x3c000000u}, .cmask = true,
.eliminate_fast_clear = true, .samples_log2 = 2},
FillCase{.fill = 0x40404040u, .texel = {0, 0x3c000000u}, .eliminate_fast_clear = true},
};
EnsureRuntimeContext();
// Astro's generic metadata fill, through S_ENDPGM; trailing debug data is omitted.
@@ -11610,12 +11631,13 @@ public:
const auto bytes_per_pixel = TextureGetRenderTargetFormat(
layout, type, Prospero::ChannelOrder::kStandard).bytes_per_element;
const uint32_t probe_width = bytes_per_pixel == 2 ? 2 : 1;
const uint32_t selected_layer = fill_case.cmask || fill_case.eliminate_fast_clear ? 1 : 0;
TileSizeAlign dcc_size{};
(void)TileGetDccSize(512, 256, 1, bytes_per_pixel, 1,
Prospero::TileMode::kRenderTarget, dcc_size, 0);
const uint64_t dcc_address = base + (fill_case.eliminate_fast_clear ? 0x200000 :
(void)TileGetDccSize(512, 256, selected_layer + 1, bytes_per_pixel, 1,
Prospero::TileMode::kRenderTarget, dcc_size, fill_case.samples_log2);
const uint64_t dcc_address = base + (fill_case.samples_log2 != 0 ? 0x800000 :
fill_case.eliminate_fast_clear ? 0x200000 :
fill_case.reuse_mips ? 0x160000 : 0x100000);
const uint32_t selected_layer = fill_case.cmask ? 1 : 0;
const uint64_t metadata_size = fill_case.cmask ? 0x2000 : dcc_size.size;
const std::vector<u32> expected(
fill_case.texel.begin(), fill_case.texel.begin() + std::max(bytes_per_pixel / 4u, 1u));
@@ -11629,13 +11651,16 @@ public:
auto &scheduler = context.GetCommandScheduler();
registers.SetColorBase(0, {.addr = base});
registers.SetColorInfo(
0, {.cmask_fast_clear_enable = !fill_case.reuse_unorm,
0, {.fmask_compression_enable = fill_case.samples_log2 != 0,
.cmask_fast_clear_enable = !fill_case.reuse_unorm,
.dcc_compression_enable = !fill_case.cmask,
.format = layout,
.channel_type = type,
.channel_order = Prospero::ChannelOrder::kStandard});
registers.SetColorView(0, {.base_array_slice_index = selected_layer,
.last_array_slice_index = selected_layer});
registers.SetColorAttrib(0, {.num_samples = fill_case.samples_log2,
.num_fragments = fill_case.samples_log2});
registers.SetColorAttrib2(0, {.height = 255, .width = 511});
registers.SetColorAttrib3(0,
{.tile_mode = Prospero::TileMode::kRenderTarget,
@@ -11643,6 +11668,7 @@ public:
.metadata_pipe_aligned = true});
registers.SetColorDccAddr(0, {.addr = dcc_address});
registers.SetColorCmask(0, {.addr = dcc_address});
registers.SetColorFmask(0, {.addr = fill_case.samples_log2 != 0 ? base + 0xc00000 : 0});
registers.SetColorClearWord0(0, {.word0 = fill_case.cmask ? expected[0] : fill_case.clear_word});
registers.SetColorClearWord1(0, {.word1 = fill_case.cmask && expected.size() > 1u ? expected[1] : 0});
registers.SetRenderTargetMask(0x0f);
@@ -11724,9 +11750,10 @@ public:
Require(name, "expanded metadata readback",
LibKernel::Memory::TryReadBacking(dcc_address, bytes.data(), bytes.size()),
"native color metadata could not be read back");
const auto selected = bytes.begin() + (fill_case.cmask ? 0x1000 : 0);
const auto selected = bytes.begin() + metadata_size * selected_layer / (selected_layer + 1);
Require(name, "selected metadata slice expansion",
std::all_of(bytes.begin(), selected, [](uint8_t v) { return v == 0; }) &&
std::all_of(bytes.begin(), selected,
[&](uint8_t v) { return v == static_cast<uint8_t>(fill_case.fill); }) &&
std::all_of(selected, bytes.end(), [](uint8_t v) { return v == 0xff; }),
"materialization retained a clear key or changed an unselected slice");
};
@@ -11752,7 +11779,8 @@ public:
color.image_id == preexisting_id &&
texture_cache.FindTexture(color.image_id, sampled) != nullptr,
"FCE failed to retain the sampled allocation");
const std::vector<u32> stale = layout == Prospero::ChannelLayout::k11_11_10
const std::vector<u32> stale = layout == Prospero::ChannelLayout::k8_8_8_8
? std::vector<u32>{0xffff00ffu} : layout == Prospero::ChannelLayout::k11_11_10
? std::vector<u32>{0x780003c0u} : layout == Prospero::ChannelLayout::k32_32
? std::vector<u32>{0x3f800000u, 0u} : std::vector<u32>{0x00003c00u, 0x3c003c00u};
Require(name, "FCE before graphics shader or target binding",
@@ -11769,7 +11797,7 @@ public:
Require(name, "repeated indexed FCE preserves expanded color",
ReadCachedTexel(name, context, color.image_id, {}, {1, 1, 1}, 1) == stale,
"FCE reapplied the consumed clear after rendering");
WriteMetadata(context, dcc_address, metadata_size, 0);
WriteMetadata(context, dcc_address, metadata_size, fill_case.fill);
RenderExecutorTestAccess::DrawIndex(executor, scheduler.Current(),
{.index_count = 3, .instance_count = 1});
(void)texture_cache.FindTexture(color.image_id, sampled);
@@ -11948,7 +11976,7 @@ public:
ImageDesc previous_depth{};
previous_depth.type = BindingType::DepthTarget;
previous_depth.info.data = {base + 0x180000, 0x10000};
previous_depth.info.data = {base + 0xa00000, 0x10000};
previous_depth.info.pixel_format = vk::Format::eD32Sfloat;
previous_depth.info.guest_format = Prospero::BufferFormat::k32Float;
previous_depth.info.type = Prospero::ImageType::kColor2D;