luvcraft/mirror.lisp
The child: render into a surface somebody else named.
Return an adopted texture over the IOSurface surface, sized like session's
frame, fit to be session's frame mirror. The caller destroys it.
Be the child: greet the parent on output naming screen, open a hidden game
of the size the parent answers with on input, tell it the frame's pixel extent,
and from then on render into whatever surfaces input names.
The window is in points; the frame is in pixels, and the surfaces have to be the frame's size, so the parent learns the pixel extent from us and creates the ring after.
The parent: a child on the far end of a stream and the surfaces it draws into.
Only when this process spawned the child itself.
What the child asked to appear on, from its hello.
The slot the child most recently finished, or NIL before the first.
Surface slot of mirror's ring; with no slot, the last completed one.
Read the child's lines until one starts with word; NIL if it never does.
A spawned child's stdout also carries the ordinary startup chatter of the game.
Take mirror from a fresh child's hello to a ready ring: hear the hello,
name a width x height window (points), hear the pixel extent, make slots
surfaces that size, hand their IDs over, and hear ready. Returns mirror.
Spawn a child game in a hidden width x height window (in points) over pipes,
give it a ring of slots surfaces the size of its pixel frame, and return the
mirror once it is drawing. Its stderr lands in build/luvcraft-mirror.log.
Ask the child for one frame into slot (default: the slot after the last
completed one) and return the slot once its pixels are in the surface.
Tell the child to quit, drop the stream, and release the ring.
The child's pixel at X, Y as (B G R A) bytes.
The child's whole frame as tightly packed BGRA bytes, copied out of the surface.
Write the child's current frame to pathname.
The interactive block world built on luv.
(session surface)Return an adopted texture over the IOSurface SURFACE, sized like SESSION's frame, fit to be SESSION's frame mirror. The caller destroys it.
(&key (input *standard-input*)
(output *standard-output*)
(screen "-")
(provider (make-instance 'luv:metal-gpu-provider)))Be the child: greet the parent on OUTPUT naming SCREEN, open a hidden game of the size the parent answers with on INPUT, tell it the frame's pixel extent, and from then on render into whatever surfaces INPUT names.
A provider for the system's preferred Metal device.
Logical conjunction of tests and raw truth values.
(&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…
(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.
(context)Return CONTEXT's configured drawable extent as (WIDTH HEIGHT), or NIL.
Logical disjunction of tests and raw truth values.
Test whether two compatible scalars are equal.
Logical negation of one test or raw truth value.
Test whether one compatible scalar is less than another.
(session timestamp &optional sample)Division of two represented quantities.
Convert one scalar float or unsigned value to a 32-bit float.
(queue)Block until all work submitted to QUEUE so far has completed on the GPU.
(handle)Logically invalidate HANDLE immediately. Native teardown may be deferred until submitted work which captured HANDLE has completed.
(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.
(surface)A child luvcraft rendering into a ring of IOSurfaces this process owns. WIDTH and HEIGHT are the surfaces', in pixels.
(mirror &optional slot)(mirror)(mirror control &rest arguments)(mirror word)Read the child's lines until one starts with WORD; NIL if it never does. A spawned child's stdout also carries the ordinary startup chatter of the game.
(mirror &key (width 640) (height 400) (slots 3))Take MIRROR from a fresh child's hello to a ready ring: hear the hello, name a WIDTH x HEIGHT window (points), hear the pixel extent, make SLOTS surfaces that size, hand their IDs over, and hear ready. Returns MIRROR.
(mirror)(width height &key (bytes-per-element 4)
(pixel-format +iosurface-pixel-format-bgra+)
(global-p t))Create a WIDTH x HEIGHT IOSurface and return its raw IOSurfaceRef. The caller owns one CF reference; release it with RELEASE-IOSURFACE. GLOBAL-P (deprecated by Apple, still honoured) is what lets another process find the surface by its integer ID with LOOKUP-IOSURFACE.
(&key (width 640) (height 400) (slots 3) executable)Spawn a child game in a hidden WIDTH x HEIGHT window (in points) over pipes, give it a ring of SLOTS surfaces the size of its pixel frame, and return the mirror once it is drawing. Its stderr lands in build/luvcraft-mirror.log.
(mirror &optional slot)Ask the child for one frame into SLOT (default: the slot after the last completed one) and return the slot once its pixels are in the surface.
The non-negative remainder of integer division.
(mirror x y)(surface x y)(mirror)Multiplication and scalar scaling.
((surface &key read-only) &body body)Addition over compatible quantities.
(mirror pathname)(pathname pixels width height format)
A luvcraft that draws for another luvcraft.
The experiment: the shipped executable is a boot core -- a Lisp with the whole game loaded and nothing yet asked of the GPU. A playing game can spawn one as a child, hand it the integer name of an IOSurface over the child's own stdin, and the child renders its own hidden game into that surface while the parent samples the very same pixels as a texture. No ports, no shared memory ceremony: a pipe carrying one number each way.
The protocol is lockstep and tiny. Child writes "hello SCREEN" (the wall it wants to appear on, or "-"). Parent writes "size W H" (points); child opens its hidden game and answers "extent W H" (pixels). Parent creates a ring of surfaces that size and writes "surfaces ID ID ID"; child wraps each and answers "ready". Parent writes "frame K", child renders one frame into slot K, waits for the GPU, and answers "done K". Parent writes "quit" or closes the pipe; child stops. The ring is what keeps the picture from tearing: the parent only ever asks for a slot it is not showing and was not showing a frame ago.
The lines travel over whichever two-way stream the two ends share: the pipes of a child the parent spawned itself, or a Unix socket the child connected to because
luvcraft_PARENT_SOCKET was in its environment (see portal-server.lisp).