luv

Workshop wiki

mirror.lisp

mcclim/mirror.lisp

system luv/mcclim · 63 definitions · on GitHub

in-package#:mcluv
defclassluv-mirror
sheet:initarg:sheet:readermirror-sheet
target:initarg:target:readermirror-target
context:initarg:context:initformnil:accessormirror-context
device:initarg:device:initformnil:accessormirror-device
embedded-p:initarg:embedded-p:initformnil:readermirror-embedded-p
:documentation

A McCLIM sheet's relationship with a luv presentation target.

TARGET is initially a native canvas. It is intentionally not part of the mirror's identity: a later target may be a texture presented on a 3D quad.

defclassluv-raster-mirror
luv-mirrormcclim-render:image-mirror-mixin
texture:initformnil:accessormirror-texture
compositor:initformnil:accessormirror-compositor
:documentation

A luv mirror retaining McCLIM's CPU raster image for later upload.

defclassluv-gpu-mirror
texture:initformnil:accessormirror-texture
compositor:initformnil:accessormirror-compositor
frame-states:initform
make-hash-table:test#'equal
:readergpu-mirror-frame-states
vertex-module:initformnil:accessorgpu-mirror-vertex-module
fragment-module:initformnil:accessorgpu-mirror-fragment-module
layout:initformnil:accessorgpu-mirror-layout
uniform-buffer:initformnil:accessorgpu-mirror-uniform-buffer
bind-group:initformnil:accessorgpu-mirror-bind-group
pipeline:initformnil:accessorgpu-mirror-pipeline
format:initformnil:accessorgpu-mirror-format
analytic-vertex-module:initformnil:accessorgpu-mirror-analytic-vertex-module
analytic-fragment-module:initformnil:accessorgpu-mirror-analytic-fragment-module
analytic-pipeline:initformnil:accessorgpu-mirror-analytic-pipeline
relief-vertex-module:initformnil:accessorgpu-mirror-relief-vertex-module
relief-fragment-module:initformnil:accessorgpu-mirror-relief-fragment-module
relief-pipeline:initformnil:accessorgpu-mirror-relief-pipeline
gradient-analytic-vertex-module:initformnil:accessorgpu-mirror-gradient-analytic-vertex-module
gradient-analytic-fragment-module:initformnil:accessorgpu-mirror-gradient-analytic-fragment-module
gradient-analytic-pipeline:initformnil:accessorgpu-mirror-gradient-analytic-pipeline
image-vertex-module:initformnil:accessorgpu-mirror-image-vertex-module
image-fragment-module:initformnil:accessorgpu-mirror-image-fragment-module
image-layout:initformnil:accessorgpu-mirror-image-layout
image-sampler:initformnil:accessorgpu-mirror-image-sampler
image-pipeline:initformnil:accessorgpu-mirror-image-pipeline
image-paints:initform
make-hash-table:test#'eq
:readergpu-mirror-image-paints
lattice-vertex-module:initformnil:accessorgpu-mirror-lattice-vertex-module
lattice-fragment-module:initformnil:accessorgpu-mirror-lattice-fragment-module
lattice-layout:initformnil:accessorgpu-mirror-lattice-layout
lattice-pipeline:initformnil:accessorgpu-mirror-lattice-pipeline
lattice-paints:initform
make-hash-table:test#'eq
:readergpu-mirror-lattice-paints
slug-cache:initformnil:accessorgpu-mirror-slug-cache
text-vertex-module:initformnil:accessorgpu-mirror-text-vertex-module
text-fragment-module:initformnil:accessorgpu-mirror-text-fragment-module
text-layout:initformnil:accessorgpu-mirror-text-layout
text-pipeline:initformnil:accessorgpu-mirror-text-pipeline
text-bind-groups:initform
make-hash-table:test#'eq
:readergpu-mirror-text-bind-groups

Repaint constructs a complete immutable CPU revision, then publishes its pointer under this small lock. A direct compositor materializes that revision into the destination drawable's bounded frame slot; the mirror itself never owns one mutable source buffer shared by in-flight frames.

