diff --git a/src/graphics/host_gpu/renderer/cache/textureCache.cpp b/src/graphics/host_gpu/renderer/cache/textureCache.cpp index b93e70e68..b42d8a92b 100644 --- a/src/graphics/host_gpu/renderer/cache/textureCache.cpp +++ b/src/graphics/host_gpu/renderer/cache/textureCache.cpp @@ -222,14 +222,6 @@ TextureCache::BindingType TextureCache::UploadBinding(const Image& image) { return image.usage.storage ? BindingType::Storage : BindingType::Texture; } -bool TextureCache::SafeToDownload(const Image& image) { - if (!image.SafeToDownload()) { - return false; - } - const auto range = image.info.data; - return !m_buffer_cache.HasGpuDirtyBytes(range.address, range.size); -} - ImageId TextureCache::InsertImage(const ImageInfo& info) { const auto id = m_slot_images.insert(m_graphics, m_scheduler, info); if (!info.data.Empty()) { @@ -1360,7 +1352,7 @@ ImageId TextureCache::FindImageFromRange(uint64_t address, uint64_t size, bool e if (ensure_valid && owner->depth_id) { owner = m_slot_images.try_get(owner->depth_id); } - if (owner == nullptr || (ensure_valid && !SafeToDownload(*owner))) { + if (owner == nullptr || (ensure_valid && !owner->SafeToDownload())) { continue; } matches.push_back(id); @@ -1751,7 +1743,7 @@ bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, uint64_t vaddr, uin std::scoped_lock lock {m_texture_cache.m_lock}; auto& image = m_texture_cache.m_slot_images[selected]; // The GPU thread owns image retirement; CPU invalidation can dirty this image after lookup. - if (!m_texture_cache.SafeToDownload(image)) { + if (!image.SafeToDownload()) { return false; } if (!buffer.IsInBounds(image.info.data.address, 1)) { @@ -1814,7 +1806,7 @@ bool TextureCache::DownloadImageMemory(ImageId id) { return false; } auto transfer = BuildDownload(image); - if (!transfer.valid || !SafeToDownload(image)) { + if (!transfer.valid || !image.SafeToDownload()) { return false; } const auto range = image.info.data; @@ -2001,7 +1993,7 @@ void TextureCache::RunGarbageCollector() { continue; } if (owner->IsGpuModified()) { - const bool safe = SafeToDownload(*owner); + const bool safe = owner->SafeToDownload(); if (safe && owner->info.IsTiled()) { continue; } diff --git a/src/graphics/host_gpu/renderer/cache/textureCache.h b/src/graphics/host_gpu/renderer/cache/textureCache.h index 66c5f9088..5fe0c4b63 100644 --- a/src/graphics/host_gpu/renderer/cache/textureCache.h +++ b/src/graphics/host_gpu/renderer/cache/textureCache.h @@ -128,7 +128,6 @@ private: [[nodiscard]] static bool SameBacking(const ImageInfo& cached, const ImageInfo& requested, bool exact_format); [[nodiscard]] static BindingType UploadBinding(const Image& image); - [[nodiscard]] bool SafeToDownload(const Image& image); // Caller holds m_lock; it also serializes the per-image query epoch. [[nodiscard]] ImageIds FindImagesInRegion(uint64_t address, uint64_t size, diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index c8e11a225..7ee761377 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -7288,12 +7288,16 @@ public: "failed to create the overlapping dirty Buffer owner"); exact_buffer.first->Fill(exact_buffer.second, sizeof(uint32_t), dirty_sibling_value); + texture_cache.InvalidateMemoryFromGPU(base + exact_image_offset, + sizeof(uint32_t)); Libs::Graphics::Buffer exact_download( m_runtime_context, scheduler, MemoryUsage::DeviceLocal, base + exact_image_offset, AllFlags, sizeof(uint32_t)); Require(name, "overlapping buffer ownership rejection", !texture_cache.FindImageFromRange(base + exact_image_offset, sizeof(uint32_t)) && + !TextureCacheTestAccess::TryDownload(texture_cache, + exact_image) && !BufferCacheTestAccess::SynchronizeBufferFromImage( resources.GetBufferCache(), exact_download, base + exact_image_offset, sizeof(uint32_t)), @@ -7350,8 +7354,9 @@ public: Require( name, "CPU-dirty image range validity", !texture_cache.FindImageFromRange(gc_image_desc_a.info.data.address, - gc_image_desc_a.info.data.size), - "FindImageFromRange accepted CPU-dirty native contents"); + gc_image_desc_a.info.data.size) && + !TextureCacheTestAccess::TryDownload(texture_cache, gc_images[0]), + "CPU-dirty native contents remained eligible for image readback"); texture_cache.MarkGpuWritten(gc_images[0]); for (size_t index = 0; index < gc_image_offsets.size(); index++) { Libs::LibKernel::Memory::WriteBacking(base + gc_image_offsets[index], @@ -7440,11 +7445,33 @@ public: TextureCacheTestAccess::SetLinearReadback(texture_cache, true); const auto submit_readback_image = texture_cache.FindImage(submit_readback_desc); + auto submit_buffer = resources.GetBufferCache().ObtainBuffer( + submit_readback_desc.info.data.address, + submit_readback_desc.info.data.size, true); + Require(name, "linear submit dirty buffer allocation", + submit_buffer.first != nullptr, + "failed to create the older GPU Buffer contents"); + submit_buffer.first->Fill(submit_buffer.second, sizeof(uint32_t), + submit_readback_stale); + texture_cache.InvalidateMemoryFromGPU( + submit_readback_desc.info.data.address, + submit_readback_desc.info.data.size); + (void)texture_cache.FindTexture(submit_readback_image, + submit_readback_desc); Require(name, "linear submit image clear", texture_cache.ClearImageFromBuffer( command, submit_readback_desc.info.data.address, submit_readback_desc.info.data.size, submit_readback_value), "failed to create GPU-current linear storage contents"); + Require(name, "older buffer permits current image readback", + resources.GetBufferCache().HasGpuDirtyBytes( + submit_readback_desc.info.data.address, + submit_readback_desc.info.data.size) && + texture_cache.FindImageFromRange( + submit_readback_desc.info.data.address, + submit_readback_desc.info.data.size) == + submit_readback_image, + "historical GPU Buffer dirtiness rejected newer image contents"); TextureCacheTestAccess::TrackDownload(texture_cache, submit_readback_image); Libs::LibKernel::Memory::WriteBacking( @@ -35485,7 +35512,9 @@ int main(int argc, char **argv) { vulkan.CheckRenderExecutorColorStandardTileDiscovery(); vulkan.CheckRenderExecutorColorDepthTileDiscovery(); vulkan.CheckRenderExecutorStencilBindingDiscovery(); +#endif vulkan.CheckUnifiedTextureCacheFlow(); +#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS vulkan.CheckBgra16Readback(); vulkan.CheckRasterization(false); vulkan.CheckRasterization(false, true);