feat(vk): bind segment-straddling buffers and texel views as piece arrays, and split transfers at segment cuts, on macOS #663
No reviewers
Labels
No labels
area/accessibility
area/audio
area/input
area/install
area/menu
area/renderer
area/saves
area/upscaling
beanstalk: approved @ 1697603
beanstalk: approved @ 2c5a62e
beanstalk: approved @ 4fd82b7
beanstalk: approved @ 64d6d09
beanstalk: approved @ 6f91fae
beanstalk: approved @ 70a78f9
beanstalk: approved @ 8015c9a
beanstalk: approved @ 8d361dc
beanstalk: approved @ 9d8356e
beanstalk: approved @ abf2227
beanstalk: approved @ d82f202
beanstalk: approved @ e5f1e40
beanstalk: approved @ ee8ab5f
ci: approved
good first issue
gpu/amd
gpu/igpu
gpu/intel
gpu/nvidia
help wanted
kind
bug
kind
chore
kind
docs
kind
feature
kind
performance
kind
question
needs measurements
platform/linux
platform/macos
platform/steam-deck
platform/windows
preview
priority
critical
priority
high
priority
low
regression
status
blocked
status
confirmed
status
duplicate
status
invalid
status
needs-info
status
wontfix
testing wanted
ci: approved
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Longshot!663
Loading…
Reference in a new issue
No description provided.
Delete branch "mooki/Longshot:mac/seg-consumers"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Type: feat · Risk: medium · Tested on: macOS 26.7.1, Apple M4 Max (MoltenVK 1.4.2), game-free at this head; GPU probes on both CPU routes (history) · Needs: Windows, Linux, macOS game run
Why
On a Mac, one GPU memory can hold at most about 41.7 GB, and the game's memory window is larger. #658 imports a guest range
that one operation needs and that is larger than that cap as several adjacent pieces (segments). Until now, anything that
crosses a cut between two segments could not be bound: a shader's storage buffer or texel buffer, a draw's vertices,
indices or indirect records, a uniform buffer, a buffer copy, fill or inline write, a texture upload's source. It was left
out, so the GPU read nothing there and the frame lost that work. This PR binds and moves those ranges piece by piece on
macOS, through the contract #662 froze, and gives MoltenVK stand-ins where a descriptor is absent and Metal would otherwise
read garbage. Other systems never make a segment, so nothing changes for them. It is three of the macOS port's topics in one
PR, as the owner decided (TexelDesc, SegBind, SegXfer: they share the arena and the descriptor build). It is part of the
macOS port. See #420.
What changed
Every path below starts in the failure branch of the consumer's own
gpu_buffer_bind/gpu_buffer(the range is not onebuffer) and tests
gpu_span_any()first, which is false for the whole run off macOS; the stand-ins are on only on Applewithout
nullDescriptor. Off macOS every descriptor write, layout, push range, command and SPIR-V module is what it was,which the fixtures check against this PR's base.
gpu/vk/vk_texture.{h,cpp},gpu/vk/null_dummies.hnew):texel_buffer_view(t, v, storage, plan)makes #649's carrier views (R8/R16/R32 UINT over the same bytes, whole-texelranges, the
maxTexelBufferElements× element clamp), through one cache helpertexel_view_locked;texel_zero_view(16 bytes of a zero buffer).
texel_view_pieces/texel_piece_views(#662's stubs filled): piece views on each segment's buffer, cut at thedomain's cuts and, on Apple, at runs of
maxTexelBufferElements; all or nothing, refusals counted and logged.nullDescriptor:null_dummies_make()makes 35 1×1 images, 66 views, 18 texelzero views and 4 samplers at start-up, each checked with the format query first; absent texel, sampled and storage-image
descriptors take the stand-in of their class and format, and a presence bit in
Program::presence_words, pushed atoffset 88 (
push_presence).gpu/vk/vk_draw.cpp):draw()anddispatch()pick the segmented variant (seg_sig_of, #648'sprogram_segmented), compiled synchronously; one that does not translate, or a descriptor in more than 255 pieces,leaves the operation out, logged and counted.
ensure_layoutand the descriptor counts take piece counts (seg_layout_count); push range88 + 4 × presence_words.build_descriptors: storage piece arrays (single, tablek·P+j, the prefix rule),span_unused_piecefor unused slots(a 1-byte range of a 64 KiB
null_storagebuffer); unbound uniform buffers on Apple take 64 KiB ofzero_buffer.120 idle recordings; a full arena is a loud skip) between two full barriers outside any pass: uniform buffers, vertex
streams, indices, indirect and mesh records, the dispatch grid. Gathered or stale piece sets are never reused.
pipeline_noteand the compute notes skip segmented variants.gpu/vk/span_xfer.hnew, pure arithmetic):gpu_write(onevkCmdUpdateBufferper piece),buffer-to-buffer copies (regions wherever either side's piece ends) and fills in
vk_replay.cpp; buffer↔image copiesthrough the arena in
vk_xfer.cpp; the GPU unswizzle's source invk_texture.cpp; the temporal actor's poses, verticesand indices in
vk_temporal.cpp; the draw statistics' and dumps' readers, the mesh indirect convert and the fallback'sinput in
vk_draw.cpp.docs/segmented-binding.md(one section per consumer),docs/texel-buffer-formats.md,docs/texel-buffer-ownership.md.test_texel_views_plan,test_null_dummies,test_null_descriptors,test_null_layout_push,test_seg_bind,test_seg_gathers,test_seg_notes,test_seg_xfer; extended:test_texel_buffer_ownership,test_vulkan_copy_span,test_vulkan_copy_span_gpu_compile,test_draw_side_effects,test_desc_counts,test_push_state,test_vertex_bind,test_set_for_tables,test_resolver_coverage(SITES_BIND,SITES_XFER,SITES_TEXEL); anchors and stubs that the change moved in nine other checks.6c9edffe(own commits at the end, below): #252's compute-variant path indispatch()names SegBind'spre-seam
plain; the fixtures follow #162'srec::OwnSlot, #59's actor motion and #329's ordered inline writes.Measurements
Verdict: passes. Every consumer's fixture passes at this head on both CPU routes and fails on this PR's base; off macOS
(and on macOS with no domain) every recorded command, descriptor write and push is byte for byte the base's. On the GPU
(history) MoltenVK read and wrote exactly the modelled bytes in all three probes on both routes, and this head's fixtures
write those probes' inputs byte for byte. No frame-time measurement: no frame on this Mac reaches a segmented domain before
the out-of-memory blocker of the game runs is fixed, and other systems run none of this code.
Hardware: macOS 26.7.1, Apple M4 Max, 64 GB, Apple clang 21 (game-free; the GPU probes ran on the same Mac earlier)
Scene: none (game-free fixtures over the real functions, and frozen GPU probe packets)
Method: each consumer's test at this head (positive) and with
--sourcean archive of this PR's base (negative), arm64natively and x86_64 under Rosetta, through
tools/slot.pywith a clean environment; the common-path comparisons take thebase archive. One run per route: every check is an exact comparison.
How to reproduce:
python -I -B tests/test_seg_bind.py --cxx clang++ --headers <Vulkan-Headers/include> [--arch x86_64] [--base <tree>] [--source <tree>], the same for the other tests named below.Baseline: the "before" column is this PR's base (#662 with #643, #644 and #645 merged), because the consumers fill #662's
contract and main has no segmented domain to compare with.
f4c4ddb6b8579f1atest_texel_buffer_ownership,test_texel_views_plan,test_null_dummies,test_null_descriptors,test_null_layout_pushnullDescriptor: 6 and 22 writes and the 88-byte push byte-identical to the base'stest_seg_bind,test_seg_gathers,test_seg_notes,test_draw_side_effects,test_desc_counts,test_push_state,test_vertex_bind,test_set_for_tablestest_set_for_tablesx86_64: see Testing)test_seg_xfer,test_vulkan_copy_spantest_resolver_coveragetexel-views,texel-pieces,null-dummies,null-push,seg-bind-layout,seg-gathers,seg-xfer,seg-b2t, arm E)(The fixtures still name the base "B2", the topics' branch point; here it is this PR's base archive.)
History, GPU (MoltenVK 1.4.2, M4 Max; arm64 and x86_64 under Rosetta, each route run once):
reports/2026-11-14-texel-desc.md): 186 of 186 data checks on both routes, carrier views and codec SPIR-Vexact (TD1), pushed texel sizes right (TD2), stand-ins read as zero (TD3, TD4), presence words (TD6). Its four gate
deviations were the push layout of TD5, Metal's 8 read_write textures per stage, which #645 fixes in
layout_pushes.reports/2026-11-14-segbind.md): 61 of 61 data checks, both gates MATCH in the probe log,status=PASSonboth routes (the driver's
pass=0is a parse defect in the packet: a GATE line with spaces in its evidence). OnKosmicKrisp (arm64,
reports/2026-11-14-kosmickrisp.md, kk02): 29 of 29 predictions.reports/2026-11-14-seg-xfer.md): 20 of 20 data checks,pass=1, 0[mvk-error], on both routes.this head's fixtures write all of them byte for byte (table above; arm E module
arm-e.spvtoo).Flags and settings
None in this PR's own commits. The diff against main also shows the switches of the PRs it stacks on, theirs to list:
HOS_KEEP_FREED_PAGES,HOS_ZERO_ALL_PAGES,RX_NVN_WATCH_AFTER,RX_NVN_WATCH_ALL,RX_NVN_WATCH_TEXELS_SRC(#480),RX_VK_LIBRARY(#438),RX_VK_DLSS_ACTORS,RX_VK_GPU_DUMP,RX_VK_MESH_IND_COUNT_WORD,RX_VK_NO_UPSCALE_STUB(#658),RX_VK_TEXEL_CODECS(#649),RX_VK_SET_FOR_TABLES(#643) andRX_VK_SET_FOR_LENGTHS(#644).Testing
Tested: on macOS 26.7.1, Apple M4 Max, at
b8579f1a, this PR's head before the rebases ontoe090cc48andaa95d35f(main
6c9edffe, #658's and #649's stacks, #662, #643/#644/#645, this PR), game-free, every run under the shared checkslot with a fresh HOME and TMPDIR:
x86_64 are left out for
test_set_for_tables,test_null_descriptorsandtest_null_layout_push(the owner's rule: theendpoint agent killed those fixtures before), and
test_set_for_tables's x86_64 positive was killed in this lane'sfirst run (exit -9, no crash report) and not retried; its 145 layouts passed on arm64.
presence-map.jsonfromtest_null_descriptorsequals TexelDesc's and NullShader's byte for byte.test_program_cache_one_variant,test_intel_compiler_gate,test_intel_quick_compute,test_seg_notes(the checksof #252's dispatch path),
test_inline_write_order(#329) andtest_set_for_lengthspass. The two program-cache testscannot compile their fixture on this Mac on main either (
POSIX_FADV_WILLNEED); with #476's two Darwin hunks in scratchcopies they pass at the base and at this stack's first build, on both routes (a).
tools/ls-suite.pyby name): 211 pass. The 14 others fail onmain
6c9edffetoo: 10 in main's own macOS suite (test_crash_record,test_longshot_ctl,test_pcache_header_read,test_pipeline_cache_save,test_pipeline_play_cache,test_pipeline_tour,test_program_cache_cleanup,test_program_cache_one_variant,test_readback_late,test_walk_key), and 4 that main's suite skips here and thatfail when named (
nx2/test_bc_storage_views,test_precompile_compute_guard,test_present_aspect_vulkan,test_shader_module_guard: a Windows device or the Vulkan loader).vk_draw.cpp,vk_texture.cpp,vk_temporal.cpp,vk_replay.cpp,vk_xfer.cpp,vk_memory.cpp,vk_shader.cppwith main's renderer flags, arm64 and x86_64, at the base and at this stack's first build (a): 14 of 14each. Every tree got #492's two Darwin fixes and a compile-only prelude declaring
posix_fallocateandposix_fadvise(#476 gives them Darwin paths).
gpu/,nx2/andhost/arebyte-identical between the two builds (the rebuild changed
tests/test_split_ub_flush.pyonly), so these runs and theprobe-input comparison (at the first build) hold here; the consumer checks and the named tests above ran at this head.
full suite, each with no new failure.
reports/PubWave-files/PubSeg/(consumers-*/,related-*/,objects-*.out,proof/fadv-*.log,probe-inputs-*/,offapple-consumers-*.txt).aa95d35f, at5400f1a0(b8579f1abefore the rebases ontoe090cc48andaa95d35f):Sweep3's checks of this head (the C/C++ it changes compiled, and its own tests run, on arm64 and x86_64 where they
apply) gave the same results as before;
python -I -B tests/run_all.pyon wave B's mergedb0e2e16, which carriesthis head: 569/32/18 (passed/failed/skipped) against main aa95d35f's 543/33/17 on this Mac, no new failure
(
reports/2026-11-15-rebase-sweep-3.md).78ec1c47, which carries every MacPR through #708 and the I41 fixes: a start from the field-dawn save to a 1,197 s hold in play, the runner's 12 of 12
checks passed, 0 skipped draws in the hold, 0 XPC errors, a clean exit (
reports/2026-11-15-mac-perf-E9.md); x86_64under Rosetta 2: run #34 on I38
3f872b0c: 16 m 52 s of play, 19 of 19 checks, 0 skipped draws, 1,906 draws a frame,no device loss, a clean exit (
reports/2026-11-14-mac-startup-34-x86_64.md).Not tested: the game: no game run reaches a segmented domain yet (the GPU runs out of memory in the first scene load
before that,
reports/2026-11-14-*game runs), and per-PR game runs are not planned. The arena's growth and give-back undera real frame. Windows and Linux: not built on Windows or Linux (6 changed renderer files and the 2 new headers they include
differ from the base's off Apple too,
unifdef -U__APPLE__); its test request covers it.Must be tested:
5400f1a0, it carries #662, #643, #644 and #645 with the PRs they stackon) and main
aa95d35fthe same way and start each to the title, and runpython -I -B tests/run_all.pyon Linux,expecting the build to compile this PR's files on every platform (each differs from the base's with every
__APPLE__branch resolved off:
gpu/vk/vk_draw.cpp,vk_replay.cpp,vk_temporal.cpp,vk_texture.{h,cpp},vk_xfer.cpp, andthe new
null_dummies.handspan_xfer.hthey include), the new tests to pass, no shader preparation from this PR (itstranslator stamp is its base's), the stacked PRs' own lines (#649's
vk: texel codecs: off (…), #643/#644'svk: vendor fixesfields), and every other line to match main's (by hand: test request T-MVDX)across a segment's cutrefusal lines and no missing geometry where a draw reads across a cut (by hand: the owner, thelog's
vk:lines and screenshots).Risks
What could break: on macOS, the descriptor build, the layouts' counts and push range, and the variant choice in
draw()/dispatch()for programs that touch a segmented domain; the arena's memory (from 4 MiB per recording slot, grownon demand). On other systems: one
gpu_span_any()test (a relaxed load) in each consumer's failure branch and one flagcheck per draw; descriptor writes, layouts, pushes and commands are the base's.
Turning it off: revert the commit (no switch: off macOS it never runs).
Compatibility: nothing changes for a player off macOS. The translator stamp (
dc271e7fc99bb36c) and the program cacheformat are unchanged, so no shader cache is rebuilt. On macOS the stand-ins take about a hundred small Vulkan objects at
start-up. Nothing changes in saves,
longshot_options.inior the vk ABI (MKWVK_API_VERSION).Reviewer notes
f4c4ddb6..b8579f1a(this PR's own commits, after the merge of #645): TexelDesc's two commits, SegBind's six(its first, a
wip:commit on the topic branch, reworded; content the topic's), SegXfer's one, then four commits thatfollow main. Read
docs/segmented-binding.mdfirst, thenseg_sig_of,build_descriptorsand the arena invk_draw.cpp, thentexel_piece_viewsandnull_dummies_makeinvk_texture.cpp, thenspan_xfer.h.were (
-xlines name them).fixes:
layout_pushescounts SegBind's pieces). This branch is #662's head with #645's head merged (no conflicts); untilthey merge, this PR's diff shows their commits too.
6c9edffe, resolved in the picks and followed in the last four commits:gpu/vk/vk_draw.cpp(3 blocks) andtests/test_program_cache_one_variant.py(2): main's #252 compute-variant path indispatch()against SegBind's rename of the pre-seam program toplain. The pick kept #252's lines as main has them(so that pick alone does not compile
vk_draw.cpp); the commit "dispatch()'s compute-variant path (#252) names SegBind'spre-seam
plain" renames them and follows the rename intest_program_cache_one_variant,test_intel_compiler_gateand
test_seg_notes. The notes take SegBind's guard; #252's owed optimized build and its bind stay onp.gpu/vk/vk_temporal.cpp(1 block): main's #59 (actor motion pairs copies by place) against SegXfer's per-piece posecopy; the copy across a cut sits in #59's copy-from-memory branch, the kept pose takes #59's
placed/place. Thefixtures and resolver sites follow in "SegXfer's fixture and resolver sites follow #59's actor motion".
tests/test_texel_buffer_ownership.py(1 block): main's #324 reworded the eviction comment the cut ends at.rec::OwnSlotindraw_begin_recording(seg_gathers_test.cpp), #329'sbarrier before each inline update (
test_seg_xfer's recorded common digest,test_inline_write_order's stubs).tests/test_set_for_tables.py(3 blocks) in SegBind's picks, both sides kept, as in the macOSintegration (Integration 17); #646 (
mac/mvk-pushlimits, after this PR) will meet the same file: three blocks, keep both.mac/precompile-stall, beside this PR):gpu/vk/vk_draw.cpp, one block indispatch()'s synchronous computecompile, before the seam: #652's
const Tagged tagged(compute_tag(p));and&p.compute_pipelineagainst this PR'splain. Whichever lands second takes #652's tag withplain(compute_tag(plain),&plain.compute_pipeline); #652'sother
compute_tag(p)/graphics_tag(p)lines are outsidedraw()anddispatch()and merge as they are. Thebranches of other lanes built on #652 meet the same block.
#652 against #643/#644/#645 in
vk_main.cpp,vk_progcache.cpp,vk_shader.h,vk_vendor.h(the plan's §6.2, thevk: vendor fixesline). With #437, #476, #479, #492 and the other wave branches on main: none (git merge-tree).reports/2026-11-14-*.md(the port's design notes), which are not in this repository.Issues
See #420
Checklist
Everyone
type(scope): description) of the status line's type.main: onaa95d35f(#761, 2026-11-15), head5400f1a0, on #645b8b1ba99, on #6620401eeb2, without a conflict; this PR's own commits, old → new:f4c4ddb6→918e6214,230a9158→da741bdb,4b6360a0→39d6490d,0b79711a→7d5d5127,57093580→7104d832,5a3a795d→31d52778,c715168e→4462202f,e37c097d→0f743f0b,264a578d→3b5f3b47,0a056baf→3200ff46,a997b362→c13540fc,50f37433→8089dc40,8d0541ef→1ab6beb2,b8579f1a→5400f1a0; the commit names elsewhere in thisdescription are from before the rebase.
python -I -B tests/run_all.pypasses (any failure that also fails onmainis named). (On the wave's merge8217be31(msl-depth, which carries this PR, + mvk-pushlimits): 556 passed, 32 failed, 18 skipped against main 6c9edffe's 531/32/17 on this Mac; the failures are main's own excepttest_gpu_hang_watch, which failed under load and passes on its own on that tree;test_collect_widenow passes; 25 new tests pass;test_darwin_audio_service, Darwin-only, is skipped by the runner;reports/PubWave-files/waveB/. After the rebase onto mainaa95d35f(head5400f1a0): wave B's mergedb0e2e16, 569/32/18 against main aa95d35f's 543/33/17, no new failure.)third_party/untouched (a new folder with aPROVENANCE.mdinstead).When it applies
Assisted-by:/Co-Authored-By:trailers.mainand passing here.tests/that reads changed source updated in this PR, not weakened.Every scan down vk_memory.cpp's import table by start (pool_lookup's covering import and merge plan, bg_drain, prefetch_chunks, import_in_background, memory_note_gpu_write and memory_guest_imported) stopped once an import ended more than 1 GiB below its address, on the belief that no import is larger than that. pool_lookup's own merge escape leaves an import above 1 GiB over the smaller ones inside it ("left overlapping"), and the device allows imports up to max_import_size: a scan stopped at a small import inside a large one, missed the large one, and the same bytes were imported again, two memories over the same bytes. import_reach.h counts the live imports by span class; its bound is above every live span, within twice the largest. imported_add and imported_take, now the table's only ways in and out, keep the count. A scan stops only where no import further down can reach its address, so it also stops sooner than before when the imports are small. The covering-import checks in pool_lookup and import_in_background look past the last import at or below the range for the same reason. Tests: test_import_reach.py runs the actual scans with a 4 GiB import over a 16 MiB chunk, left by the merge escape (imports are recorded ranges: no payload, no Vulkan, no game). Its lookup, query, note, drain, background and prefetch cases fail on main5e072fc8and pass here, and a source check keeps every change to the table in the two helpers. test_import_windows runs pool_lookup with the helpers; test_mirror_gpu_only names the new publication point. Assisted-by: Claude Opus 5.5 Co-Authored-By: Claude Opus 5.5 <[email protected]>044635ea added vma_startup_rollback (vk_vma.cpp, run after a failed replay_start), which calls vmaDestroyAllocator. vk_vma.cpp includes vk_work.h last so that RX_VK_RECORDER=defer/thread drains before its destructions, and every such boundary call in a vk_work.h file must be in RX_REC_BOUNDARIES with a drain-first macro. This one was not, and test_recorder_sync caught it ("vk_vma.cpp calls ['vmaDestroyAllocator'], which vk_work.h does not drain before"): it passes at 58c5c5aa (044635ea's parent) and fails at 044635ea. Add it to the list and to vk_work.h, next to vmaDestroyBuffer and vmaDestroyImage. With the recorder off (the default) boundary() is a relaxed load and a branch. Rollback runs before any image or buffer exists, so nothing should be held there today, but the allocator is no longer destroyed while a recorder could still hold packets. The test is unchanged (sources: 42 boundaries drained, was 41). Assisted-by: Claude Opus 5.5 Co-Authored-By: Claude Opus 5.5 <[email protected]>A guest span larger than one device allocation (the cap on MoltenVK/M4 Max is 41,747,087,360 bytes) is bound as pieces: one descriptor per allocation segment, at the descriptor's own binding (reports/2026-11-14-full-range-design.md sections 4.1-4.4). This is the shader half; the draw side (descriptors, layout, gathers) comes later. - runtime_info.h: SegEntry {Storage|Table|TexBuf|ImgBuf, desc, pieces} and RuntimeInfo::segmented. - Back end: a segmented storage buffer is an array of pieces at its binding (a table count * pieces, entry k's piece j at k * pieces + j); its loads, stores and atomics call DontInline helpers made after main, which find the piece by the pieces' OpArrayLength in order, with constant indices. The global memory fallbacks sum the pieces' lengths for a segmented descriptor's bound end and branch to its piece (single, native candidate, table written out and in the loop). Texel and image buffers are arrays too, their fetches, reads, writes and atomics through helpers that pick the piece by OpImageQuerySize, and a texel buffer's size query sums the pieces. ImageQuery was declared for every module already. With RuntimeInfo::segmented empty nothing new is made: the SPIR-V is byte-identical. - vk_shader: SegSig (entries, hash) on Program, seg_sig and seg_variant; program_segmented finds or translates the variant of a plain program with plain's texture questions and answers and checks its descriptors are plain's; program_pick, mesh_program_locked, program_find_plain, loaded_variants and portable_variant keep the plain and segmented variants apart; every stage's RuntimeInfo::segmented comes from Program::seg. - vk_progcache: a segmented variant is never written to the program cache nor listed among the loaded programs. - tests/test_seg_shader.py + tests/seg_shader_ir.cpp: synthetic IR programs through the real back end, plain and segmented; structure checks, spirv-val (Vulkan 1.3, uniform buffer standard layout), and with --base-tree a byte comparison of the plain modules against an older tree. Five source-reading checks follow the new Program field and the atomics' new branch. The translator stamp changes with this (eden/, vk_shader.cpp, sizeof(Program)): every program cache is rebuilt once. Land it in the same release as the vk_api.h change that already does so. Assisted-by: Claude Opus 5.5 Co-Authored-By: Claude Opus 5.5 <[email protected]> (cherry picked from commit b7928876dab846485f667c1c934d34d25ecef035)The interfaces the four segmented-consumer lanes (SegBind, SegXfer, TexelDesc, NullShader; reports/2026-11-14-seg-consumer-plan.md section 3) code against, frozen before they fork. Inert: no caller of anything new, no SPIR-V, layout, push range or stream change, sizeof(Program) unchanged. - vk_internal.h / vk_memory.cpp, SegMem's consumer interface: gpu_span_any() (a relaxed count of the live domains, stored in seg_commit; domains are kept for the run), gpu_span (gpu_buffer's flavour of gpu_span_bind: import buffers, no written marks, no mirror), SpanLayout::bind_stamp (gpu_bind_stamp(): a segment can be imported again with its cuts kept, so cut_gen alone never keys piece buffers), span_unused_piece's semantics ({null_storage, 0, 1}), and the SegArena API (SegArenaRef, seg_arena_take, seg_gather, seg_scatter; stub bodies after stream_alloc that give no room and log, SegBind's to fill). - Review fixes in the same interface (renderer-core review P2-3, P3.2): every piece of gpu_span_bind carries its buffer's device address (the import's or the mirror's; 0 only with bufferDeviceAddress off), and its one-piece path stores the translated guest address, not a virtual one. - vk_texture.h/.cpp: texel_view_pieces / texel_piece_views, stubs that return 1 and false (TexelDesc's to fill). - vk_shader.h: Program::presence_words, in what was padding. - Back end: PRESENCE_PUSH_OFFSET (88), Profile::emulate_null_descriptors, Bindings::presence / presence_read with the bit numbering rule. - tests: test_resolver_coverage lists gpu_span_bind / gpu_span sites too (scoped to gpu/vk: gpu/render has an rx::gpu_span of its own), checks that gpu_span follows its own gpu_buffer, and splits SITES into SITES_BASE plus one block per lane. test_segmented_domains executes the contract (the count 0, 1, 2 -> 1 on a merge, 0; gpu_span's pieces; addresses with the fixture's gpu_buffer_bind returning none as the real one does; the bind stamp; a virtual address's guest). test_seg_shader accepts a base tree of this back end's generation and compares every module. - docs/segmented-binding.md: the gate (every new behaviour Apple-only, other platforms byte-identical) and one heading per lane. Conflict on this branch (not with main): tests/test_seg_shader.py's docstring, against the array_pointer_calls paragraph #P20's msl-arrayptr port added; both kept, this paragraph first. Assisted-by: Claude Opus 5.5 Co-Authored-By: Claude Opus 5.5 <[email protected]> (cherry picked from commit 95fa06d4e6526601bd2e1bc88c2f8f5720855350)The segmented single storage array's case (plan 4.3 item 5) had no negative: the check always cut its own tree. With --source <B2 archive> it fails as required ("moltenvk.segmented_single want=set got=push"). Conflict with #P28 (read_write textures; not with main): the docstring keeps #P28's paragraph with this usage line, and main() cuts from --source's vk_draw.cpp with #P28's read_write count (as in the macOS integration, Integration 17). Assisted-by: Claude Opus 5.5 Co-Authored-By: Claude Opus 5.5 <[email protected]> (cherry picked from commit 5824f6828e21cf810dcceaa6be425a9224b15994)plaine37c097da2plaina997b3629ab8579f1a70c22c9a9a11c22c9a9a115400f1a0b6WIP: feat(vk): bind segment-straddling buffers and texel views as piece arrays, and split transfers at segment cuts, on macOSto feat(vk): bind segment-straddling buffers and texel views as piece arrays, and split transfers at segment cuts, on macOSFolded into #848 (feat(vk): the SPIR-V back end on MoltenVK and guest spans past one device allocation)
Pull request closed