luv

Workshop wiki

portal.lisp

luvcraft/portal.lisp

system luvcraft/core · 13 definitions · on GitHub

A portal: another luvcraft, running in another process, on a world screen.

The child draws into an IOSurface (see mirror.lisp); this side wraps the same surfaces as textures and draws one on a world panel through the video screen's quad vertex stage and a fragment stage of its own, the :PORTAL-SCREEN role in shaders.lisp, which sets the picture on a matte inside the panel and marks it as a transmission. A small thread keeps asking the child for frames, one ring slot at a time. The scene shows the slot the child finished last; the pump never asks the child to draw into that slot or the one shown before it, which a parent frame in flight may still be sampling. So the ring has three slots and nobody tears.

in-package#:luvcraft
defclassluvcraft-portal
mirror:initarg:mirror:readerluvcraft-portal-mirror
session:initarg:session:readerluvcraft-portal-session

One texture and view per ring slot.

textures:initarg:textures:readerluvcraft-portal-textures
views:initarg:views:readerluvcraft-portal-views

The slot the scene draws, and the slot it drew before that.

shown-slot:initform0:accessorluvcraft-portal-shown-slot
previous-slot:initform0:accessorluvcraft-portal-previous-slot
sampler:initarg:sampler:readerluvcraft-portal-sampler
uniform-buffer:initarg:uniform-buffer:readerluvcraft-portal-uniform-buffer
layout:initarg:layout:readerluvcraft-portal-layout
pipeline:initarg:pipeline:readerluvcraft-portal-pipeline
vertex-buffer:initarg:vertex-buffer:readerluvcraft-portal-vertex-buffer
instance-buffer:initarg:instance-buffer:readerluvcraft-portal-instance-buffer
frame-bind-groups:initform
make-hash-table:test#'eq
:readerluvcraft-portal-frame-bind-groups
pump:initformnil:accessorluvcraft-portal-pump
running-p:initformt:accessorluvcraft-portal-running-p
frames-per-second:initarg:frames-per-second:initform30:readerluvcraft-portal-frames-per-second
on-stop:initarg:on-stop:initformnil:readerluvcraft-portal-on-stop
:documentation

A child game's frames on a rectangle in this game's world.

defunluvcraft-portal-frame-bind-groups-for
portalframe

One bind group per ring slot for frame, made on first use.

