luv

Workshop wiki

live-artifact.lisp

hal/live-artifact.lisp

system luv · 26 definitions · on GitHub

Live application artifacts and shader pipelines.

A live artifact is an application-owned derivative of redefinable source. The protocol deliberately says nothing about the artifact's internal grain: a Luvcraft render pipeline and a luft renderer cohort can both be one artifact. Each implementation builds its candidate away from the installed state, publishes only a complete replacement at its owning application boundary, and keeps the last-known-good value on failure.

in-package#:luv

Application-level protocol ------------------------------------------------

defgenericapplication-live-artifacts
application
:documentation

Return application's live artifacts as a fresh list.

The list is an enumeration, not a transfer of ownership. Applications with no live artifacts inherit the empty default. An artifact may represent one GPU pipeline, a complete renderer cohort, or another transactionally replaced application derivative; the protocol does not impose a resource grain.

defmethodapplication-live-artifacts
applicationt
declare
ignoreapplication
nil
defgenericlive-artifact-label
artifact
:documentation

Return a concise human-readable label for artifact.

defgenericlive-artifact-status
artifact
:documentation

Return artifact's lifecycle status.

Concrete artifacts normally use :BUILDING, :INSTALLED, :FAILED, :releasing, and :RELEASED. :FAILED means that the last attempt failed while the previous installed artifact, when any, remains usable.

defgenericlive-artifact-diagnostic
artifact
:documentation

Return artifact's newest build or retirement diagnostic, or NIL.

defgenericlive-artifact-installed-revision
artifact
:documentation

Return artifact's monotonically increasing installed revision.

defgenericrefresh-live-artifact
artifact
:documentation

Attempt pending source for artifact at its owning application boundary.

Implementations must build away from installed state, publish only a complete candidate, preserve the last-known-good artifact on failure, and coalesce newer invalidations without performing GPU work in definition callbacks. Explicitly released artifacts must not be resurrected.

defgenericrelease-live-artifact
artifact
:documentation

Explicitly and idempotently release artifact and its subscriptions.

Release is terminal. Application owners remain responsible for calling this at a boundary where no new encoding can borrow the installed GPU resources.

defunrefresh-application-live-artifacts
application

Refresh the current live-artifact enumeration of application.

dolist
artifact
application

Mathematical shader pipelines -------------------------------------------

defclasslive-shader-pipeline
role:initarg:role:readerlive-shader-pipeline-role
stage:initarg:stage:readerlive-shader-pipeline-stage
vertex-role:initarg:vertex-role:initformnil:readerlive-shader-pipeline-vertex-role
label:initarg:label:readerlive-shader-pipeline-label
device:initarg:device:readerlive-shader-pipeline-device
layout:initarg:layout:readerlive-shader-pipeline-layout
vertex-module:initarg:vertex-module:initformnil:accessorlive-shader-pipeline-vertex-module
vertex-module-owned-p:initformnil:accessorlive-shader-pipeline-vertex-module-owned-p
vertex-buffers:initarg:vertex-buffers:readerlive-shader-pipeline-vertex-buffers
target-format:initarg:target-format:readerlive-shader-pipeline-target-format
target-blend:initarg:target-blend:initformnil:readerlive-shader-pipeline-target-blend
primitive:initarg:primitive:readerlive-shader-pipeline-primitive
depth-stencil:initarg:depth-stencil:readerlive-shader-pipeline-depth-stencil
dependent:initarg:dependent:accessorlive-shader-pipeline-dependent
vertex-dependent:initarg:vertex-dependent:initformnil:accessorlive-shader-pipeline-vertex-dependent
vertex-specification:initformnil:accessorlive-shader-pipeline-vertex-specification
vertex-lowering:initformnil:accessorlive-shader-pipeline-vertex-lowering
specification:initformnil:accessorlive-shader-pipeline-specification
lowering:initformnil:accessorlive-shader-pipeline-lowering
fragment-module:initformnil:accessorlive-shader-pipeline-fragment-module
pipeline:initformnil:accessorlive-shader-pipeline-native-pipeline
status:initform:building:accessorlive-shader-pipeline-status
diagnostic:initformnil:accessorlive-shader-pipeline-diagnostic
installed-revision:initform0:accessorlive-shader-pipeline-installed-revision
installed-source-revision:initform0:accessorlive-shader-pipeline-installed-source-revision
attempted-source-revision:initform0:accessorlive-shader-pipeline-attempted-source-revision

The live named values folded into the installed and most recently attempted sources, as (NAME . VALUE). Failed attempts deliberately replace ATTEMPTED-SOURCE-VALUES so a value which repairs the failed source requests another attempt without disturbing the installed value.

installed-source-values:initformnil:accessorlive-shader-pipeline-installed-source-values
attempted-source-values:initformnil:accessorlive-shader-pipeline-attempted-source-values

Source notifications never acquire this lock. It serializes only the owner-side build/install/release transition, preventing concurrent refresh or teardown from publishing or retiring the same resources twice.

mutation-lock:initform
sb-thread:make-mutex:name"luv live shader pipeline"
:readerlive-shader-pipeline-mutation-lock
:documentation

A last-known-good GPU render pipeline following live shader definitions.

The owning application calls refresh-live-artifact at a frame boundary. Definition callbacks only advance thread-safe revision sources; compilation, GPU creation, publication, and retirement happen here, outside MOP locks.

defmethodlive-artifact-label
live-shader-pipeline-labelartifact
defmethodlive-artifact-status
live-shader-pipeline-statusartifact
defmethodlive-artifact-diagnostic
live-shader-pipeline-diagnosticartifact
defmethodlive-artifact-installed-revision
live-shader-pipeline-installed-revisionartifact
defstruct
live-shader-pipeline-candidate
:constructor%make-live-shader-pipeline-candidate

A complete unpublished shader-pipeline candidate and its owned resources.

vertex-specificationvertex-loweringvertex-module
vertex-module-owned-pnil
specificationloweringfragment-modulepipelinesource-values
defunrelease-live-shader-pipeline-candidate
candidate

Release candidate resources which have not transferred to an artifact.

dolist
resource
remove-duplicates
removenil
list
live-shader-pipeline-candidate-pipelinecandidate
live-shader-pipeline-candidate-fragment-modulecandidate
and
live-shader-pipeline-candidate-vertex-module-owned-pcandidate
live-shader-pipeline-candidate-vertex-modulecandidate
:test#'eq

Preserve the primary build condition while still giving each resource an independent retirement attempt. GPU backends retain failed native retirements in their durable queue-owned ledgers.

ignore-errors
destroyresource
setf
live-shader-pipeline-candidate-pipelinecandidate
nil
live-shader-pipeline-candidate-fragment-modulecandidate
nil
live-shader-pipeline-candidate-vertex-modulecandidate
nil
live-shader-pipeline-candidate-vertex-module-owned-pcandidate
nil
nil
defunbuild-live-shader-pipeline-candidate
artifact

Build and return a complete candidate without replacing artifact's state.

let*
vertex-only-p
eq
live-shader-pipeline-stageartifact
:vertex
source-values
listnil
candidate
%make-live-shader-pipeline-candidate
completed-pnil
unwind-protect
let*
vertex-specification
cond
live-shader-pipeline-vertex-roleartifact
luv.shader:shader-specification-for
live-shader-pipeline-vertex-roleartifact
:vertex
vertex-only-p
luv.shader:shader-specification-for
live-shader-pipeline-roleartifact
:vertex
vertex-lowering
whenvertex-specification
specification
unlessvertex-only-p
luv.shader:shader-specification-for
live-shader-pipeline-roleartifact
live-shader-pipeline-stageartifact
lowering
device
live-shader-pipeline-deviceartifact
setf
live-shader-pipeline-candidate-vertex-specificationcandidate
vertex-specification
live-shader-pipeline-candidate-vertex-loweringcandidate
vertex-lowering
live-shader-pipeline-candidate-specificationcandidate
specification
live-shader-pipeline-candidate-loweringcandidate
lowering
live-shader-pipeline-candidate-vertex-modulecandidate
ifvertex-lowering
createdevice
make-shader-module-descriptor:label
formatnil"~A vertex module"
live-shader-pipeline-labelartifact
:language:mathematical:codevertex-specification
live-shader-pipeline-vertex-moduleartifact
live-shader-pipeline-candidate-vertex-module-owned-pcandidate
not
nullvertex-lowering
whenspecification
setf
live-shader-pipeline-candidate-fragment-modulecandidate
createdevice
make-shader-module-descriptor:label
formatnil"~A fragment module"
live-shader-pipeline-labelartifact
:language:mathematical:codespecification
setf
live-shader-pipeline-candidate-pipelinecandidate
createdevice
make-render-pipeline-descriptor:label
live-shader-pipeline-labelartifact
:layout
live-shader-pipeline-layoutartifact
:vertex`
:module,
live-shader-pipeline-candidate-vertex-modulecandidate
:buffers,
live-shader-pipeline-vertex-buffersartifact
:fragment
let
module
live-shader-pipeline-candidate-fragment-modulecandidate
whenmodule`
:module,module:targets
:format,
live-shader-pipeline-target-formatartifact
:blend,
live-shader-pipeline-target-blendartifact
:primitive
live-shader-pipeline-primitiveartifact
:depth-stencil
live-shader-pipeline-depth-stencilartifact
live-shader-pipeline-candidate-source-valuescandidate
copy-list
carsource-values
completed-pt
candidate

