Repository navigation
feat(agc): storage image access to depth planes through 2D array views - #5003
Conversation
|
Windows test (MinGW GCC 15.2, Release, Windows 10, RTX 4080 SUPER). Built on main
Not tested: no game run. The batch was built once, not per PR; nothing failed, so no bisect was needed. AI-assisted (Claude Code). |
|
Linux test on a second title: Disney Epic Mickey: Rebrushed (PPSA15657, UE4) on an RX 7600 (RADV, Mesa 25.2.8). Without this PR the title stops before the main menu on a D16 depth plane: With it, the title gets past that, reaches the menu, loads the save and plays the level. A 5-minute scripted play session ran without a throw. ctest passed 1033 of 1033. Build: main ad653c9 + #3763 #4551 #4603 #4465 #4449 #5030 + this PR. The only conflict was Not checked: whether the image is correct. On this main-based build the level renders with almost no blue channel. The same scene on an older base (main e7c70a2 plus the same Mickey PRs, without this PR) is correct, and we are bisecting AI-assisted: yes (Claude Code) |
|
A data point from Black Myth: Wukong (PPSA23226) on Windows. I cherry-picked this PR's commit onto my integration branch ( Several compute shaders in the title hit this, over three different depth surfaces (0x3043c20000, 0x3043c90000, 0x3043db0000 in other runs), always 1920x1080, format 5, mip 0, dimension 2, tile 4. I have not yet checked which remaining condition it fails: a later base slice, the stencil plane, or a layer pitch different from the depth slice size. |
|
@fdfawef324 thanks. It is most likely the stencil plane:
So AI-assisted (Claude Code). |
|
Follow-up to my PPSA15657 report above: the missing blue channel was not this PR. It was #4842 (the ancillary render-target index read as 0), and #5097 fixes it (test report: #5097 (comment)). On AI-assisted: yes (Claude Code) |
9abfd89 to
4ad6423
Compare
|
Evidence for the case this handles: saved runs from 2026-10-11/12 (before this change) stop with AI-assisted (Claude Code). |
|
Data point: Diablo II: Resurrected (PPSA03912, Linux, RTX 3080 Ti, main 0f2683c) hits the later-slice storage case this PR still refuses, right after the sampling case in #1186: A one-slice view (BASE_ARRAY=LAST_ARRAY=1) of a D16 array. Taking the depth surface at |
|
Follow-up to my Wukong data point above. That view turned out to be a one-layer 2D array view of the stencil plane: guest format 5 (8_UINT), This PR now conflicts with
|
|
@fdfawef324 thanks for confirming. The PR is already rebased on AI-assisted (Claude Code). |
|
Resident Evil Village (PPSA01556, 01.210.000) needs this too. It hits it while loading its first scene, on a local branch past its earlier stops. A compute shader stores to a 1024x1024 R32 2D-array storage view over the game's shadow depth surfaces (guest format 22, With this PR merged on that branch, that dispatch passes. A second compute shader then stores through a one-slice view that starts at slice 1 of the same array, which this PR still refuses: The small change below, on top of yours, gets the title past it. Together with local changes for other stops, it reaches its title screen. Why it seems enough:
One thing I haven't settled: two views with different base slices over the same array now get separate entries, and each copies every slice in and out. If both are written in one frame, the order of their copy-outs could matter. There is no test for this yet. Setup: Windows 11, WinLibs GCC 15.2.0 r7, Release, RTX 3070 Laptop. Change on top of this PR (DepthSurface.cpp)diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
index c18de16f4..4ea99be84 100644
--- a/core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
+++ b/core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
@@ -520,8 +520,8 @@ private:
}
};
-std::map<std::tuple<VkDevice, std::uint64_t, std::uint32_t, std::uint32_t, std::uint32_t, std::uint32_t, int, int>, std::unique_ptr<LayeredDepthStorage>>& layeredStorages() {
- static auto* storages = new std::map<std::tuple<VkDevice, std::uint64_t, std::uint32_t, std::uint32_t, std::uint32_t, std::uint32_t, int, int>, std::unique_ptr<LayeredDepthStorage>>();
+std::map<std::tuple<VkDevice, std::uint64_t, std::uint32_t, std::uint32_t, std::uint32_t, std::uint32_t, int, int, std::uint32_t>, std::unique_ptr<LayeredDepthStorage>>& layeredStorages() {
+ static auto* storages = new std::map<std::tuple<VkDevice, std::uint64_t, std::uint32_t, std::uint32_t, std::uint32_t, std::uint32_t, int, int, std::uint32_t>, std::unique_ptr<LayeredDepthStorage>>();
return *storages;
}
@@ -788,11 +788,6 @@ std::shared_ptr<StorageTexture> layeredDepthStorage(const Context& context, Dept
const auto layers = resource.depthOrLastArray + 1u;
const bool d16 = target.format == VK_FORMAT_D16_UNORM || target.format == VK_FORMAT_D16_UNORM_S8_UINT;
const auto bytesPerTexel = d16 ? 2u : 4u;
- if (resource.baseArray != 0) {
- char text[256];
- std::snprintf(text, sizeof(text), "AGC graphics: storage image access to the depth planes of depth surface 0x%llx through a 2D array view of slices %u-%u is not implemented", static_cast<unsigned long long>(target.address), resource.baseArray, resource.depthOrLastArray);
- throw std::runtime_error(text);
- }
surface.RequireStorable(resource, mip);
const auto geometry = DescribeSurface(resource);
if (layers > 1 && geometry.GuestLayerOffset(1) != DepthSliceBytes(target.extent, bytesPerTexel)) {
@@ -800,7 +795,7 @@ std::shared_ptr<StorageTexture> layeredDepthStorage(const Context& context, Dept
std::snprintf(text, sizeof(text), "AGC graphics: storage image access to the depth planes of depth surface 0x%llx with a layer pitch of 0x%llx bytes, not the depth slice size 0x%llx, is not implemented", static_cast<unsigned long long>(target.address), static_cast<unsigned long long>(geometry.GuestLayerOffset(1)), static_cast<unsigned long long>(DepthSliceBytes(target.extent, bytesPerTexel)));
throw std::runtime_error(text);
}
- auto& entry = layeredStorages()[{context.device, resource.baseAddress, resource.width, resource.height, layers, resource.format, static_cast<int>(resource.tileMode), static_cast<int>(resource.dimension)}];
+ auto& entry = layeredStorages()[{context.device, resource.baseAddress, resource.width, resource.height, layers, resource.format, static_cast<int>(resource.tileMode), static_cast<int>(resource.dimension), resource.baseArray}];
if (entry == nullptr) entry = std::make_unique<LayeredDepthStorage>(context, resource, target, bytesPerTexel, layers);
else static_cast<void>(entry->storage->Refresh());
const auto slices = layeredSlices(context, *entry);This PR conflicts with current |
4ad6423 to
bfe9c4a
Compare
|
@EagleBearerYS thanks, that is the second title after @mrfelek's Diablo II: Resurrected that needs a view from a later slice, so it is clearly wanted. I would keep it out of this PR and take it as the follow-up @mrfelek offered (https://github.com/mrfelek/AnyPS5/tree/pr/depth-storage-slice-view), so the two of you can compare approaches there. The concern you raise is real with a
So the follow-up probably needs one image per array that both views share, with a view per base slice, rather than one image per view. AI-assisted (Claude Code). |
|
HOT WHEELS Infinite Rush (PPSA22408, UE) data point, Windows 11, RTX 3080 (610.74). Without this PR (on AI-assisted (Claude Code); I ran the above on my machine. |
|
Follow-up for the later-slice case, rebased on top of this PR's head: https://github.com/mrfelek/AnyPS5/tree/pr/depth-storage-slice-view-v2 (one commit, 3d2c808). It drops the refusal for a view whose base slice is above 0. The layered image already spans slices 0..last and the view starts at its base slice, so the change only copies the slices from the base slice to the last in and out, and adds the base slice to the cache key. DepthStorageDispatch stores through a view from base slice 2 and checks the other slices stay untouched; it fails without the change with the "through a 2D array view of slices 2-2 is not implemented" refusal. DepthStorage's old refusal case now pins the layer-pitch refusal for a later-slice view. agc_driver_depth_storage, depth_storage_reuse, depth_plane_sampling, graphics and depth_storage_dispatch pass; check_conventions passes. It is not the single shared image per array suggested above: each base slice still gets its own image. Views that are written before a flush and share a slice can still overwrite each other (the later copy-out wins); the debt entry says so. Disjoint views, and views written one after the other with a flush in between, are fine. Game check: Diablo II: Resurrected (PPSA03912, Linux, RTX 3080 Ti, NVIDIA 615.71.09) on current main plus #1186, #5205 and this branch gets from the main menu through Offline > New Character into the Rogue Encampment and ran 5+ minutes with no abort. It stops at the later-slice storage refusal on main without it. I will open a PR for this once this one is in, if it is still wanted. AI-assisted; I reviewed the change and ran the tests and the title. |
|
One more data point: Beast of Reincarnation (PPSA29343, Unreal Engine), Linux, RTX 3050 Laptop (NVIDIA 615.78.08). On The view is a 6-slice 2D array (base slice 0, last slice 5) over a 32x32 D16 surface in Z_64KB_X: slice 0 is the live depth surface and slices 1-5 are guest memory with no surface at their addresses (16384 linear bytes and 64 KiB in memory per slice). With this PR (bfe9c4a) the title reaches its menu and draws it correctly. |
|
Another title this unblocks: High on Life (PPSA15388). On With this PR ( Tested and written by Claude (AI assistant) on a contributor's machine. |
A 2D array storage view of a depth plane from slice 0 threw. It now gets a storage image over all its slices: each slice with a depth surface at the plane's address plus a whole depth slice per layer is copied from that surface before every use and back after the work; the other slices hold guest memory like any storage image. Cubes, stencil arrays, later base slices and layer pitches other than the depth slice size still throw. GTA V (PPSA04264) stops at compute shader 0x242d4a200 on a 5-slice R32 Z_64KB_X array over its shadow depth surfaces. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V1W4jdNSKTV7xwuS6xQPJV
bfe9c4a to
696d166
Compare
|
Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.
|
What
A storage view of a depth plane as a 2D array (base slice 0, level 0, the plane's extent and format) used to throw
storage image access to depth/stencil surface ... is not implemented (... dimension=2 ...). It now gets a storage image over all its slices:DepthSliceBytes, the same slice addressing as the sampled depth plane copies) is copied from that surface before every use. After the work it is copied back to the surface. Both directions go through a staging buffer.Still refused, with their current messages:
The debt entry says what is supported and what still throws.
Why
GTA V (PPSA04264) stops on current
mainin Story mode at compute shader0x242d4a200:Its storage image is a 5-slice
R322D array inZ_64KB_Xover its shadow depth surfaces (layers=5 base=0 dim=2 tile=4). The 2D path from #4500 can't serve it, and since 889e5d4 it throws rather than read stale guest bytes.Related: #2076 (now in
main) takes a one-layer 2D array view from slice 0 as the plane, for both planes; such views still go that way. This change covers the depth plane views with more slices. Since a two-layer view or one from slice 1 no longer reaches the 2D path, theagc_driver_depth_storagerefusals #2076 added for them now expect this path's messages (layer pitch, later slice). #1368 is about sampling depth arrays, not storage views.Tested
Windows 11, RTX 4090, MinGW-w64 GCC 15.2.0 (WinLibs r7), Release, on
main665ad7d.agc_driver_depth_storage_dispatch: the array-view case replaces the old refusal. It uses two depth surfaces on slices 0 and 1 and a guest-only slice 2 (Z_64KB_X, 64x32, 32-bit float). The dispatch stores through a 3-slice 2D array view into slice 1, then into slice 2. The test then checks:On
mainit fails with the GTA message above:storage image access to depth/stencil surface 0x... is not implemented (storage=64x32 guest format=22 mip=0 dimension=2 tile=4). With the change it passes.ctest -R "agc_driver_depth": 9/9 pass.ctest -R "agc_driver|agc_masked|agc_shader": 386/386 pass (agc_driver_emulated_color_compareincluded).tools/check_conventions.py --base upstream/mainpasses.In game: GTA V on our test stack (
main37dd483 plus feat(agc): implement image and sampler tables indexed at runtime offsets #757 and the other open PRs it needs, our previous local revert of 889e5d4 dropped). With this commit,0x242d4a200dispatches and the Story prologue renders correctly for 150 s.Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file.mdfiles or images added to the repository (attach them to this PR)🤖 Generated with Claude Code
https://claude.ai/code/session_01V1W4jdNSKTV7xwuS6xQPJV