Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commite8beb5604c

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

fieldeffect
targetMsThe GPU frame span to hold. 16.6 is 60 Hz, 33.3 is 30.
minScale / maxScaleThe range the scale may move in, within the engine's own [0.25, 1].
rateHow much of the distance to the target one step takes. Lower settles more slowly and holds steadier.
deadbandHow 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