Record dependencies even when parsing, lowering, or GPU creation fails. A live value which changes after the failed attempt can then request a retry without waiting for an unrelated source edit.

setf
live-shader-pipeline-attempted-source-valuesartifact
copy-list
carsource-values
unlesscompleted-p
defuninstall-live-shader-pipeline-candidate
artifactrevisionsource-revisioncandidate

Publish candidate into artifact and retire the previous complete artifact.

let*
old-pipeline
live-shader-pipeline-native-pipelineartifact
old-vertex-module
and
live-shader-pipeline-vertex-module-owned-partifact
live-shader-pipeline-vertex-moduleartifact
old-fragment-module
live-shader-pipeline-fragment-moduleartifact
new-pipeline
live-shader-pipeline-candidate-pipelinecandidate
new-vertex-module
live-shader-pipeline-candidate-vertex-modulecandidate
new-fragment-module
live-shader-pipeline-candidate-fragment-modulecandidate
new-owned-resources
removenil
listnew-pipelinenew-fragment-module
and
live-shader-pipeline-candidate-vertex-module-owned-pcandidate
new-vertex-module
retirement-errorsnil

Publish the complete replacement as one owner-side state transition. The application calls at a frame boundary, so command encoding before this transition observes the old cohort and encoding after it the new.

setf
live-shader-pipeline-vertex-specificationartifact
live-shader-pipeline-candidate-vertex-specificationcandidate
live-shader-pipeline-vertex-loweringartifact
live-shader-pipeline-candidate-vertex-loweringcandidate
live-shader-pipeline-vertex-moduleartifact
new-vertex-module
live-shader-pipeline-vertex-module-owned-partifact
live-shader-pipeline-candidate-vertex-module-owned-pcandidate
live-shader-pipeline-specificationartifact
live-shader-pipeline-candidate-specificationcandidate
live-shader-pipeline-loweringartifact
live-shader-pipeline-candidate-loweringcandidate
live-shader-pipeline-fragment-moduleartifact
new-fragment-module
live-shader-pipeline-native-pipelineartifact
new-pipeline
live-shader-pipeline-statusartifact
:installed
live-shader-pipeline-diagnosticartifact
nil
live-shader-pipeline-installed-revisionartifact
revision
live-shader-pipeline-installed-source-revisionartifact
source-revision
live-shader-pipeline-attempted-source-revisionartifact
source-revision
live-shader-pipeline-installed-source-valuesartifact
copy-list
live-shader-pipeline-candidate-source-valuescandidate

Ownership has crossed to artifact. Detach before retirement so cleanup after any future change to this function cannot destroy installed state.

setf
live-shader-pipeline-candidate-pipelinecandidate
nil
live-shader-pipeline-candidate-fragment-modulecandidate
nil
live-shader-pipeline-candidate-vertex-modulecandidate
nil
live-shader-pipeline-candidate-vertex-module-owned-pcandidate
nil

Backends retire resources across their actual submission-completion frontier. Do not retire an object a backend deliberately reused for the new cohort, and try every distinct old owner even if one retirement errs.

dolist
resource
remove-duplicates
remove-if
lambda
resource
or
nullresource
memberresourcenew-owned-resources:test#'eq
listold-pipelineold-vertex-moduleold-fragment-module
:test#'eq
handler-case
destroyresource
error
condition
pushconditionretirement-errors
whenretirement-errors

The replacement remains installed. Preserve the exceptional native retirement as diagnostic state; backend ledgers retain retry custody.

setf
live-shader-pipeline-diagnosticartifact
firstretirement-errors
artifact
defunmake-live-shader-pipeline
&keyrole
stage:fragment
vertex-rolelabeldevicelayoutvertex-modulevertex-bufferstarget-formattarget-blendprimitivedepth-stencil

Create and install a live mathematical render pipeline.

vertex-module is borrowed when vertex-role is NIL and stage is not :VERTEX. Every module compiled from role or vertex-role is owned by the returned artifact and retired on replacement or explicit release.

let
generic-function
dependentnil
vertex-dependentnil
artifactnil
completed-pnil

Begin the cleanup boundary before subscribing: failed initialization must never leave an invisible dependent attached to the shader generic.

