diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/IndirectDraw.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/IndirectDraw.cpp index 21d38fdea8e7..addd524abd41 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/IndirectDraw.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/IndirectDraw.cpp @@ -1,6 +1,7 @@ #include "prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp" #include "prx/libSceAgcDriver/Execution/include/Driver/Diagnostics.hpp" #include "prx/libSceAgcDriver/Execution/include/Pm4.hpp" +#include "prx/libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp" #include namespace AgcDriver::DriverDetail { @@ -51,6 +52,12 @@ std::optional Driver::ClassifyIndirectDraw(const Sha if (!support.firstInstance && (indirect.instanceRule == Rule::InPlace || indirect.instanceConstant != 0)) fail(Path::FeatureGap); if ((indirect.vertexRule == Rule::Constant || indirect.instanceRule == Rule::Constant) && indirect.count > 256) fail(Path::FeatureGap); + const auto rangeBytes = indirect.RangeBytes(); + if (rangeBytes > 0) { + if (!Graphics::RegisteredReadableCovers(indirect.arguments, static_cast(rangeBytes))) fail(Path::NotImported); + if (indirect.countIndirect && !Graphics::RegisteredReadableCovers(indirect.countAddress, 4)) fail(Path::NotImported); + } + static const std::uint64_t vertexCap = [] { const char* text = std::getenv("APS5_INDIRECT_VERTEX_MIB"); return (text ? std::strtoull(text, nullptr, 10) : 64ull) << 20u; }(); for (const auto& attribute : result.vertexAttributes) { const auto stride = (attribute.resource.fields[1] >> 16u) & 0x3fffu; diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/DrawOffsetIndices.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/DrawOffsetIndices.cpp index 6754f0c84df9..ebf855c79a04 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/DrawOffsetIndices.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/DrawOffsetIndices.cpp @@ -475,6 +475,25 @@ std::string Run(Fixture& fixture) { fixture.Draw(vertex, draw); failures += fixture.Check(what + ", drawn on the GPU", bufferIndices, 0x51u, 0x62u, true); } + { + const std::string what = "indexed indirect record whose arguments are outside registered guest memory"; + const auto vertex = fixture.Vertex(PostAddCode, staleUserData); + AgcDriver::Pm4::DrawParameters draw{indexAddress, static_cast(Indices.size()), 2, 0, 0, true, 0, 0}; + auto unmapped = indexedIndirect; + unmapped.arguments = 0xdeadbeef0000ull; + draw.indirect = unmapped; + failures += Classified(fixture, what, vertex, staleUserData, draw, Path::NotImported, Rule::InPlace); + } + { + const std::string what = "indexed indirect record whose count buffer is outside registered guest memory"; + const auto vertex = fixture.Vertex(PostAddCode, staleUserData); + AgcDriver::Pm4::DrawParameters draw{indexAddress, static_cast(Indices.size()), 2, 0, 0, true, 0, 0}; + auto unmapped = indexedIndirect; + unmapped.countIndirect = true; + unmapped.countAddress = 0xdeadbeef1000ull; + draw.indirect = unmapped; + failures += Classified(fixture, what, vertex, staleUserData, draw, Path::NotImported, Rule::InPlace); + } } return failures; } diff --git a/docs/dev/debt/libSceAgcDriver.md b/docs/dev/debt/libSceAgcDriver.md index 8fbdd6a72b73..820c6bc7120c 100644 --- a/docs/dev/debt/libSceAgcDriver.md +++ b/docs/dev/debt/libSceAgcDriver.md @@ -52,6 +52,7 @@ - [Background link-time-optimized relink of library pipelines](../../../core/libs/prx/libSceAgcDriver/Execution/include/PipelineLinkOptimization.hpp) (libSceAgcDriver) is skipped on every GPU under NVIDIA's proprietary Vulkan driver (`VK_DRIVER_ID_NVIDIA_PROPRIETARY`), so those pipelines keep their fast-linked form. The optimized link loses the device when bound on an RTX 3080 (Ampere, #2866) and an RTX 4060 (Ada, #3434); an RTX 5060 and an RTX 5080 (Blackwell) on the same driver do not, so the scope is wider than the evidence. No standard Vulkan query tells the architecture apart, and the driver version that fixes it is unknown. `APS5_NO_PIPELINE_LTO` disables the relink on every driver. - [8_SRGB and 8_8_SRGB textures](../../../core/libs/prx/libSceAgcDriver/Graphics/src/TextureFormat.cpp) (libSceAgcDriver) that the device samples as R8_SRGB or R8G8_SRGB use its own decode, which need not match gfx1035: NVIDIA's R8_SRGB is one bf16 step off in 29 of 256 codes. - [WAIT_REG_MEM](../../../core/libs/prx/libSceAgcDriver/Execution/src/Driver/Synchronization/WaitMemory.cpp) reports a diagnostic after `APS5_GPU_WAIT_TIMEOUT_MS` without treating the timeout as completion. An unsatisfied wait keeps polling and checking driver failures; a missing producer can therefore leave its queue waiting indefinitely. Existing recorded-label shortcuts remain in use. +- [Indirect draw argument range validation](../../../core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/IndirectDraw.cpp) (libSceAgcDriver): an indirect draw whose argument or count buffer address is not covered by registered readable guest memory falls back to CPU-side execution (Path::NotImported); whether the console faults or drops draws on unmapped indirect argument buffers is not measured. - [COND_EXEC](../../../core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/PacketExecution.cpp) reads its condition after the earlier packets of its queue complete. Without PFP_SYNC_ME the console's graphics PFP reads it ahead of the ME, so a title that races the read sees a later value here. - A [flip inside a COND_EXEC range](../../../core/libs/prx/libSceAgcDriver/Execution/src/Driver/Packets/CommandValidation.cpp) throws: the flip is reserved with the video output when the buffer is submitted, and how the console withdraws a skipped one is unknown. - A [COND_EXEC range](../../../core/libs/prx/libSceAgcDriver/Execution/src/Driver/Queues/Submission.cpp) must hold whole packets and end inside its command buffer. A chained INDIRECT_BUFFER or a REWIND inside a range throws, as what the CP does with the skipped dwords then is unknown.