Seeing a frame on the CPU
Frame time is several clocks #P49PBP
“The frame took eight milliseconds” is not yet an explanation. At least four intervals can appear under that sentence:
- CPU work in the game and encoder;
- CPU waiting for a drawable or a completion frontier;
- wall time between submissions; and
- execution on the GPU itself.
The first two are visible to a monotonic host clock. The fourth eventually
needs backend timestamp queries or a GPU capture; naming a host interval
gpu_ms would not make it GPU time. Likewise, a hidden demand-driven canvas
avoids luvcraft's 60 Hz scheduler but does not promise that CAMetalLayer will
make drawables available without pacing.
A statistical profiler and an instrumented trace answer different questions. Sampling is valuable when it can observe the actual native frame thread and attribute stacks. Tracy-style zones answer the immediate structural question: which named interval surrounded the wait, encoding pass, or submission? Luv's first tool deliberately implements the latter. It records nested host-clock intervals; it does not pretend to sample CPU instructions or time GPU work.
DONE Trace one real frame with nested CPU zones #OHNIWM
Intent: make a coarse frame explanation one ordinary benchmark away, with an opt-in macro whose disabled path neither constructs events nor changes backend control flow.
Evidence:
- The trace buffer records a zone name, parent, depth, monotonic start/end
ticks, allocated bytes, garbage-collection time, and collections crossing
the extent.
with-cpu-trace-zoneis a thinunwind-protectwrapper; buffers reuse event and stack storage after their first capture. The same module provideswith-runtime-observationfor a retained aggregate when a benchmark needs time, allocation, and GC evidence without a zone tree. - The SDL request boundary explicitly carries the selected trace onto the native frame thread. Dynamic Lisp bindings alone do not cross that thread handoff.
- Both presentation backends name the same semantic acquisition, encoding,
finish, submit, and present regions. Backend-only work remains visibly
named
:metal/...or:vulkan/...inside its implementation. - The existing benchmark phases now compose with those zones, so luvcraft contributes simulation, streaming, shader refresh, mesh publication, uniform update, shadow, scene, and surface-copy regions without asking which backend owns the encoder.
benchmark-luvcraft-frame-performancestill measures its ordinary batch without tracing. It then warms the reusable trace buffer and prints one separate representative frame.
One standalone Apple M2 Pro run measured 60 steady frames at an 8.367 ms CPU median and then printed this representative structure:
luvcraft/frame 7.376 ms
luvcraft/presentation 7.365 ms
canvas/frame 7.363 ms
canvas/acquire-drawable 5.747 ms
metal/allocate-frame-resources 0.099 ms
gpu/encode 1.146 ms
luvcraft/mesh-publication 0.403 ms
luvcraft/shadow-pass 0.383 ms
luvcraft/scene-pass 0.223 ms
gpu/submit 0.242 msThat single frame is localization evidence, not a distribution. The adjacent fixed metrics retain median, p95, mean, and maximum across the measured batch. More zones should be added only when a coarse region earns a focused question.
Done when (met): a real frame produces a bounded nested report across the native thread handoff, the untraced benchmark remains the comparison baseline, tests prove nesting and storage reuse, and Vulkan and Metal smoke paths remain valid.
Mentioned in: Ambient telemetry is a bounded semantic skeleton
Referenced from code: Measure TRACY-VALUE, when supplied, is attached to the Tracy zone at exit. It does
not affect the bounded CPU trace, whose zones retain time and runtime costs. Two independent things may be: a Tracy viewer attached to this image, and an
opt-in CPU-TRACE capture. Each disabled path is one special-variable test and
does not allocate, so instrumenting a frame path costs nothing when nobody is
measuring it. Active traces reuse their zone storage after the first capture. The Tracy zone is the outer one. When only Tracy is watching there is no
bookkeeping inside it to measure, and when a CPU-TRACE capture is running its
own cost belongs to the zone it is attributed to rather than being hidden from
it. #OHNIWM compiler's input. TRACE, rather than INDEX, guards cleanup because zero is a valid first zone index.defmethod request-canvas-frame canvas.lisp:692 ↗
defmacro with-cpu-trace-zone trace.lisp:212 ↗
body as nested zone name for whichever measurement is watching.body occurs once so nested instrumentation does not multiply the
Ambient telemetry is a bounded semantic skeleton #7SOIWP
The useful ambition is broad coverage, but the unit of coverage is not every call. An ambient zone should name one event, frame, pass, batch, job, queue drain, publication, resource transaction, or other operation whose complete dynamic extent means something in a timeline. A loop may have a zone around the whole traversal and attach its visit or byte count; its individual sites, vertices, glyphs, contacts, and commands normally should not each open a zone.
This gives zdefun and zdefmethod a simple admission rule. Use them when
the definition owns a bounded semantic operation. Use zone inside a large
definition when its phases answer different questions. Keep ordinary
defun and defmethod for accessors, predicates, coordinate arithmetic,
per-element callbacks, foreign leaf calls, and tiny helpers. Literal
semantic names are valuable where different implementations should align
(:gpu/submit on two backends); inferred package-qualified names are better
for cold or uniquely named operations where another vocabulary adds nothing.
The desired trace is therefore a semantic skeleton with measured dense work hanging from it, not a call graph. This revises the deliberately sparse first frame proof in #OHNIWM without discarding its boundedness: broad ambient telemetry is admitted at coarse grains, and a focused investigation may still add temporary finer zones.
Mentioned in: Convert the outer runtime boundaries
The first ambient-zone census #1G3ORG
Verified now: the source index contains 7,536 top-level definitions across the project's ASDF systems. A mechanical ranking selected functions and methods by loop ownership, body extent, boundary words, and existing trace regions; the list below was then reviewed to remove accessors, predicates, per-element geometry and arithmetic, tests, probes, and generated foreign or shader vocabularies. It is intentionally generous: about two hundred candidates, grouped so each conversion can still be judged in context.
The names are a census, not a command to wrap every body unchanged. Large
entries such as encode-luvcraft-frame and start-luvcraft want a zoned outer
definition plus their existing or newly identified phases. Queue and loop
owners should attach counts. Generic operations named create or encode
belong only where the method specializer denotes a coarse resource or batch,
not where it denotes one command.
Mentioned in: Convert the outer runtime boundariesEvent, thread, and frame ownership
call-with-sdl-main-thread,
process-sdl-canvas-requests, call-on-sdl-canvas-thread,
request-canvas-frame, fence-canvas, handle-sdl-canvas-event,
wait-for-sdl-canvas-event, run-sdl-canvas-frame,
sdl-canvas-event-loop, run-sdl-canvas, start-sdl-canvas-thread,
open-canvas, and close-canvas. dispatch-sdl-key and
drain-sdl-canvas-events are already zoned; pointer motion, button, and
wheel dispatch are candidates only if an input trace needs that distinction.bind-vulkan-canvas-device, configure-vulkan-canvas-context,
unconfigure-vulkan-canvas-context, rebuild-vulkan-canvas-swapchain,
ensure-vulkan-canvas-swapchain, acquire-vulkan-canvas-image, and
%call-with-vulkan-canvas-frame.synchronize-metal-canvas-drawable-size,
configure-metal-canvas-context, unconfigure-metal-canvas-context,
destroy-metal-canvas-context, and call-with-metal-canvas-frame.make-luvcraft-frame-attachments, install-luvcraft-frame-attachments,
release-luvcraft-frame-attachments, luvcraft-frame-state,
discard-luvcraft-frame-states, ensure-luvcraft-frame-extent,
encode-luvcraft-frame, render-luvcraft-frame, refresh-block-atlas,
start-luvcraft, and stop-luvcraft. The existing frame-phase zones remain
the inner semantic partition rather than being replaced by one giant span.dispatch-luvcraft-focus-event is already zoned; add
refresh-luvcraft-inventory and the coarse target activation or overlay
ownership operations, not every key-specialized method.Production, residency, lighting, and meshing
take-production-system-request, run-production-system,
schedule-production-request, receive-production-result-no-hang, and
stop-production-system. The request count and request kind are useful
values; the condition-variable wait should remain visible as its own phase.perform-production-request methods, maintain-generated-luvcraft-residency,
maintain-static-luvcraft-residency, maintain-luvcraft-residency,
destroy-luvcraft-chunk-products,
ready-luvcraft-mesh-publication-groups,
discard-stale-luvcraft-staged-products,
publish-ready-luvcraft-meshes, schedule-luvcraft-chunk-loads,
schedule-luvcraft-meshes, schedule-luvcraft-lighting,
publish-production-result methods, drain-luvcraft-production,
evict-luvcraft-products, refresh-luvcraft-mesh, and
wait-for-luvcraft-streaming-quiescence. Counts should distinguish
scheduled, drained, stale, published, and evicted products.capture-light-region, make-light-candidate, publish-light-region,
relight-block-world, attach-lighting-state, and reconcile-lighting.
Captured chunks, changed chunks, and publication revisions are the values;
solver work belongs to the compiled realization below.solve-compiled-light-region, make-compiled-light-reconciliation,
drain-compiled-light-removals, reseed-compiled-open-sky,
seed-compiled-arrived-chunk, drain-compiled-light-additions,
and reconcile-compiled-lighting. Existing
inner zones and their visit counts should be retained while the containing
definitions become concise.solve-light-region and compare-voxel-light-solvers are explicit
test/reference instrumentation, not production fallback. Reference and
candidate visits, allocation, GC, and mismatched keys remain valuable
differential evidence when that system is deliberately loaded.make-block-mesh-snapshot, mesh-block-snapshot, and mesh-block-chunk.
Candidate values are visited blocks, emitted faces, and output vertices.populate-little-world-chunk, center-little-world-residency, and
little-world-landmarks-for-chunk.Physics, simulation, and populations
rebuild-physics-grid, prune-physics-contacts,
generate-physics-body-pairs, generate-physics-terrain-contacts,
generate-physics-box-contacts, prepare-physics-constraints,
physics-integrate-velocities, physics-integrate-positions,
%physics-warm-start-scalar, physics-warm-start,
%physics-solve-contacts-scalar, physics-solve-contacts,
%physics-apply-restitution-scalar, physics-apply-restitution,
store-physics-impulses, finalize-physics-bodies,
wake-physics-bodies-near, step-physics-world, and
validate-physics-world. The outer step should expose body, pair, contact,
constraint, awake, and event counts; SIMD/scalar kernel choice belongs in a
value or an enclosing phase, not one zone per constraint.advance-camera-focus, map-body-overlapping-blocks, move-body-axis,
and step-block-world-player.maintain-critter-population, advance-luvcraft-physics,
build-physics-body-vertices, and advance-block-particles. Geometry
emitters such as emit-critter-box remain unzoned unless a containing batch
cannot explain their cost.raycast-block-world
is the coarse traversal candidate; coordinate and block accessors are not.Terminals, McCLIM, and interactive surfaces
send-pty-device-bytes, read-pty-stream-bytes,
perform-pty-device-message, drain-pty-device-messages,
update-pty-child-status, release-pty-device-process,
run-pty-device, launch-pty-child, open-pty-device,
wait-for-pty-device, and close-pty-device. Bytes and message counts are
natural values.read-screen-cells
and terminal-screen, valued by dirty cells or grid area.open-terminal-display-portal, discover-terminal-component,
find-terminal-surface, initialize-terminal-surface-dependencies,
reconcile-terminal-surface, make-terminal-display-glyph-instances,
make-terminal-display-cell-instances,
make-terminal-display-screen-instances,
make-terminal-display-glyph-population, make-terminal-cell-run,
make-terminal-display-glyph-run, refresh-luvcraft-overlay,
encode-luvcraft-overlay, handle-luvcraft-focus-event,
open-activated-wall-display, attach-terminal-display-shell,
attach-terminal-display-pty, open-terminal-display, and
make-terminal-display. Component sites, cells, glyphs, runs, and draw
populations are the useful work values.repaint-gpu-mirror, render-gpu-mirror-frame, present-mirror,
capture-gpu-mirror-screenshot, release-gpu-mirror-frame-states, and the
coarse ensure-gpu-mirror-* pipeline/resource constructors. Composition,
preparation, individual text batches, and upload are already zoned; drawing
one polygon, segment, or glyph remains below the ambient grain.prepare-direct-widget-overlay, the coarse encode-luvcraft-overlay
methods, ensure-world-widget-text-pipeline,
ensure-widget-overlay-relief-pipeline, and embed-luvcraft-frame.handle-repaint methods in
hotbar,
inventory,
metabar,
terminal film browser,
legend,
command menu, and
tape. Their substantial paint helpers
belong beneath those methods, following the communicator example.console-transcript-lines only if transcript reconstruction remains visible
outside the paint zones.GPU submission, resource construction, and compilation
submit-vulkan-command-buffers, record-vulkan-texture-write, and coarse
create methods for devices, pipelines, bind groups, and large resources.
Per-command encode methods are excluded unless they batch an entire pass.submit-metal-command-buffers, configure-metal-pass-bind-group, and the
corresponding coarse device, pipeline, bind-group, and resource create
methods. One buffer write or one encoded command is below the ambient
grain.parse-shader-specification, whole shader-function parsing, and live source
revision/dependent notification. Per-expression parsing is excluded.compile-shader-specification and the complete task, mesh, vertex, and
fragment entry-point lowering operations; expression-level lowering is
excluded.lower-traditional-msl-specification, lower-task-msl-specification,
lower-mesh-msl-specification, lower-msl-bindings, and
write-msl-entry-point. lower-msl-expression and
write-msl-statement methods are too fine for ambient use.compile-frontier-program, emit-frontier-drain-form,
emit-frontier-admit-form, and emit-frontier-relate-form. Runtime
execution belongs to the client operation; generated per-site forms do not
open zones.create-slug-glyph-atlas, collect-slug-shaped-glyphs, and
shape-slug-text, valued by glyph and outline counts.Media, portals, persistence, and tools
enable-vulkan-decoding,
enable-videotoolbox-decoding, open-video, close-video,
decode-next-frame, ensure-video-scaler, scale-frame-into, and
copy-rgba-words. Bytes or pixels distinguish setup, decode, conversion,
and copying.open-audio,
decode-next-audio-frame, and audio-frame-mono-samples.make-video-screen, advance-video-screen, release-video-screen,
start-tape-download, open-film-sound, and run-film-sound.open-luvcraft-portal, release-luvcraft-overlay,
capture-hidden-luvcraft-frames, and temporal-derivative-rgba.make-luvcraft-save-description,
restore-luvcraft-resume-save-description,
restore-luvcraft-save-description, read-luvcraft-save,
write-luvcraft-save-description, perform-world-checkpoint,
run-world-checkpoint-writer, request-world-checkpoint, and
stop-world-checkpoint-writer.
DONE Convert the outer runtime boundaries #G3CFYX
Intent: make one ordinary live capture explain the outer event, frame, production, streaming, simulation, and overlay structure before adding the denser subsystem waves in #1G3ORG.
Evidence: the communicator instrumentation already localized a roughly
5.4 MB keypress repaint to about 4.4 MB of McCLIM command preparation. The
new zdefun, zdefmethod, and zone forms have a zero-allocation disabled
path, while #7SOIWP supplies a bounded admission rule for wider coverage.
Done when: SDL requests/events and frame ownership, luvcraft frame and simulation ownership, production scheduling/draining/publication, top-level streaming phases, and overlay refresh/encode each have stable ambient zones; loop owners attach semantic counts; a representative live capture stays legible rather than emitting per-element spans; and the relevant tests and both source-structure checks pass.
Outcome: forty-two outer functions and methods now use the concise zoned definition forms. SDL request draining, event dispatch, and native frame service; Vulkan and Metal frame ownership; production request, worker, and publication transactions; the streaming reconciliation phases; fixed-step simulation; frame encoding; terminal and McCLIM overlay refresh, preparation, and encoding all have stable semantic names. Scheduling, publication, fixed-step, and retained-command owners carry their natural work counts.
A live focused-phone frame produced 54 CPU zones and remained a readable
frame -> simulation/streaming/presentation -> overlay tree. It attributed
about 4.36 MiB in total, including the terminal -> Telegram -> McCLIM overlay
path, without per-glyph, per-cell, or per-command spans. Caller-side
request-canvas-frame and call-on-sdl-canvas-thread deliberately remain
ordinary definitions: the former transports the same opt-in CPU trace onto
the native thread, so opening that trace on both sides would manufacture
cross-thread nesting. Native request draining and frame service provide the
honest ownership zones instead.
The full test matrix, strict Parinfer source check, cold luvcraft system
load, and wiki build pass with this coverage.
DONE Stream one chunk window under measurement #887PO7
Intent: measure the frames where traversal creates real asynchronous chunk load, lighting, mesh, GPU upload, and publication work, rather than infer that cost from the fully resident steady benchmark.
Evidence:
LUVCRAFT_BENCHMARK_FRAMES=240 make metal-streaming-benchmark
This begins from the same settled 9 by 9 resident world as the steady case, then moves the player one chunk in +X at the measured boundary. One edge of nine chunks leaves and one edge of nine genuinely new chunks enters. Every CSV frame now records resident chunks, pending production, staged products, and rendered products; the printed transition summary stops at the first frame where residency, meshes, and the worker are settled again.
The first Apple M2 Pro run settled at frame 57. Across those 58 transition frames, CPU frame time had an 8.453 ms median, 43.225 ms p95, and 46.078 ms maximum; 10 frames exceeded the 16.667 ms 60 Hz deadline. The worst repeated cost was not worker generation: owner-side mesh/lighting publication reached 42.209 ms. This is now a reproducible hitch and a next optimization target, not a claim that streaming performance is solved.
Done when (met): one command produces a bounded steady/streaming comparison, names how many chunks entered and when the transition settled, reports 60 Hz deadline misses, and leaves per-frame lifecycle counts in a comparison-ready CSV.
Referenced from code: Measure steady or streaming world frames through the real Metal path. The streaming scenario moves one chunk in +X after warmup and records the
entire asynchronous load, mesh, and owner-publication transition. #887PO7 The hidden demand-clock canvas avoids the application's 60 Hz scheduler, but
CAMetalLayer may still pace drawable availability. All desired chunk products
arrive before warmup, then Warm the reusable zone buffer, then capture one extra
frame after the measured batch so detailed zones and
their first-use allocation cannot perturb its metrics.defun benchmark-luvcraft-frame-performance benchmark.lisp:61 ↗
frame-count consecutive frames reuse the same world,
pipelines, attachments, and drawable pool. Per-frame values measure CPU
orchestration and encoding. Completion throughput includes final shared-event
drainage and is intentionally not labelled as GPU time.width and height remain the logical scene controls. The
Retina benchmark deliberately lets SDL make the drawable
denser, then records both extents below so the physical
post-pass cost cannot disappear behind the input size.
The game names usage; backends realize hazards #T5MQO0
Luvcraft used to specialize prepare-luvcraft-shadow-map-sampling on Vulkan
and Metal encoder classes. That put Vulkan image layout and Metal 4 barrier
mechanics in the game even though the game knows only one fact: the completed
shadow depth texture will be sampled by the following pass.
The game now emits prepare-texture with :texture-binding usage. Vulkan
validates the declared usage and records a shader-read layout transition.
Metal validates the same relationship and installs its consumer barrier when
the following native render encoder begins. This is not an assertion that all
backend mechanics can be identical. It is a placement rule: application code
states resource intent, while the backend that owns the hazard model realizes
it. The cold Vulkan smoke and Metal API-validated smoke both cross this
command; the Metal image stayed bit-identical to its pre-change baseline.
Referenced from code:defun prepare-texture gpu.lisp:1187 ↗
TODO Reuse Metal frame submission resources #YKITZ8
Intent: stop constructing and releasing one MTL4CommandAllocator and one
MTL4CommandBuffer for every frame when a bounded frame-slot owner can reuse
them safely.
Evidence: the current trace exposes :metal/allocate-frame-resources as a
separate region, and source inspection confirms that both native objects are
created after every drawable acquisition. The representative frame above
spent 0.099 ms there. That one number is not a stable estimate, but it makes
the ownership experiment measurable. #JAM5XR is the related lifetime work:
reuse must follow the shared-event frontier rather than reintroduce a queue
stall.
Done when: a bounded Metal frame-slot structure owns reusable submission resources, the completion frontier prevents early reuse, traces show the creation region absent from steady frames, allocation and frame distributions improve in a repeated A/B run, and API validation remains clean.
TODO Separate drawable pacing from encoding distributions #JBZ4SC
Intent: report how much host frame time is runnable encoding work and how much is waiting for presentation availability, instead of treating both as one “encoding” number.
Evidence: native sampling after #WEE1DX found the frame thread mostly sleeping
inside CAMetalLayer nextDrawable, while the instrumented representative frame
above localized 5.747 ms to drawable acquisition and 1.146 ms to the inclusive
encoding region. These are compatible observations from different windows,
not a fixed ratio.
Done when: repeated captures summarize drawable-acquisition and encoding distributions separately for Metal and Vulkan, the benchmark explains which intervals include pacing, and eventual GPU timestamps remain a distinct measurement rather than being inferred from host waits.
(canvas function)Run FUNCTION with a timestamp on CANVAS's native frame/event thread. The initial native implementation is synchronous: the caller waits for the function's values. The protocol leaves room for a real frame scheduler.
A native destination with a lifetime and frame clock.
The current opt-in CPU trace, or NIL on the ordinary execution path.
(canvas function)(canvas)((name &key (tracy-value nil tracy-value-supplied-p)) &body body)Measure BODY as nested zone NAME for whichever measurement is watching. TRACY-VALUE, when supplied, is attached to the Tracy zone at exit. It does not affect the bounded CPU trace, whose zones retain time and runtime costs. Two independent things may be: a Tracy viewer attached to this image, and an opt-in…
((name &key (color 0) (value nil value-supplied-p)) &body body)Measure BODY as a Tracy zone named NAME. When VALUE is supplied, evaluate it as the zone exits and attach the resulting unsigned integer to the zone. This is useful for semantic work counts such as sites visited: Tracy can then distinguish a slower realization from one which simply performed more work. A literal…
Logical conjunction of tests and raw truth values.
(trace name)(trace index)(&key (frame-count 120) (warmup-count 30)
(width 960) (height 640)
(world (make-empty-little-block-world :seed 121))
(camera (make-instance 'fly-camera))
(scenario :steady) (high-pixel-density-p nil)
csv-pathname (stream *standard-output*))Measure steady or streaming world frames through the real Metal path. The streaming scenario moves one chunk in +X after warmup and records the entire asynchronous load, mesh, and owner-publication transition. #887PO7 The hidden demand-clock canvas avoids the application's 60 Hz scheduler, but CAMetalLayer may…
(&key (chunk-width 16)
(chunk-height 16)
(chunk-depth 16)
(seed 121))A provider for the system's preferred Metal device.
(&key
(title "luv little block world — click, look, walk")
;; NIL means "as much of this display as
;; comfortably fits"; a capture asks for the
;; exact frame it intends to write out. A
;; KMSDRM console can only present at a real
;; display mode, so the environment may pin
;; the canvas to the panel's native size.
(width (let ((value (uiop:getenv "LUVCRAFT_WIDTH")))
(and value (parse-integer value))))
(height (let ((value (uiop:getenv "LUVCRAFT_HEIGHT")))
(and value (parse-integer value))))
(frames-per-second 60)
(visible-p t)
(fullscreen-p nil)
(high-pixel-density-p t)
(world (make-empty-little-block-world))
(mesher (make-instance
'exposed-face-mesher))
(camera (make-instance 'fly-camera))
player
(selected-block *stone-block*)
(inventory (make-block-inventory))
quit-function
;; A hidden capture wanting animals in frame
;; hands in a population it has already
;; placed; the ordinary game grows its own
;; around the player as it plays.
(critters
(make-instance 'critter-population))
checkpoint-writer
(provider *gpu-provider*)
(sky-clock (make-instance 'sky-clock))
(sky-profile (make-default-sky-profile))
(shadow-diagnostic-p nil)
;; The world-text banner is a proof of the
;; Slug path, not scenery: a caller that
;; wants one asks for it, and the ordinary
;; game sky stays empty.
(world-text-string nil)
(world-text-font-pathname
(cl-dejavu:font-pathname "DejaVuSans.ttf"))
(world-text-distance 8.0)
(world-text-lift 3.0)
(world-text-units-per-em 0.55)
(video-pathname nil)
(video-distance 13.0)
(video-lift 4.5)
(video-height 5.0)
(residency-radius 6)
(publication-limit 2)
(load-schedule-limit 4)
(mesh-capture-limit 1))Open a little CPU-meshed block world. Click to capture the pointer, look with the mouse, walk with WASD, and jump with Space. Once captured, left click removes the block at the centre of view and right click places the selected block. Number keys select materials, middle click picks the targeted material, Shift…
Mutable time-of-day state owned by a luvcraft session. SLY can pause it, set a time, change its rate, or pin it without restarting.
(session &key minimum (timeout 10.0))Wait outside frame encoding for a useful initial set of chunk meshes. MINIMUM defaults to nine or the whole desired set when it is smaller. The visible startup path asks only for the nearest product before its first presentation; deterministic captures wait for the broader default set.
(session &optional sample)(queue)Block until all work submitted to QUEUE so far has completed on the GPU.
(scenario session)Sample a two-dimensional texture through a sampler at a UV coordinate.
(before session)(&key label)Division of two represented quantities.
Subtraction or unary negation.
((trace) &body body)(benchmark &optional (stream *standard-output*))(trace &optional (stream *standard-output*))(benchmark pathname)(session)Stop SESSION exactly once and publish its result to every caller. The sole owner attempts every named release step and closes the canvas and device last. Concurrent and later callers observe the same values or RELEASE-ERROR without releasing a native handle twice.
(encoder texture usage)Prepare TEXTURE for semantic USAGE in ENCODER's following commands. The backend owns any layout transition, hazard barrier, or validation needed to realize the usage. Application code does not dispatch on the backend. #T5MQO0
(&optional (stream *standard-output*))Verified now: the source index contains 7,536 top-level definitions across the project's ASDF systems. A mechanical ranking selected functions and methods by loop ownership, body extent, boundary words, and existing trace regions; the list below was then reviewed to remove accessors, predicates, per-element geometry…
The useful ambition is broad coverage, but the unit of coverage is not every call. An ambient zone should name one event, frame, pass, batch, job, queue drain, publication, resource transaction, or other operation whose complete dynamic extent means something in a timeline. A loop may have a zone around the whole…
Intent: preserve logical frame-boundary publication while removing the conservative queue stall from Metal resource destruction. Evidence: every currently supported Metal encode relationship captures its buffers, textures, views, samplers, bind groups, and pipelines in the general encoder. finish transfers that set…
Intent: keep direct objc_msgSend direct by removing dynamic symbol lookup from every declared message. Evidence: – SBCL call profiling counted 160,400 calls to objective-c-message-send-pointer, CFFI's symbol lookup, and SBCL's dynamic foreign-symbol resolver across 200 steady luvcraft frames: exactly 802 resolutions…
Intent: make a coarse frame explanation one ordinary benchmark away, with an opt-in macro whose disabled path neither constructs events nor changes backend control flow. Evidence: – The trace buffer records a zone name, parent, depth, monotonic start/end ticks, allocated bytes, garbage-collection time, and collections…
Intent: measure the frames where traversal creates real asynchronous chunk load, lighting, mesh, GPU upload, and publication work, rather than infer that cost from the fully resident steady benchmark. Evidence: This begins from the same settled 9 by 9 resident world as the steady case, then moves the player one chunk…
Luvcraft used to specialize prepare-luvcraft-shadow-map-sampling on Vulkan and Metal encoder classes. That put Vulkan image layout and Metal 4 barrier mechanics in the game even though the game knows only one fact: the completed shadow depth texture will be sampled by the following pass. The game now emits…
Dynamic bindings do not follow a closure onto another thread. Preserve the opt-in trace explicitly so a caller can measure the real native frame rather than only the time spent waiting for its request. #OHNIWM