From 13499502c909544244151823a838aee7b77b94a5 Mon Sep 17 00:00:00 2001 From: nmzik Date: Fri, 17 Jul 2026 07:26:42 +0200 Subject: [PATCH] Support raw buffer writes to sampled textures --- src/graphics/host_gpu/renderer/bufferCache.cpp | 8 ++++---- src/graphics/host_gpu/renderer/imageInfo.h | 13 +++++++++---- tests/ShaderRecompilerComputeTests.cpp | 17 ++++++++++++++++- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/graphics/host_gpu/renderer/bufferCache.cpp b/src/graphics/host_gpu/renderer/bufferCache.cpp index 42dcec149..cc7f089c3 100644 --- a/src/graphics/host_gpu/renderer/bufferCache.cpp +++ b/src/graphics/host_gpu/renderer/bufferCache.cpp @@ -704,20 +704,20 @@ std::pair BufferCache::ObtainBuffer(CommandBuffer* com } // Cache allocations are tracker-page aligned, but byte-disjoint buffers and images may share // an edge page. Clean read-only buffer and image views may coexist; Kyty retains a hard failure - // when either cache owns newer GPU bytes. Only a formatted buffer write - // performs the image ownership transition below. + // when either cache owns newer GPU bytes. Writable buffers delegate the ownership transition + // to TextureCache, which distinguishes raw texture-data writes from formatted target paths. if (m_texture_cache->HasPageOverlap(begin, end - begin) && m_texture_cache->HasRangeOverlap(vaddr, size)) { const bool coherent_read = is_read && !is_written && !m_memory_tracker.IsRegionGpuModified(vaddr, size) && !m_texture_cache->HasGpuModifiedRangeOverlap(vaddr, size); if (!coherent_read) { - if (!is_written || !is_formatted) { + if (!is_written) { EXIT("BufferCache: unsupported buffer/image alias, addr=0x%016" PRIx64 " size=0x%016" PRIx64 " read=%d written=%d formatted=%d\n", vaddr, size, is_read, is_written, is_formatted); } - (void)m_texture_cache->InvalidateMemoryFromGPU(vaddr, size, true); + (void)m_texture_cache->InvalidateMemoryFromGPU(vaddr, size, is_formatted); } } if (is_written) { diff --git a/src/graphics/host_gpu/renderer/imageInfo.h b/src/graphics/host_gpu/renderer/imageInfo.h index 0db9d94aa..ee0086456 100644 --- a/src/graphics/host_gpu/renderer/imageInfo.h +++ b/src/graphics/host_gpu/renderer/imageInfo.h @@ -442,11 +442,16 @@ ClassifyBufferImageWrite(uint64_t buffer_address, uint64_t buffer_size, uint64_t if (!ImagePageRangesOverlap(buffer_address, buffer_size, image_address, image_size)) { return BufferImageWrite::None; } - const bool exact = buffer_address == image_address && buffer_size == image_size; - const bool page_aligned = ((buffer_address | buffer_size) & (TRACKER_PAGE_SIZE - 1)) == 0; + const bool exact = buffer_address == image_address && buffer_size == image_size; + const auto offset = buffer_address >= image_address ? buffer_address - image_address : UINT64_MAX; + const bool contained = offset <= image_size && buffer_size <= image_size - offset; + const bool buffer_page_aligned = + ((buffer_address | buffer_size) & (TRACKER_PAGE_SIZE - 1)) == 0; + const bool image_page_aligned = + ((image_address | image_size) & (TRACKER_PAGE_SIZE - 1)) == 0; switch (binding) { case BufferImageBinding::Texture: - return exact && page_aligned && !image_gpu_modified + return contained && image_page_aligned && !image_gpu_modified ? BufferImageWrite::InvalidateTexture : BufferImageWrite::Unsupported; case BufferImageBinding::VideoOut: @@ -454,7 +459,7 @@ ClassifyBufferImageWrite(uint64_t buffer_address, uint64_t buffer_size, uint64_t ? BufferImageWrite::InvalidateVideoOut : BufferImageWrite::Unsupported; case BufferImageBinding::RenderTarget: - return exact && page_aligned && buffer_formatted && image_gpu_modified + return exact && buffer_page_aligned && buffer_formatted && image_gpu_modified ? BufferImageWrite::SynchronizeRenderTarget : BufferImageWrite::Unsupported; case BufferImageBinding::Unsupported: return BufferImageWrite::Unsupported; diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index a7b6f906e..70e479a1f 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -10361,8 +10361,23 @@ void CheckBufferImageWrites() { Case{"sampled exact", sampled, sampled_size, sampled, sampled_size, BufferImageBinding::Texture, false, false, BufferImageWrite::InvalidateTexture}, - Case{"sampled partial", sampled, sampled_size - TRACKER_PAGE_SIZE, + Case{"sampled contained prefix", sampled, sampled_size - TRACKER_PAGE_SIZE, sampled, sampled_size, BufferImageBinding::Texture, false, false, + BufferImageWrite::InvalidateTexture}, + Case{"sampled contained raw", sampled + 0x23030, 0xfa0, sampled, + sampled_size, BufferImageBinding::Texture, false, false, + BufferImageWrite::InvalidateTexture}, + Case{"sampled prefix crossing", sampled - 0x10, 0x20, sampled, + sampled_size, BufferImageBinding::Texture, false, false, + BufferImageWrite::Unsupported}, + Case{"sampled suffix crossing", sampled + sampled_size - 0x10, 0x20, + sampled, sampled_size, BufferImageBinding::Texture, false, false, + BufferImageWrite::Unsupported}, + Case{"sampled contained GPU-owned", sampled + 0x23030, 0xfa0, + sampled, sampled_size, BufferImageBinding::Texture, true, false, + BufferImageWrite::Unsupported}, + Case{"sampled contained unsupported", sampled + 0x23030, 0xfa0, + sampled, sampled_size, BufferImageBinding::Unsupported, false, false, BufferImageWrite::Unsupported}, Case{"sampled unaligned", sampled + 1, sampled_size, sampled + 1, sampled_size, BufferImageBinding::Texture, false, false,