diff --git a/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp b/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp index 17ba542ea..aa6c6ca2a 100644 --- a/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp +++ b/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp @@ -764,7 +764,10 @@ void RenderExecutor::FindBuffers(std::span stages) { size = std::min({size, uint64_t {256} * 1024 * 1024, limit}); } } - size = Libs::LibKernel::Memory::ClampRangeSize(address, size); + // Finite GPU descriptors can span sparse PRT holes; clamp only oversized/sentinel ranges. + if (size >= (uint64_t {1} << 30u) || descriptor.NumRecords() == UINT32_MAX) { + size = Libs::LibKernel::Memory::ClampRangeSize(address, size); + } source = {address, size, cache.FindBuffer(address, size)}; } } diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 22699004a..cd5275a61 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -13474,6 +13474,49 @@ public: return program; }; + { + constexpr uint64_t page = 0x10000; + constexpr uint64_t address = base - page; + constexpr u32 resident_value = 0x13572468u; + void *reservation = reinterpret_cast(address); + Require(name, "sparse descriptor reservation", + LibKernel::Memory::KernelReserveVirtualRange(&reservation, page, 0x10, page) == 0, + "leading sparse page reservation failed"); + resources.MapMemory(address, page); + LibKernel::Memory::WriteBacking(base, &resident_value, sizeof(resident_value)); + const auto program = make_buffer_program(ShaderType::Compute, {.read = true}); + ShaderRecompiler::IR::ResourceSnapshot snapshot; + auto &value = snapshot.buffers.emplace_back(); + value.dword_count = 4; + value.dwords = {static_cast(address), static_cast(address >> 32), 2u * page}; + ShaderStageRuntime runtime{&program, &snapshot}; + PreparedBindings bindings; + executor.PrepareBindings(runtime, bindings); + std::array stages{&bindings}; + executor.FindBuffers(stages); + executor.RebindBuffers(bindings); + const auto &source = bindings.buffer_sources[0]; + const auto &owner = resources.GetBufferCache().GetBuffer(source.id); + Require(name, "finite sparse descriptor extent", + source.address == address && source.size == 2u * page && + bindings.buffers[0].range == 2u * page, + "finite descriptor lost its leading hole or resident tail"); + auto output = CreateHostBuffer(name, 2u * sizeof(u32), vk::BufferUsageFlagBits::eTransferDst, {}); + const std::array copies{{ + {owner.Offset(address), 0, sizeof(u32)}, + {owner.Offset(base), sizeof(u32), sizeof(u32)}}}; + scheduler.Current().Handle().copyBuffer(owner.Handle(), output.buffer, copies.size(), copies.data()); + scheduler.Finish(); + Require(name, "finite sparse descriptor upload", + ReadBuffer(name, output, 2) == std::vector{0u, resident_value}, + "sparse descriptor upload lost zero-filled holes or resident bytes"); + DestroyBuffer(&output); + resources.UnmapMemory(address, page); + Require(name, "sparse descriptor reservation release", + LibKernel::Memory::KernelMunmap(address, page) == 0, + "leading sparse page reservation release failed"); + } + { const auto buffer_program = make_buffer_program(ShaderType::Compute, {.read = true});