Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Submit phases and feature renderers

Verified against Minecraft 26.3 · Reference · Hand-kept from SubmitNodeCollection and the FeatureRenderDispatcher constructor.

Everything an entity, block entity, particle or debug renderer draws in a level arrives as a submit node — the sky is drawn by its own renderer in its own frame-graph pass; the clouds, the weather and the world border by their own renderers inside the main pass; and terrain by its chunk sections. SubmitNodeCollection sorts those nodes into fifteen named phases as they come in, and twelve feature renderers turn them into vertices. The lecture that frames both is entity rendering, which names four of the phases and one of the renderers. Two more renderers are named elsewhere — TextFeatureRenderer on text and fonts, QuadParticleFeatureRenderer on particles — and everything else here is only here.

The fifteen phases

One SubmitNodeCollection holds fourteen of them, and a SubmitNodeStorage holds one collection per order bucket — the global draw order a submit chooses with SubmitNodeCollector.order — and the fifteenth, seeThrough, which every one of its collections shares. So “every order bucket” below means every one of those collections in turn.

Those fifteen are the set with improved transparency off, which is how every storage but LevelRenderer’s always runs; the level’s follows GameRenderer.useImprovedTransparency each frame, through SubmitNodeStorage.setUseImprovedTransparency. With it on, rows 2 to 10 and row 12 are ten names for one simple phase, SubmitNodeCollection.oitTranslucent, which FeatureRenderDispatcher.PreparedFrame.executeOit drains three times, once per OitStage. Row 11 is drained on its own, by FeatureRenderDispatcher.PreparedFrame.executeWaterMask — twice a frame when there is a water mask.

Rows 1 to 14 are in the order of SubmitNodeCollection.allPhases, and row 15 comes last because SubmitNodeStorage.drainPhases hands it over after every bucket’s. That is not the order they are drawn in, and neither is the last column: FeatureRenderDispatcher.PreparedFrame.executeTranslucent alone makes three separate sweeps over every order bucket, so the phases it drains are numbered by sweep — but within a sweep the order is the one the sweep’s own statements run in, not the order of the rows. Sweep 1 drains shadows, then translucent models, then name tags, then texts, then translucent custom geometry — so in each bucket translucent models, row 7, are drawn before the name tags and texts of rows 3 and 4.

Three of the fifteen are a TranslucentFeatureRenderPhase — rows 6, 7 and 15, translucentBlocksAndItems, translucentModels and seeThrough — and the other twelve a SimpleFeatureRenderPhase, which is not what two of the four translucent names suggest. What lets those three sort at all is on the node rather than the phase: TranslucentSubmit is the interface that carries it, through TranslucentSubmit.distanceToCameraSq, and five of the twelve renderers’ nested Submit records implement it — BlockModelFeatureRenderer, ItemFeatureRenderer, ModelFeatureRenderer, MovingBlockFeatureRenderer and TextFeatureRenderer. A phase can only order what its nodes can answer. What the two kinds do differently to a node’s chance of sharing a draw belongs to entity rendering.

#phasewhat lands in itdrained by
1SubmitNodeCollection.solidthe opaque default: models whose RenderType is not an outline and does not blend or forces the solid phase (RenderType.forceSolidModelPhase), block models whose render type neither blends nor outlines, moving blocks whose model does not declare the translucent material flag, an item’s quads whose render type does not blend, custom geometry that neither blends nor outlines, the opaque half of every quad-particle group, every flame and every leash — plus the opaque cases rows 3, 4, 5, 9, 10 and 12 send here.executeSolid
2SubmitNodeCollection.shadowsone node per SubmitNodeCollection.submitShadow, carrying the radius and the EntityRenderState.ShadowPiece list sampled at extract.executeTranslucent, sweep 1
3SubmitNodeCollection.nameTagsevery name tag’s node in Font.DisplayMode.NORMAL, see-through or not — a see-through one’s carries an emission bump on its light, opaque white and no background, and while the level renders goes to solid instead.executeTranslucent, sweep 1
4SubmitNodeCollection.textsworld-space text that is not a name tag, from SubmitNodeCollection.submitText, and a text display’s background, from SubmitNodeCollection.submitTextBackground, when not see-through — though while the level renders, text in opaque colour with no background goes to solid.executeTranslucent, sweep 1
5SubmitNodeCollection.shapeOutlinesVoxelShape edge outlines submitted without the after-terrain flag, unless their colour is fully opaque — those go to solid, flag or not.executeTranslucent, sweep 2
6SubmitNodeCollection.translucentBlocksAndItemsan item’s quads whose render type blends — an item with both kinds is two nodes, here and in solid — block models whose render type blends, and moving blocks whose model declares the translucent material flag.executeTranslucent, sweep 3
7SubmitNodeCollection.translucentModelsmodels whose RenderType blends, does not force the solid phase and is not the water mask.executeTranslucent, sweep 1
8SubmitNodeCollection.translucentCustomGeometrycustom geometry whose RenderType blends — a simple phase in spite of the name, so it is not distance-sorted.executeTranslucent, sweep 1
9SubmitNodeCollection.translucentGizmosthe translucent debug primitive group submitted without the on-top flag — its opaque twin goes to solid.executeTranslucent, sweep 2
10SubmitNodeCollection.breakingOverlaythe crumbling decal over geometry that blends: a model’s ModelFeatureRenderer.CrumblingOverlay, from SubmitNodeCollection.submitCrumblingOverlay on a render type that admits one, and a SubmitNodeCollection.submitBreakingBlockModel for a block whose model declares the translucent material flag — over anything opaque the decal goes to solid.executeTranslucent, sweep 3
11SubmitNodeCollection.waterMaskmodels submitted with the water-mask render type.executeTranslucent, sweep 3
12SubmitNodeCollection.afterTerrainoutlines flagged after-terrain, unless fully opaque, plus the translucent half of every quad-particle group.executeTranslucentAfterTerrain
13SubmitNodeCollection.alwaysOnTopGizmosgizmo groups flagged on top, opaque or not.executeAlwaysOnTop
14SubmitNodeCollection.outlinea second copy of a model, block model, moving block or item whose outline colour was non-zero — a model or block model only where its render type has an outline variant, and a moving block or item with its ordinary type, re-typed quad by quad inside the feature renderer to the outline variant every item and moving-block render type has — plus custom geometry, and a model or block model submitted with an outline render type (an invisible entity that glows), which are not a second copy at all: an outline render type routes the only copy here.executeOutline, which FeatureRenderDispatcher.renderAllFeatures never calls — LevelRenderer does, into its own target
15SubmitNodeCollection.seeThroughthe storage’s one phase, SubmitNodeStorage.seeThrough, not one per bucket: the second node a see-through name tag emits, in Font.DisplayMode.SEE_THROUGH with the background restored, and any text or text background submitted in that mode — sorted by distance across every bucket at onceFeatureRenderDispatcher.PreparedFrame.executeSeeThrough, once for the whole storage

