Repository navigation
Winding 1.9.0
Ready for games. A lost GPU device is recovered from instead of ending the session, and
prepare builds a level's pipelines behind the loading screen. Meshes can be made from arrays,
and nodes hidden, tinted, lit up, found by name and placed in the world whatever their parents,
all without leaving their batches. The clock slows and stops for a hit-stop or a pause. Shadows
cost less: cascades skip the casters that don't reach them and cast a level of detail their maps
can show, and a point light redraws only the faces something moved in. And it starts faster,
from 3.1 to 2.3 s on a first launch in Electron.
Added
engine.clock.timeScale: slow motion, hit-stop and pause.
0.25runs the world at a quarter speed and0stops it:updatesteps keep their length and
come less often, and animations and particles slow with them, while a HUD scene keeps real time
so a pause menu still moves.clock.deltais the frame's simulated seconds.node.name,node.find(name)and
node.findAll(name): a node keeps its name from the glTF file, and
a model's or a level's nodes can be found by it -- spawn points and drop zones marked in Blender,
the hand bone a weapon goes in, the door to take off. A RegExp finds a family (/^spawn/).
createNodetakes anametoo.node.setWorldPosition,
node.setWorldRotation,
node.worldRotationand
setParent(node, { keepWorld }): a node placed in the world
whatever its parents, from where they are now -- a physics body's pose on a ragdoll's bone, set
root to tip in one step -- and moved to a new parent without moving: a wall piece or a car door
breaking off, a weapon picked up where it lay.node.setTint(color)andnode.setGlow(color):
colour a mesh and everything under it at runtime -- factions, classes, players, a hit's flash --
without a copy of the model per colour. Tints multiply and glows add down the hierarchy, and
the meshes stay in their batches: tinting every unit differently draws with the same calls as
tinting none. They ride in padding the per-object data already had, so nothing grew, and a
frame where tints change spends 0.1 ms on 9,000 nodes.engine.prepare(scene, camera): builds everything a scene will
draw with, behind a loading screen. Probes, decals, AO, OIT, TAA and FXAA are built the first
time a frame asks for them, and until then frames draw without them, so they appeared a few
frames into play. A scene with decals, AO, TAA and particles started 17 pipeline builds in its
first frames; afterprepare, none, and AO draws from the first frame.engine.createMesh(options)makes a mesh from arrays --
positions, and optionally indices, normals, UVs and vertex colours, with a colour, metalness and
roughness -- for terrain, generated maps and props built in code. Without normals it is shaded
flat, the low-poly look. It comes back as a model, asloaddoes: culled, merged, shadowed and
rebuilt after a lost GPU device like any loaded file.node.setVisible(visible)shows or hides a node and everything
under it -- meshes and their shadows, sprites, text, shapes, paths, tilemaps, emitters, decals,
splats and lights -- andnode.visiblereads it back. A hidden node
is not hit by a ray or a pick. Nothing is rebuilt to hide or show, so a pool of bullets or debris
is far cheaper than removing and adding: the work is one pass over the nodes in a frame where
something changed.- A lost GPU device is recovered from. A driver reset, the GPU
process crashing, or a Steam Deck waking from sleep used to end the session: the engine destroyed
itself and the page had to reload. Now it gets a new device on the same canvas and rebuilds what
it loaded in place -- models read again from where they came from, under the same material ids
and geometry offsets -- so scenes, nodes and loaded objects carry on untouched. A crashed GPU
process in Electron took 1.7 s to come back from.onDeviceLostis toldaction: 'recover'
andrecovered, a promise for when it is drawing again.engine.gpu.loseForTest()
loses the device on purpose, to test it.
Changed
- A point light's shadow redraws only the faces something moved in. Its six cube faces were
all drawn again whenever anything moved inside its reach; now only the faces the moved box
reaches into are, and the frame is the same to the pixel. A walker beside a torch redraws two or
three faces, not six. A dungeon of 36 shadowed torches and 40 walkers: 21.9 to 13.8 ms of GPU
time on Iris Xe. - Starting is faster.
Winding.createbuilt its passes' pipelines one after another, and made
16 compute pipelines synchronously, each compiled on the GPU process's main thread while
everything else waited. They now compile side by side, off that thread. A 300-character scene
in Electron on Iris Xe, launch to first frame: 3.1 to 2.3 s on a first launch, with empty
shader caches (create1.7 to 1.05 s); 1.2 to 1.0 s on every launch after. engine.preparedraws the shadow cascades both ways they draw.
The first frame to cull a batch of casters for a cascade -- the first time a crowd straddles a
cascade's edge -- paid 0.1 s of the driver's recording, once, mid-play. That frame is now one of
prepare's. The first frame of play after it went from 125-180 ms to 25 ms, and the first two
seconds from 7-19 missed frames to none.- A shadow cascade casts a level of detail its map can show. A group of levels (
MSFT_lod)
cast its shadow at the level the camera showed, into every cascade, however few texels the
cascade had for it -- and at its finest from behind the camera, which the camera's test takes for
infinitely close. Each cascade now casts the coarser of the camera's level and the one its own
resolution calls for, never finer than the mesh on screen. Nine hundred spheres in three levels,
every cascade redrawn: shadows from 5.2 to 3.7 ms of GPU time. - A shadow cascade draws only the casters that reach it. A cascade used to skip a batch of
instances only when the box around all of them missed it, so a crowd spread across a map drew
every member into every cascade. Each cascade drawn whole is now culled caster by caster on the
GPU first. Three hundred walkers, 9,000 parts, moving under a sun: shadows from 12.9 to 9.5 ms
of GPU time on Iris Xe, the near cascade from 3.0 to 0.6 ms. The far cascades, which hold the
whole crowd, cost what they did.
npm install winding-engine