prepared-revision-lock:initform
sb-thread:make-mutex:name"McCLIM prepared GPU revision"
:readergpu-mirror-prepared-revision-lock
prepared-revision-counter:initform0:accessorgpu-mirror-prepared-revision-counter
prepared-revision:initformnil:accessor%gpu-mirror-prepared-revision
:documentation

A direct GPU target for an ordered luv-gpu-medium drawing stream.

An embedded mirror retains an immutable prepared CPU revision for the game's final pass; a standalone mirror renders into its native canvas drawable.

defmethodprint-object
stream
print-unreadable-object
mirrorstream:typet:identityt
formatstream"~S on ~S"
mirror-sheetmirror
mirror-targetmirror
defunsheet-title
sheet
or
and
typepsheet'top-level-sheet-mixin
sheet-pretty-namesheet
"McCLIM on luv"
defgenericmake-luv-mirror
portsheettargetregion
:documentation

Construct port's renderer-specific mirror for target.

defmethodmake-luv-mirror
sheettargetregion
declare
ignoreportregion
make-instance'luv-mirror:sheetsheet:targettarget
defmethodmake-luv-mirror
sheettargetregion
let
mirror
make-instance'luv-raster-mirror:sheetsheet:targettarget
mcclim-render::%set-image-regionmirrorregion
mirror
defmethodmake-luv-mirror
sheettargetregion
declare
ignoreportregion
make-instance'luv-gpu-mirror:sheetsheet:targettarget
defuncanvas-button-to-clim-button
button
ecasebutton
:left+pointer-left-button+
:middle+pointer-middle-button+
:right+pointer-right-button+
defunupdate-luv-pointer-position
pointercanvasevent
multiple-value-bind
canvas-xcanvas-y
setf
luv-pointer-xpointer
+
orcanvas-x0
luv:canvas-pointer-event-xevent
luv-pointer-ypointer
+
orcanvas-y0
luv:canvas-pointer-event-yevent
defundrain-luv-frame-events
sheet

Handle events for a callback-only frame while dispatching native input.

Conventional RUN-FRAME-TOP-LEVEL frames consume their own queues instead.

let
queue
climi::frame-event-queue
pane-framesheet
loopforevent=
climi::queue-read-no-hangqueue
whileeventdo
handle-event
event-sheetevent
event
defgenericservice-luv-frame-events
mirror
:documentation

Drain callback-only input and publish any resulting visual state.

defmethodservice-luv-frame-events

Service callback-only frames without stealing a real frame loop's queue.

let*
sheet
mirror-sheetmirror
frame
pane-framesheet
unless
climi::frame-processframe
defundistribute-canvas-pointer-event
mirrorcanvaseventclass&keybutton
let*
sheet
mirror-sheetmirror
port
portsheet
distribute-eventport
apply#'make-instanceclass:sheetsheet:pointerpointer:x
luv:canvas-pointer-event-xevent
:y
luv:canvas-pointer-event-yevent
:timestamp
luv:canvas-event-timestampevent
whenbutton
list:buttonbutton
defmethodluv:handle-canvas-event
when
member
luv:canvas-pointer-event-buttonevent
'
:left:middle:right
let*
port
port
mirror-sheetmirror
button
canvas-button-to-clim-button
luv:canvas-pointer-event-buttonevent
setf
luv-pointer-button-statepointer
logior
luv-pointer-button-statepointer
button
distribute-canvas-pointer-eventmirrorcanvasevent'pointer-button-press-event:buttonbutton
defmethodluv:handle-canvas-event
when
member
luv:canvas-pointer-event-buttonevent
'
:left:middle:right
let*
port
port
mirror-sheetmirror
button
canvas-button-to-clim-button
luv:canvas-pointer-event-buttonevent
setf
luv-pointer-button-statepointer
logandc2
luv-pointer-button-statepointer
button
distribute-canvas-pointer-eventmirrorcanvasevent'pointer-button-release-event:buttonbutton
defuncanvas-modifiers-to-clim-state
modifiers

The CLIM modifier state for a luv modifiers list.

Lock modifiers (caps lock, num lock) have no CLIM bit and a player with one on is still playing the game, so they contribute nothing rather than fail.

