Graphics tiers¶
Goanna's lowest tier is the least expensive presentation that still earns its place in this client. All five tiers use Forward+. An all-off render is an experiment, not a shipping tier. Luanti clients can still join the same server; supporting older Android hardware does not define this ladder.
These are candidate presets as of 2026-09-27. Their hardware roles express intent, not measured compatibility. Desktop results cannot establish Deck frame times by multiplying by a single GPU performance ratio.
Tier contract¶
| Tier | Intended reference | Visual priority |
|---|---|---|
| Lowest | Steam Deck-class hardware, four local players, 1280x800 total, 30 FPS | Readable nearby world, convincing materials and sky, reliable local lighting |
| Low | The same handheld, one player | Spend the released per-view budget on distance and atmosphere |
| Medium | Modest gaming laptop | More world, indirect light and authored surface depth |
| High | Midrange desktop | Long views, richer indirect lighting and nearby lamp shadows |
| Ultra | Powerful desktop with spare frame budget | Longest views, full sampling and broad lamp shadow coverage |
Thirty FPS gives 33.3 ms per frame. For the handheld multiplayer target, use 25-27 ms as a provisional ordinary-frame budget so streaming has room. This is a design allowance, not a measured result. Resolution, player count and desired frame rate must accompany any future recommendation. Other tiers do not promise a particular frame rate until calibrated.
Every tier must retain terrain silhouettes, readable interactable nodes, night navigation, underwater visibility limits, transparent ice, moving water, foliage movement, weather response and a recognisable clouded sky. Sun shadows, material normals and material response remain available at Lowest. Exposure, night visibility and appearance strength remain player preferences. A screenshot alone cannot establish acceptable motion, streaming, weather transitions or small split-screen readability.
Current implementation¶
graphics_profiles.gd is the source of truth. Every preset names the same controlled keys, including all 20 feature gates and the texture resolution. Applying Ultra after Lowest restores its effects; applying Lowest after an experiment restores its intended features. Quality edits outside those values show Custom. Player preferences remain separate. Renderer-wide screen-space quality and directional shadow atlas size still apply to the whole process; local play uses one shared preset. View-local feature gates and cloud materials can be varied independently for controlled tests.
| Control | Lowest | Low | Medium | High | Ultra |
|---|---|---|---|---|---|
| Near distance, 16-node blocks | 4 | 6 | 8 | 12 | 16 |
| LOD distance | 6 | 8 | 12 | 20 | 32 |
| Far cap, nodes | 96 | 128 | 256 | 1024 | Server grant |
| Cloud style | Block | Fluffy block | Volume | Volume | Volume |
| Cloud layers | 1 | 1 | 2 | 3 | 3 |
| Volume view samples | n/a | n/a | 24 | 24 | 24 |
| Volume sun samples | n/a | n/a | 3 | 4 | 4 |
| Volume cubemap samples | n/a | n/a | 8 | 8 | 8 |
| Volumetric atmosphere quality | Off | 0.35 | 0.65 | 1 | 1 |
| SSAO | Off | On | On | On | On |
| Light shafts | Off | On | On | On | On |
| SDFGI strength | Off | Off | 1.4 | 1.4 | 1.4 |
| Texture resolution, pixels per 16 art texels | 128 | 128 | 256 | 256 | 512 |
| Parallax | Off | On | On | On | On |
| Parallax march | n/a | Short | Full | Full | Full |
| Micro shadows | On | On | On | On | On |
| SSIL strength | Off | Off | Off | 1.4 | 1.4 |
| Screen-space detail | 0 | 0 | 1 | 2 | 3 |
| Directional shadow detail | 0 | 0 | 1 | 1 | 2 |
| Lamp pool | 24 | 32 | 48 | 64 | 256 |
| Lamp shadow budget | 0 | 0 | 0 | 8 | 48 |
| Lamp shadow distance, nodes | 16 | 24 | 32 | 48 | 64 |
| Procedural grass | Off | Off | On | On | On |
| Blade density, fraction of full | 0.2 | 0.3 | 0.4 | 0.7 | 1 |
| Grass distance, nodes | 12 | 20 | 32 | 48 | 80 |
| Bending distance, nodes | 0 | 4 | 6 | 10 | 16 |
| Bending actors | 0 | 1 | 2 | 4 | 8 |
| Grass AA when enabled | Existing | FXAA | 2x MSAA + FXAA | 2x MSAA + FXAA | 4x MSAA + FXAA |
Micro shadows (mat_micro_shadow) darken the pack's baked occlusion when
light rakes across it, in light(), with no texture reads. They are on at
every tier: on 2026-10-05 their GPU cost did not show above the noise at
Low or Medium. Low now runs the short parallax march
(mat_parallax_short): four steps plus two refinements, only within eight
nodes of the eye. It cost 0.075 ms per frame (spread 0.047 to 0.106 over
sixteen alternated rounds) with a near wall filling most of a 1280x800
frame, and nothing measurable at the benchmark vista. Those are RTX 3090
numbers, about 6 per cent of that close frame's GPU time; the Steam Deck
has not been measured. Medium keeps the full march: the short one saved
0.05 ms there and was not distinguishable at three nodes. Lowest keeps
parallax off. See materials, "Micro shadows and the short
march", and the
measurement driver.
Cloud layer count, style and lighting quality are independent controls.
The Advanced Lighting setting Cloud layers saves a count from one to
three as cloud_layer_count. Lowest and Low use one, Medium two, and High
and Ultra three. Reducing the count skips inactive layers in both the sky
and local cloud fog. Reduced budgets prioritise layers above the regional
terrain, so high mountains retain overhead clouds; existing layers keep
their heights and patterns when the count changes. The separate Sky clouds
switch still turns sky clouds off. These counts are provisional budgets,
not measured performance guarantees.
Cloud style and cloud lighting quality retain their existing budgets. Plain blocks use solid cells without a 3D noise texture. Fluffy blocks fill rounded box envelopes with noisy density, translucent edges and internal sun attenuation. Their quality levels now select 12/16/24 view samples per occupied cell and 2/3/4 local sun samples. Low currently uses 16 and three; the lower budget is a calibration candidate. The radiance cubemap uses eight view samples. Empty cells are skipped and nearly opaque rays stop early. Sun attenuation is local to each cell, so neighbouring cells do not cast shadows into one another.
Fluffy blocks and the full volume style allocate the same kind of 3D noise texture lazily and reuse it when switching styles within a view. The full volume style retains its existing silhouette sampling; reducing it previously produced unacceptable grain at 800p. Its lighting quality still selects two, three or four sun samples. The fluffy style's performance has not been calibrated against the full volume style or target hardware. See the fluffy cloud review.
Ordinary sky and distance fog remain when volumetric atmosphere is off; underwater murk also remains. Cloud style is a candidate artistic choice, not a guarantee of a particular saving in every scene.
The heat shimmer over flames (render_fire_shimmer) is off on Lowest
and Low and on from Medium; the flame material itself is the same at
every tier. See fire material.
All tiers retain bloom, cloud shadows, dynamic and carried lights, water waves and reflections, wet surfaces, foliage wind, ice detail and transmission, and lava detail. Keeping a feature is not a claim it is free. The existing screening did not establish a worthwhile visual trade for removing these from the baseline.
Grass density widens the procedural blade grid, reducing the number of candidate roots searched. Draw distance clips tracing and collapses fully distant proxy geometry before rasterisation. Grass fades back to ordinary terrain over the final fifth of its range. Bending range and actor count bound the interaction work separately; zero disables it. AA quality is independent of density and restores the original viewport settings when lowered or disabled. Stronger pre-existing AA remains respected.
Lowest and Low keep procedural grass off, with inexpensive budgets ready if enabled in Advanced. Medium, High and Ultra now add progressively denser and longer-range grass. These are calibration candidates. Native game plants remain at every tier and none of these controls changes the world.
Lamp admission now obeys the light pool alone. A separate shadow selection ranks admitted lamps by distance to their lit region, bounded by shadow count and distance. Existing shadow owners get a two-node preference to reduce switching. Other admitted lamps remain lit without maps; propagated block light remains the fallback outside the direct pool. Ultra retains 48 shadow lamps and a 256-light pool.
Unshadowed point lights can shine through walls. An experimental
lamp_occlusion switch now checks nearby full opaque blocks along each
node lamp's ray. It uses only the owning connection's received map, in a
64-node cube refreshed at most ten times per second. A static grid avoids
repeated uploads. It is off in all presets while correctness and cost are
measured in the tier cycle.
This is a coarse supplement to shadow maps. Partial nodes, carved openings, carried lights, water, ice and volumetric fog retain their existing paths. Unknown cells and unmatched lights keep ordinary lighting. The shader has only a light direction, so exactly collinear sources cannot always be distinguished. Emission and propagated block light remain separate. Dense builds, motion and openings need review before enabling this in a tier.
The initial hardware heuristic now chooses Medium for a discrete adapter and Low for shared graphics. This is deliberately only a starting choice. Core count cannot establish GPU capability. Existing saved settings still win; opening the client does not overwrite a user's chosen configuration. Local play retains its existing Low default until Lowest is calibrated.
Texture resolution¶
texture_size (Advanced, Materials, Texture resolution) is 128, 256
or 512: the most map pixels an enhanced texture may have across 16 texels
of the game's art, so 8, 16 or 32 to an art texel. It applies evenly to
everything: block albedo and companions, animation strips, mob and player
skin companions, every chiseled bookshelf front. A texture over it is
reduced when a world's textures are loaded, before anything is drawn from
it; one under it is left alone, never enlarged. The art each image is
counted against is the server's own image of that name, before a pack
replaces it. How the reduction filters each map, and what it does to node
layers sized from their companions, is in materials,
"Texture resolution".
The setting takes effect at the next join, not live. The server's media
exists only in the source image cache, the reduced images replace the
pack's there, and every array, material and mesh of the session is built
from them; raising it would need the pack read again and every texture
rebuilt, which is what a rejoin does. The menu row says so when it is
changed in game. GOANNA_TEXTURE_SIZE=128 (or 256, 512) pins it for a
scripted run, over any profile. A bench plan's tier variants carry it so
tools/bench/check-bench-plans.py stays in step, but a live sweep cannot
vary it; measure it a session per value
(driver).
The pack itself is built at 256 and shipped at 256 for now. With it, Low
and Lowest reduce on load and Ultra draws the 256 maps as they are. A pack
built at 128 or 512 (GOANNA_PBR_SIZE, tools/pbr_author/README.md) is
what each tier should download; the bundles and the catalogue gain them
with the next release.
Measured 2026-10-05 on the RTX 3090 (NVIDIA open module 615), Godot
4.5.1, test_world (Mineclonia) through the occlusion fixture's copy,
Medium profile at 1920x1080 with only texture_size and the pack changed,
a session per configuration, two rounds in rotation, four bursts of 400
back to back draws at each place
(driver,
summary). Texture memory is
Godot's RENDER_TEXTURE_MEM_USED at the benchmark vista, after the
platform for the wall:
| Configuration | Texture memory, MiB | Vista GPU ms, median (bursts) | Wall GPU ms, median (bursts) |
|---|---|---|---|
| 256 pack reduced to 128 | 1282 to 1295 | 4.65 (4.34 to 5.02) | 2.88 (2.65 to 3.53) |
| 128 pack | 1279 to 1281 | 4.29 (4.21 to 4.32) | 2.84 (2.68 to 2.96) |
| 256 pack | 2522 to 2561 | 4.11 (3.89 to 4.29) | 2.76 (2.72 to 2.84) |
Texture memory falls by about 1,240 MiB at 128, to half. The join's log line counts the images the cap reduced: 4,067 MiB of RGBA8 source images brought to 1,016, before mipmaps and before only the arrays drawn reach the card; the server's own copies of the companions, which this test world's server mod sends at 256, are counted there too. What is left at 128 includes the renderer's own targets, shadow maps and SDFGI, which no texture setting moves. A reduced 256 pack and a pack built at 128 cost the same. GPU time does not follow: the sessions of one configuration differ by more (4.35 against 4.81 ms at the vista, 128 reduced) than the configurations do, and 256 was not slower than 128. On this card at this scene the texture resolution is a memory setting, not a frame time one; a handheld with shared memory has not been measured. The 512 pack was built but not measured on the GPU: other clients held the card for the two and a half hours it was waited for.
Frames at a low sun, centre crops of the fixture's walls:
stone and
stone brick, each the
128 pack, the 256 pack reduced to 128, and the 256 pack, left to right.
The reduced 256 reads as the 256 one, a little softer. The 128 pack in
them is the build before its occlusion fix (tools/pbr_author/README.md,
"The baked occlusion"), with its joints near black; that frame is why the
fix was made, and the fixed pack has not been seen on the GPU. The
zombie frame of that run showed open sky (the platform's western map
block lost its near mesh, as in the occlusion review), so no mob close
up was taken at any size.
Evidence and next implementation work¶
The feature screening, scene controls and streaming report describe the exact configurations recorded there. Those results do not benchmark the revised presets in this document. In particular, cloud sample reductions are a new control, not an established percentage saving.
The first three implementation steps are now present: cloud alternatives,
independent lamp shadows and graded grass. Remaining work is visual review
across weather and motion, tuning the preset values, and calibration on the
actual target hardware. A full-block lamp visibility prototype is available
behind lamp_occlusion; it remains off in the presets while its visual
limits and performance are evaluated. See the
tier cycle.
World demand and presentation quality still need separate internal budgets. View range, terrain sharing and streaming scheduling address CPU work; cloud, screen-space and shadow sampling address GPU work. Worker count and main-thread time budgets are machine-wide scheduling controls. Shared terrain must be tested with overlapping views and players travelling apart.
For each candidate, record CPU frame time, renderer CPU time, GPU time, median and tail frame time, terrain queues and publication time, RAM and VRAM. Compare one and four players, stationary and streaming, with warm and cold state labelled. Include day, sunset, wet weather, night lamps, underwater, close grass and foliage, ice and magma. A feature that happens to be inactive at noon is not evidence of a free effect.
Use resolution changes and isolated feature changes to distinguish GPU cost from CPU work. CPU and GPU overlap, so their timings are not additive. Memory capacity is a separate constraint: avoid paging and allocation spikes without treating smaller RAM use as a frame-time saving by itself. A future recommendation should select world and rendering budgets from measured limits, then expose a simple preset with optional advanced tuning.
The three profile benchmark plans and the Deck plan now include Lowest
and the complete settings for their named tiers. Run
tools/bench/check-bench-plans.py before collecting a report. The local-play
harness also accepts --profile lowest; scene overrides must be reported
because they can change a preset's actual configuration.
--setting-sweep accepts named numeric overrides, for example
'{"sparse":{"grass_density":0.2},"dense":{"grass_density":1}}'.
The harness records the effective per-view settings, restores the starting
values between variants, and brackets them with controls. Use stationary
phases; streaming comparisons still require fresh runs.
--software uses CPU rendering for visual checks when the GPU is occupied.
Its results carry software_renderer: true and are not GPU benchmarks.
See the validation and screenshots for the cloud, lamp and grass changes.