Builtin Library
readme.md
renderScale system
The scene's resolution as a budget rather than a constant.
Shading cost scales with pixel count and with nothing else, so how many pixels the scene is rasterized into is the one lever that buys frame time without taking anything out of the frame — no feature switched off, no geometry removed, nothing missing from the image. What it spends instead is sharpness.
The renderer owns the lever (renderer.setResolutionScale). This package owns
the decision: read what the GPU frame actually costs and move the scale toward
the cost you asked for.
Using it
Put DynamicResolution on any entity in the scene and give it a frame budget:
entity(id).component.add("DynamicResolution", { targetMs = 16.6 })
Every field is live: edit targetMs in the inspector and the controller aims at
the new number from the next frame.
For code that wants the budget without a component — a settings screen, a benchmark harness — reach for the module:
local RenderScale = require("@builtin::systems.renderScale.renderScale")
RenderScale.set({ target = 16.6, min = 0.6 }) -- take automatic control
RenderScale.step() -- one step toward the target
RenderScale.get() -- what it aims for, and last read
RenderScale.setScale(0.7) -- set it directly
RenderScale.scale() -- what the renderer has
RenderScale.off() -- stop steering
What the knobs do
| field | effect |
|---|---|
targetMs | The GPU frame span to hold. 16.6 is 60 Hz, 33.3 is 30. |
minScale / maxScale | The range the scale may move in, within the engine's own [0.25, 1]. |
rate | How much of the distance to the target one step takes. Lower settles more slowly and holds steadier. |
deadband | How far off the target a frame must be before the scale moves, as a fraction of the target. Without it the scale never sits still. |
How it steers
The reading is profiler.gpuFrame().frame_span_ms — the median across the
profiler's window, not one frame. A single frame's GPU span carries whatever the
queue happened to be waiting on, and a controller fed that oscillates instead of
settling.
Pixel count moves with the square of the scale, so the scale that would have hit
the target is the current one over the square root of how far off the frame was.
A step takes rate of the way there, which is what keeps one heavy frame from
throwing the scale across its whole range.
Files
renderScale.package/
DynamicResolution.component/ -- the budget, authored and persisted
renderScale.module/ -- set / step / get / setScale / scale / off