reduce#'logior
mapcar
lambda
modifier
casemodifier
:shift+shift-key+
:control+control-key+
:meta+meta-key+
:super+super-key+
t0
modifiers
:initial-value0
defundistribute-canvas-key-event
mirroreventclass
let*
sheet
mirror-sheetmirror
port
portsheet
modifier-state
canvas-modifiers-to-clim-state
luv:canvas-key-event-modifiersevent
setf
luv-port-modifier-stateport
modifier-state
multiple-value-bind
xy
climi::sheet-pointer-positionsheetpointer
distribute-eventport
apply#'make-instanceclass:sheetsheet:xx:yy:timestamp
luv:canvas-event-timestampevent
:modifier-statemodifier-state:key-name
luv:canvas-key-event-key-nameevent
when
luv:canvas-key-event-characterevent
list:key-character
luv:canvas-key-event-characterevent
defmethodluv:handle-canvas-event
declare
ignorecanvas
let
sheet
mirror-sheetmirror
distribute-event
portsheet
make-instance'window-manager-focus-event:sheetsheet:timestamp
luv:canvas-event-timestampevent
defunmake-mirror-configuration-event
mirrorwidthheighttimestamp
let
sheet
mirror-sheetmirror

SDL resize events contain dimensions but not an origin. Preserve the native origin McCLIM already knows when constructing its region.

with-bounding-rectangle*
xy
climi::sheet-mirror-geometrysheet
make-instance'window-configuration-event:sheetsheet:timestamptimestamp:region
make-bounding-rectanglexy
defmethodluv:handle-canvas-event
declare
ignorecanvas
let
sheet
mirror-sheetmirror
width
luv:canvas-window-event-widthevent
height
luv:canvas-window-event-heightevent
distribute-event
portsheet
make-mirror-configuration-eventmirrorwidthheight
luv:canvas-event-timestampevent
defmethodluv:handle-canvas-event
declare
ignorecanvas

Configuration events are placed ahead of repaint events by McCLIM, so a logical resize is laid out before we redraw for the new pixel extent.

let
sheet
mirror-sheetmirror
distribute-event
portsheet
make-instance'window-repaint-event:sheetsheet:timestamp
luv:canvas-event-timestampevent
:region+everywhere+
defmethodluv:handle-canvas-event
declare
ignorecanvas

The GPU path has no dirty image to resize, but a new drawable extent still requires McCLIM to rebuild device-coordinate geometry before presentation.

let
sheet
mirror-sheetmirror
distribute-event
portsheet
make-instance'window-repaint-event:sheetsheet:timestamp
luv:canvas-event-timestampevent
:region+everywhere+
defmethodluv:handle-canvas-event
declare
ignorecanvas
let*
sheet
mirror-sheetmirror
frame
pane-framesheet

Callback-only demos are torn down by the canvas itself. A conventional frame top level needs the CLIM delete event so its unwind protocol runs.

when
climi::frame-processframe
distribute-event
portsheet
make-instance'window-manager-delete-event:sheetsheet:timestamp
luv:canvas-event-timestampevent
defunraster-mirror-image-size
mirror
let
image
mcclim-render:image-mirror-imagemirror
list
pattern-widthimage
pattern-heightimage
defunensure-raster-mirror-context
mirror
when
mirror-embedded-pmirror
return-fromensure-raster-mirror-context
mirror-contextmirror
let*
target
mirror-targetmirror
device
or
mirror-devicemirror
context
or
mirror-contextmirror
setf
mirror-contextmirror
luv:make-canvas-contexttargetluv:*gpu-provider*
luv:make-canvas-configuration:devicedevice
multiple-value-bind
context
defunrelease-mirror-device
mirror
alexandria:when-let
device
mirror-devicemirror
setf
mirror-devicemirror
nil
mirror
defunensure-raster-mirror-texture
mirrorcontextsize
let
texture
mirror-texturemirror
unless
andtexture
equal
luv:gpu-texture-sizetexture
appendsize'
1
eq
luv:gpu-texture-formattexture
format
let
replacement
luv:create
luv:make-texture-descriptor:label"McCLIM raster upload":sizesize:usage'
:copy-src:copy-dst:texture-binding
:dimensions:2d:formatformat
whentexture