Two things in the table are worth reading twice. A quad-particle group reaches the collector in one call and becomes two nodes, one in solid and one in afterTerrain, each wrapping the same render state with a flag that picks which of its layers it draws. And outline is not simply solid submitted twice: the glow is a second submission for a model, a block model, a moving block or an item, but flames, leashes and quad particles never reach it at all, a blending model pairs its outline copy with translucentModels rather than solid unless its render type forces the solid phase, and custom geometry, like a model submitted with an outline render type, goes to one phase or the other and never both.

The twelve feature renderers

In the order FeatureRenderDispatcher’s constructor registers them. All but one extend RenderTypeFeatureRenderer, which owns the shared StagedVertexBuffer draw and the merging of geometry that shares a render type.

feature rendererwhat it writes
ShadowFeatureRendererfour vertices per EntityRenderState.ShadowPiece onto one shared shadow render type, with the piece’s alpha as the colour and UVs derived from its bounds and the shadow radius
FlameFeatureRenderera stack of fire quads scaled to the entity’s bounding box, alternating two block-atlas sprites and flipping their U every other pair, at full block light
ModelFeatureRenderermodels — an entity’s, a block entity’s, a special item’s such as a shield or trident, and the elder guardian particle’s: Model.setupAnim and then Model.renderToBuffer over the ModelPart tree, into a buffer optionally wrapped for a sheeted decal or a UvMapping — an atlas sprite, or an armour trim’s paletted texture
TextFeatureRendererarbitrary world-space text and every name tag: glyph quads and background prepared through Font at the submitted pose and display mode, with an eight-direction outline pass and a polygon-offset second pass when an outline colour is set, and a text display’s background submitted on its own
LeashFeatureRenderera strip between the two ends of an EntityRenderState.LeashState, walked twice, as two ribbons that cross — twenty-five stations out and twenty-five back, a pair of vertices at each, a hundred in all — with its light interpolated between the endpoints and its colour alternating per station
ItemFeatureRendereritem quads, in one pass over the submits — a stack with a foil draws each quad on its material’s glint render type, which samples the enchantment glint beside the item texture, so geometry and foil go out together
CustomFeatureRenderernothing of its own: it hands the caller’s SubmitNodeCollector.CustomGeometryRenderer a vertex consumer for the requested render type
BlockModelFeatureRendererthe quads of a BlockStateModelPart list, in Direction order, at one fixed light and overlay coordinate for the whole submit
MovingBlockFeatureRenderera whole block state re-tesselated through ModelBlockRenderer — pistons, falling blocks and the transient blocks a server sends — honouring the ambient-occlusion and cutout-leaves options
QuadParticleFeatureRendererthe only one outside RenderTypeFeatureRenderer: it appends particle draws to the StagedVertexBuffer per SingleQuadParticle.Layer and issues them into the RenderPass it is handed, on each layer’s own pipeline
ShapeOutlineFeatureRenderertwo line vertices per edge of a VoxelShape, each carrying a per-vertex line width
GizmoFeatureRendererthe debug vocabulary: quads, triangle fans, lines, texts and points out of a DrawableGizmoPrimitives.Group, camera-relative

Those twelve are the answer to what can be drawn from a submit node — which, by the exclusions the opening makes, is everything in a level except the sky, the clouds, the weather, the world border and terrain. Anything a renderer wants that none of the other eleven draws goes through CustomFeatureRenderer.


Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes tools/verify_names.py.