luv

Workshop wiki

instruments.lisp

luft/render/instruments.lisp

system luft/render · 24 definitions · on GitHub

in-package#:luft.render

A viewer instrument is a sparse application attachment: a metabar, M-x, inspector-like tool, or other developer surface which participates in the existing game presentation without owning another canvas or frame loop. Attachments are ordered once at the boundary; their dense GPU state remains inside their renderer or McCLIM compositor.

defvar*viewer-instruments*
make-hash-table:test#'eq:weakness:key
"Weak viewer-to-instrument-list ownership registry."
defvar*viewer-instruments-lock*
sb-thread:make-mutex:name"LUFT viewer instruments"
defgenericviewer-instrument-canvas
:documentation

Return viewer's live mutation owner, or NIL before native startup.

:method
declare
ignoreviewer
nil
:method
viewer-canvasviewer
defgenericviewer-instrument-stop-controller
:documentation

Return viewer's terminal attachment gate, or NIL for a bare test owner.

:method
declare
ignoreviewer
nil
:method
viewer-stop-controllerviewer
defgenericviewer-instrument-priority
instrument
:documentation

Return instrument's input and visual stacking priority.

:method
instrument
declare
ignoreinstrument
0
defgenericviewer-instrument-present-p
instrumentviewer
:documentation

Whether instrument currently participates in viewer.

:method
instrumentviewer
declare
ignoreinstrumentviewer
t
defgenericrefresh-viewer-instrument
instrumentviewer
:documentation

Publish instrument's already-owned state at viewer's frame boundary.

:method
instrumentviewer
declare
ignoreviewer
instrument
defgenericencode-viewer-instrument
instrumentviewerpasssurface-texturephysical-extent
:documentation

Encode instrument into viewer's final pass at physical-extent.

:method
instrumentviewerpasssurface-texturephysical-extent
declare
ignoreviewerpasssurface-texturephysical-extent
instrument
defgenerichandle-viewer-instrument-event
instrumentviewercanvasevent
:documentation

Handle event for instrument and return true when world input must stop.

:method
instrumentviewercanvasevent
declare
ignoreinstrumentviewercanvasevent
nil
defgenericrelease-viewer-instrument
instrumentviewer
:documentation

Release instrument while viewer's GPU device is live.

:method
instrumentviewer
declare
ignoreinstrumentviewer
nil
defunviewer-instruments

Return a priority-descending snapshot of viewer's instruments.

sb-thread:with-mutex
defunsort-viewer-instruments
instruments
stable-sortinstruments#'>:key#'viewer-instrument-priority
defuncall-with-viewer-instrument-mutation

Run function at viewer's native frame boundary when one exists.

Before the canvas is open, and after it has quiesced, the caller still owns the attachment list directly. An open canvas owns both mutation and release: request-canvas-frame serializes this call after any frame which borrowed an instrument snapshot and propagates function's values or condition back to the caller.

defun%add-viewer-instrument
viewerinstrument
let
unwind-protect-releasing
progn
flet
present-p
sb-thread:with-mutex
memberinstrument:test#'eq
publish
sb-thread:with-mutex
let
instruments

Recheck under the registry lock so bare owners and same-object concurrent adds remain duplicate-free.

unless
memberinstrumentinstruments:test#'eq
setf
sort-viewer-instruments
consinstrument
copy-listinstruments
instrument
ifcontroller
call-with-running-stop-controllercontroller#'publish:attachmentinstrument:already-attached-p#'present-p
publish
setfinstalled-pt
instrument

The registry consumes a newly offered instrument on success or rejection. Release on this same owner boundary so an opening constructor can unwind without stranding partial resources.

unlessinstalled-p
releasing:rejected-instrument
defunadd-viewer-instrument
viewerinstrument

Attach instrument at viewer's frame boundary in explicit priority order.

ADD consumes a newly offered instrument on both success and terminal rejection. Once viewer begins stopping, the rejected instrument is released exactly once before application-attachment-closed is signalled.

A duplicate is already application-owned. Resolve it before asking a closing native canvas to run a callback, because pre-callback rejection must never release an object which remains in the viewer registry.

when
memberinstrument:test#'eq
return-fromadd-viewer-instrumentinstrument
let
consumed-pnil
unwind-protect-releasing
progn
handler-case
application-attachment-closed
condition
errorcondition
error
condition

Preserve the application's semantic terminal rejection when the native canvas refuses the request before our callback.

let*
cond
and
notconsumed-p
memberinstrument:test#'eq
setfconsumed-pt
instrument
and
notconsumed-p
state
not
eq:runningstate
error'application-attachment-closed:controllercontroller:attachmentinstrument:statestate
t
errorcondition
instrument

A closing canvas may reject the owner request before the callback starts. In that case the registry never consumed instrument, so no frame can have borrowed it. GPU destroy remains safe on this caller: backend retirement locks serialize it with terminal device teardown.

unlessconsumed-p
releasing:unpublished-instrument
defun%remove-viewer-instrument
viewerinstrument
let
removed-pnil
sb-thread:with-mutex
let*
instruments
remaining
removeinstrumentinstruments:test#'eq
unless
=
lengthinstruments
lengthremaining
setfremoved-pt
ifremaining
setfremaining
whenremoved-p
removed-p
defunremove-viewer-instrument
viewerinstrument

Detach and release instrument at a frame boundary.

Return true when it was attached. Release is synchronous with the caller and therefore cannot overlap a native frame which still borrows the instrument.

defunrefresh-viewer-instruments

Refresh each present instrument outside the render-pass encoder.

viewer
defunencode-viewer-instruments
viewerpasssurface-texturephysical-extent

Encode lower-priority instruments first and modal surfaces last.

dolist
instrument
when
encode-viewer-instrumentinstrumentviewerpasssurface-texturephysical-extent
viewer
defundispatch-viewer-instrument-event

Offer event from highest to lowest priority until one consumes it.

defun%release-viewer-instruments
let
instrumentsnil
errorsnil
dolist
instrumentinstruments
handler-case
error
condition
push
consinstrumentcondition
errors
whenerrors
error"LUFT instrument release failed for ~{~S~^, ~}: ~A"
mapcar#'car
reverseerrors
cdarerrors
nil
defunrelease-viewer-instruments

Atomically detach then release every instrument at a frame boundary.

This is deliberately the only application-level event wrapper. Individual instruments add methods on handle-viewer-instrument-event, so independently loaded adapters never replace one another at an identical method coordinate.