Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,7 @@ struct VulkanDevice::State {
bool demoteToHelperInvocation = false;
bool earlyFragmentSampleMaskTestBeforeSampleCounting = false;
bool robustBufferAccess2 = false;
bool exactRobustLoads = false;
bool subgroupControlBarriers = true;
bool workgroupScopeFences = true;
// VK_EXT_descriptor_indexing with non-uniform image array indexing (bindless image tables in
Expand Down Expand Up @@ -2084,6 +2085,12 @@ VulkanDevice::VulkanDevice(const PresentationWindow* window, bool compileOnly) :
state->InstanceFunction<PFN_vkGetPhysicalDeviceFeatures2>("vkGetPhysicalDeviceFeatures2")(selected, &queried);
}
state->robustBufferAccess2 = robustness2.robustBufferAccess2 == VK_TRUE && enabled.robustBufferAccess == VK_TRUE;
if (state->robustBufferAccess2 && std::getenv("APS5_NO_EXACT_ROBUST_LOADS") == nullptr) {
VkPhysicalDeviceRobustness2PropertiesEXT robustnessLimits{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_PROPERTIES_EXT};
VkPhysicalDeviceProperties2 queriedLimits{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, &robustnessLimits};
state->InstanceFunction<PFN_vkGetPhysicalDeviceProperties2>("vkGetPhysicalDeviceProperties2")(selected, &queriedLimits);
state->exactRobustLoads = robustnessLimits.robustStorageBufferAccessSizeAlignment == 1u;
}
if (robustness2.nullDescriptor == VK_TRUE || state->robustBufferAccess2) {
const bool nullDescriptor = robustness2.nullDescriptor == VK_TRUE;
robustness2 = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT};
Expand Down Expand Up @@ -3928,6 +3935,7 @@ ShaderRecompiler::SpirvTarget VulkanDevice::buildTarget() const {
target.fixedPushSlots = state->graphicsPipelineLibrary;
target.registrationModules = state->graphicsPipelineLibrary;
target.robustBufferAccess2 = state->robustBufferAccess2;
target.exactRobustLoads = state->exactRobustLoads;
target.waveLdsMemoryBarriers = !state->subgroupControlBarriers;
target.largePixelFunctionLdsBuffer = state->driverId == VK_DRIVER_ID_NVIDIA_PROPRIETARY;
target.pixelFunctionStorageDwords = state->pixelFunctionStorageDwords;
Expand Down Expand Up @@ -4056,6 +4064,7 @@ Graphics::Context VulkanDevice::buildContext() const {
context.drawIndirectCount = state->drawIndirectCount;
context.occlusionQueryPrecise = state->occlusionQueryPrecise;
context.emptyBuffer = state->emptyBuffer ? state->emptyBuffer->Handle() : VK_NULL_HANDLE;
context.exactRobustLoads = state->exactRobustLoads;
context.copiedWriters = state->copiedWriters.get();
context.functions = state->functionsReady ? &state->deviceFunctions : nullptr;
context.descriptorIndexing = state->descriptorIndexing;
Expand Down
1 change: 1 addition & 0 deletions core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ struct Context {
bool conservativeRasterization = false;
bool meshWave32 = false;
VkBuffer emptyBuffer = VK_NULL_HANDLE;
bool exactRobustLoads = false;
// The device's list of recorded dispatches whose copied written buffers await a CPU write-back
// (VulkanDevice's State::copiedWriters; the draw counterpart is DrawCopiedWriters): an indirect
// draw whose records one of them produces reads them on the CPU. Null in tests.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,7 @@ class ShaderResources {
void release() noexcept;
void prepareAddressBindings(std::span<const CompiledShader> shaders, std::span<const GuestMemorySnapshot> snapshots);
VkDescriptorBufferInfo descriptor(Allocation& allocation);
VkDescriptorBufferInfo wholeDwordRange(VkDescriptorBufferInfo info) const;
void noteReusable();
void reportDescriptorCaches() const;
// What a sampled texture was proved current against when the build (or the last full Revalidate)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ void ShaderResources::prepareAddressBindings(std::span<const CompiledShader> sha
}

VkDescriptorBufferInfo ShaderResources::descriptor(Allocation& allocation) {
if (allocation.guest) return guestMemory.Descriptor(allocation.address, allocation.size, allocation.adjustment);
if (allocation.guest) return wholeDwordRange(guestMemory.Descriptor(allocation.address, allocation.size, allocation.adjustment));
if (allocation.role == ShaderRecompiler::DescriptorRole::GuestBuffers) {
Require(context.emptyBuffer != VK_NULL_HANDLE, "no placeholder buffer for a null V#");
return {context.emptyBuffer, 0, allocation.size};
Expand All @@ -164,4 +164,9 @@ VkDescriptorBufferInfo ShaderResources::descriptor(Allocation& allocation) {
return {allocation.buffer->Handle(), 0, allocation.size};
}

VkDescriptorBufferInfo ShaderResources::wholeDwordRange(VkDescriptorBufferInfo info) const {
if (context.exactRobustLoads && info.range >= 4u) info.range &= ~VkDeviceSize{3};
return info;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5764,7 +5764,7 @@ std::shared_ptr<ShaderResources::DrawBindings> ShaderResources::PrepareDrawBindi
const auto snapshotIndex = snapshotIndices[binding.allocations[element]];
if (snapshotIndex == selected.size()) continue;
const auto& snapshot = result->snapshots[snapshotIndex];
buffers[offset + element] = {snapshot.buffer->Handle(), snapshot.offset, snapshot.bytes};
buffers[offset + element] = wholeDwordRange({snapshot.buffer->Handle(), snapshot.offset, snapshot.bytes});
}
}
}
Expand Down
38 changes: 36 additions & 2 deletions core/libs/prx/libSceAgcDriver/tests/GuardedMemoryLoads.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ struct LoadCase {
std::uint32_t components = 1;
};

std::vector<std::uint32_t> emitLoad(const LoadCase& test, bool active, std::uint32_t offset, std::uint32_t records, bool robust, bool valid = true) {
std::vector<std::uint32_t> emitLoad(const LoadCase& test, bool active, std::uint32_t offset, std::uint32_t records, bool robust, bool exact = false, bool valid = true) {
IrProgram program;
program.SetWaveSize(32);
program.Resources().stage = IrShaderStage::Compute;
Expand Down Expand Up @@ -100,6 +100,7 @@ std::vector<std::uint32_t> emitLoad(const LoadCase& test, bool active, std::uint
target.spirvVersion = 0x00010300;
target.subgroupSize = 32;
target.robustBufferAccess2 = robust;
target.exactRobustLoads = exact;
return SpirvEmitter().Emit(program, inputs, bindings, target);
}

Expand Down Expand Up @@ -280,7 +281,7 @@ std::uint32_t ldsLength(const std::vector<std::uint32_t>& words) {
}

void expectReads(const LoadCase& test, bool robust, bool active, std::uint32_t offset, std::vector<std::uint32_t> expected, std::uint32_t records = 64, std::uint32_t misalignment = 0, bool valid = true) {
const auto module = emitLoad(test, active, offset, records, robust, valid);
const auto module = emitLoad(test, active, offset, records, robust, false, valid);
const auto actual = AccessTrace(module, misalignment).Run();
if (actual != expected) {
std::string detail = std::string(robust ? "robust" : "plain") + " guest reads:";
Expand Down Expand Up @@ -352,6 +353,39 @@ int main() {
expectReads(test, true, true, 8, {2, 3}, 64, 1);
expectReads(test, true, true, 60, {Unreachable, Unreachable}, 64, 1);
});
run("exact robust loads read what the checked variant reads", [] {
const LoadCase test{ResourceKind::Buffer, IrOpcode::LoadBufferU32, IrType::U32};
const auto effective = [](std::vector<std::uint32_t> reads) {
for (auto& index : reads) if (index >= 16u) index = Unreachable;
return reads;
};
unsigned compared = 0;
for (const bool active : {false, true}) {
for (const std::uint32_t offset : {0u, 4u, 8u, 52u, 56u, 60u, 64u, 68u, 4092u}) {
for (const std::uint32_t records : {32u, 64u, 128u}) {
for (const std::uint32_t misalignment : {0u, 1u, 2u, 3u}) {
for (const bool valid : {false, true}) {
const auto checked = emitLoad(test, active, offset, records, true, false, valid);
const auto exact = emitLoad(test, active, offset, records, true, true, valid);
const auto expected = effective(AccessTrace(checked, misalignment).Run());
const auto actual = effective(AccessTrace(exact, misalignment).Run());
if (actual != expected) {
std::string detail = "exact reads differ at offset " + std::to_string(offset) + " records " + std::to_string(records) + " misalignment " + std::to_string(misalignment) + " active " + std::to_string(active) + " valid " + std::to_string(valid) + ":";
for (const auto index : actual) detail += " " + std::to_string(index);
detail += "; checked:";
for (const auto index : expected) detail += " " + std::to_string(index);
throw std::runtime_error(detail);
}
++compared;
}
}
}
}
}
require(compared == 2u * 9u * 3u * 4u * 2u, "exact robust loads: the comparison grid shrank");
const auto plain = emitLoad(test, true, 8, 64, false, true);
require(AccessTrace(plain, 0).Run() == std::vector<std::uint32_t>({2}), "exact request without robustBufferAccess2 changed the plain variant");
});
run("private scratch selection", [] {
const LoadCase test{ResourceKind::Scratch, IrOpcode::LoadAddressU32, IrType::U32};
for (const bool robust : {false, true}) {
Expand Down
1 change: 1 addition & 0 deletions core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,7 @@ void verifyKeySensitivity() {
changes("f32 denormal preservation", [](SampleRequest& sample) { sample.request.target.float32DenormPreserve = true; });
changes("spanned waves", [](SampleRequest& sample) { sample.request.target.spannedWaves = true; });
changes("robust buffer access 2", [](SampleRequest& sample) { sample.request.target.robustBufferAccess2 = true; });
changes("exact robust loads", [](SampleRequest& sample) { sample.request.target.exactRobustLoads = true; });
changes("wave LDS memory barriers", [](SampleRequest& sample) { sample.request.target.waveLdsMemoryBarriers = true; });
changes("device-scope LDS memory barriers", [](SampleRequest& sample) { sample.request.target.deviceScopeLdsMemoryBarriers = true; });
changes("the workgroup size limit", [](SampleRequest& sample) { sample.request.target.maxWorkgroupSize[2] = 128; });
Expand Down
1 change: 1 addition & 0 deletions core/shader/recompiler/CacheKey.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ class RecompileCacheKey {
append(key, value.float32FlushToZero);
append(key, value.waveLdsMemoryBarriers);
append(key, value.float32DenormPreserve);
append(key, value.exactRobustLoads);
append(key, value.fusedExactArithmetic);
append(key, value.largePixelFunctionLdsBuffer);
append(key, value.pixelFunctionStorageDwords);
Expand Down
1 change: 1 addition & 0 deletions core/shader/recompiler/Recompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,7 @@ CompiledVariant emitVariant(const RecompileRequest& request, IrProgram& program)
targetOptions.float16DenormPreserve = request.target.float16DenormPreserve;
targetOptions.spannedWaves = request.target.spannedWaves;
targetOptions.robustBufferAccess2 = request.target.robustBufferAccess2;
targetOptions.exactRobustLoads = request.target.exactRobustLoads;
targetOptions.float32FlushToZero = HardwareFloat32Flush(request);
targetOptions.float32DenormPreserve = request.target.float32DenormPreserve;
targetOptions.fusedExactArithmetic = request.target.fusedExactArithmetic;
Expand Down
1 change: 1 addition & 0 deletions core/shader/recompiler/Recompiler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ struct SpirvTarget {
bool float16DenormPreserve = false;
bool spannedWaves = false;
bool robustBufferAccess2 = false;
bool exactRobustLoads = false;
bool depthRangeRemap = false;
bool float32FlushToZero = false;
bool waveLdsMemoryBarriers = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ struct SpirvTargetOptions {
bool float16DenormPreserve = false;
bool spannedWaves = false;
bool robustBufferAccess2 = false;
bool exactRobustLoads = false;
bool float32FlushToZero = false;
bool float32DenormPreserve = false;
bool fusedExactArithmetic = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ struct SpirvEmitterState {
bool fragmentDescriptorSet = false;
bool float16DenormPreserve = false;
bool robustBufferAccess2 = false;
bool exactRobustLoads = false;
bool float32FlushToZero = false;
bool float32DenormPreserve = false;
bool fusedExactArithmetic = false;
Expand Down
1 change: 1 addition & 0 deletions core/shader/recompiler/SpirvBackend/src/SpirvEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ std::vector<std::uint32_t> SpirvEmitter::Emit(const IrProgram& program, const Sh
state.meshPerVertexLocations = target.meshPerVertexLocations;
state.float16DenormPreserve = target.float16DenormPreserve;
state.robustBufferAccess2 = target.robustBufferAccess2;
state.exactRobustLoads = target.robustBufferAccess2 && target.exactRobustLoads;
state.float32FlushToZero = target.float32FlushToZero;
state.waveLdsMemoryBarriers = target.waveLdsMemoryBarriers;
state.float32DenormPreserve = target.float32DenormPreserve;
Expand Down
17 changes: 15 additions & 2 deletions core/shader/recompiler/SpirvBackend/src/SpirvRuntimeBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,9 @@ std::uint32_t readBuffer(SpirvValueEmitContext& context, const IrValue& instruct
const auto unaligned = nonzero(state, misalignment);
const auto next = EmitAddU32(state, index, ConstantU32(state, 1u));
const auto last = Select(state, TypeU32(state), unaligned, next, index);
const auto bounds = EmitMemoryElementInBounds(state, access, last);
const auto inBounds = enable != 0u ? both(state, enable, bounds) : bounds;
const bool exact = state.exactRobustLoads && LoadsSelectable(state, access);
const auto bounds = exact ? 0u : EmitMemoryElementInBounds(state, access, last);
const auto inBounds = exact ? 0u : (enable != 0u ? both(state, enable, bounds) : bounds);
const auto load = [&](std::uint32_t word) {
const auto value = state.module.AllocateId();
const auto pointer = EmitMemoryElementPointer(state, access, word);
Expand All @@ -224,6 +225,18 @@ std::uint32_t readBuffer(SpirvValueEmitContext& context, const IrValue& instruct
return EmitAndConstant(state, value, bits == 32u ? 0xffffffffu : (1u << bits) - 1u);
};
const auto highShift = Binary(state, spv::OpISub, TypeU32(state), ConstantU32(state, 32u), shift);
if (exact) {
auto guard = enable;
if (access.kind != ResourceKind::ScalarBuffer) {
const auto nextFits = Binary(state, spv::OpLogicalOr, TypeBool(state), Unary(state, spv::OpLogicalNot, TypeBool(state), unaligned), EmitMemoryElementInBounds(state, access, next));
guard = guard != 0u ? both(state, guard, nextFits) : nextFits;
}
const auto lowIndex = guard != 0u ? GuardedElementIndex(state, access, guard, index) : index;
const auto highIndex = Select(state, TypeU32(state), guard != 0u ? both(state, guard, unaligned) : unaligned, next, lowIndex);
const auto low = Binary(state, spv::OpShiftRightLogical, TypeU32(state), load(lowIndex), shift);
const auto high = EmitValueOrZero(state, unaligned, Binary(state, spv::OpShiftLeftLogical, TypeU32(state), load(highIndex), highShift));
return combine(low, high);
}
if (!LoadsSelectable(state, access)) {
return EmitValueOrZeroIfCondition(state, inBounds, [&] {
const auto low = Binary(state, spv::OpShiftRightLogical, TypeU32(state), load(index), shift);
Expand Down
1 change: 1 addition & 0 deletions docs/dev/ENVIRONMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ The code reads environment names through `getenv`, `IsEnvironmentSet`, `kibSetti
| `APS5_NO_EMPTY_INDIRECT_SKIP` | presence | `core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/Draw.cpp` | | builds the stages of an indirect draw whose front program last took the CPU path even when none of its records draws anything, instead of reading its records first and skipping it |
| `APS5_NO_EPOCH_COLLECT_MAP` | presence | `core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp` | | drops the per-epoch map of completed collects, leaving only the fixed-size thread memo to answer a repeated collect |
| `APS5_NO_EPOCH_REVALIDATE` | presence | `core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp` | | runs every Revalidate's per-element and per-region checks instead of the epoch gate |
| `APS5_NO_EXACT_ROBUST_LOADS` | presence | `core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp` | | keeps the descriptor-range comparison in direct buffer loads on a device that zeroes past the range |
| `APS5_NO_FAST_REVALIDATE` | presence | `core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp` | | always repeats the resource lookups instead of using the fast proof |
| `APS5_NO_FILL_CLEAR` | presence | `core/libs/prx/libSceAgcDriver/Execution/src/Driver/Memory/BufferFill.cpp` | | disables the clear-through-DCC-keys path in a fill |
| `APS5_NO_FILL_HLE` | presence | `core/libs/prx/libSceAgcDriver/Execution/src/Driver/Memory/BufferFill.cpp` | | disables the fill-kernel HLE, so matching fills run as compute |
Expand Down
Loading
Loading