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
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,59 @@ void Run(AgcDriver::VulkanDevice& device, bool registrationModules) {
}
}

void ScalarReads(AgcDriver::VulkanDevice& device) {
constexpr std::array<std::uint32_t, 8> scalarCode{0x34020082u, 0xf4200200u, 0xfa000004u, 0xbf8cc07fu, 0x7e040208u, 0xe0701000u, 0x80010201u, 0xbf810000u};
std::array<std::uint32_t, 8> userData{};
const auto output = BufferDescriptor(Output.data(), sizeof(Output));
std::copy(output.begin(), output.end(), userData.begin() + 4);
auto request = Request(userData, device.ComputeTarget(Lanes), {});
request.shader.code = scalarCode;
request.shader.codeAddress = reinterpret_cast<std::uintptr_t>(scalarCode.data());
request.useCache = false;
std::uint32_t expected = 0;
std::memcpy(&expected, Input.data() + 4u, sizeof(expected));
for (const auto ignoredWord1 : {0u, 0x40000000u}) {
for (const auto word : {0u, 0x31016facu, 0x91b00f2eu, 0xffffffffu}) {
auto input = BufferDescriptor(Input.data(), InputBytes);
input[1] |= ignoredWord1;
input[3] = word;
std::copy(input.begin(), input.end(), userData.begin());
Output.fill(Fill);
const auto result = ShaderRecompiler::Recompile(request);
device.Dispatch(result, 1, 1, 1);
device.WaitIdle();
Require(std::all_of(Output.begin(), Output.end(), [expected](std::uint32_t value) { return value == expected; }), "scalar buffer read changed with ignored descriptor word " + Hex(word));
}
}
constexpr std::array<std::uint32_t, 13> mixedCode{0xf4200200u, 0xfa000004u, 0xbf8cc07fu, 0x34020082u, 0xe0301000u, 0x80000201u, 0xbf8c3f70u, 0xe0701000u, 0x80010201u, 0x7e060208u, 0xe0701000u, 0x80010301u, 0xbf810000u};
for (const auto code : {std::span<const std::uint32_t>(Code), std::span<const std::uint32_t>(mixedCode)}) {
request.shader.code = code;
request.shader.codeAddress = reinterpret_cast<std::uintptr_t>(code.data());
try {
static_cast<void>(ShaderRecompiler::Recompile(request));
} catch (const std::runtime_error& error) {
Require(std::string(error.what()).find("buffer descriptor uses an unsupported type") != std::string::npos, "vector buffer descriptor failed for another reason");
continue;
}
throw std::runtime_error("a vector or mixed buffer access accepted a non-buffer descriptor type");
}
for (const auto code : {std::span<const std::uint32_t>(Code), std::span<const std::uint32_t>(mixedCode)}) {
request.shader.code = code;
request.shader.codeAddress = reinterpret_cast<std::uintptr_t>(code.data());
auto input = BufferDescriptor(Input.data(), InputBytes);
input[1] |= 0x40000000u;
std::copy(input.begin(), input.end(), userData.begin());
const auto result = ShaderRecompiler::Recompile(request);
try {
device.Dispatch(result, 1, 1, 1);
} catch (const std::runtime_error& error) {
Require(std::string(error.what()).find("buffer descriptor has reserved bits set") != std::string::npos, "vector or mixed buffer descriptor failed for another reason");
continue;
}
throw std::runtime_error("a vector or mixed buffer access accepted reserved descriptor bits");
}
}

}

