mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 10:56:58 +02:00
Remove extra DCC tracking
This commit is contained in:
+69
-170
@@ -33,23 +33,16 @@ namespace {
|
||||
|
||||
constexpr uint64_t NumFramesBeforeRemoval = 32;
|
||||
|
||||
[[nodiscard]] bool IsDccClearCode(uint32_t fill) {
|
||||
switch (fill) {
|
||||
case 0x00000000u:
|
||||
case 0x20202020u:
|
||||
case 0x40404040u:
|
||||
case 0x80808080u:
|
||||
case 0xc0c0c0c0u: return true;
|
||||
[[nodiscard]] bool DecodeDccClear(const TextureCache::ImageDesc& desc, vk::Format format,
|
||||
uint8_t code, vk::ClearColorValue& clear) {
|
||||
switch (code) {
|
||||
case 0x00:
|
||||
case 0x20:
|
||||
case 0x40:
|
||||
case 0x80:
|
||||
case 0xc0: break;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool DecodeDccClear(const TextureCache::ImageDesc& desc, vk::Format format,
|
||||
uint32_t fill, vk::ClearColorValue& clear) {
|
||||
if (!IsDccClearCode(fill)) {
|
||||
return false;
|
||||
}
|
||||
const auto code = static_cast<uint8_t>(fill);
|
||||
const auto& metadata = desc.info.metadata;
|
||||
if (code == 0x20) {
|
||||
// Clear-to-register is a color-buffer operation; the texture pipe cannot decode it.
|
||||
@@ -196,9 +189,6 @@ TextureCache::TextureCache(GraphicContext& graphics, CommandScheduler& scheduler
|
||||
}
|
||||
|
||||
TextureCache::~TextureCache() {
|
||||
for (auto& [address, metadata]: m_surface_metas) {
|
||||
UntrackMetadata(address, metadata);
|
||||
}
|
||||
m_slot_images.ForEach([&](ImageId id, const Image& image) {
|
||||
if (image.registered) {
|
||||
UnregisterImage(id);
|
||||
@@ -1133,139 +1123,71 @@ void TextureCache::InitializeImage(ImageId id) {
|
||||
}
|
||||
}
|
||||
|
||||
void TextureCache::UntrackMetadata(uint64_t address, MetaDataInfo& metadata) {
|
||||
if (metadata.tracked) {
|
||||
m_page_manager.UpdatePageWatchers<false>(address, metadata.size);
|
||||
metadata.tracked = false;
|
||||
}
|
||||
}
|
||||
|
||||
void TextureCache::InvalidateDccMetadata(uint64_t address, uint64_t size) {
|
||||
for (auto& [base, metadata]: m_surface_metas) {
|
||||
if (metadata.type != MetaDataInfo::Type::Dcc ||
|
||||
metadata.size == 0 || base >= address + size || address >= base + metadata.size) {
|
||||
continue;
|
||||
}
|
||||
UntrackMetadata(base, metadata);
|
||||
metadata.dirty = true;
|
||||
metadata.clear_mask = UINT32_MAX;
|
||||
metadata.revision = ++m_metadata_revision;
|
||||
}
|
||||
}
|
||||
|
||||
void TextureCache::RefreshDccMetadata(const ImageDesc& desc) {
|
||||
const auto range = desc.info.metadata.range;
|
||||
if (desc.info.metadata.kind != ImageMetadataKind::Dcc || range.size == 0 ||
|
||||
desc.info.resources.levels != 1) {
|
||||
return;
|
||||
}
|
||||
// DCC allocations use complete 4 KiB metadata blocks. Besides validating
|
||||
// the descriptor, this ensures that a CPU page fault identifies the metadata it modifies.
|
||||
if (!range.Valid() || range.address % TRACKER_PAGE_SIZE != 0 ||
|
||||
range.size % TRACKER_PAGE_SIZE != 0) {
|
||||
EXIT("TextureCache: DCC allocation must contain aligned 4 KiB blocks\n");
|
||||
}
|
||||
for (;;) {
|
||||
uint64_t revision = 0;
|
||||
{
|
||||
std::scoped_lock lock {m_lock};
|
||||
auto [entry, inserted] = m_surface_metas.try_emplace(range.address);
|
||||
auto& metadata = entry->second;
|
||||
if (inserted || metadata.type != MetaDataInfo::Type::Dcc) {
|
||||
UntrackMetadata(range.address, metadata);
|
||||
metadata = {.type = MetaDataInfo::Type::Dcc, .clear_mask = UINT32_MAX};
|
||||
}
|
||||
if (metadata.size != range.size) {
|
||||
UntrackMetadata(range.address, metadata);
|
||||
metadata.size = range.size;
|
||||
metadata.dirty = true;
|
||||
metadata.revision = ++m_metadata_revision;
|
||||
}
|
||||
if (!metadata.dirty) {
|
||||
return;
|
||||
}
|
||||
if (!metadata.tracked) {
|
||||
m_page_manager.UpdatePageWatchers<true>(range.address, range.size);
|
||||
metadata.tracked = true;
|
||||
}
|
||||
revision = metadata.revision;
|
||||
}
|
||||
// Discovery precedes descriptor/vertex uploads. Drain metadata writes before taking
|
||||
// the texture lock; completion can retire buffers and run memory-tracking callbacks.
|
||||
m_buffer_cache.ReadMemory(range.address, range.size, false);
|
||||
std::vector<uint8_t> bytes(range.size);
|
||||
std::scoped_lock lock {m_lock};
|
||||
const auto entry = m_surface_metas.find(range.address);
|
||||
if (entry == m_surface_metas.end() || entry->second.revision != revision) {
|
||||
continue;
|
||||
}
|
||||
if (!LibKernel::Memory::TryReadBacking(range.address, bytes.data(), range.size)) {
|
||||
EXIT("TextureCache: failed to read DCC metadata backing\n");
|
||||
}
|
||||
auto& metadata = entry->second;
|
||||
metadata.fill_value = UINT32_MAX;
|
||||
if (std::all_of(bytes.begin(), bytes.end(), [&](uint8_t byte) { return byte == bytes[0]; })) {
|
||||
metadata.fill_value = static_cast<uint32_t>(bytes[0]) * 0x01010101u;
|
||||
}
|
||||
metadata.dirty = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TextureCache::PrepareDccClear(ImageId id, const ImageDesc& desc,
|
||||
uint32_t metadata_base_layer) {
|
||||
void TextureCache::MaterializeDccClear(ImageId id, const ImageDesc& desc,
|
||||
uint32_t metadata_base_layer) {
|
||||
if (desc.info.metadata.kind != ImageMetadataKind::Dcc) {
|
||||
return;
|
||||
}
|
||||
auto& image = m_slot_images[id];
|
||||
image.info.metadata = desc.info.metadata;
|
||||
const auto entry = m_surface_metas.find(desc.info.metadata.range.address);
|
||||
if (entry == m_surface_metas.end() || entry->second.type != MetaDataInfo::Type::Dcc) {
|
||||
return;
|
||||
const auto range = desc.info.metadata.range;
|
||||
vk::Format format;
|
||||
{
|
||||
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.
|
||||
m_surface_metas.erase(range.address);
|
||||
if (range.size == 0 || desc.info.resources.levels != 1 || image.info.resources.levels != 1) {
|
||||
return;
|
||||
}
|
||||
format = image.backing.format;
|
||||
}
|
||||
auto& metadata = entry->second;
|
||||
if (metadata.dirty || metadata.clear_mask == 0 || image.info.resources.levels != 1 ||
|
||||
metadata.size != desc.info.metadata.range.size) {
|
||||
return;
|
||||
}
|
||||
vk::ClearValue clear {};
|
||||
if (!DecodeDccClear(desc, image.backing.format, metadata.fill_value, clear.color)) {
|
||||
return;
|
||||
const auto layers = desc.info.TransferLayers();
|
||||
// These one-mip surfaces use complete 4 KiB DCC 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");
|
||||
}
|
||||
const auto& view = desc.view_info;
|
||||
const bool volume_texture = image.info.IsVolume() && view.type == vk::ImageViewType::e3D;
|
||||
const bool volume_texture = desc.info.IsVolume() && view.type == vk::ImageViewType::e3D;
|
||||
const auto first = volume_texture ? 0u : metadata_base_layer;
|
||||
const auto image_first = volume_texture ? 0u : view.base_layer;
|
||||
const auto count = volume_texture ? std::max(desc.info.extent.depth >> view.base_level, 1u)
|
||||
: view.layer_count;
|
||||
if (first >= 32 || count > 32 - first) {
|
||||
return;
|
||||
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");
|
||||
}
|
||||
const auto metadata_slice_size = metadata.size / desc.info.TransferLayers();
|
||||
// The metadata fill covers the complete allocation. Consume each layer only after its
|
||||
// native image contents exist; already materialized layers may have been rendered since.
|
||||
for (uint32_t layer = first; layer < first + count;) {
|
||||
if ((metadata.clear_mask & (1u << layer)) == 0) {
|
||||
layer++;
|
||||
// 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)) {
|
||||
m_buffer_cache.ReadMemory(range.address, range.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;
|
||||
if (!LibKernel::Memory::TryReadBacking(address, &code, sizeof(code))) {
|
||||
EXIT("TextureCache: failed to read DCC metadata backing\n");
|
||||
}
|
||||
vk::ClearValue clear {};
|
||||
if (!DecodeDccClear(desc, format, code, clear.color)) {
|
||||
continue;
|
||||
}
|
||||
const auto start = layer;
|
||||
uint32_t mask = 0;
|
||||
do {
|
||||
mask |= 1u << layer++;
|
||||
} while (layer < first + count && (metadata.clear_mask & (1u << layer)) != 0);
|
||||
ClearImage(m_scheduler.Current(), id,
|
||||
{vk::ImageAspectFlagBits::eColor, view.base_level, view.level_count,
|
||||
image_first + start - first,
|
||||
layer - start},
|
||||
clear);
|
||||
// The host now owns expanded color data. Publish uncompressed (0xff)
|
||||
// keys for these slices, so a later partial write cannot reuse stale clear codes.
|
||||
const auto address = desc.info.metadata.range.address + metadata_slice_size * start;
|
||||
const auto size = metadata_slice_size * (layer - start);
|
||||
auto [buffer, offset] = m_buffer_cache.ObtainBuffer(address, size, true, false);
|
||||
buffer->Fill(offset, size, UINT32_MAX);
|
||||
metadata.clear_mask &= ~mask;
|
||||
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");
|
||||
}
|
||||
if (!std::all_of(bytes.begin(), bytes.end(), [code](uint8_t byte) { return byte == code; })) {
|
||||
continue;
|
||||
}
|
||||
{
|
||||
std::scoped_lock lock {m_lock};
|
||||
ClearImage(m_scheduler.Current(), id,
|
||||
{vk::ImageAspectFlagBits::eColor, view.base_level, view.level_count,
|
||||
image_first + slice, 1}, clear);
|
||||
}
|
||||
// Native expanded keys own consumption. Existing buffer tracking publishes this CPU
|
||||
// write to future GPU readers; FillBuffer can fault and must run outside the texture lock.
|
||||
m_buffer_cache.FillBuffer(address, slice_size, UINT32_MAX, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1330,7 +1252,6 @@ ImageId TextureCache::FindImage(ImageDesc& desc, bool exact_format) {
|
||||
std::scoped_lock lock {m_lock};
|
||||
return GetNullImage(desc);
|
||||
}
|
||||
RefreshDccMetadata(desc);
|
||||
const auto metadata_base_layer = desc.view_info.base_layer;
|
||||
|
||||
ImageId result {};
|
||||
@@ -1396,10 +1317,10 @@ ImageId TextureCache::FindImage(ImageDesc& desc, bool exact_format) {
|
||||
if (view_layer >= 0) {
|
||||
desc.view_info.base_layer = static_cast<uint32_t>(view_layer);
|
||||
}
|
||||
PrepareDccClear(result, desc, metadata_base_layer);
|
||||
image.tick_accessed_last = m_scheduler.CurrentTick();
|
||||
TouchImage(image);
|
||||
}
|
||||
MaterializeDccClear(result, desc, metadata_base_layer);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1515,17 +1436,9 @@ vk::ImageView TextureCache::FindDepthTarget(ImageId id, const ImageDesc& desc) {
|
||||
RefreshImage(id);
|
||||
if (desc.info.HasMetadata()) {
|
||||
image.info.metadata = desc.info.metadata;
|
||||
auto [metadata, inserted] =
|
||||
m_surface_metas.try_emplace(desc.info.metadata.range.address,
|
||||
MetaDataInfo {.type = MetaDataInfo::Type::HTile,
|
||||
.clear_mask = image.info.htile_clear_mask});
|
||||
if (!inserted && metadata->second.type != MetaDataInfo::Type::HTile) {
|
||||
// PS5 allocations can reuse DCC storage as HTile while the old color image is cached.
|
||||
// The depth binding defines the new type; incompatible fill state cannot carry over.
|
||||
UntrackMetadata(metadata->first, metadata->second);
|
||||
metadata->second = {.type = MetaDataInfo::Type::HTile,
|
||||
.clear_mask = image.info.htile_clear_mask};
|
||||
}
|
||||
m_surface_metas.emplace(desc.info.metadata.range.address,
|
||||
MetaDataInfo {.type = MetaDataInfo::Type::HTile,
|
||||
.clear_mask = image.info.htile_clear_mask});
|
||||
}
|
||||
CommitGpuWrite(image);
|
||||
if (desc.info.HasStencil()) {
|
||||
@@ -1696,7 +1609,6 @@ void TextureCache::InvalidateMemory(uint64_t address, uint64_t size) {
|
||||
EXIT("TextureCache: invalid memory-invalidation range\n");
|
||||
}
|
||||
std::scoped_lock lock {m_lock};
|
||||
InvalidateDccMetadata(address, size);
|
||||
InvalidateCpuAliases(address, size);
|
||||
}
|
||||
|
||||
@@ -1905,7 +1817,6 @@ void TextureCache::InvalidateMemoryFromGPU(uint64_t address, uint64_t size) {
|
||||
return;
|
||||
}
|
||||
std::scoped_lock lock {m_lock};
|
||||
InvalidateDccMetadata(address, size);
|
||||
for (const auto id: FindImagesInRegion(address, size, true)) {
|
||||
auto& image = m_slot_images[id];
|
||||
if (image.depth_id || !image.Overlaps(address, size)) {
|
||||
@@ -1963,28 +1874,19 @@ bool TextureCache::IsMeta(uint64_t address) {
|
||||
return found != m_surface_metas.end();
|
||||
}
|
||||
|
||||
bool TextureCache::IsMetaCleared(uint64_t address, uint32_t slice, uint32_t* fill_value) {
|
||||
bool TextureCache::IsMetaCleared(uint64_t address, uint32_t slice) {
|
||||
std::scoped_lock lock {m_lock};
|
||||
const auto found = m_surface_metas.find(address);
|
||||
if (found == m_surface_metas.end() || slice >= 32) {
|
||||
return false;
|
||||
}
|
||||
if (fill_value != nullptr) {
|
||||
*fill_value = found->second.fill_value;
|
||||
}
|
||||
if (found->second.type == MetaDataInfo::Type::Dcc &&
|
||||
(found->second.dirty || !IsDccClearCode(found->second.fill_value))) {
|
||||
return false;
|
||||
}
|
||||
return (found->second.clear_mask & (1u << slice)) != 0;
|
||||
}
|
||||
|
||||
bool TextureCache::ClearMeta(uint64_t address) {
|
||||
std::scoped_lock lock {m_lock};
|
||||
const auto found = m_surface_metas.find(address);
|
||||
if (found == m_surface_metas.end() || found->second.type == MetaDataInfo::Type::Dcc) {
|
||||
// Preserve the broad metadata-clear operation for CMask/FMask/HTile. DCC requires a
|
||||
// validated fill value, so an arbitrary compute write must not clear it.
|
||||
if (found == m_surface_metas.end()) {
|
||||
return false;
|
||||
}
|
||||
found->second.clear_mask = UINT32_MAX;
|
||||
@@ -2012,10 +1914,7 @@ void TextureCache::UnmapMemory(uint64_t address, uint64_t size) {
|
||||
std::scoped_lock lock {m_lock};
|
||||
for (auto metadata = m_surface_metas.begin(); metadata != m_surface_metas.end();) {
|
||||
const auto base = metadata->first;
|
||||
if ((base >= address && base < address + size) ||
|
||||
(metadata->second.size != 0 && base < address + size &&
|
||||
address < base + metadata->second.size)) {
|
||||
UntrackMetadata(base, metadata->second);
|
||||
if (base >= address && base < address + size) {
|
||||
metadata = m_surface_metas.erase(metadata);
|
||||
} else {
|
||||
++metadata;
|
||||
|
||||
Reference in New Issue
Block a user