unwind-protect
let
setfdependent
luv.shader:make-shader-definition-dependentgeneric-function
listrolestage
vertex-dependent
whenvertex-role
luv.shader:make-shader-definition-dependentgeneric-function
listvertex-role:vertex
artifact
make-instance'live-shader-pipeline:rolerole:stagestage:labellabel:devicedevice:vertex-rolevertex-role:vertex-dependentvertex-dependent:layoutlayout:vertex-modulevertex-module:vertex-buffersvertex-buffers:target-formattarget-format:target-blendtarget-blend:primitiveprimitive:depth-stencildepth-stencil:dependentdependent
let
unwind-protect
install-live-shader-pipeline-candidateartifact0source-revisioncandidate
setfcompleted-pt
artifact
unlesscompleted-p
whenvertex-dependent
defunlive-shader-pipeline-refresh-p
artifactsource-revision
or
luv.shader:shader-definition-change-pending-p
live-shader-pipeline-dependentartifact
let
dependent
live-shader-pipeline-vertex-dependentartifact
>source-revision
live-shader-pipeline-attempted-source-revisionartifact
not
luv.shader:shader-source-value-references-current-p
live-shader-pipeline-attempted-source-valuesartifact
defun%refresh-live-shader-pipeline
artifact

Owner-side implementation; caller holds artifact's mutation lock.

unless
member
live-shader-pipeline-statusartifact
'
:releasing:released
let
when
live-shader-pipeline-refresh-partifactsource-revision
multiple-value-bind
revisionevent
luv.shader:shader-definition-change-snapshot
live-shader-pipeline-dependentartifact
declare
ignoreevent
multiple-value-bind
vertex-revisionvertex-event
let
dependent
live-shader-pipeline-vertex-dependentartifact
ifdependent
values0nil
declare
ignorevertex-event
setf
live-shader-pipeline-statusartifact
:building
handler-case
let
unwind-protect
install-live-shader-pipeline-candidateartifact
1+
live-shader-pipeline-installed-revisionartifact
source-revisioncandidate
error
condition

A failed edit is diagnostic state, not a rendering outage.

setf
live-shader-pipeline-statusartifact
:failed
live-shader-pipeline-diagnosticartifact
condition
live-shader-pipeline-attempted-source-revisionartifact
source-revision

A definition which races this attempt remains pending because acknowledgement is capped at the snapshots taken above.

luv.shader:acknowledge-shader-definition-change
live-shader-pipeline-dependentartifact
revision
let
dependent
live-shader-pipeline-vertex-dependentartifact
whendependent
artifact
defmethodrefresh-live-artifact
sb-thread:with-mutex
live-shader-pipeline-mutation-lockartifact
defunrefresh-live-shader-pipeline
artifact

Compatibility spelling for refresh-live-artifact.

defun%release-live-shader-pipeline
artifact

Owner-side implementation; caller holds artifact's mutation lock.

unless
eq
live-shader-pipeline-statusartifact
:released
let*
dependent
live-shader-pipeline-dependentartifact
vertex-dependent
live-shader-pipeline-vertex-dependentartifact
pipeline
live-shader-pipeline-native-pipelineartifact
fragment-module
live-shader-pipeline-fragment-moduleartifact
vertex-module
and
live-shader-pipeline-vertex-module-owned-partifact
live-shader-pipeline-vertex-moduleartifact
resources
remove-duplicates
removenil
listpipelinefragment-modulevertex-module
:test#'eq

Relinquish every application-visible ownership claim before native retirement. A backend keeps custody when destruction reports an error; retaining the wrappers here would permit resurrection or a second retirement attempt by a later RELEASE call.

setf
live-shader-pipeline-statusartifact
:releasing
live-shader-pipeline-dependentartifact
nil
live-shader-pipeline-vertex-dependentartifact
nil
live-shader-pipeline-native-pipelineartifact
nil
live-shader-pipeline-fragment-moduleartifact
nil
live-shader-pipeline-vertex-moduleartifact
nil
live-shader-pipeline-vertex-module-owned-partifact
nil
unwind-protect
handler-case
with-release-report
releasing:shader-definition-dependent
releasing:vertex-shader-definition-dependent
whenvertex-dependent
loopforresourceinresourcesforindexfrom0do
releasing
list:live-shader-resourceindex
destroyresource
error
condition
setf
live-shader-pipeline-diagnosticartifact
condition
errorcondition

RELEASED is terminal even when reporting retirement failure.

setf
live-shader-pipeline-statusartifact
:released
nil
defmethodrelease-live-artifact
sb-thread:with-mutex
live-shader-pipeline-mutation-lockartifact
defunrelease-live-shader-pipeline
artifact

Compatibility spelling for release-live-artifact.