int main() {
Expand All @@ -102,6 +155,7 @@ int main() {
for (std::uint32_t index = 0; index < InputBytes; ++index) Input[index] = static_cast<std::uint8_t>(index * 41u + 3u + (index >> 3u));
Run(*device, false);
Run(*device, true);
ScalarReads(*device);
std::puts("buffer descriptor runtime tests passed");
return 0;
} catch (const std::exception& error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,13 @@ std::uint32_t emulatedCompareState(const ShaderInfo& info, const ResourceSnapsho

void materializeTables(const IrResourcePlan& plan, ResourceSnapshot& snapshot, const std::vector<TableResolution>& tables) {
for (std::uint32_t i = 0u; i < plan.info.buffers.size(); ++i) {
const auto& buffer = plan.info.buffers[i];
const bool scalarOnly = buffer.scalar && !buffer.written && !buffer.atomic && !buffer.formatted && buffer.imageAlias == BufferResource::NoImageAlias
&& std::none_of(plan.memoryInfo.begin(), plan.memoryInfo.end(), [i](const MemoryInfo& memory) { return memory.kind == ResourceKind::Buffer && memory.resource == i; });
if (scalarOnly) {
snapshot.buffers.at(i).dwords[1] &= ~0x40000000u;
snapshot.buffers.at(i).dwords[3] = 0u;
}
const auto decoded = decodeBufferDescriptor(snapshot.buffers.at(i));
if (decoded.Type() != 0u) throw std::runtime_error("buffer descriptor uses an unsupported type");
if (plan.stage != IrShaderStage::Compute && decoded.AddTid()) throw std::runtime_error("buffer ADD_TID is only valid for compute shaders");
Expand Down
1 change: 1 addition & 0 deletions docs/dev/debt/shader.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,4 @@
- [BDA span reads](../../../core/shader/recompiler/SpirvBackend/src/SpirvBdaRead.cpp) (shader recompiler) - `read_bda_span` is one selection: one arm calls `load_bda_span`, a `DontInline` function that holds the in-place dword loads and calls nothing, and the other arm is the per-dword byte fallback. Two other shapes of the same reads each hang a group of NVIDIA drivers, where a dispatch never finishes and the device is lost. With the loads emitted in the arm itself, a dispatch whose lanes took both arms hung on an RTX 3060 and an RTX 4060 Laptop GPU (Windows, driver 591.86) and an RTX 5060 (Linux, driver 590.48.01) ([#3291](https://github.com/boykopovar/AnyPS5/issues/3291)). With the loads and the fallback as two selections, the first under the span's condition and the second under its negation, it hung on an RTX 4070 Laptop GPU (Windows, drivers 595.97 and 596.36), an RTX 5080 (Windows, driver 610.62) and an RTX 4090 (Linux, driver 610.57.04) ([#4975](https://github.com/boykopovar/AnyPS5/issues/4975)). The current shape is measured on the RTX 4090 with driver 610.57.04 only, where it does not hang with or without SPIRV-Tools; a shape with the loads in their own `DontInline` function was reported passing on driver 591.86 in #3291. The cause inside the driver is not known.
- [SDWA source selects on `v_cvt_f32_ubyte1`, `v_cvt_f32_ubyte2` and `v_cvt_f32_ubyte3`](../../../core/shader/recompiler/RdnaDecoder/src/RdnaVectorOpDecoder.cpp) (shader recompiler) - byte N is taken from source 0 after `SRC0_SEL` and `SRC0_SEXT` select and extend it, since SDWA supplies source 0 (RDNA 2 ISA guide 6.8 and 13.3.7) and the opcode reads `S0.u[8N+7:8N]` (12.8). Not measured on hardware.
- [Flag structurization](../../../core/shader/recompiler/ControlFlow/src/Structurizer.cpp) (shader recompiler) - a graph that every structurizer attempt refuses, irreducible and non-terminating graphs included, is rebuilt with one reach flag per block and one again flag per loop (`structurizeWithFlags`): each block keeps one copy and runs behind a guard on its flag, and each strongly connected component becomes a loop whose single latch repeats it while a back edge set its flag. The output has about 8 times the input's blocks and one route variable per block, and ends in an empty Return that never runs. Only blocks that start the top-level order unconditionally run unguarded, so resource planning also sees the static paths that skip a guarded block and can plan extra descriptor candidates for them. The attempts still run first. Shared merge splitting refuses an attempt once the graph has grown past 16 times its block count at the start of splitting (at least 64 blocks); succeeding attempts grew at most 10.6 times on random graphs of up to 128 blocks, and an attempt that would succeed only beyond the budget leaves the graph to a later attempt or to this construction. Splitting that adds one block per round stays within that budget until its 4 rounds per block run out, so the attempts can still take more than 10 s (9 of 2,000 random graphs of up to 128 blocks, 63 s at most). `APS5_FORCE_STRUCTURIZER_FALLBACK=1` builds every graph this way, for tests
- [Scalar buffer descriptors](../../../core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp) use only the base address, stride and record count for resources accessed exclusively by scalar loads; their fourth word is normalized to zero, following AMD RDNA2 ISA section 7.2.2. Vector and mixed accesses retain descriptor validation. Native Vulkan readbacks cover ignored fourth-word fields on Windows/NVIDIA; their console behavior has not been measured.
Loading