From 3740c233bca5f89ef744e7cfb7d7d02b0a82d848 Mon Sep 17 00:00:00 2001 From: jweidenh <38249793+jweidenh@users.noreply.github.com> Date: Sun, 11 Oct 2026 16:28:38 -0400 Subject: [PATCH] fix(agc): accept core Vulkan capabilities during compute replay --- .../Execution/src/VulkanDevice.cpp | 20 ++++++++++++++++++- .../tests/ComputePipelineReplay.cpp | 17 ++++++++++++++++ docs/dev/BUILD.md | 2 +- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp index 3c4750421..b3fdb6860 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp @@ -73,6 +73,24 @@ void require(bool condition, const char* reason) { if (!condition) throw std::runtime_error(std::string("Vulkan presentation: ") + reason); } +bool CoreVulkanCapability(std::uint32_t capability) { + switch (capability) { + case spv::CapabilityMatrix: + case spv::CapabilityShader: + case spv::CapabilityInputAttachment: + case spv::CapabilitySampled1D: + case spv::CapabilityImage1D: + case spv::CapabilitySampledBuffer: + case spv::CapabilityImageBuffer: + case spv::CapabilityImageQuery: + case spv::CapabilityDerivativeControl: + case spv::CapabilityStorageImageExtendedFormats: + return true; + default: + return false; + } +} + void CreateComputePipeline(VkDevice device, PFN_vkCreateComputePipelines create, VkPipelineCache cache, VkShaderModule module, VkPipelineLayout layout, VkPipelineCreateFlags flags, VkPipelineShaderStageCreateFlags stageFlags, const VkSpecializationInfo* specialization, std::uint32_t subgroupSize, VkPipeline* pipeline) { VkComputePipelineCreateInfo info{VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO}; info.flags = flags; @@ -722,7 +740,7 @@ ComputePipelineReplayResult VulkanDevice::ReplayComputePipeline(const ComputePip for (std::size_t cursor = 5; cursor < capture.spirv.size(); cursor += capture.spirv[cursor] >> 16u) { const auto count = capture.spirv[cursor] >> 16u; if ((capture.spirv[cursor] & 0xffffu) == spv::OpCapability) { - require(count == 2 && std::ranges::find(target.supportedCapabilities, capture.spirv[cursor + 1]) != target.supportedCapabilities.end(), "captured SPIR-V capability is not enabled"); + require(count == 2 && (CoreVulkanCapability(capture.spirv[cursor + 1]) || std::ranges::find(target.supportedCapabilities, capture.spirv[cursor + 1]) != target.supportedCapabilities.end()), "captured SPIR-V capability is not enabled"); } } #if ANYPS5_ENABLE_SPIRV_TOOLS diff --git a/core/libs/prx/libSceAgcDriver/tests/ComputePipelineReplay.cpp b/core/libs/prx/libSceAgcDriver/tests/ComputePipelineReplay.cpp index df6e3b8b5..3e1f17936 100644 --- a/core/libs/prx/libSceAgcDriver/tests/ComputePipelineReplay.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/ComputePipelineReplay.cpp @@ -38,6 +38,23 @@ int main() { std::printf("skipped, no usable Vulkan device: %s\n", error.what()); return 77; } + auto sampled1D = decoded; + sampled1D.spirv.insert(sampled1D.spirv.begin() + 7, {0x00020011u, 43u}); + CheckResult(device->ReplayComputePipeline(sampled1D)); + std::puts("compute pipeline replay: core Vulkan Sampled1D capability compiled"); + for (const auto capability : {6u, 0xffffffffu}) { + auto unsupported = decoded; + unsupported.spirv.insert(unsupported.spirv.begin() + 7, {0x00020011u, capability}); + bool refused = false; + try { + device->ReplayComputePipeline(unsupported); + } catch (const std::exception& error) { + Require(std::string_view(error.what()) == "Vulkan presentation: captured SPIR-V capability is not enabled", "unsupported capability failed outside the replay capability guard"); + refused = true; + } + Require(refused, "non-Vulkan or unknown SPIR-V capability accepted"); + } + std::puts("compute pipeline replay: non-Vulkan Kernel and unknown capabilities rejected"); const auto fresh = device->ReplayComputePipeline(decoded); CheckResult(fresh); Require(fresh.initialCacheBytes == 0, "fresh replay imported cache bytes"); diff --git a/docs/dev/BUILD.md b/docs/dev/BUILD.md index 43c74cb7d..9ad3ab0ed 100644 --- a/docs/dev/BUILD.md +++ b/docs/dev/BUILD.md @@ -101,7 +101,7 @@ build/core/libs/prx/libSceAgcDriver/agc_compute_pipeline_replay capture.aps5cp - build/core/libs/prx/libSceAgcDriver/agc_compute_pipeline_replay capture.aps5cp --cache-in pipeline.cache --output imported.json ``` -On Windows use `agc_compute_pipeline_replay.exe` and prepend `build/core/libs/libs/unpatched` to the process `PATH`. `ANYPS5_GPU` selects a device by name as for the driver. The tool uses the driver's compute pipeline creation path, without recording commands, dispatching work or importing guest memory. Compile-only devices also enable supported partially-bound, variable-count and runtime-array descriptor features to replay captured layouts; ordinary runtime devices are unchanged. Binding flags are preserved independently of update-after-bind. Unsupported features, non-highest variable bindings and counts exceeding layout support fail explicitly. Unsupported inputs and mismatched cache vendor, device or UUID fail explicitly. Output files must not already exist. +On Windows use `agc_compute_pipeline_replay.exe` and prepend `build/core/libs/libs/unpatched` to the process `PATH`. `ANYPS5_GPU` selects a device by name as for the driver. The tool uses the driver's compute pipeline creation path, without recording commands, dispatching work or importing guest memory. Compile-only devices also enable supported partially-bound, variable-count and runtime-array descriptor features to replay captured layouts; ordinary runtime devices are unchanged. Binding flags are preserved independently of update-after-bind. Replay accepts the unconditional Vulkan 1.0 capabilities listed in the pinned [Vulkan registry](https://github.com/KhronosGroup/Vulkan-Headers/blob/2fa203425eb4af9dfc6b03f97ef72b0b5bcb8350/registry/vk.xml); other capabilities must be enabled by the device target. Non-Vulkan capabilities such as Kernel and unknown capabilities fail before pipeline creation. Unsupported features, non-highest variable bindings and counts exceeding layout support fail explicitly. Unsupported inputs and mismatched cache vendor, device or UUID fail explicitly. Output files must not already exist. Each invocation creates one pipeline. With no `--cache-in`, its application pipeline cache starts empty; `--cache-in` imports a raw cache previously written by `--cache-out` (at most 64 MiB), and `--cache-out` exports the resulting cache. The title's wrapped `vk-pipelines-*.bin` format is not accepted. It does not load or save the title's usual pipeline cache. NVIDIA and other driver caches remain uncontrolled: a fresh application cache is not a cold driver cache. JSON reports the capture identity, GPU/driver identifiers, word count, cache policy and separate wall times for shader module, layouts, pipeline creation and cache work. Device initialization is outside these timings. Pipeline statistics captures and `APS5_PIPELINE_STATS` are refused because they change compilation cost.