A compositor may retain a view and descriptor for the old image. Release those dependents before destroying their source.

release-raster-mirror-compositor
mirror-compositormirror
setf
mirror-texturemirror
replacement
mirror-texturemirror
defgenericpresent-raster-mirror-texture
mirrorcontexttexturecompositor
:documentation

Present an uploaded McCLIM texture through an optional compositor.

defmethodpresent-raster-mirror-texture
contexttexture
compositornull
unless
mirror-embedded-pmirror
luv:present-canvas-framecontext
lambda
surfaceencoder
luv:encodeencoder
luv:make-gpu-copy-texture-command:sourcetexture:destinationsurface
defgenericrelease-mirror-compositor
compositor
:documentation

Exhaustively release resources retained by a mirror compositor.

defmethodrelease-mirror-compositor
compositornull
values
defgenericrelease-raster-mirror-compositor
compositor
:documentation

Legacy adapter for the application-neutral release-mirror-compositor.

defgenericprepare-mirror-compositor-revision
compositormirrorrevision
:documentation

Prepare compositor for immutable mirror revision outside a render pass.

defmethodprepare-mirror-compositor-revision
compositormirrorrevision
declare
ignorecompositormirrorrevision
values
defgenericprepare-mirror-compositor-target-revision
compositormirrorrevision&keytarget-formatdepth-stencil
:documentation

Prepare compositor for revision and an application-owned attachment.

This target-aware protocol is used when the application renders a retained mirror somewhere other than its canvas format. The default preserves the ordinary revision protocol for compositors without target-specific state.

defmethodprepare-mirror-compositor-target-revision
compositormirrorrevision&keytarget-formatdepth-stencil
declare
ignoretarget-formatdepth-stencil
prepare-mirror-compositor-revisioncompositormirrorrevision
defmethodpresent-mirror

Upload and present mirror when McCLIM has marked its image dirty.

let
target
mirror-targetmirror
deferred-sizenil
when
and
eq:open
not
region-equal
mcclim-render:image-dirty-regionmirror
+nowhere+

Acquire the image lock on the native thread. Acquiring it before dispatch would deadlock against native callbacks that draw.

luv:request-canvas-frametarget
lambda
timestamp
declare
ignoretimestamp
mcclim-render:with-image-locked
mirror
unless
region-equal
mcclim-render:image-dirty-regionmirror
+nowhere+
let*
image
mcclim-render:image-mirror-imagemirror
if
and
not
mirror-embedded-pmirror

Swapchain creation is sometimes the first point where a Wayland compositor reveals its assigned size. Leave the raster dirty and let McCLIM lay it out before presenting.

setfdeferred-size
let
luv:write-texture
luv:make-texture-copy:texturetexture
pattern-arrayimage
luv:make-texture-data-layout:bytes-per-row
*4
firstsize
:rows-per-image
secondsize
size
present-raster-mirror-texturemirrorcontexttexture
mirror-compositormirror
setf
mcclim-render:image-dirty-regionmirror
+nowhere+
whendeferred-size
destructuring-binddeferred-size
setf
luv:canvas-widthtarget
width
luv:canvas-heighttarget
height
let
sheet
mirror-sheetmirror
distribute-event
portsheet
distribute-event
portsheet
make-instance'window-repaint-event:sheetsheet:timestamp0:region+everywhere+
unless
climi::frame-process
pane-framesheet
mirror
defmethodrelease-mirror-presentation
release-raster-mirror-compositor
mirror-compositormirror
setf
mirror-compositormirror
nil
unless
mirror-embedded-pmirror
alexandria:when-let
texture
mirror-texturemirror
setf
mirror-texturemirror
nil
mirror
defmethodrealize-mirror
sheetmirrored-sheet-mixin
with-bounding-rectangle*
xy:widthwidth:heightheight
sheet
let*
region
make-rectangle*00
max1
ceilingwidth
max1
ceilingheight
mirror
make-luv-mirrorportsheetcanvasregion
whenembedded-p
setf
slot-valuemirror'embedded-p
t
mirror-contextmirror
*embedded-mirror-context*
mirror-devicemirror
*embedded-mirror-device*
unlessembedded-p
handler-case
progn
unlessembedded-p

