Skip to content

Scene Pipeline

Deepratna Awale edited this page Oct 10, 2026 · 5 revisions

Scene pipeline

How a scene wallpaper goes from files on disk to pixels: format loaders, values and bindings, content building, the Metal renderer, the effect graph, render targets and text.

1. Format loaders (Scene/Format)

Plain Decodable models with no side effects.

Loader Reads
PKGParser WE's PKGV archives (scene.pkg)
TEXParser, TEXImageFormat, TEXSpriteFrames .tex textures (TEXV0005 containers: embedded JPEG/PNG, raw R8/RG88/RGBA8888, DXT1/3/5 mipmaps) and TEXS sprite timelines
SceneDocument, SceneObject, SceneFlexibleValues, TolerantJSON scene.json, whose fields may be plain values or {"value", "user", "script", "animation"} objects
EffectDocument, MaterialDocument, SceneMaterial effect.json and materials
SceneParticles, SceneModel, Model/ Particle systems and .mdl models
SceneLight, SceneLightConfig, SceneCameraLayer, SceneCameraPath, SceneProjection Lights, cameras and projection
SceneSound, SceneTimelineDocument, SceneAnimationLayer Sounds, timelines, animation layers
WETextDefaults, WETextEffectFields Text defaults and font effects
TextureReduction WE's Texture Resolution rule

Collections decode element by element, so one bad entry doesn't drop its siblings.

2. Values and bindings (Scene/Values)

Every value that can be user-, script- or animation-bound is resolved through SceneValueSource and SceneValueResolver, never read raw. Key pieces:

  • SceneUserBindings, SceneUserPropertyService, SceneUserPropertyStores: user properties per wallpaper instance and display, with conditions.
  • ShaderConstantResolver, MaterialPassConstantSources: a pass's constants (literals or bound values), with usershadervalues mapping and annotation defaults.
  • SceneAnimationSet and friends: timelines (see Timelines and the SceneClock).
  • SceneGeneralSettings, SceneCameraSettings, SceneFogSettings, SceneColorCorrectionSettings, SceneLightValues: the general block with WE's defaults.
  • SceneParticleOverrides: particle instanceoverride values, resolved every frame.

Priority: a timeline beats the static and user value; a script's return wins for its frame. A Scene Edit / Export edit replaces only the static base (SceneValueSource.replacingBase), so bindings survive.

3. Content building (Scene/Loading)

SceneWallpaperViewModel and SceneWallpaperInstance load a wallpaper and build its content:

  • SceneEffectPlan resolves each effect: effect.json, its materials, combos, textures and constants per pass, and the translated shader variant.
  • ImageMaterialPlan, ParticleMaterialPlan, ModelMaterialPlan: materials through WE's own shaders (genericimage*, genericparticle, model shaders).
  • ParticleSystemBuilder (+ initializer and operator builders), ParticleBudget.
  • SceneModelBuilder, SceneSoundContentBuilder, SceneFontResolver.
  • WorkshopAssetResolver finds files from a wallpaper's Workshop dependencies.
  • WEImageBlendModes: WE's 33 blend modes.

Layers keep their authored object order and full parent transform.

4. The renderer (Scene/Rendering)

SceneMetalRenderer draws each frame:

  1. Advance the wallpaper's SceneClock, collect script output, evaluate timelines.
  2. Step particles (GPU), prepare lights and shadows, the reflection pass.
  3. Run each layer's effect graph, then draw the layer with its transform and blending in the scene pass.
  4. Post-process (bloom, HDR chain), then present to each display.

Built-in uniforms (g_Time, g_Screen, g_Texture*Resolution, g_PointerPosition, g_PointerPositionLast, g_PointerState, g_ParallaxPosition, g_AudioSpectrum*, g_Daytime, …) are written per frame by UniformWriter, which maps NaN to 0 for integer uniforms.

The effect graph

EffectGraphRenderer runs WE effects on a layer's image with WE's own shaders:

  • The image goes through the passes using two ping-pong targets at the layer's size. A pass without a target renders into the other buffer and becomes the current image; a pass with a target renders into that effect's FBO. previous is the effect's input.
  • Declared fbos (including half/quarter scales), bind and target are honoured; _rt_FullFrameBuffer, _rt_imageLayerComposite_<id>_a and the mip-mapped frame buffer are provided.
  • A chain's static prefix is kept and reused while its inputs, colour, alpha, script revision and live-bound values hold (StaticChainKey).
  • Effect Detail: Match Display runs a layer's effects at its on-screen size on an area-averaged copy, while built-ins report the full-size chain's sizes so texel-sized kernels span the same image.

Render targets

SceneRenderTargetPool hands out scratch targets bucketed by size, format and usage:

  • Frame leases (returned at endFrame()) and persistent leases (feedback targets, never evicted).
  • Free targets are evicted least-recently-used past a 256 MB budget and after 10 s idle. A request always gets a texture.
  • Effect chains keep spare targets by bytes and idle time, so a clock whose text size changes reuses its targets.

Text

TextLayout lays text out as WE does: glyph size from pointsize at 300 dpi in scene units, advances floored to whole units, wrapping only with limitwidth, limitrows with ellipsis, and blockalign justification. Text is rasterised at its on-screen density (SceneTextRasterScale) and can carry effects (SceneTextEffects) and font effects (outline, blur, drop shadow).

Scene target size

The scene target size is set by Render Resolution alone (SceneRenderResolution): the display's own pixels, 4K at its shape, or the wallpaper's own size; Upscaling then draws 75, 67 or 50 % of each side and scales it up. With several displays the frame renders once at the largest target any display needs and each display presents it with its own size and placement.

User guide: Scene wallpapers and Settings › Performance

Clone this wiki locally