Rendering and performance

How Strut draws, how it behaves under shader packs, and how to read the F9 overlay.

How Strut draws

Strut poses and draws models on the GPU: entities that share a model and a texture are drawn together in one instanced call, with bone matrices computed on the graphics card. This is what lets a hundred skinned characters cost a few draw calls instead of thousands. The GPU is the only path: entities, block entities, held and dropped items, worn armour and the models worn by another (armour, a hat) are all drawn by it, in the world, in a screen and under a shader pack. There is no CPU render path to fall back to, so a computer that cannot run it (OpenGL 4.3 with shader storage buffers) draws no Strut models.

Shader packs (Iris)

With Iris and a shader pack active, Strut draws through the pack's own programs with the pack's own vertex format, so entities are lit, shaded, and post-processed like any other entity. Nothing to configure and no toggle. Entities also cast shadows, because Strut draws them again into the shadow map from the sun's point of view, inside Iris's own shadow pass.

Translucency and overlays

Models draw cutout by default: opaque, with pixels below a small alpha discarded. Three things change that, all working the same with and without a shader pack.

A translucent model. Override these on your model. renderMode blends the whole model after everything opaque; shadingMode chooses how the model is shaded by the way its surfaces face (below), FLAT for none; translucentDepthBias and translucentCullsBackFaces tune the blended draws.

@Override
public RenderMode renderMode(HeroEntity creature) {
	return RenderMode.TRANSLUCENT;                 // the whole model blends, after everything opaque
}

@Override
public ShadingMode shadingMode(HeroEntity creature) {
	return ShadingMode.FLAT;                        // no shading by direction; the world's own light still applies
}

@Override
public DepthBias translucentDepthBias(HeroEntity creature) {
	return new DepthBias(-1f, -2f);                 // pull translucent bones and overlays towards the camera; not the model itself
}

@Override
public boolean translucentCullsBackFaces(HeroEntity creature) {
	return true;                                    // hide a closed cube's inside faces; a one-sided plane vanishes from behind
}

Per-bone layers. On an opaque model, flag one bone translucent, or draw a second texture over a bone. Both are bone overrides, so they inherit down the tree, a child can opt out, and calling them on the client makes them this viewer's own.

// A model that is opaque by default, with one bone drawn blended
creature.overrides().setTranslucent("blush", true);

// A second texture over a bone, translucent, on top of its own. Layers 0 to 3, a higher layer draws later
creature.overrides().setOverlay("head", 0, freckles);

// On the client the same calls are this viewer's own, never sent anywhere: a skin, a player's setting
creature.overrides().setTexture("head", skin);
creature.overrides().clearOverlay("head", 0);

Shading

How a model is shaded by the way its surfaces face is a choice of the model, StrutModel#shadingMode(animatable):

SOFT and TOON are lit by the light that is really there: the sun by day and the moon by night (through what is above the model), and the blocks that glow round it. Up to four lights act at once, the sky's and the three strongest of the others (the weaker ones are added together into the last), each lighting the side of the model that faces it and tinting it with its colour (white unless a mod sets one, see Coloured lights). The invisible light block is not counted. The side of a character facing a torch is its lit side, in the open the sun's side is, and in a dim place the model is flatter. A model drawn into a screen, or under a shader pack, has no such light and uses fixed ones.

The world's own light (block and sky light) darkens the model in every mode, and is eased over about a fifth of a second, so walking toward a torch brightens it smoothly instead of a level at a time. The faces of a flat cube (a plane or a card, with two faces on one plane) are lit by the side the eye is on, so the two look alike. Under a shader pack the pack lights the model and the mode has no effect.

A mod can change the shader itself, add coloured lights and run code around the draw: see Changing the render pipeline.

Animation level of detail

Animations are evaluated less often the farther an entity is from the camera. In a crowd (more than about two dozen entities drawn) the distances shrink, so the nearest entities stay smooth every frame and the rest take turns. It affects only how often a pose is recomputed, never server-side events. To keep an animatable frame-exact at any distance, override useAnimationLod() to return false.

The F9 overlay

Press F9 to toggle. Every line is a total for the last completed frame unless it says cumulative.

LineMeaning
Iris: active=…, shadow flush unreliable=…Whether a shader pack is driving Strut's Iris path, and whether the safety fallback has tripped.
events fired: server N / client N, unbound keys, capped sweeps, binding setsCumulative since launch. Compare before and after an action to see events fire. Unbound keys are sound cues no binding defines.
asset sync: …Whether server assets are active, how many files, and what the last join had to download.
shadow pass: submitted, flushes, instances drawn, skipped, pose failedThe shadow pass stage by stage: entities queued for the shadow map, flushes that ran, instances actually drawn, draws skipped because the pack supplied no program, and pose passes that failed. A missing shadow shows up as the stage where the numbers stop matching.
GPU instances drawn, GPU draw calls, Iris GPU draw callsThe GPU path's work. Instances count entities; draw calls count batches.
blended: model instances, translucent bones, overlaysThe blended passes' work: entities drawn as a translucent model, translucent bone ranges, and overlay ranges. All zero when nothing uses translucency.
animation, animations evaluated / skipped (LOD)Animation cost, and how many were skipped by level of detail.
submission, gpu flush (CPU), gpu draw (GPU)CPU time spent submitting and flushing, and GPU time spent drawing, in milliseconds.
render-thread allocMemory allocated on the render thread per frame. Steady state should be small.
physicsWhether physics is on and at which quality, how many instances and spring bones are simulated, and the steps taken and their cost.
lights (you), lights (last drawn)The lights the shading is given, for your player and for the model queued last: each is [direction |strength| #colour], in the order sky, strongest glowing block, the others together.
strongest block lightThe glowing block that is the strongest light round the last model looked at, and where it is.

When a shadow is missing

Read the shadow pass line as a funnel: