Merge pull request #6486 from karanvora2599/perf/agc-stage-swept-raw-buffers

perf(agc): stage raw buffer elements a dispatch sweeps
This commit is contained in:
Adrià authored and GitHub committed 2026-10-19 04:29:08 +02:00
commit de611511de
9 files changed
+41 -18

No files matched your search

@@ -1264,6 +1264,12 @@ if(BUILD_TESTING)
set_tests_properties(agc_storage_baseline_settled PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test(NAME agc_read_written_staging COMMAND agc_driver_recorder_tests --read-written-staging-only)
set_tests_properties(agc_read_written_staging PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 30)
if(WIN32)
set_tests_properties(agc_read_written_staging PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test(NAME agc_storage_written_buffer_order COMMAND agc_driver_recorder_tests --written-buffer-order-only)
set_tests_properties(agc_storage_written_buffer_order PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 30)
if(WIN32)
@@ -422,7 +422,7 @@ private:
// `written` is the element's DescriptorBinding::bufferWritten: a read-only element binds the
// same way but is left out of the write set (no write-back, no pending-write note). `atomic`
// is its bufferAtomic (see GuestBufferMemory::AddWritable).
std::size_t addGuestBuffer(std::span<const std::uint32_t> words, const ColorTarget* target, std::uint64_t indexAddress, std::size_t indexBytes, bool written, bool atomic, bool read);
std::size_t addGuestBuffer(std::span<const std::uint32_t> words, const ColorTarget* target, std::uint64_t indexAddress, std::size_t indexBytes, bool written, bool atomic, bool read, std::uint32_t extent);
std::uint64_t guestBufferBytes(std::span<const std::uint32_t> words) const;
std::size_t addDataBuffer(std::span<const std::uint32_t> words);
// Stage A: the layout entry of an image binding (samplers are taken at once, the sampler cache
@@ -1832,7 +1832,8 @@ void ShaderResources::buildPrepare(std::span<const CompiledShader> shaders, cons
const bool written = element >= binding.bufferWritten.size() || binding.bufferWritten[element];
const bool atomic = element < binding.bufferAtomic.size() && binding.bufferAtomic[element];
const bool read = element < binding.bufferRead.size() && binding.bufferRead[element];
const auto index = addGuestBuffer(std::span<const std::uint32_t>(binding.guestDescriptor).subspan(static_cast<std::size_t>(element) * 4, 4), target, indexAddress, indexBytes, written, atomic, read);
const std::uint32_t extent = element < binding.bufferExtent.size() ? binding.bufferExtent[element] : 0u;
const auto index = addGuestBuffer(std::span<const std::uint32_t>(binding.guestDescriptor).subspan(static_cast<std::size_t>(element) * 4, 4), target, indexAddress, indexBytes, written, atomic, read, extent);
const auto& push = shader.program->pushConstants;
if (!push.empty()) {
const auto position = shader.program->memoryOffsetDword * 4u + element;
@@ -3962,7 +3963,7 @@ std::uint64_t ShaderResources::guestBufferBytes(std::span<const std::uint32_t> w
return byteSize;
}
std::size_t ShaderResources::addGuestBuffer(std::span<const std::uint32_t> words, const ColorTarget* target, std::uint64_t indexAddress, std::size_t indexBytes, bool written, bool atomic, bool read) {
std::size_t ShaderResources::addGuestBuffer(std::span<const std::uint32_t> words, const ColorTarget* target, std::uint64_t indexAddress, std::size_t indexBytes, bool written, bool atomic, bool read, std::uint32_t extent) {
Require(words.size() == 4, "buffer descriptor must contain four DWORDs");
const ShaderRecompiler::ShaderBufferResource descriptor{{words[0], words[1] & ~0x40000000u, words[2], words[3]}};
Require(descriptor.Type() == 0u, "buffer descriptor uses an unsupported type");
@@ -3980,7 +3981,8 @@ std::size_t ShaderResources::addGuestBuffer(std::span<const std::uint32_t> words
static const bool allWritten = std::getenv("APS5_ALL_BUFFERS_WRITTEN") != nullptr;
Require(!written || !overlap(address, size, indexAddress, indexBytes), "writable shader buffer aliases the index buffer");
written = written || allWritten;
const bool swept = read && dispatchThreads != 0 && descriptor.Stride() != 0 && dispatchThreads * descriptor.Stride() * 2u >= byteSize;
const std::uint64_t recordBytes = descriptor.Stride() != 0 ? descriptor.Stride() : extent;
const bool swept = read && dispatchThreads != 0 && recordBytes != 0 && dispatchThreads * recordBytes * 2u >= byteSize;
if (written) guestMemory.AddWritable(address, size, atomic, swept);
else {
guestMemory.AddReadable(address, size);
@@ -2593,11 +2593,11 @@ void resourceReadTests(const Device& device, Recorder& recorder) {
HostImportFor(context, address, bytes);
}
void readWrittenStagingTests(const Device& device, Recorder& recorder) {
bool readWrittenStagingTests(const Device& device, Recorder& recorder) {
const auto& context = device.GetContext();
if (context.hostImportAlignment == 0) {
std::cout << "host imports unavailable: read and written staging not tested\n";
return;
return false;
}
constexpr std::size_t bytes = 8u << 20u;
#ifdef _WIN32
@@ -2614,7 +2614,7 @@ void readWrittenStagingTests(const Device& device, Recorder& recorder) {
}
if (HostImportFor(context, address, bytes) == nullptr) {
std::cout << "host import of the test block refused: read and written staging not tested\n";
return;
return false;
}
const auto boundInPlace = [&](std::uint64_t element, std::size_t elementBytes, bool read) {
GuestBufferMemory memory(context);
@@ -2631,10 +2631,10 @@ void readWrittenStagingTests(const Device& device, Recorder& recorder) {
Require(!boundInPlace(address + 4096, 1u << 20u, false), "a written 1 MiB element was not staged");
Require(boundInPlace(address + 4096, 4u << 20u, false), "a written-only 4 MiB element was staged past the written window");
Require(!boundInPlace(address + 4096, 4u << 20u, true), "a read and written 4 MiB element was bound in place");
const auto buildInPlace = [&](std::uint64_t dispatchThreads, bool read) {
const auto buildInPlace = [&](std::uint64_t dispatchThreads, bool read, std::uint32_t stride, std::uint32_t extent) {
const auto element = address + 4096;
constexpr std::uint32_t stride = 16;
constexpr std::uint32_t records = (4u << 20u) / stride;
constexpr std::uint32_t elementBytes = 4u << 20u;
const std::uint32_t records = stride != 0 ? elementBytes / stride : elementBytes;
ShaderRecompiler::RecompileResult program;
ShaderRecompiler::DescriptorBinding binding;
binding.kind = ShaderRecompiler::DescriptorKind::StorageBuffer;
@@ -2645,25 +2645,30 @@ void readWrittenStagingTests(const Device& device, Recorder& recorder) {
binding.guestDescriptor = {static_cast<std::uint32_t>(element), (static_cast<std::uint32_t>(element >> 32u) & 0xffffu) | (stride << 16u), records, 0x31000000u};
binding.bufferWritten = {true};
binding.bufferRead = {read};
binding.bufferExtent = {extent};
program.bindings.push_back(binding);
const CompiledShader compute{ShaderRecompiler::ShaderStage::Compute, &program, 0};
ShaderResources resources(context, compute, {}, false, dispatchThreads);
const auto reads = resources.InPlaceReads();
const bool direct = std::any_of(reads.begin(), reads.end(), [&](const auto& range) { return range.first <= element && range.second >= element + records * stride; });
const bool direct = std::any_of(reads.begin(), reads.end(), [&](const auto& range) { return range.first <= element && range.second >= element + elementBytes; });
resources.MarkGpuWrites(recorder);
recorder.Sync();
return direct;
};
Require(!buildInPlace((4u << 20u) / 16u, true), "a dispatch sweeping a read and written 4 MiB element bound it in place");
Require(buildInPlace(1024, true), "a dispatch touching 16 KiB of a read and written 4 MiB element staged all of it");
Require(buildInPlace(0, true), "a dispatch of unknown size staged a read and written 4 MiB element");
Require(buildInPlace((4u << 20u) / 16u, false), "a dispatch sweeping a written-only 4 MiB element staged it");
Require(!buildInPlace((4u << 20u) / 16u, true, 16, 0), "a dispatch sweeping a read and written 4 MiB element bound it in place");
Require(buildInPlace(1024, true, 16, 0), "a dispatch touching 16 KiB of a read and written 4 MiB element staged all of it");
Require(buildInPlace(0, true, 16, 0), "a dispatch of unknown size staged a read and written 4 MiB element");
Require(buildInPlace((4u << 20u) / 16u, false, 16, 0), "a dispatch sweeping a written-only 4 MiB element staged it");
Require(!buildInPlace((4u << 20u) / 32u, true, 0, 32), "a dispatch sweeping a read and written 4 MiB raw element bound it in place");
Require(buildInPlace(1024, true, 0, 32), "a dispatch touching 32 KiB of a read and written 4 MiB raw element staged all of it");
Require(buildInPlace((4u << 20u) / 32u, true, 0, 0), "a raw element without an access extent was staged");
Require(std::all_of(static_cast<const std::byte*>(block), static_cast<const std::byte*>(block) + bytes, [](std::byte value) { return value == std::byte{0x11}; }), "a staged copy-back changed the guest bytes");
{
GuestAllocations::Mutation mutation;
mutation.Remove(block);
}
HostImportFor(context, address, bytes);
return true;
}
bool sparseRunImportTests(const Device& device, bool copied) {
@@ -9086,6 +9091,11 @@ int main(int argc, char** argv) {
std::cout << "Completion label storage and ordering tests passed\n";
return 0;
}
if (argc == 2 && std::string_view(argv[1]) == "--read-written-staging-only") {
if (!readWrittenStagingTests(device, recorder)) return 77;
std::cout << "read and written staging tests passed\n";
return 0;
}
if (argc == 2 && std::string_view(argv[1]) == "--written-buffer-order-only") {
if (!writtenBufferOrderTests(device, recorder)) return 77;
std::cout << "written buffer order tests passed\n";
@@ -84,7 +84,7 @@ void removeCacheDirectory(const std::filesystem::path& directory) {
}
bool sameBinding(const DescriptorBinding& left, const DescriptorBinding& right) {
return left.kind == right.kind && left.role == right.role && left.descriptorSet == right.descriptorSet && left.binding == right.binding && left.count == right.count && left.guestDescriptor == right.guestDescriptor && left.readOnly == right.readOnly && left.imageShape == right.imageShape && left.samplerDepthCompare == right.samplerDepthCompare && left.imageWritten == right.imageWritten && left.imageDepthCompare == right.imageDepthCompare && left.imageAtomic == right.imageAtomic && left.imageShapes == right.imageShapes && left.bufferAtomic == right.bufferAtomic && left.bufferWritten == right.bufferWritten && left.bufferRead == right.bufferRead && left.samplerUnnormalized == right.samplerUnnormalized && left.samplerIntegerBorder == right.samplerIntegerBorder && left.samplerCoordinatesIgnored == right.samplerCoordinatesIgnored && left.imageUnnormalized == right.imageUnnormalized && left.imageUnnormalizedBaseLevel == right.imageUnnormalizedBaseLevel && left.imageSamplers == right.imageSamplers;
return left.kind == right.kind && left.role == right.role && left.descriptorSet == right.descriptorSet && left.binding == right.binding && left.count == right.count && left.guestDescriptor == right.guestDescriptor && left.readOnly == right.readOnly && left.imageShape == right.imageShape && left.samplerDepthCompare == right.samplerDepthCompare && left.imageWritten == right.imageWritten && left.imageDepthCompare == right.imageDepthCompare && left.imageAtomic == right.imageAtomic && left.imageShapes == right.imageShapes && left.bufferAtomic == right.bufferAtomic && left.bufferWritten == right.bufferWritten && left.bufferRead == right.bufferRead && left.bufferExtent == right.bufferExtent && left.samplerUnnormalized == right.samplerUnnormalized && left.samplerIntegerBorder == right.samplerIntegerBorder && left.samplerCoordinatesIgnored == right.samplerCoordinatesIgnored && left.imageUnnormalized == right.imageUnnormalized && left.imageUnnormalizedBaseLevel == right.imageUnnormalizedBaseLevel && left.imageSamplers == right.imageSamplers;
}
bool sameBindings(const std::vector<DescriptorBinding>& left, const std::vector<DescriptorBinding>& right) {
@@ -181,6 +181,7 @@ DescriptorBinding sampleBinding(std::uint32_t seed) {
binding.imageUnnormalizedBaseLevel = {false, true, seed % 3 == 0};
binding.imageSamplers = {0x5u, 0u, 0x80000000u};
binding.bufferRead = {true, false, false, true, true, false};
binding.bufferExtent = {32, 0, 16, 4096, 8, 0};
return binding;
}
@@ -435,6 +435,7 @@ DescriptorBindingPlan DescriptorBindingBuilder::Prepare(const IrBindingLayout& l
physical.bufferAtomic.push_back(buffer.atomic);
physical.bufferWritten.push_back(buffer.written || buffer.atomic);
physical.bufferRead.push_back(buffer.read);
physical.bufferExtent.push_back(buffer.maxByteExtent);
if (buffer.written || buffer.atomic) ++entry.writtenBuffers;
else ++entry.readOnlyBuffers;
}
+1
View File
@@ -450,6 +450,7 @@ struct DescriptorBinding {
std::vector<bool> imageUnnormalizedBaseLevel;
std::vector<std::uint32_t> imageSamplers;
std::vector<bool> bufferRead;
std::vector<std::uint32_t> bufferExtent;
};
struct ImageTableEntryPoison {
+3 -1
View File
@@ -87,7 +87,7 @@ namespace ShaderRecompiler::ShaderDiskCache {
static_assert(sizeof(CompiledShaderArtifact) == 240, "CompiledShaderArtifact changed: update the artifact encoder");
static_assert(sizeof(ShaderInvocation) == 200, "ShaderInvocation changed: update the invocation encoder");
static_assert(sizeof(RecompileResult) == 472, "RecompileResult changed: update EncodeResult and DecodeResult");
static_assert(sizeof(DescriptorBinding) == 632, "DescriptorBinding changed: update the binding encoder");
static_assert(sizeof(DescriptorBinding) == 656, "DescriptorBinding changed: update the binding encoder");
static_assert(sizeof(VertexAttribute) == 32, "VertexAttribute changed: update the attribute encoder");
static_assert(sizeof(VertexInput) == 16, "VertexInput changed: update the vertex input encoder");
static_assert(sizeof(FragmentParameter) == 16, "FragmentParameter changed: update the parameter encoder");
@@ -430,6 +430,7 @@ void encodeBinding(Writer& writer, const DescriptorBinding& binding) {
writer.Flags(binding.samplerCoordinatesIgnored);
writer.Values(std::span<const std::uint32_t>(binding.imageSamplers));
writer.Flags(binding.bufferRead);
writer.Values(std::span<const std::uint32_t>(binding.bufferExtent));
}
void decodeBinding(Reader& reader, DescriptorBinding& binding) {
@@ -458,6 +459,7 @@ void decodeBinding(Reader& reader, DescriptorBinding& binding) {
reader.Flags(binding.samplerCoordinatesIgnored);
reader.Values(binding.imageSamplers);
reader.Flags(binding.bufferRead);
reader.Values(binding.bufferExtent);
}
void encodeArtifact(Writer& writer, const CompiledShaderArtifact& result) {
+1 -1
View File
@@ -13,7 +13,7 @@
namespace ShaderRecompiler::ShaderDiskCache {
inline constexpr std::uint32_t FormatVersion = 39;
inline constexpr std::uint32_t FormatVersion = 40;
enum class LoadStatus {
Loaded,