or
gethashframe
luvcraft-portal-frame-bind-groupsportal
setf
gethashframe
luvcraft-portal-frame-bind-groupsportal
map'vector
lambda
view
create
luvcraft-session-device
luvcraft-portal-sessionportal
make-bind-group-descriptor:label"luvcraft portal frame bindings":layout
luvcraft-portal-layoutportal
:entries`
:binding0:resource,view
:binding1:resource,
luvcraft-portal-samplerportal
:binding2:resource,
luvcraft-frame-uniform-bufferframe
:binding3:resource,
luvcraft-portal-uniform-bufferportal
luvcraft-portal-viewsportal
defmethodencode-luvcraft-overlay
sessionpasssurface-texture
encode-luvcraft-portal-pictureportalsessionpasssurface-texture
defunencode-luvcraft-portal-picture
portalsessionpasssurface-texture

Draw portal's panel into the open scene pass. Overlays that own a portal (the terminal wall) call this from their own drawing so their glass goes on top; a free portal is an overlay itself and calls it from encode-luvcraft-overlay.

let*
frame
luvcraft-frame-statesessionsurface-texture
mirror
luvcraft-portal-mirrorportal
slot
or
luvcraft-mirror-completed-slotmirror
0

Publish which slot this frame samples, so the pump steers clear of it and of the one the previous frame may still be reading.

unless
=slot
luvcraft-portal-shown-slotportal
setf
luvcraft-portal-previous-slotportal
luvcraft-portal-shown-slotportal
luvcraft-portal-shown-slotportal
slot
set-pipelinepass
live-shader-pipeline-native-pipeline
luvcraft-portal-pipelineportal
set-vertex-bufferpass0
luvcraft-portal-vertex-bufferportal
set-vertex-bufferpass1
luvcraft-portal-instance-bufferportal
drawpass61
defunluvcraft-portal-free-slot
portal

A ring slot no parent frame is showing, was showing a frame ago, or is about to show (the child's last completed slot); NIL when the ring is full, which means the parent has not yet caught up and the pump should wait.

let*
mirror
luvcraft-portal-mirrorportal
shown
luvcraft-portal-shown-slotportal
previous
luvcraft-portal-previous-slotportal
completed
luvcraft-mirror-completed-slotmirror
loopforcandidatefrom
1+shown
forslot=
modcandidatecount
repeatcountunless
or
=slotshown
=slotprevious
eqlslotcompleted
returnslot
defmethodluvcraft-overlay-live-shader-pipelines
list
luvcraft-portal-pipelineportal
defmethodrelease-luvcraft-overlay
setf
luvcraft-portal-running-pportal
nil
let
pump
luvcraft-portal-pumpportal

The pump itself may be the one releasing us, through ON-STOP.

when
andpump
sb-thread:thread-alive-ppump
not
eqpumpsb-thread:*current-thread*
sb-thread:join-threadpump:defaultnil
stop-luvcraft-mirror
luvcraft-portal-mirrorportal
with-release-warnings
loopforgroupsbeingthehash-valuesof
luvcraft-portal-frame-bind-groupsportal
do
loopforgroupacrossgroupsdo
releasing:portal-bind-group
destroygroup
clrhash
luvcraft-portal-frame-bind-groupsportal
releasing:portal-pipeline
release-live-shader-pipeline
luvcraft-portal-pipelineportal
dolist
resource
append
list
luvcraft-portal-instance-bufferportal
luvcraft-portal-vertex-bufferportal
luvcraft-portal-uniform-bufferportal
luvcraft-portal-samplerportal
luvcraft-portal-layoutportal
coerce
luvcraft-portal-viewsportal
'list
coerce
luvcraft-portal-texturesportal
'list
releasing:portal-resource
destroyresource
values
defunadopt-luvcraft-mirror-texture
devicemirrorslotformat

This process's texture over mirror's surface slot, for sampling.

let*
surface
native
luv.metal:new-metal-texture-for-iosurface
luv::metal-native-objectdevice
surfaceluv.metal:+texture-usage-shader-read+:label"luvcraft portal picture"
adopt-native-texturedevicenative
make-texture-descriptor:label"luvcraft portal picture":size
list
luvcraft-mirror-widthmirror
luvcraft-mirror-heightmirror
:dimensions:2d:formatformat:usage'
:texture-binding
defunluvcraft-portal-uniform-data
picture-rectopenedtexel-utexel-v

The two vec4s of *PORTAL-UNIFORM-MEMBERS*: picture u0 v0 u1 v1, then the moment the link opened and the picture's texel size.

let
data
make-array8:element-type'single-float
loopforvaluein
appendpicture-rect
listopenedtexel-utexel-v0.0
forindexfrom0do
setf
arefdataindex
coercevalue'single-float
data
defunluvcraft-session-elapsed-seconds
session

The value the frame environment carries as elapsed time, for shaders.

mod
or
luvcraft-session-last-frame-timesession
0d0
3600d0
defunopen-luvcraft-portal
session&keymirror
lift1.5
screen-height3.0
rectangleon-stop
attach-pt
frames-per-second30

Show a child game on a world panel. With no mirror, spawn a child of width x height points; otherwise mirror is a negotiated, ready one (say, from the portal server).

rectangle, when given, is called with the picture's aspect and returns the panel's world origin, right edge, and up edge, and optionally a fourth value: the picture's rectangle inside the panel as (u0 v0 u1 v1), the whole panel by default. Without rectangle the picture is its own panel, screen-height cells tall before the camera.

on-stop, when given, is called with the portal if its pump stops on its own, which is to say the child went away. With attach-p the portal is added to session's overlays and draws itself; an overlay that owns the portal passes NIL and draws it through encode-luvcraft-portal-picture. Returns the portal.

let*
device
luvcraft-session-devicesession
camera
luvcraft-session-camerasession
mirror
format
canvas-format
luvcraft-session-contextsession
textures
views
samplernil
pipelinenil
uniform-buffernil
vertex-buffernil
instance-buffernil
portalnil
unwind-protect
let*
aspect
/
luvcraft-mirror-widthmirror
luvcraft-mirror-heightmirror
screen-width
*screen-heightaspect
setftextures
coerce
loopforslotbelowcollect
adopt-luvcraft-mirror-texturedevicemirrorslotformat
'vector
views
map'vector
lambda
texture
createdevice
make-texture-view-descriptor:texturetexture
textures
sampler
createdevice
make-sampler-descriptor:label"luvcraft portal sampler":mag-filter:linear:min-filter:linear:mipmap-filter:nearest
uniform-buffer
createdevice
make-buffer-descriptor:label"luvcraft portal uniform":size
*48
:usage'
:uniform
layout
createdevice
make-bind-group-layout-descriptor:label"luvcraft portal layout":entries'
:binding0:type:texture
:binding1:type:sampler
:binding2:type:uniform-buffer
:binding3:type:uniform-buffer
vertex-buffer
createdevice
make-buffer-descriptor:label"luvcraft portal quad":size
*418
:usage'
:vertex:copy-dst
instance-buffer
createdevice
make-buffer-descriptor:label"luvcraft portal instance":size
*49
:usage'
:vertex:copy-dst
multiple-value-bind
originright-edgeup-edgepicture-rect
ifrectangle
funcallrectangleaspect
video-screen-rectangle-before-cameracameradistanceliftscreen-widthscreen-height
write-bufferinstance-buffer
make-video-screen-instancesoriginright-edgeup-edge
write-bufferuniform-buffer
luvcraft-portal-uniform-data
orpicture-rect'
0.00.01.01.0
/1.0
luvcraft-mirror-widthmirror
/1.0
luvcraft-mirror-heightmirror
setfpipeline
make-live-shader-pipeline:role:portal-screen:vertex-role:video-screen:label"luvcraft portal pipeline":devicedevice:layoutlayout:vertex-buffers'
:array-stride12:attributes
:shader-location0:offset0:format:float32x3
:array-stride36:step-mode:instance:attributes
:shader-location1:offset0:format:float32x3
:shader-location2:offset12:format:float32x3
:shader-location3:offset24:format:float32x3
:target-format+luvcraft-scene-color-format+:primitive'
:topology:triangle-list
:depth-stencil'
:format:depth32-float:depth-write-enabledt:depth-compare:less
setfportal
make-instance'luvcraft-portal:mirrormirror:sessionsession:texturestextures:viewsviews:samplersampler:uniform-bufferuniform-buffer:layoutlayout:pipelinepipeline:vertex-buffervertex-buffer:instance-bufferinstance-buffer:frames-per-secondframes-per-second:on-stopon-stop
setf
luvcraft-portal-pumpportal
sb-thread:make-thread:name"luvcraft portal pump"
whenattach-p
portal
unlessportal
with-release-warnings
whenpipeline
dolist
resource
append
listinstance-buffervertex-bufferuniform-buffersamplerlayout
coerceviews'list
coercetextures'list
whenresource
releasing:portal-resource
destroyresource
releasing:portal-mirror
defunpump-luvcraft-portal
portal

Ask the child for frames at the portal's rate until the portal is released.

let
period
/1.0
luvcraft-portal-frames-per-secondportal
handler-case
loopwhile
luvcraft-portal-running-pportal
do
let
start
get-internal-real-time
cond
slot
request-luvcraft-mirror-frame
luvcraft-portal-mirrorportal
slot
let
elapsed
/
-
get-internal-real-time
start
floatinternal-time-units-per-second
when
<elapsedperiod
sleep
-periodelapsed

Every slot is spoken for until the parent draws again.

t
sleep0.001
error
condition
when
luvcraft-portal-running-pportal
warn"The luvcraft portal pump stopped: ~A"condition
setf
luvcraft-portal-running-pportal
nil
alexandria:when-let
on-stop
luvcraft-portal-on-stopportal
funcallon-stopportal
defunclose-luvcraft-portal
sessionportal

Stop portal and release it, whether or not it was one of session's overlays.

if
memberportal
luvcraft-session-overlayssession