realize-mirror's standard :AROUND method normally installs the direct mirror only after this primary method returns. Geometry initialization needs it sooner: without a native region the render medium clips every drawing operation to NOWHERE.

setf
sheet-direct-mirrorsheet
mirror

Synchronize McCLIM with whatever logical geometry is observable now. Presentation performs a second reconciliation if Wayland reveals the compositor-assigned extent only at swapchain time.

ifembedded-p
multiple-value-bind
actual-widthactual-height
setf
luv:canvas-widthcanvas
actual-width
luv:canvas-heightcanvas
actual-height
handle-eventsheet
make-mirror-configuration-eventmirroractual-widthactual-height0
pushmirror
port-mirrorsport
mirror
error
condition
when
eq
sheet-direct-mirrorsheet
mirror
setf
sheet-direct-mirrorsheet
nil
when
and
notembedded-p
member'
:opening:open
errorcondition
defmethodrealize-mirror
sheetmirrored-sheet-mixin

Resolve the renderer/base-port diamond explicitly. Renderer selection changes the mirror class, not the native host lifecycle.

call-next-method
defmethodrealize-mirror
sheetmirrored-sheet-mixin
call-next-method
defmethoddestroy-mirror
sheetmirrored-sheet-mixin
let
mirror
sheet-direct-mirrorsheet
whenmirror
let
target
mirror-targetmirror
unless
mirror-embedded-pmirror
when
member'
:opening:open
setf
mirror-contextmirror
nil
setf
port-mirrorsport
deletemirror
port-mirrorsport
defmethodenable-mirror
sheetmirrored-sheet-mixin
declare
ignoreport
alexandria:when-let
mirror
sheet-direct-mirrorsheet
let
target
mirror-targetmirror
unless
mirror-embedded-pmirror

There is no separate McCLIM top-level loop to provoke the first exposure. Paint and present before open-widget-lab returns instead of waiting for the first pointer event to dirty a gadget.

luv:request-canvas-frametarget
lambda
timestamp
declare
ignoretimestamp
repaint-sheetsheet+everywhere+
defmethoddisable-mirror
sheetmirrored-sheet-mixin
declare
ignoreport
alexandria:when-let
mirror
sheet-direct-mirrorsheet
unless
mirror-embedded-pmirror
luv:hide-canvas
mirror-targetmirror
defmethodset-mirror-geometry
sheetmirrored-sheet-mixin
region
declare
ignoreport
with-bounding-rectangle*
x1y1x2y2:widthwidth:heightheight
region
alexandria:when-let
mirror
sheet-direct-mirrorsheet
unless
mirror-embedded-pmirror
let
target
mirror-targetmirror
valuesx1y1x2y2
defmethodset-mirror-name
sheettop-level-sheet-mixin
name
declare
ignoreport
alexandria:when-let
mirror
sheet-direct-mirrorsheet
unless
mirror-embedded-pmirror
setf
luv:canvas-title
mirror-targetmirror
name
defmethodset-mirror-icon
sheettop-level-sheet-mixin
icon
declare
ignoreportsheeticon
nil
defmethodraise-mirror
sheettop-level-sheet-mixin
declare
ignoreport
alexandria:when-let
mirror
sheet-direct-mirrorsheet
luv:raise-canvas
mirror-targetmirror
defmethodbury-mirror
sheettop-level-sheet-mixin
declare
ignoreportsheet
nil
defmethodshrink-mirror
sheettop-level-sheet-mixin
declare
ignoreport
alexandria:when-let
mirror
sheet-direct-mirrorsheet
luv:minimize-canvas
mirror-targetmirror
defmethodunshrink-mirror
sheettop-level-sheet-mixin
declare
ignoreport
alexandria:when-let
mirror
sheet-direct-mirrorsheet
luv:restore-canvas
mirror-targetmirror