texture cache: select color-metadata clears on the GPU with conditional rendering

This commit is contained in:
Mr. Big Britches
2026-10-22 11:50:35 +02:00
committed by nmzik
parent b6495a5b67
commit b54057942d
12 changed files with 311 additions and 20 deletions
+69 -5
View File
@@ -1067,15 +1067,78 @@ void TextureCache::MaterializeColorClear(ImageId id, const ImageDesc& desc,
if (first >= layers || count > layers - first) {
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.
if (m_buffer_cache.IsRegionGpuModified(range.address, range.size)) {
const auto slice_size = range.size / layers;
const GuestRange viewed {range.address + slice_size * first, slice_size * count};
if (m_buffer_cache.IsRegionGpuModified(viewed.address, viewed.size)) {
std::array<vk::ClearAttachment, TileManager::ColorClearSlots> clears {};
uint32_t keys = 0;
for (uint32_t key = 0; key < clears.size(); key++) {
clears[key].aspectMask = vk::ImageAspectFlagBits::eColor;
if (DecodeColorClear(desc, static_cast<uint8_t>(key << 5u),
clears[key].clearValue.color)) {
keys |= 1u << key;
}
}
// No selected key to clear, or none left since the last check.
if (m_buffer_cache.IsColorMetadataChecked(viewed.address, viewed.size, keys)) {
return;
}
{
std::scoped_lock lock {m_lock};
auto& image = m_slot_images[id];
// Compressed images cannot upload, so they must already hold native contents.
const bool native_current = (image.usage.render_target || image.IsGpuModified()) &&
!image.IsBufferModified() && !image.IsCpuDirty();
// 2D-array attachment views select 2D layers or, of a volume, depth slices.
if (m_graphics.conditional_rendering_enabled &&
image.backing.image_type != vk::ImageType::e1D &&
count <= m_graphics.GetPhysicalDeviceProperties().limits.maxFramebufferLayers &&
(image.backing.usage & vk::ImageUsageFlagBits::eColorAttachment) &&
(m_graphics.GetFormatProperties(view.format).optimalTilingFeatures &
vk::FormatFeatureFlagBits::eColorAttachment) &&
(desc.info.metadata.compression == VideoOutCompression::Uncompressed ||
native_current)) {
// The GPU selects each slice's clear, so the CPU never waits for the metadata.
Common::DebugCounters::Add(Common::DebugCounters::Counter::ColorMetadataChecks);
InitializeImage(id);
const bool expand = desc.type != BindingType::VideoOut;
const auto [buffer, offset] =
m_buffer_cache.ObtainBuffer(viewed.address, viewed.size, expand);
const auto predicates = m_tiler.CheckColorClear(
{buffer->Handle(), offset, viewed.size}, count, keys, expand);
if (expand) {
m_buffer_cache.MarkColorMetadataChecked(viewed.address, viewed.size, keys);
}
const auto command = m_scheduler.Current().Handle();
const auto area = BeginColorRendering(image, view.format, view.base_level,
image_first, count, nullptr, command);
vk::ClearRect rect {.rect = area, .layerCount = 1};
for (uint32_t slice = 0; slice < count; slice++) {
rect.baseArrayLayer = slice;
for (uint32_t key = 0; key < clears.size(); key++) {
if ((keys & (1u << key)) != 0) {
const vk::ConditionalRenderingBeginInfoEXT predicate {
.buffer = predicates,
.offset = (slice * clears.size() + key) * sizeof(uint32_t)};
command.beginConditionalRenderingEXT(&predicate);
command.clearAttachments(1, &clears[key], 1, &rect);
command.endConditionalRenderingEXT();
}
}
}
command.endRendering();
CommitGpuWrite(image);
return;
}
}
// 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.
Common::DebugCounters::ScopedTimer timer(
Common::DebugCounters::Counter::ColorMetadataReadbacks,
Common::DebugCounters::Counter::ColorMetadataReadbackNs);
m_buffer_cache.ReadMemory(range.address, range.size, false);
m_buffer_cache.ReadMemory(viewed.address, viewed.size, false);
}
const auto slice_size = range.size / layers;
for (uint32_t slice = 0; slice < count; slice++) {
const auto address = range.address + slice_size * (first + slice);
uint8_t code = 0;
@@ -1768,6 +1831,7 @@ bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, uint64_t vaddr, uin
copy_size != image.info.data.size)) {
return false;
}
ForgetColorMetadataChecks(image.info.data.address, copy_size);
m_texture_cache.DownloadImage(image, buffer, buf_offset, copy_size, levels);
return true;
}