Graphics: materialize CMASK clears before publishing expanded metadata

This commit is contained in:
nmzik 2026-09-27 13:48:11 +04:00
parent da33a1c4a3
commit 0f1ff37f23
9 changed files with 164 additions and 118 deletions

View File

@ -1213,20 +1213,12 @@ bool TileGetRenderTargetSize(uint32_t width, uint32_t height, uint32_t pitch,
return true;
}
bool TileGetDccSize(uint32_t width, uint32_t height, uint32_t slices,
uint32_t bytes_per_element, uint32_t levels, Prospero::TileMode tile,
TileSizeAlign& total_size, uint32_t num_fragments_log2) {
total_size = {};
if (width == 0 || height == 0 || slices == 0 || levels != 1 || num_fragments_log2 != 0 ||
!std::has_single_bit(bytes_per_element) || bytes_per_element > 16 ||
(tile != Prospero::TileMode::kRenderTarget && tile != Prospero::TileMode::kDepth)) {
static bool TileGetColorMetadataSize(uint32_t width, uint32_t height, uint32_t slices,
uint32_t block_width, uint32_t block_height,
TileSizeAlign& total_size) {
if (width == 0 || height == 0 || slices == 0) {
return false;
}
// Gen5 color metadata uses 4 KiB blocks, with one byte per 256 bytes of color data.
// Metadata stays thin for volume surfaces, so every depth slice has its own block raster.
const uint32_t coverage_bits = 20u - std::countr_zero(bytes_per_element);
const uint32_t block_width = 1u << ((coverage_bits + 1u) / 2u);
const uint32_t block_height = 1u << (coverage_bits / 2u);
const uint64_t blocks_x = (static_cast<uint64_t>(width) + block_width - 1u) / block_width;
const uint64_t blocks_y = (static_cast<uint64_t>(height) + block_height - 1u) / block_height;
const uint64_t blocks = blocks_x * blocks_y;
@ -1238,6 +1230,29 @@ bool TileGetDccSize(uint32_t width, uint32_t height, uint32_t slices,
return true;
}
bool TileGetDccSize(uint32_t width, uint32_t height, uint32_t slices,
uint32_t bytes_per_element, uint32_t levels, Prospero::TileMode tile,
TileSizeAlign& total_size, uint32_t num_fragments_log2) {
total_size = {};
if (levels != 1 || num_fragments_log2 != 0 ||
!std::has_single_bit(bytes_per_element) || bytes_per_element > 16 ||
(tile != Prospero::TileMode::kRenderTarget && tile != Prospero::TileMode::kDepth)) {
return false;
}
// Gen5 DCC uses 4 KiB blocks, with one byte per 256 bytes of color data.
// Metadata stays thin for volume surfaces, so every depth slice has its own block raster.
const uint32_t coverage_bits = 20u - std::countr_zero(bytes_per_element);
return TileGetColorMetadataSize(width, height, slices, 1u << ((coverage_bits + 1u) / 2u),
1u << (coverage_bits / 2u), total_size);
}
bool TileGetCmaskSize(uint32_t width, uint32_t height, uint32_t slices, uint32_t levels,
TileSizeAlign& total_size) {
total_size = {};
// Gen5 CMASK stores four bits per 8x8 tile in 4 KiB blocks covering 1024x512 pixels.
return levels == 1 && TileGetColorMetadataSize(width, height, slices, 1024, 512, total_size);
}
void TileGetTextureSize(Prospero::BufferFormat format, uint32_t width, uint32_t height,
uint32_t levels, Prospero::TileMode tile, TileSizeAlign* total_size,
TileSizeOffset* level_sizes, TilePaddedSize* padded_size) {

View File

@ -126,6 +126,8 @@ bool TileGetRenderTargetSize(uint32_t width, uint32_t height, uint32_t pitch
bool TileGetDccSize(uint32_t width, uint32_t height, uint32_t slices,
uint32_t bytes_per_element, uint32_t levels, Prospero::TileMode tile,
TileSizeAlign& total_size, uint32_t num_fragments_log2 = 0);
bool TileGetCmaskSize(uint32_t width, uint32_t height, uint32_t slices, uint32_t levels,
TileSizeAlign& total_size);
void TileGetTextureSize(Prospero::BufferFormat format, uint32_t width, uint32_t height,
uint32_t levels, Prospero::TileMode tile, TileSizeAlign* total_size,
TileSizeOffset* level_sizes, TilePaddedSize* padded_size);

View File

@ -33,24 +33,27 @@ namespace {
constexpr uint64_t NumFramesBeforeRemoval = 32;
[[nodiscard]] bool DecodeDccClear(const TextureCache::ImageDesc& desc, uint8_t code,
[[nodiscard]] bool DecodeColorClear(const TextureCache::ImageDesc& desc, uint8_t code,
vk::ClearColorValue& clear) {
const auto& metadata = desc.info.metadata;
const auto format = desc.view_info.format;
const bool cmask = metadata.kind == ImageMetadataKind::Cmask;
if (cmask ? code == 0 : code == 0x20) {
// Register clears belong to the color buffer; the texture pipe cannot decode them.
return desc.type == TextureCache::BindingType::RenderTarget &&
metadata.clear_register_valid &&
DecodePackedColorClear(format, metadata.clear_word, clear);
}
if (cmask) {
return false;
}
switch (code) {
case 0x00:
case 0x20:
case 0x40:
case 0x80:
case 0xc0: break;
default: return false;
}
const auto& metadata = desc.info.metadata;
const auto format = desc.view_info.format;
if (code == 0x20) {
// Clear-to-register is a color-buffer operation; the texture pipe cannot decode it.
return desc.type == TextureCache::BindingType::RenderTarget &&
metadata.dcc_clear_register_valid &&
DecodePackedColorClear(format, metadata.dcc_clear_word, clear);
}
clear = {};
if (code == 0x00) {
return true;
@ -1125,9 +1128,10 @@ void TextureCache::InitializeImage(ImageId id) {
}
}
void TextureCache::MaterializeDccClear(ImageId id, const ImageDesc& desc,
void TextureCache::MaterializeColorClear(ImageId id, const ImageDesc& desc,
uint32_t metadata_base_layer) {
if (desc.info.metadata.kind != ImageMetadataKind::Dcc) {
if (desc.info.metadata.kind != ImageMetadataKind::Dcc &&
desc.info.metadata.kind != ImageMetadataKind::Cmask) {
return;
}
const auto range = desc.info.metadata.range;
@ -1135,18 +1139,18 @@ void TextureCache::MaterializeDccClear(ImageId id, const ImageDesc& desc,
std::scoped_lock lock {m_lock};
auto& image = m_slot_images[id];
image.info.metadata = desc.info.metadata;
// A native DCC allocation must not retain a reused HTile/CMask/FMask interpretation.
// Native color metadata must not retain a reused HTile/CMask/FMask clear flag.
m_surface_metas.erase(range.address);
if (range.size == 0 || desc.info.resources.levels != 1 || image.info.resources.levels != 1) {
return;
}
}
const auto layers = desc.info.TransferLayers();
// These one-mip surfaces use complete 4 KiB DCC metadata blocks.
// These one-mip surfaces use complete 4 KiB color metadata blocks.
constexpr uint64_t MetadataBlockSize = 0x1000;
if (!range.Valid() || range.address % MetadataBlockSize != 0 || layers == 0 ||
range.size % layers != 0 || (range.size / layers) % MetadataBlockSize != 0) {
EXIT("TextureCache: DCC slices must contain aligned 4 KiB blocks\n");
EXIT("TextureCache: color metadata slices must contain aligned 4 KiB blocks\n");
}
const auto& view = desc.view_info;
const bool volume_texture = desc.info.IsVolume() && view.type == vk::ImageViewType::e3D;
@ -1154,7 +1158,7 @@ void TextureCache::MaterializeDccClear(ImageId id, const ImageDesc& desc,
const auto image_first = volume_texture ? 0u : view.base_layer;
const auto count = volume_texture ? desc.info.extent.depth : view.layer_count;
if (first >= layers || count > layers - first) {
EXIT("TextureCache: DCC view exceeds its native metadata slices\n");
EXIT("TextureCache: color view exceeds its native metadata slices\n");
}
// Finish native metadata writes before reading backing bytes. This can submit the scheduler,
// so discovery runs before final draw uploads and never holds the texture lock across it.
@ -1166,15 +1170,15 @@ void TextureCache::MaterializeDccClear(ImageId id, const ImageDesc& desc,
const auto address = range.address + slice_size * (first + slice);
uint8_t code = 0;
if (!LibKernel::Memory::TryReadBacking(address, &code, sizeof(code))) {
EXIT("TextureCache: failed to read DCC metadata backing\n");
EXIT("TextureCache: failed to read color metadata backing\n");
}
vk::ClearValue clear {};
if (!DecodeDccClear(desc, code, clear.color)) {
if (!DecodeColorClear(desc, code, clear.color)) {
continue;
}
std::vector<uint8_t> bytes(slice_size);
if (!LibKernel::Memory::TryReadBacking(address, bytes.data(), bytes.size())) {
EXIT("TextureCache: failed to read DCC metadata slice\n");
EXIT("TextureCache: failed to read color metadata slice\n");
}
if (!std::all_of(bytes.begin(), bytes.end(), [code](uint8_t byte) { return byte == code; })) {
continue;
@ -1319,7 +1323,7 @@ ImageId TextureCache::FindImage(ImageDesc& desc, bool exact_format) {
image.tick_accessed_last = m_scheduler.CurrentTick();
TouchImage(image);
}
MaterializeDccClear(result, desc, metadata_base_layer);
MaterializeColorClear(result, desc, metadata_base_layer);
if (desc.type == BindingType::VideoOut &&
desc.info.metadata.compression != VideoOutCompression::Uncompressed) {
std::scoped_lock lock {m_lock};

View File

@ -139,7 +139,7 @@ private:
ImageId cached);
[[nodiscard]] ImageId ExpandImage(const ImageInfo& info, ImageId source);
void RefreshImage(ImageId id);
void MaterializeDccClear(ImageId id, const ImageDesc& desc,
void MaterializeColorClear(ImageId id, const ImageDesc& desc,
uint32_t metadata_base_layer);
void InitializeImage(ImageId id);
[[nodiscard]] TextureTransfer

View File

@ -139,8 +139,7 @@ void RenderExecutor::ResolveRenderColorTarget(CommandBuffer& buffer, RenderColor
// Color-control state selects the color-buffer operation and logical blend operation.
// The normal copy operation is a regular color write, not an attachment clear.
// Nonlinear clear values are still stored as normalized components.
// Fast color clears are metadata driven and must be handled explicitly when
// that metadata path is implemented; render-pass load must preserve contents.
// Metadata clears are materialized during image discovery; render-pass loads preserve contents.
uint32_t width = 0;
uint32_t height = 0;
uint32_t pitch = 0;
@ -290,15 +289,25 @@ void RenderExecutor::ResolveRenderColorTarget(CommandBuffer& buffer, RenderColor
desc.info.samples = samples;
desc.info.tile_mode = rt.attrib3.tile_mode;
const bool has_dcc = rt.info.dcc_compression_enable && rt.dcc_addr.addr != 0;
if (has_dcc) {
const bool has_cmask = !rt.info.dcc_compression_enable && rt.info.cmask_fast_clear_enable &&
rt.cmask.addr != 0 && samples == 1 &&
!rt.info.fmask_compression_enable &&
!rt.attrib3.write_vrs_rate_hint_to_cmask;
if (has_dcc || has_cmask) {
TileSizeAlign metadata_size {};
(void)TileGetDccSize(width, height, volume ? depth : view.image_layers, bytes_per_element,
levels, rt.attrib3.tile_mode, metadata_size, rt.attrib.num_fragments);
desc.info.metadata.kind = ImageMetadataKind::Dcc;
desc.info.metadata.range = {rt.dcc_addr.addr, metadata_size.size};
desc.info.metadata.dcc_clear_word = rt.clear_word0.word0;
desc.info.metadata.dcc_clear_register_valid = true;
desc.info.metadata.dcc_alpha_msb = DccAlphaOnMsb(rt.info);
const auto layers = volume ? depth : view.image_layers;
if (has_dcc) {
(void)TileGetDccSize(width, height, layers, bytes_per_element, levels,
rt.attrib3.tile_mode, metadata_size, rt.attrib.num_fragments);
desc.info.metadata.dcc_alpha_msb = DccAlphaOnMsb(rt.info);
} else {
(void)TileGetCmaskSize(width, height, layers, levels, metadata_size);
}
// DCC owns the clear when both planes are enabled; single-sample CMASK stays expanded.
desc.info.metadata.kind = has_dcc ? ImageMetadataKind::Dcc : ImageMetadataKind::Cmask;
desc.info.metadata.range = {has_dcc ? rt.dcc_addr.addr : rt.cmask.addr, metadata_size.size};
desc.info.metadata.clear_word = rt.clear_word0.word0;
desc.info.metadata.clear_register_valid = true;
}
for (uint32_t level = 0; level < levels; level++) {
if (volume) {

View File

@ -219,17 +219,6 @@ static void RtCheck(const HW::RenderTarget& rt) {
}
}
if (rt.info.cmask_fast_clear_enable || rt.info.dcc_compression_enable) {
static bool logged = false;
if (!logged) {
LOGF("RenderTarget: temporary: ignoring PS5 color metadata fast_clear=%s dcc=%s "
"cmask=0x%016" PRIx64 " dcc_addr=0x%016" PRIx64 "\n",
rt.info.cmask_fast_clear_enable ? "true" : "false",
rt.info.dcc_compression_enable ? "true" : "false", rt.cmask.addr,
rt.dcc_addr.addr);
logged = true;
}
}
if (rt.info.blend_bypass) {
static bool logged = false;
if (!logged) {

View File

@ -627,12 +627,13 @@ void Validate(const ImageInfo& info) {
}
break;
case ImageMetadataKind::Dcc:
case ImageMetadataKind::Cmask:
if (info.metadata.range.address == 0 ||
info.metadata.range.address >= TRACKER_ADDRESS_SIZE ||
(info.metadata.range.size != 0 &&
info.metadata.range.size > TRACKER_ADDRESS_SIZE - info.metadata.range.address) ||
info.metadata.compression == VideoOutCompression::Unsupported) {
EXIT("invalid DCC metadata\n");
EXIT("invalid color metadata\n");
}
break;
}

View File

@ -17,17 +17,17 @@ namespace Libs::Graphics {
enum class VideoOutCompression : uint8_t { Uncompressed, Dcc256_256_0, Dcc256_64_64, Unsupported };
enum class ImageMetadataKind : uint8_t { None, Htile, Dcc };
enum class ImageMetadataKind : uint8_t { None, Htile, Dcc, Cmask };
struct ImageMetadataInfo {
GuestRange range;
ImageMetadataKind kind = ImageMetadataKind::None;
uint32_t control = 0;
uint32_t dcc_clear_word = 0;
VideoOutCompression compression = VideoOutCompression::Uncompressed;
bool stencil_compressed = false;
bool dcc_clear_register_valid = false;
bool dcc_alpha_msb = true;
ImageMetadataKind kind = ImageMetadataKind::None;
uint32_t control = 0;
uint32_t clear_word = 0;
VideoOutCompression compression = VideoOutCompression::Uncompressed;
bool stencil_compressed = false;
bool clear_register_valid = false;
bool dcc_alpha_msb = true;
};
struct ImageSubresources {

View File

@ -9029,8 +9029,8 @@ public:
std::printf("[gpu] %-32s ok\n", name);
}
void CheckRenderExecutorDccFixedClearFloat() {
constexpr const char *name = "RenderExecutorDccFixedClearFloat";
void CheckRenderExecutorColorMetadataClear() {
constexpr const char *name = "RenderExecutorColorMetadataClear";
constexpr uintptr_t base = 0x0000000204100000ull;
constexpr uint64_t allocation_size = 0x200000;
constexpr uint64_t allocation_alignment = 0x10000;
@ -9039,11 +9039,13 @@ public:
uint32_t fill;
std::array<uint32_t, 2> texel;
bool reuse_unorm = false;
bool cmask = false;
};
constexpr std::array cases{
FillCase{0x40404040u, {0, 0x3c000000u}},
FillCase{0x80808080u, {0x3c003c00u, 0x00003c00u}},
FillCase{0x40404040u, {0, 0x3c000000u}, true},
FillCase{0, {0x804020ffu, 0}, false, true},
};
EnsureRuntimeContext();
// Astro's generic metadata fill, through S_ENDPGM; trailing debug data is omitted.
@ -9081,9 +9083,9 @@ public:
{.type = Prospero::ShaderBinaryType::kCs,
.code_size_bytes = sizeof(native_fill)});
}
TileSizeAlign metadata_size{};
TileSizeAlign dcc_size{};
(void)TileGetDccSize(512, 256, 1, 8, 1,
Prospero::TileMode::kRenderTarget, metadata_size, 0);
Prospero::TileMode::kRenderTarget, dcc_size, 0);
int64_t direct_offset = -1;
Require(name, "direct allocation",
@ -9101,6 +9103,10 @@ public:
std::memset(mapped, 0, allocation_size);
for (const auto &fill_case : cases) {
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() + (fill_case.cmask ? 1 : 2));
RenderContext context(m_runtime_context);
HW::Context registers{};
HW::UserConfig user_config{};
@ -9111,17 +9117,23 @@ public:
auto &scheduler = context.GetCommandScheduler();
registers.SetColorBase(0, {.addr = base});
registers.SetColorInfo(
0, {.dcc_compression_enable = true,
.format = Prospero::ChannelLayout::k16_16_16_16,
.channel_type = Prospero::ChannelType::kFloat,
0, {.cmask_fast_clear_enable = !fill_case.reuse_unorm,
.dcc_compression_enable = !fill_case.cmask,
.format = fill_case.cmask ? Prospero::ChannelLayout::k8_8_8_8
: Prospero::ChannelLayout::k16_16_16_16,
.channel_type = fill_case.cmask ? Prospero::ChannelType::kUNorm
: Prospero::ChannelType::kFloat,
.channel_order = Prospero::ChannelOrder::kStandard});
registers.SetColorView(0, {.base_array_slice_index = selected_layer,
.last_array_slice_index = selected_layer});
registers.SetColorAttrib2(0, {.height = 255, .width = 511});
registers.SetColorAttrib3(0,
{.tile_mode = Prospero::TileMode::kRenderTarget,
.dimension = 1,
.metadata_pipe_aligned = true});
registers.SetColorDccAddr(0, {.addr = dcc_address});
registers.SetColorClearWord0(0, {.word0 = 0});
registers.SetColorCmask(0, {.addr = dcc_address});
registers.SetColorClearWord0(0, {.word0 = fill_case.cmask ? expected[0] : 0});
registers.SetColorClearWord1(0, {.word1 = 0});
registers.SetRenderTargetMask(0x0f);
scheduler.Begin(registers, user_config, shaders);
@ -9162,7 +9174,7 @@ public:
}
executor.DispatchDirect(0, scheduler.Current(), (count + 63) / 64, 1, 1, 0x41u);
};
const auto metadata_words = static_cast<uint32_t>(metadata_size.size / 4);
const auto metadata_words = static_cast<uint32_t>(metadata_size / 4);
fill_metadata(metadata_words);
Require(name, "metadata before descriptor",
!texture_cache.IsMeta(dcc_address),
@ -9181,28 +9193,36 @@ public:
vk::Format::eR16G16B16A16Unorm,
"the aliased clear did not reuse its existing UNORM allocation");
}
Require(name, "fixed clear on a float target",
Require(name, "color clear metadata discovery",
color.image_id &&
color.desc.info.metadata.kind == ImageMetadataKind::Dcc &&
color.desc.info.metadata.kind ==
(fill_case.cmask ? ImageMetadataKind::Cmask : ImageMetadataKind::Dcc) &&
color.desc.info.metadata.range.address == dcc_address &&
color.desc.info.metadata.range.size == metadata_size &&
rendering.num_color_attachments == 1 &&
!texture_cache.IsMeta(dcc_address),
"a DCC fixed clear code was not materialised on an RGBA16F "
"target");
const auto cleared = ReadCachedTexel(name, context, color.image_id);
Require(name, "GPU float clear value",
cleared ==
std::vector<u32>(fill_case.texel.begin(), fill_case.texel.end()),
"expected " + Hex(fill_case.texel[0]) + ", " + Hex(fill_case.texel[1]) +
"; got " + Hex(cleared[0]) + ", " + Hex(cleared[1]));
context.GetBufferCache().ReadMemory(dcc_address, metadata_size.size, false);
std::vector<uint8_t> expanded_metadata(metadata_size.size);
Require(name, "expanded native metadata",
LibKernel::Memory::TryReadBacking(dcc_address, expanded_metadata.data(),
expanded_metadata.size()) &&
std::all_of(expanded_metadata.begin(), expanded_metadata.end(),
[](uint8_t value) { return value == 0xff; }),
"materialized color retained stale native DCC clear codes");
"color metadata did not retain its type and full native allocation");
const auto read_texel = [&] {
return ReadCachedTexel(name, context, color.image_id, {}, {1, 1, 1}, selected_layer);
};
Require(name, "GPU color clear value",
read_texel() == expected &&
ReadCachedTexel(name, context, color.image_id, {511, 255, 0},
{1, 1, 1}, selected_layer) == expected,
"native metadata did not materialize the expected color");
const auto check_expanded_metadata = [&] {
context.GetBufferCache().ReadMemory(dcc_address, metadata_size, false);
std::vector<uint8_t> bytes(metadata_size);
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);
Require(name, "selected metadata slice expansion",
std::all_of(bytes.begin(), selected, [](uint8_t v) { return v == 0; }) &&
std::all_of(selected, bytes.end(), [](uint8_t v) { return v == 0xff; }),
"materialization retained a clear key or changed an unselected slice");
};
check_expanded_metadata();
const auto bind = [&] {
RenderExecutorTestAccess::ResetBindings(executor);
@ -9214,16 +9234,19 @@ public:
clear.color.float32 = std::array{1.0f, 0.0f, 1.0f, 1.0f};
TextureCacheTestAccess::ClearImage(
texture_cache, scheduler.Current(), color.image_id,
{vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}, clear);
{vk::ImageAspectFlagBits::eColor, 0, 1, selected_layer, 1}, clear);
};
std::vector<u32> painted{0x00003c00u, 0x3c003c00u};
if (fill_case.reuse_unorm) {
painted = {0x0000ffffu, 0xffffffffu};
} else if (fill_case.cmask) {
painted = {0xffff00ffu};
}
const auto retains_painted_texel = [&] {
const auto texels = ReadCachedTexel(name, context, color.image_id, {}, {512, 256, 1});
for (size_t i = 0; i < texels.size(); i += 2) {
if (texels[i] == painted[0] && texels[i + 1] == painted[1]) {
const auto texels = ReadCachedTexel(name, context, color.image_id, {},
{512, 256, 1}, selected_layer);
for (size_t i = 0; i < texels.size(); i += painted.size()) {
if (std::equal(painted.begin(), painted.end(), texels.begin() + i)) {
return true;
}
}
@ -9232,8 +9255,19 @@ public:
paint();
bind();
Require(name, "unchanged metadata preserves rendering",
ReadCachedTexel(name, context, color.image_id) == painted,
"rebinding an unchanged DCC allocation reapplied an old clear");
read_texel() == painted,
"rebinding unchanged color metadata reapplied an old clear");
if (fill_case.cmask) {
WriteMetadata(context, dcc_address, metadata_size, 0x40404040u);
bind();
Require(name, "nonzero CMASK is not a DCC clear", read_texel() == painted,
"CMASK bytes were decoded as a DCC fixed-color key");
}
WriteMetadata(context, dcc_address, metadata_size, fill_case.fill);
bind();
Require(name, "CPU metadata re-clear", read_texel() == expected,
"CPU metadata writes did not clear an already drawn target");
paint();
fill_metadata(metadata_words - 1);
bind();
Require(name, "partial same-value metadata fill",
@ -9242,11 +9276,10 @@ public:
fill_metadata(metadata_words, true);
bind();
Require(name, "same-value raw native re-clear",
ReadCachedTexel(name, context, color.image_id) ==
std::vector<u32>(fill_case.texel.begin(), fill_case.texel.end()),
read_texel() == expected,
"a repeated metadata fill did not clear an already drawn target");
paint();
WriteMetadata(context, dcc_address, metadata_size.size, UINT32_MAX);
WriteMetadata(context, dcc_address, metadata_size, UINT32_MAX);
fill_metadata(metadata_words - 1);
bind();
Require(name, "partial native metadata fill",
@ -9255,8 +9288,7 @@ public:
fill_metadata(metadata_words);
bind();
Require(name, "complete fill after partial fill",
ReadCachedTexel(name, context, color.image_id) ==
std::vector<u32>(fill_case.texel.begin(), fill_case.texel.end()),
read_texel() == expected,
"completing the metadata overwrite did not restore its clear");
ImageDesc previous_depth{};
@ -9273,7 +9305,7 @@ public:
previous_depth.info.tile_mode = Prospero::TileMode::kLinear;
previous_depth.info.mip_layout[0] = {0, 0x10000, 128, 128};
previous_depth.info.metadata.kind = ImageMetadataKind::Htile;
previous_depth.info.metadata.range = {dcc_address, metadata_size.size};
previous_depth.info.metadata.range = {dcc_address, metadata_size};
previous_depth.info.htile_clear_mask = 0;
previous_depth.view_info.format = vk::Format::eD32Sfloat;
previous_depth.view_info.type = vk::ImageViewType::e2D;
@ -9286,21 +9318,15 @@ public:
"the old depth target did not register its metadata allocation");
paint();
bind();
Require(name, "HTile allocation reused as expanded DCC",
Require(name, "HTile allocation reused as expanded color metadata",
!texture_cache.IsMeta(dcc_address) && !texture_cache.ClearMeta(dcc_address) &&
ReadCachedTexel(name, context, color.image_id) == painted,
"DCC discovery retained the old HTile classification or erased rendered color");
read_texel() == painted,
"color discovery retained the old HTile classification or erased rendering");
fill_metadata(metadata_words);
bind();
context.GetBufferCache().ReadMemory(dcc_address, metadata_size.size);
Require(name, "native DCC dispatch after HTile reuse",
ReadCachedTexel(name, context, color.image_id) ==
std::vector<u32>(fill_case.texel.begin(), fill_case.texel.end()) &&
LibKernel::Memory::TryReadBacking(dcc_address, expanded_metadata.data(),
expanded_metadata.size()) &&
std::all_of(expanded_metadata.begin(), expanded_metadata.end(),
[](uint8_t byte) { return byte == 0xff; }),
"the former HTile entry swallowed the native DCC fill dispatch");
Require(name, "native color clear after HTile reuse", read_texel() == expected,
"the former HTile entry swallowed the native color metadata fill");
check_expanded_metadata();
RenderExecutorTestAccess::ResetBindings(executor);
resources.UnmapMemory(base, allocation_size);
scheduler.Finish();
@ -34602,7 +34628,7 @@ int main(int argc, char **argv) {
vulkan.CheckRasterization(false);
vulkan.CheckRenderExecutorColorDiscovery();
vulkan.CheckRenderExecutorColorVolumeDiscovery();
vulkan.CheckRenderExecutorDccFixedClearFloat();
vulkan.CheckRenderExecutorColorMetadataClear();
vulkan.CheckSampledDccClear();
vulkan.CheckRenderExecutorColorDepthTileDiscovery();
vulkan.CheckRenderExecutorStencilBindingDiscovery();
@ -34633,7 +34659,7 @@ int main(int argc, char **argv) {
}
if (argc == 2 && std::strcmp(argv[1], "--dcc-clear-only") == 0) {
VulkanHarness vulkan;
vulkan.CheckRenderExecutorDccFixedClearFloat();
vulkan.CheckRenderExecutorColorMetadataClear();
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--compute-meta-clear-only") == 0) {
@ -34642,7 +34668,7 @@ int main(int argc, char **argv) {
vulkan.CheckComputeMetaClearClassification();
vulkan.CheckNativeIndirectDispatch();
vulkan.CheckRenderExecutorColorVolumeDiscovery();
vulkan.CheckRenderExecutorDccFixedClearFloat();
vulkan.CheckRenderExecutorColorMetadataClear();
vulkan.CheckSampledDccClear();
vulkan.CheckRenderExecutorStencilBindingDiscovery();
return 0;
@ -34838,7 +34864,7 @@ int main(int argc, char **argv) {
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
vulkan.CheckRenderExecutorColorDiscovery();
vulkan.CheckRenderExecutorColorVolumeDiscovery();
vulkan.CheckRenderExecutorDccFixedClearFloat();
vulkan.CheckRenderExecutorColorMetadataClear();
vulkan.CheckSampledDccClear();
vulkan.CheckRenderExecutorColorStandardTileDiscovery();
vulkan.CheckRenderExecutorColorDepthTileDiscovery();