graphics: index live cache slots directly

This commit is contained in:
nmzik committed 2026-10-24 04:26:16 +02:00
1 parent 170c107779
commit 269e411ef2
5 files changed
+67 -95

No files matched your search

+1 -4
View File
@@ -95,10 +95,7 @@ public:
private: private:
friend struct BufferCacheTestAccess; friend struct BufferCacheTestAccess;
bool IsBufferInvalid(BufferId id) const { bool IsBufferInvalid(BufferId id) const { return !id || m_slot_buffers[id].is_deleted; }
const auto* buffer = m_slot_buffers.try_get(id);
return buffer == nullptr || buffer->is_deleted;
}
using BufferMap = std::map<uint64_t, BufferId>; using BufferMap = std::map<uint64_t, BufferId>;
struct OverlapResult { struct OverlapResult {
+45 -60
View File
@@ -251,24 +251,24 @@ void TextureCache::UnregisterImage(ImageId id) {
} }
void TextureCache::DeleteImage(ImageId id) { void TextureCache::DeleteImage(ImageId id) {
auto* image = m_slot_images.try_get(id); auto& image = m_slot_images[id];
if (image == nullptr || !image->registered) { if (!image.registered) {
return; return;
} }
if (image->depth_id) { if (image.depth_id) {
std::erase(m_slot_images[image->depth_id].stencil_ids, id); std::erase(m_slot_images[image.depth_id].stencil_ids, id);
} else { } else {
// Detach first: deleting each association also unlinks it from its depth owner. // Detach first: deleting each association also unlinks it from its depth owner.
for (const auto association: std::exchange(image->stencil_ids, {})) { for (const auto association: std::exchange(image.stencil_ids, {})) {
FreeImage(association); FreeImage(association);
} }
} }
if (image->IsGpuModified()) { if (image.IsGpuModified()) {
EXIT("TextureCache: deleting a GPU-modified image without resolving its contents\n"); EXIT("TextureCache: deleting a GPU-modified image without resolving its contents\n");
} }
m_download_images.erase(id); m_download_images.erase(id);
if (image->info.metadata.kind == ImageMetadataKind::Htile) { if (image.info.metadata.kind == ImageMetadataKind::Htile) {
m_surface_metas.erase(image->info.metadata.range.address); m_surface_metas.erase(image.info.metadata.range.address);
} }
UnregisterImage(id); UnregisterImage(id);
if (m_scheduler.Active()) { if (m_scheduler.Active()) {
@@ -779,15 +779,14 @@ void Resolve(TextureCache& cache, ImageId cached_id, ImageId merged_id) {
TextureCache::OverlapResult TextureCache::ResolveOverlap(const ImageInfo& requested, TextureCache::OverlapResult TextureCache::ResolveOverlap(const ImageInfo& requested,
BindingType binding, ImageId cached_id, BindingType binding, ImageId cached_id,
ImageId merged_id) { ImageId merged_id) {
auto owner = m_slot_images.try_get(cached_id); auto& cached = m_slot_images[cached_id];
if (owner == nullptr || !owner->registered) { if (!cached.registered) {
return {merged_id}; return {merged_id};
} }
if (BcAlias::Matches(requested, owner->info) && !owner->IsCpuDirty()) { if (BcAlias::Matches(requested, cached.info) && !cached.IsCpuDirty()) {
BcAlias::Resolve(*this, cached_id, merged_id); BcAlias::Resolve(*this, cached_id, merged_id);
return {merged_id}; return {merged_id};
} }
auto& cached = *owner;
const auto current_tick = m_scheduler.CurrentTick(); const auto current_tick = m_scheduler.CurrentTick();
const bool safe_to_delete = const bool safe_to_delete =
current_tick - std::min(current_tick, cached.tick_accessed_last) > NumFramesBeforeRemoval && current_tick - std::min(current_tick, cached.tick_accessed_last) > NumFramesBeforeRemoval &&
@@ -861,9 +860,8 @@ TextureCache::OverlapResult TextureCache::ResolveOverlap(const ImageInfo& reques
if (requested.pixel_format != cached.info.pixel_format || if (requested.pixel_format != cached.info.pixel_format ||
requested.data.size <= cached.info.data.size) { requested.data.size <= cached.info.data.size) {
const auto result_id = merged_id ? merged_id : cached_id; const auto result_id = merged_id ? merged_id : cached_id;
const auto result = m_slot_images.try_get(result_id); return {ImageViewOps::FormatsCompatible(m_slot_images[result_id].info.pixel_format,
return {result != nullptr && ImageViewOps::FormatsCompatible(result->info.pixel_format, requested.pixel_format)
requested.pixel_format)
? result_id ? result_id
: ImageId {}}; : ImageId {}};
} }
@@ -1210,9 +1208,8 @@ ImageId TextureCache::AssociateStencil(ImageId depth_id, GuestRange stencil) {
ImageId association {}; ImageId association {};
for (const auto id: FindImagesInRegion(stencil.address, stencil.size, false)) { for (const auto id: FindImagesInRegion(stencil.address, stencil.size, false)) {
const auto owner = m_slot_images.try_get(id); const auto& owner = m_slot_images[id];
if (owner != nullptr && owner->info.data == stencil && if (owner.info.data == stencil && owner.info.extent == depth.info.extent) {
owner->info.extent == depth.info.extent) {
association = id; association = id;
} }
} }
@@ -1307,10 +1304,9 @@ ImageId TextureCache::FindImage(ImageDesc& desc, bool exact_format) {
} }
} else if (block_alias) { } else if (block_alias) {
for (const auto candidate: candidates) { for (const auto candidate: candidates) {
const auto* owner = m_slot_images.try_get(candidate); const auto& owner = m_slot_images[candidate];
if (candidate != result && owner != nullptr && owner->registered && if (candidate != result && owner.registered && !owner.IsCpuDirty() &&
!owner->IsCpuDirty() && BcAlias::Matches(m_slot_images[result].info, owner.info)) {
BcAlias::Matches(m_slot_images[result].info, owner->info)) {
BcAlias::Resolve(*this, candidate, result); BcAlias::Resolve(*this, candidate, result);
} }
} }
@@ -1373,14 +1369,14 @@ ImageId TextureCache::FindImageFromRange(uint64_t address, uint64_t size, bool e
std::scoped_lock lock {m_lock}; std::scoped_lock lock {m_lock};
ImageIds matches; ImageIds matches;
for (const auto id: FindImagesInRegion(address, size, false)) { for (const auto id: FindImagesInRegion(address, size, false)) {
auto owner = m_slot_images.try_get(id); const auto* owner = &m_slot_images[id];
if (owner == nullptr || owner->info.data.address != address) { if (owner->info.data.address != address) {
continue; continue;
} }
if (ensure_valid && owner->depth_id) { if (ensure_valid && owner->depth_id) {
owner = m_slot_images.try_get(owner->depth_id); owner = &m_slot_images[owner->depth_id];
} }
if (owner == nullptr || (ensure_valid && !owner->SafeToDownload())) { if (ensure_valid && !owner->SafeToDownload()) {
continue; continue;
} }
matches.push_back(id); matches.push_back(id);
@@ -1397,11 +1393,8 @@ ImageId TextureCache::FindImageFromRange(uint64_t address, uint64_t size, bool e
} }
} }
} }
if (selected && ensure_valid) { if (selected && ensure_valid && m_slot_images[selected].depth_id) {
const auto owner = m_slot_images.try_get(selected); selected = m_slot_images[selected].depth_id;
if (owner != nullptr && owner->depth_id) {
selected = owner->depth_id;
}
} }
return selected; return selected;
} }
@@ -1422,9 +1415,8 @@ vk::ImageView TextureCache::FindTexture(ImageId id, const ImageDesc& desc) {
desc.info.data.End() <= image.info.stencil.End()) { desc.info.data.End() <= image.info.stencil.End()) {
for (const auto stencil_id: for (const auto stencil_id:
FindImagesInRegion(image.info.stencil.address, image.info.stencil.size, false)) { FindImagesInRegion(image.info.stencil.address, image.info.stencil.size, false)) {
const auto* stencil = m_slot_images.try_get(stencil_id); const auto& stencil = m_slot_images[stencil_id];
if (stencil != nullptr && stencil->depth_id == id && if (stencil.depth_id == id && stencil.info.data == image.info.stencil) {
stencil->info.data == image.info.stencil) {
RefreshImage(stencil_id); RefreshImage(stencil_id);
break; break;
} }
@@ -1535,27 +1527,24 @@ bool TextureCache::ClearImageFromBuffer(CommandBuffer& command, uint64_t address
vk::ImageAspectFlags aspect {}; vk::ImageAspectFlags aspect {};
ImageSubresourceRange subresources; ImageSubresourceRange subresources;
for (const auto id: FindImagesInRegion(address, size, false)) { for (const auto id: FindImagesInRegion(address, size, false)) {
auto owner = m_slot_images.try_get(id); const auto& owner = m_slot_images[id];
if (owner == nullptr) {
continue;
}
vk::ImageAspectFlags candidate {}; vk::ImageAspectFlags candidate {};
ImageId candidate_id = id; ImageId candidate_id = id;
ImageSubresourceRange candidate_subresources { ImageSubresourceRange candidate_subresources {
0, owner->info.resources.levels, 0, owner->info.TransferLayers()}; 0, owner.info.resources.levels, 0, owner.info.TransferLayers()};
if (owner->depth_id && owner->info.data.address == address && if (owner.depth_id && owner.info.data.address == address &&
owner->info.data.size == size) { owner.info.data.size == size) {
candidate = vk::ImageAspectFlagBits::eStencil; candidate = vk::ImageAspectFlagBits::eStencil;
candidate_id = owner->depth_id; candidate_id = owner.depth_id;
candidate_subresources = owner->stencil_subresources; candidate_subresources = owner.stencil_subresources;
owner = m_slot_images.try_get(candidate_id); const auto& depth = m_slot_images[candidate_id];
if (owner == nullptr || owner->backing.image == nullptr || !owner->info.HasStencil()) { if (depth.backing.image == nullptr || !depth.info.HasStencil()) {
continue; continue;
} }
} else if (!owner->depth_id && owner->info.data.address == address && } else if (!owner.depth_id && owner.info.data.address == address &&
owner->info.data.size == size) { owner.info.data.size == size) {
candidate = owner->info.IsDepth() ? vk::ImageAspectFlagBits::eDepth candidate = owner.info.IsDepth() ? vk::ImageAspectFlagBits::eDepth
: vk::ImageAspectFlagBits::eColor; : vk::ImageAspectFlagBits::eColor;
} }
if (!candidate) { if (!candidate) {
continue; continue;
@@ -1896,23 +1885,20 @@ void TextureCache::InvalidateCpuAliases(uint64_t address, uint64_t size) {
const auto page_begin = Common::AlignDown(address, TRACKER_PAGE_SIZE); const auto page_begin = Common::AlignDown(address, TRACKER_PAGE_SIZE);
const auto page_end = Common::AlignUp(address + size, TRACKER_PAGE_SIZE); const auto page_end = Common::AlignUp(address + size, TRACKER_PAGE_SIZE);
for (const auto id: FindImagesInRegion(address, size, true)) { for (const auto id: FindImagesInRegion(address, size, true)) {
auto owner = m_slot_images.try_get(id); auto& owner = m_slot_images[id];
if (owner == nullptr) { if (owner.Overlaps(address, size)) {
continue; owner.MarkCpuDirty();
}
if (owner->Overlaps(address, size)) {
owner->MarkCpuDirty();
UntrackImage(id); UntrackImage(id);
continue; continue;
} }
const auto image_begin = owner->info.data.address; const auto image_begin = owner.info.data.address;
const auto image_end = owner->info.data.End(); const auto image_end = owner.info.data.End();
if (page_end < image_end) { if (page_end < image_end) {
UntrackImageHead(id); UntrackImageHead(id);
} else if (image_begin < page_begin) { } else if (image_begin < page_begin) {
UntrackImageTail(id); UntrackImageTail(id);
} else { } else {
MarkAsMaybeDirty(id, *owner); MarkAsMaybeDirty(id, owner);
} }
} }
} }
@@ -2052,8 +2038,7 @@ void TextureCache::ProcessDownloadImages() {
} }
std::scoped_lock lock {m_lock}; std::scoped_lock lock {m_lock};
for (const auto id: m_download_images) { for (const auto id: m_download_images) {
const auto owner = m_slot_images.try_get(id); if (m_slot_images[id].IsGpuModified()) {
if (owner != nullptr && owner->registered && owner->IsGpuModified()) {
(void)DownloadImageMemory(id); (void)DownloadImageMemory(id);
} }
} }
@@ -672,9 +672,7 @@ void RenderExecutor::BindRenderTarget(ImageId id) {
void RenderExecutor::ResetBindings() { void RenderExecutor::ResetBindings() {
for (const auto id: m_bound_images) { for (const auto id: m_bound_images) {
if (auto* image = m_context.GetTextureCache().m_slot_images.try_get(id); image != nullptr) { m_context.GetTextureCache().m_slot_images[id].binding = {};
image->binding = {};
}
} }
m_bound_images.clear(); m_bound_images.clear();
} }
@@ -823,12 +821,9 @@ void RenderExecutor::RebindImages(PreparedBindings& prepared) {
// Acquire each view before a later overlapping descriptor can replace its image. // Acquire each view before a later overlapping descriptor can replace its image.
for (uint32_t i = 0; i < program.info.images.size(); i++) { for (uint32_t i = 0; i < program.info.images.size(); i++) {
if (!images[i].image_id) continue; if (!images[i].image_id) continue;
const auto old_image = texture_cache.m_slot_images.try_get(images[i].image_id); auto& old_image = texture_cache.m_slot_images[images[i].image_id];
if (old_image == nullptr || !old_image->registered || if (!old_image.registered || old_image.binding.needs_rebind) {
old_image->binding.needs_rebind) { old_image.binding = {};
if (old_image != nullptr) {
old_image->binding = {};
}
images[i] = ResolveTexture(program.info.images[i], snapshot.images[i]); images[i] = ResolveTexture(program.info.images[i], snapshot.images[i]);
BindImage(images[i].image_id, BindImage(images[i].image_id,
images[i].desc.type == TextureCache::BindingType::Storage); images[i].desc.type == TextureCache::BindingType::Storage);
@@ -879,12 +874,9 @@ void RenderExecutor::PrepareGraphicsBindings(std::span<PreparedBindings* const>
auto& cache = m_context.GetTextureCache(); auto& cache = m_context.GetTextureCache();
for (auto& target: colors) { for (auto& target: colors) {
EXIT_IF(!target.image_id); EXIT_IF(!target.image_id);
const auto old_image = cache.m_slot_images.try_get(target.image_id); auto& old_image = cache.m_slot_images[target.image_id];
if (old_image == nullptr || !old_image->registered || if (!old_image.registered || old_image.binding.needs_rebind) {
old_image->binding.needs_rebind) { old_image.binding = {};
if (old_image != nullptr) {
old_image->binding = {};
}
target.desc.view_info.base_level = target.guest_mip_level; target.desc.view_info.base_level = target.guest_mip_level;
target.desc.view_info.base_layer = target.guest_array_layer; target.desc.view_info.base_layer = target.guest_array_layer;
target.image_id = cache.FindImage(target.desc); target.image_id = cache.FindImage(target.desc);
@@ -944,11 +936,10 @@ void RenderExecutor::CommitBindings(CommandBuffer& buffer,
} }
for (const auto [address, size]: reads) { for (const auto [address, size]: reads) {
for (const auto id: m_bound_images) { for (const auto id: m_bound_images) {
const auto* image = m_context.GetTextureCache().m_slot_images.try_get(id); const auto& image = m_context.GetTextureCache().m_slot_images[id];
if (image == nullptr || if (!image.binding.shader_write && !image.binding.is_target) continue;
(!image->binding.shader_write && !image->binding.is_target)) continue; for (const auto written: {image.info.data, image.info.stencil,
for (const auto written: {image->info.data, image->info.stencil, image.info.metadata.range}) {
image->info.metadata.range}) {
if (written.size != 0 && ImageRangeOverlaps(address, size, if (written.size != 0 && ImageRangeOverlaps(address, size,
written.address, written.size)) { written.address, written.size)) {
EXIT("scalar resource reads overlap an image or attachment write\n"); EXIT("scalar resource reads overlap an image or attachment write\n");
@@ -474,9 +474,8 @@ RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderCo
for (uint32_t i = 0; i < color_count; i++) { for (uint32_t i = 0; i < color_count; i++) {
auto& target = colors[i]; auto& target = colors[i];
EXIT_IF(!target.image_id); EXIT_IF(!target.image_id);
const auto owner = cache.m_slot_images.try_get(target.image_id); const auto& owner = cache.m_slot_images[target.image_id];
if (owner == nullptr || (!owner->registered && !owner->info.data.Empty()) || if ((!owner.registered && !owner.info.data.Empty()) || owner.binding.needs_rebind) {
owner->binding.needs_rebind) {
EXIT("color target changed after render-state discovery\n"); EXIT("color target changed after render-state discovery\n");
} }
const auto image_view = cache.FindRenderTarget(target.image_id, target.desc); const auto image_view = cache.FindRenderTarget(target.image_id, target.desc);
@@ -502,8 +501,8 @@ RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderCo
attachment.image_layout = layout; attachment.image_layout = layout;
} }
if (depth.image_id) { if (depth.image_id) {
const auto owner = cache.m_slot_images.try_get(depth.image_id); const auto& owner = cache.m_slot_images[depth.image_id];
if (owner == nullptr || !owner->registered || owner->binding.needs_rebind) { if (!owner.registered || owner.binding.needs_rebind) {
EXIT("depth target changed after render-state discovery\n"); EXIT("depth target changed after render-state discovery\n");
} }
const auto image_view = cache.FindDepthTarget(depth.image_id, depth.desc); const auto image_view = cache.FindDepthTarget(depth.image_id, depth.desc);
+6 -6
View File
@@ -4317,12 +4317,6 @@ public:
BufferCacheTestAccess::PageOwner( BufferCacheTestAccess::PageOwner(
cache, index_begin + 2 * index_page) == index_bridge, cache, index_begin + 2 * index_page) == index_bridge,
"bridging acquisition did not publish its merged Buffer range"); "bridging acquisition did not publish its merged Buffer range");
scheduler.Finish();
constexpr uint64_t recycled_offset = index_offset + 4 * index_page;
const auto recycled = cache.FindBuffer(base + recycled_offset + 0x100,
sizeof(uint32_t));
Require(name, "slot reuse", recycled == index_left || recycled == index_right,
"retired Buffer slot was not reused");
const auto [resolved_left, resolved_left_offset] = cache.ObtainBuffer( const auto [resolved_left, resolved_left_offset] = cache.ObtainBuffer(
index_begin + 0x100, sizeof(uint32_t), true, false, index_left); index_begin + 0x100, sizeof(uint32_t), true, false, index_left);
const auto [resolved_right, resolved_right_offset] = const auto [resolved_right, resolved_right_offset] =
@@ -4333,6 +4327,12 @@ public:
resolved_right == resolved_left && resolved_right == resolved_left &&
resolved_left_offset != resolved_right_offset, resolved_left_offset != resolved_right_offset,
"saved descriptor IDs did not resolve through the merged owner"); "saved descriptor IDs did not resolve through the merged owner");
scheduler.Finish();
constexpr uint64_t recycled_offset = index_offset + 4 * index_page;
const auto recycled = cache.FindBuffer(base + recycled_offset + 0x100,
sizeof(uint32_t));
Require(name, "slot reuse", recycled == index_left || recycled == index_right,
"retired Buffer slot was not reused");
constexpr uint64_t stream_page = BufferCache::CACHING_PAGESIZE; constexpr uint64_t stream_page = BufferCache::CACHING_PAGESIZE;
constexpr uint64_t stream_leap = stream_page * 128; constexpr uint64_t stream_leap = stream_page * 128;