Repository navigation
Upstream sync 9 (slice C): AGC driver onto Metal - #490
Conversation
The 16-byte fill kernel has a variant that loads its pattern with s_buffer_load_dwordx4 through the V# in s[4:7] instead of taking it from the user SGPRs. A title clears its UI canvas HTILE with it every frame; run as a shader, the depth surface never cleared and every UI draw failed its GEQUAL depth test, leaving the screen black. (cherry picked from commit 3bc3e35) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
… sRGB views A sampler with FORCE_DEGAMMA decodes the texels of a non-sRGB 8-bit texture as sRGB. Such a binding was refused; it now samples an sRGB view of the same image. Snapshot images whose format has an sRGB twin (R8, R8G8, RGBA8, BGRA8, ABGR8 packed, BC1/2/3/7 UNORM) are created with MUTABLE_FORMAT, and storage images are already mutable. The view is made on first use for the full, first-layer or base-level range, with sampled usage only and the descriptor's minimum LOD. A format without an sRGB twin is still refused. (cherry picked from commit ff3eae6) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
…_SHADER APS5_SKIP_SHADER=<code address, hex> drops every draw that runs a program at that address and every dispatch of a compute program at that address, as if the packet had run. Skipping one pass at a time shows which pass first writes a wrong value into an image: One title's green sky light was bisected this way to the copy into its sky cube (boykopovar#4551). (cherry picked from commit 0900586) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
(cherry picked from commit abc997b) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
… in the shader SrgbTwinFormat mapped R8_UNORM and R8G8_UNORM to R8_SRGB and R8G8_SRGB without asking the device. Where 8_SRGB or 8_8_SRGB is decoded in the shader (context.srgbDecodeFormats: the device cannot sample the sRGB format, or APS5_SRGB_SHADER_DECODE=1), the forced view would have been created with a format the device cannot sample. Those two formats now get no twin there, so a FORCE_DEGAMMA sampler on them keeps the refusal. The image is no longer created MUTABLE_FORMAT either. (cherry picked from commit 12d07b8) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
(cherry picked from commit a141b8e) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
(cherry picked from commit e2ee943) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
(cherry picked from commit 65eb466) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
(cherry picked from commit a296b9c) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
(cherry picked from commit 3f31403) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
Create uploads before publishing snapshot validity. Keep allocation edits separate from the per-unit validity changes in boykopovar#5024. (cherry picked from commit 0612c18) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
(cherry picked from commit 0ba462f) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
(cherry picked from commit af19dbf) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
A draw whose key has no entry captured its stages, built a variant per stage (a copy of every captured region) and inserted a draw entry. A key that never comes back paid for the variants and the entry for nothing, and its entry pushed a live one out of the cache. Draws whose user data changes every frame are such keys. The driver now remembers the keys it has seen once, in a table of four-way buckets holding four times the cache's size in keys. A key gets its entry at its second sight; a key that already has an entry still takes new variants at once. A full bucket replaces its oldest key, so keys that share a bucket do not push each other out until more than four of them come between two sights of one. Test: agc_driver_draw_cache_reuse draws three times. The first two draws find no entry, one entry is inserted, and the third draw hits it. A sighting table of one bucket keeps two keys drawn in turn and replaces its oldest key when full. (cherry picked from commit 0adbdcf) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
VGT_SHADER_STAGES_EN.GS_FAST_LAUNCH = 1 (mesh-style NGG, as RADV programs it on gfx10.3) was refused. With a point list and GE_CNTL groups of one point, it launches one subgroup per input point and the first thread reads that point's index in the ES vertex id VGPR, which the mesh prologue already provides. The subgroup's primitive limit comes from GE_NGG_SUBGRP_CNTL.PRIM_AMP_FACTOR, and the GS and ES VGPR counts are not required to be full. Fast launch 2, other group sizes or inputs, and indexed fast-launch draws still throw. (cherry picked from commit 7370aaf) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
A draw with PA_SC_AA_CONFIG.MSAA_NUM_SAMPLES > 0 whose context never wrote PA_SC_AA_SAMPLE_LOCS_PIXEL_* (context 0x2fe-0x30d) was rejected with "missing register in context bank at DWORD 0x2fe", because the registers were absent from the initial context (a title stops on it after two frames). The MSAA_ENABLE single-sample path read them the same way. The sixteen registers now start at 0, both initially and after CLEAR_STATE. That is the value in libSceAgc's own register defaults (sceAgcGetRegisterDefaults2, every public table that holds context registers, the tables the initial context already follows for PA_SU_VTX_CNTL and PA_SC_CONSERVATIVE_RASTERIZATION_CNTL), in amdgpu's GFX10 clear-state table (clearstate_gfx10.h, gfx10_SECT_CONTEXT_def_8, loaded by gfx_v10_0_cp_gfx_start before CLEAR_STATE) and the GC 10.3 register reset value (gc_10_3_0_default.h). RADV, radeonsi and PAL always write the locations themselves and do not settle the unwritten value. 0 puts every sample at the pixel center. That is not the standard pattern for any sample count, so such a draw decodes as custom locations at the center, as a single-sample scan into a multisampled target already does. (cherry picked from commit 15955c0) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
(cherry picked from commit 3960519) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
sceAgcFuseShaderHalves copied both halves' code, the fused header and its tables into the caller's memory and registered the program at that address. A title fuses different halves into the same memory a few calls apart, while draws that use the earlier result are still queued. The registry keeps one program per code address, so those draws were compiled from the later program: its code read a user-data word the bound layout does not have, and the device reported "BDA access failed" for the mesh stage. The PS5 5.02 module writes only the back half's register list into that memory (sceAgcGetFusedShaderSize reports eight bytes per register, alignment 4) and leaves the code where the halves are. The fused code, header and tables now live in memory libSceAgc owns, one program per distinct pair of halves, registered once. The caller's memory receives the fused register list and the size query reports that list. A second fusion of the same halves returns the same program; halves whose contents changed get a new one. The driver looked a header up by comparing its fields with the registered header, including the register list's address. A header whose register list is a copy elsewhere with the same contents now matches too, which is what the title passes after a fusion. (cherry picked from commit d6f67d1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
…viewMip > 0 (cherry picked from commit acbc573) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
… sceAgcDcbEventWrite (cherry picked from commit fbf619e) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
The driver had no handler for the PFP INDEX_ATTRIBUTES_INDIRECT packet, so a submission with it threw "opcode is not known in the reference". The packet now reads the 16-byte record at attribute_base + attribute_index * 16 when it executes and loads INDEX_BASE, INDEX_BUFFER_SIZE and INDEX_TYPE from it as the direct packets do. The packet encoding follows PAL's BuildIndexAttributesIndirect and the record layout PAL's IndexAttribIndirect (gfx9Chip.h, c5e80007). indirect_mode 1, reserved bits and record index types above 3 throw. (cherry picked from commit dd3c13f) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
A queued store run records each group's plain stores first and its occlusion counter dumps after them. A store started a new group only when it overlapped an earlier dump, so a label the title writes after its dumps was recorded before them. A title that polls that label then reads counters from before the dumps land. Any store after a dump now starts a new group. A title read stale or zero occlusion results every other frame and left visible geometry undrawn on alternate frames. (cherry picked from commit fce8aa8) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
(cherry picked from commit 9a1ac6a) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
…eads (cherry picked from commit 7199916) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
… its image (cherry picked from commit faef399) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
SampleSliceZero cleared the cached textures while its draw's results were still pending, so the storage image was torn down with them, which ClearCachedTextures only expects at device teardown. On some runs the next Run then read the first pass's slice 0 after filling it with the initial value (3 of 20 on an RTX 4070, reported on the PR). It now flushes and waits first, as Run does before its own clear. (cherry picked from commit 7262b10) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
Upstream 23eb459 put its debt note in docs/dev/debt/libSceAgcDriver.md; the fork keeps the AGC driver notes in docs/dev/TechnicalDebt.md, so the note moves there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
anyps5_metal_render_state_tests builds the State.cpp, QueueState.hpp and Pm4.cpp the Metal backend runs. Sync9SharedDecode checks three of this batch's shared fixes there: the sample location registers start at 0, so a 4x draw that never wrote them decodes as custom locations instead of throwing for a missing register; a single-sample view of mip 1 of a fast-clear target decodes no CMASK, while mip 0 of a mipmapped target is still refused; and INDEX_ATTRIBUTES_INDIRECT loads the index base, size and type from its record and refuses an index type above 3. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
The shared State decode now accepts VGT_SHADER_STAGES_EN.GS_FAST_LAUNCH 1 with one point per subgroup (upstream 7370aaf), and upstream's Vulkan Draw refuses such a draw when it is indexed. The Metal mesh path takes the same decoded state, so it refuses indexed fast-launch draws the same way instead of running them through the indexed mesh prologue. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
… sRGB views on Metal Upstream ff3eae6 samples a texture whose format has an sRGB twin through an sRGB view of the same image when a sampler forces sRGB decoding (S# FORCE_DEGAMMA), and refuses only formats without one. The Metal binding refused every non-sRGB format. It now does the same as the Vulkan driver: the bound view (sampled or resident) is re-viewed with the sRGB twin of its pixel format (R8, RG8, RGBA8, BGRA8, BC1, BC2, BC3, BC7), keeping its levels, slices and swizzle; a format without a twin throws "which is not sRGB and has no sRGB view". Test: anyps5_metal_driver_semantics_imagesampleforcedegamma (the upstream execution test run on the Metal driver) fails on the previous commit with "guest format 56, which is not sRGB, ... not implemented" and passes here: 8_8_8_8_UNORM through FORCE_DEGAMMA samples the decoded values of 8_8_8_8_SRGB, point and bilinear, and 32_FLOAT is refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configuration
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0a8dc74ff3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| id<MTLTexture> result = [view newTextureViewWithPixelFormat:format textureType:type levels:NSMakeRange(0, view.mipmapLevelCount) | ||
| slices:NSMakeRange(0, slices) swizzle:view.swizzle]; |
There was a problem hiding this comment.
Avoid applying the parent texture swizzle twice
When FORCE_DEGAMMA is used with a non-sRGB texture whose guest descriptor has a non-identity channel mapping, view is already a swizzled child created by MetalTexture::createViews, and passing view.swizzle while creating another child composes that same mapping a second time. Metal explicitly combines a child view's swizzle with its parent's (Apple documentation), so mappings such as R/B swaps can be undone and produce incorrect sampled colors; create the sRGB child with an identity-relative swizzle so it preserves the parent's effective mapping.
Useful? React with 👍 / 👎.
…es, Metal depth retirement, null degamma images) (#498) * fix(relinker): decode scalar compares behind address-size and segment prefixes A scalar compare carrying an address-size override (67 C5 FA C2 ..., 67 F3 0F C2 ...) or with F2/F3 on either side of another legacy prefix was not decoded, so it stayed in its Rosetta block and could still lose the later load of its destination. Legacy prefixes are now read in any order, the last F2/F3 being the mandatory one; F2/F3 before a VEX escape is still not a compare. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(agc): retire depth surfaces over every MSAA fragment and every layered slice on Metal A multisampled color target now retires the depth surfaces over all of its fragments (bytes * samples), as MetalColorSurface::OverlapsMappings already measures it. Each extra slice of a layered depth draw retires the surfaces over its depth and stencil planes, not only one at exactly its depth address. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(recompiler): skip null images before FORCE_DEGAMMA validation A null T# (TYPE=0 or zero base) reads zero whatever its other words say, so a null image left with an RGBA8_UINT format under a FORCE_DEGAMMA sampler no longer fails the one-slice-volume check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(agc): pin a swizzled T# sampled through the FORCE_DEGAMMA sRGB view An R/B-swapped UNORM and sRGB texture sampled under FORCE_DEGAMMA keeps its mapping once. Measured: a Metal view of a view takes its swizzle relative to the root texture, so passing the parent's swizzle is right and an identity swizzle drops the mapping. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(agc): keep a stencil-only target's absent depth plane absent in its layered slices The extra slices of a layered draw offset the depth address even when it is zero (a stencil-only target), and the range retirement then treated that fabricated address as a depth plane. DepthArraySlice leaves an absent plane at zero. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Upstream sync 9, slice C: AGC driver onto Metal. Range
18c438b10..upstream/main, scopesagc,agc-driver,libSceAgc,libSceAgcDriver(65 commits). 26 upstream commits are cherry-picked with-x, one each. Four fork commits follow: a debt-doc move, a Metal test, and two Metal adaptations. Commit messages that named games or title IDs were reworded.Ported
On the Metal/Rosetta path (shared
State.cpp,QueueState.hpp,Pm4.cpp, libSceAgc, or adapted in Metal code):3960519b1make host imports over writable pages on macOS (the must-have). Resolved onto the fork'sImportWatched, which does not have upstream's laterstaysWatched/WindowCopypath, so only theGuestArena::HostWritearoundimport()is added. No Metal change is needed: no Metal-path source uses write watching (GuestArenaWriteWatched/BeginHostWriteappear only in the Vulkan driver and its tests), so the Metal no-copy aliases never cover read-only watched pages.15955c089the sample location registers start at their clear-state value 0 (sharedQueueState.hpp). Test list conflict resolved.acbc57321skip CMASK decode on single-sample views with viewMip > 0 (sharedState.cpp). Applied onto the fork's!multisampledcheck. The debt note goes intoTechnicalDebt.md. The Vulkan test usescmaskAddress, because the fork has nocmaskFastClearfield.dd3c13ffdexecute INDEX_ATTRIBUTES_INDIRECT (PM4 0x91) (sharedPm4.cpp). On Metal the packet takes the default path inMetalPacketExecution.mm: it is not register-only, so prior GPU work completes beforePm4::Executereads the record.MetalPacketExecution.mmis unchanged. The debt note moves toTechnicalDebt.md(ea6808d62).7370aaf6ddecode GS_FAST_LAUNCH geometry with one point per subgroup (sharedState.cpp). Metal adaptation05a477abd: the Metal mesh path refuses indexed fast-launch draws, as upstream's VulkanDrawdoes. This is one line inMetalDraw.mm.ff3eae6c9sample non-sRGB textures through FORCE_DEGAMMA samplers as sRGB views. The upstream test conflict is resolved by dropping its ANISO_OVERRIDE line (that helper is not in the fork) and using the 3-argumentRequireSame. Metal adaptation0a8dc74ffinMetalShaderResources.mm: the bound view is re-viewed with the sRGB twin of its pixel format (R8, RG8, RGBA8, BGRA8, BC1/2/3/7), keeping its levels, slices and swizzle. A format without a twin is refused.d6f67d1bekeep fused geometry programs out of the caller's memory (libSceAgcShaderFusion.cppand the VulkanShaderRegistry.cpp; the registry helpers are added beside the fork's). The fused code now lives in libSceAgc-owned host memory. On the Rosetta direct path the CPU reads it through the host page check (HostAccessible), not the GPU ranges.fbf619ef0sceAgcDcbEventWrite ignores the address on non-sample-dump events (libSceAgc).docs/dev/debt/libSceAgc.mddoes not exist in the fork, so the note goes intoTechnicalDebt.mdwith the title name and ID dropped.0ba462f8e,af19dbfabskip an indexed draw with no indices or instances. The VulkanDraw.cppkeeps the fork'sIndexReadCount. The test keeps the fork's rect-list rejection and adds the two zero-count draws. Metal already returns early for these draws (MetalDraw.mm).Vulkan driver side, carried to stay close to upstream (not built on macOS):
3bc3e35a0fill kernel that reads its pattern from a buffer090058619APS5_SKIP_SHADER(Vulkan draws and dispatches only; resolved ontoDecodeComputeProgramAddress)abc997b0drelease the cached address space on device teardown (testmain: only the teardown case added)12d07b8ed,a141b8eb6R8/R8G8 sRGB twinse2ee9430b,65eb466dd,a296b9cac,3f314030b,0612c181fCPU snapshots on demand and their fixups0adbdcf45draw key cachefce8aa8c7,9a1ac6a23queued-write groups and their ctest entry (onlyagc_queued_write_groupsregistered; the other upstream entries need tests the fork lacks)71999169a,faef399e8,7262b10aelayers of an array surface, slice-0 array target sampled as 2DSkipped
baaad78e8,8c554cc42(Upstream sync 6: shared AGC driver fixes onto Metal (slice A, first chunk) #442);d4b59ce9f(Port upstream AGC driver shared slice (U5) #475);c6a422342(Upstream PR batch 1 U8b: Metal depth, MSAA and fill-kernel correctness #484);d7818c9e5(Upstream PR batch 1 U7: tessellation and draw-state keys #483).c60b52d2d,4589edf7f,a062eda97,ed95e6618,506215722,3ddfc2395,ccd8591fb.4307e3cb5.aec5ba133,83787e692,c99c89962.MultisampleTarget, not in the fork, which has its own model):f7fd7569f,5acd68f94,3ebad9045.0a1dff2fc,d6698c904PerUseCopy:126a1e413,cf9dd4f18696d1660c6ffc99bed955b56a2b,8d8fe39eeca95f7409,d275a5881,e9249a0a4,c871b5893,c29e06c69,972ce1224,e5d65569e,fd2c300e4875ecc139e56a7c235,db4fd5f29TessellationLoweringconflict, left to the shader slice:866ed445b.Evidence
arm64 guest replay config (
-DANYPS5_METAL_UTILITIES_ONLY=ON -DANYPS5_METAL_SHADER_BRIDGE=ON -DANYPS5_BUILD_METAL_SHADER_BRIDGE_TESTS=ON -DANYPS5_METAL_GUEST_REPLAY=ON): full build, then ctest excluding the GUIanyps5_metal_presentation_replay. Before0a8dc74ff: 57/58 passed. The failure wasanyps5_metal_driver_semantics_imagesampleforcedegamma, which runs the ported upstream execution test on the Metal driver: "guest format 56, which is not sRGB, ... not implemented". After0a8dc74ff, the affected set (driver_semantics*,render_state) passed 10/10.anyps5_metal_render_stategainsSync9SharedDecode. It covers the sample-location defaults, CMASK at view mip 1 versus mip 0, and INDEX_ATTRIBUTES_INDIRECT loading and refusal. With theQueueState.hppline removed, it fails with "a sample location register does not start at its clear-state value 0".Rosetta x86_64 (
-DANYPS5_ROSETTA_LIBS=ON):rosetta_libs rosetta_engine metal_utility_tests rosetta_direct_graphics_testsbuilt, and ctestmetal_utility_tests|rosetta_direct_graphicspassed 2/2.Vulkan-device targets (
tests/Graphics.cpp,tests/Recorder.cpp,tests/Pm4.cpp, mosttests/execution/*) are not built on macOS. Their hunks are carried but not compiled or run here.Game smokes (
tools/smoke.sh <build> 300 --map, eboot lock, private image clone), same build dir:304c53c5d05a477abd(before the Metal degamma commit)0a8dc74ff(final)All three runs reached the 3D map and ran to the 300 s wall (exit 0 on SIGTERM), with 0 unresolved imports. The only unimplemented call is the same Ajm Opus mode in each. The timings are asset-open times from a shared machine running other agents. They are not a performance comparison.
No performance claims; nothing was measured for speed.
🤖 Generated with Claude Code
https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj