pub struct Renderer { /* private fields */ }Expand description
Owns the GPU context, the built-in systems, and the loaded presets; renders one frame per call by evaluating the active preset into the active system.
Implementations§
Source§impl Renderer
impl Renderer
Sourcepub fn capture_frame(
&mut self,
frame: &AnalysisFrame,
) -> Result<CaptureImage, RenderError>
pub fn capture_frame( &mut self, frame: &AnalysisFrame, ) -> Result<CaptureImage, RenderError>
Advance the scene clock one step and capture that single frame into an offscreen texture, returning tight RGBA (Plan 0013). Off the hot path — blocks on GPU readback; never call it from a live loop.
Sourcepub fn capture_preset(
&mut self,
name: &str,
frame: &AnalysisFrame,
frames: u32,
) -> Result<CaptureImage, RenderError>
pub fn capture_preset( &mut self, name: &str, frame: &AnalysisFrame, frames: u32, ) -> Result<CaptureImage, RenderError>
Capture preset name after advancing it frames steps from a fixed
initial state, driven by a single constant frame (Plan 0013). A pure
function of (name, frame, frames): the scenes are rebuilt so any
stateful system (e.g. the seeded swarm particles) starts from its
deterministic seed, and the scene clock resets to 0.0, so the result is
independent of any earlier capture. Errors if name is not in the
roster. frames is treated as at least 1.
Sourcepub fn capture_preset_over(
&mut self,
name: &str,
stimulus: &[AnalysisFrame],
) -> Result<Vec<CaptureImage>, RenderError>
pub fn capture_preset_over( &mut self, name: &str, stimulus: &[AnalysisFrame], ) -> Result<Vec<CaptureImage>, RenderError>
Capture preset name across a time-varying stimulus (Plan 0037):
one rendered frame per entry of stimulus, read back in order, so the
returned images are the response while it changes rather than after it
settles.
This is the primitive capture_preset cannot be:
holding one frame for every step converges every smoother before the
pixels are read, which makes the result identical for any [smoothing]
constant (ADR-0039). capture_preset is left exactly as it was — four
suites and --report consume it — and this is its sibling, sharing the
same reset_for_capture seed so both are pure
functions of their arguments.
The clock advances one FALLBACK_DT per entry, so
index i is second i * dt of the response. An empty stimulus yields
no images. Errors if name is not in the roster.
Off the hot path, and more so than its sibling — it blocks on a GPU readback per frame, not once per call. The target and readback buffer are allocated up front rather than per frame, because building GPU resources mid-sequence perturbs what the feedback stages resolve to on the DX12 software adapter.
Sourcepub fn capture_preset_at(
&mut self,
name: &str,
frame: &AnalysisFrame,
at_frames: &[u32],
) -> Result<Vec<CaptureImage>, RenderError>
pub fn capture_preset_at( &mut self, name: &str, frame: &AnalysisFrame, at_frames: &[u32], ) -> Result<Vec<CaptureImage>, RenderError>
Advance preset name under a single constant frame and read back only
the frames named in at_frames (Plan 0085 Phase 1) — the long-run
primitive, and the one a horizon needs.
Its two siblings cannot serve a run of tens of thousands of frames:
capture_preset reseeds from scratch on every
call, so sampling k points costs O(k·N) renders, and
capture_preset_over reads back every
frame, so a ten-minute run at 720p would materialize ~36,000 images. This
renders N frames once and holds at_frames.len() of them.
Frame numbering matches capture_audio: frame 0
is the first advanced frame, so at_frames = [n - 1] returns exactly what
capture_preset(name, frame, n) returns — asserted in
core/tests/capture_advance.rs rather than argued, because it is the
property that lets a horizon’s rows be compared with every other capture
this repo takes.
Deterministic on the same terms as its siblings: scenes are rebuilt to
their seed, the clock resets to 0.0, and the step is a fixed
FALLBACK_DT — so a row at index k does not
depend on how far the run was asked to go. Images come back in
at_frames order; a repeated index yields the same frame twice rather
than rendering it twice. An empty at_frames renders nothing.
Off the hot path — it blocks on a GPU readback per requested frame.
The readback buffer is built once, at the first requested frame, and
reused for every later one. Both halves of that matter on the DX12
software adapter, where building GPU resources mid-sequence perturbs what
the feedback stages resolve to (the hazard
capture_preset_over documents, and a
horizon is precisely a long feedback sequence): reusing it means the
perturbation happens once rather than per sample, and doing it at the
first sample rather than up front is what puts the allocation at the same
point in the sequence capture_preset puts it —
which is what makes the two agree pixel-for-pixel on WARP as well as on
hardware. It also stays independent of the horizon requested, since the
first sample sits at the same frame index however long the run is.
Sourcepub fn capture_stream(
&mut self,
name: &str,
frames: u32,
dt: f32,
analysis: &mut dyn FnMut(u32) -> AnalysisFrame,
sink: &mut dyn FnMut(u32, &CaptureImage) -> Result<(), String>,
) -> Result<(), RenderError>
pub fn capture_stream( &mut self, name: &str, frames: u32, dt: f32, analysis: &mut dyn FnMut(u32) -> AnalysisFrame, sink: &mut dyn FnMut(u32, &CaptureImage) -> Result<(), String>, ) -> Result<(), RenderError>
Render preset name for frames frames at an injected dt, handing
each frame to sink the moment it is read back (Plan 0101 / ADR-0114) —
the streaming primitive, and the one an offline video render needs.
Its three siblings all return a Vec<CaptureImage>, which is exactly what
a video render cannot afford: a 1080p frame is 8.29 MB, so a four-minute
track at 60 fps is 119 GB of retained images. Nothing is retained here —
the frame is handed to sink and dropped, so the resident set of a
14,400-frame render is the same as a 100-frame one.
It is also the only capture entry point whose step is not the fixed
FALLBACK_DT. A render at --fps 30 advances the
scene by 1/30 s a frame, or the visuals would run at half speed against
their own soundtrack; that dt is the caller’s, exactly as it is for the
live frontend (ADR-0013). At 60 fps dt is FALLBACK_DT, which is what
makes a rendered frame comparable with every other capture this repo takes.
analysis supplies the AnalysisFrame for each frame index. The audio
hop clock and the frame clock are different clocks and only the caller
knows the mapping between them, so this deliberately does not walk PCM —
unlike capture_audio, which welds one rendered
frame to one analysis hop.
Deterministic on the same terms as its siblings: scenes rebuilt to their
seed, the clock reset to 0.0, the salt pinned. Given a deterministic
analysis the whole run is a pure function of (name, frames, dt).
Off the hot path — it blocks on a GPU readback every frame, which is
also what bounds its memory: read_back polls, so each frame’s submission
is retired before the next is encoded (the retention Plan 0099 measured).
The target and the readback buffer are built once and reused, so a
long run allocates no GPU resources mid-sequence.
A sink error stops the run and comes back as
RenderError::Sink carrying the consumer’s own
message.
Sourcepub fn capture_audio(
&mut self,
name: &str,
pcm: &[f32],
format: AudioFormat,
at_frames: &[u32],
) -> Result<Vec<CaptureImage>, RenderError>
pub fn capture_audio( &mut self, name: &str, pcm: &[f32], format: AudioFormat, at_frames: &[u32], ) -> Result<Vec<CaptureImage>, RenderError>
Drive preset name with real audio through the real analyzer and
capture the frames at at_frames (Plan 0013). The PCM is fed hop-by-hop
into a fresh Analyzer (format validated at the
intake boundary — the source-agnostic rule); each produced
AnalysisFrame drives one rendered frame, so at_frames indexes the
hop sequence (frame 0 is the first hop). Deterministic: scenes are rebuilt
to their seed and the clock resets to 0, exactly like
capture_preset.
This is in-memory PCM only — no file, decoder, or OS audio-source code,
just like a frontend pushing samples. Returned images are in at_frames
order; an index past the audio length is an error.
Sourcepub fn capture_audio_after_warmup(
&mut self,
name: &str,
pcm: &[f32],
format: AudioFormat,
at_frames: &[u32],
warmup_hops: usize,
) -> Result<AudioCapture, RenderError>
pub fn capture_audio_after_warmup( &mut self, name: &str, pcm: &[f32], format: AudioFormat, at_frames: &[u32], warmup_hops: usize, ) -> Result<AudioCapture, RenderError>
capture_audio, with the first warmup_hops hops
advanced but not rasterized (Plan 0084 Phase 3).
A warm-up hop still pushes its samples, still publishes its
AnalysisFrame, and still advances the scene clock by one
FALLBACK_DT — the hop happened, it just did not
draw. What it skips is the render pass, which is why a caller that only
needs the analyzer warm (core/tests/reactivity.rs drives
WARMUP_HOPS of them per capture, at silence, and reads none of them
back) stops paying a full rasterization per hop to reach a DSP state that
needs no pixels.
This does not warm GPU-side scene state. Analysis is a pure function of its window and the render pass never touches the analyzer, so the published frames are bit-for-bit what they would have been — but a scene that integrates on the GPU (particles, trails, reaction-diffusion) has that many fewer steps behind it at the first rendered hop. Time-driven scenes are unaffected, since the clock advances either way.
An at_frames entry inside the warm-up span was never rendered and is an
error, the same one an index past the audio length gives.
Source§impl Renderer
impl Renderer
Sourcepub fn open_tap(&self) -> FrameTap
pub fn open_tap(&self) -> FrameTap
Open a FrameTap sized to this renderer’s configured target — the one
GPU allocation a tapped run makes, so the per-frame path makes none.
The tap is fixed at the size it is built with. A resize
underneath a live tap leaves the two disagreeing, and the tap wins: it is
what render_tapped draws and copies against.
Reopen after a resize to follow it.
Infallible: RenderContext floors both dimensions at 1 where the size
enters, so there is nothing left to reject here.
Sourcepub fn render_tapped(
&mut self,
tap: &mut FrameTap,
frame: &AnalysisFrame,
dt: f32,
) -> Result<CaptureImage, RenderError>
pub fn render_tapped( &mut self, tap: &mut FrameTap, frame: &AnalysisFrame, dt: f32, ) -> Result<CaptureImage, RenderError>
Advance the scene clock by dt real seconds, draw the active preset for
frame through the same draw_frame the window presents through, and
read the result back out of tap.
dt is per call, which is the difference between this and
capture_stream’s one fixed step: a caller that
falls behind the wall clock yields fewer, correctly-timed frames rather
than a picture running slow against the music.
Draws under SaltMode::Live, because a tap is a live render path and
not a capture: a preset declaring seed = "random" (ADR-0051) must vary
per launch here exactly as it does in the window. Both salts are equal for
every preset that declares anything else, which is why a tapped frame and
a capture_frame of the same preset at the same
clock are still byte-identical (core/tests/frame_tap.rs).
Blocks on the readback, as every path through
capture::read_back does. That is what bounds a
long run’s memory — the poll retires each frame’s submission before the
next is encoded (the retention Plan 0099 measured) — and it is why this is
a source entry point and not a display one: there is no present deadline
here, only throughput.
Source§impl Renderer
The renderer-facing half: opening and closing the readback, and the two steps
a frame drawn through the intermediate performs.
impl Renderer
The renderer-facing half: opening and closing the readback, and the two steps a frame drawn through the intermediate performs.
Sourcepub fn open_preview_readback(
&mut self,
width: u32,
height: u32,
) -> Result<(), RenderError>
pub fn open_preview_readback( &mut self, width: u32, height: u32, ) -> Result<(), RenderError>
Open a non-blocking readback of the preview, yielding widthxheight
frames.
Requires an open preview — the blit that fills the readback’s tap samples that intermediate and has nothing to read without one. The size is the caller’s and is answered for the life of the readback: a renderer resize rebuilds the intermediate under the blit and moves nothing here (ADR-0187).
Calling it again replaces the readback, which is how a caller changes the size it asked for.
Refused when the frames would come out at a format no consumer can be told the order of, so the announcement that follows can always be true (ADR-0187).
Sourcepub fn close_preview_readback(&mut self)
pub fn close_preview_readback(&mut self)
Close the readback and free its staging buffer. A closed readback yields
nothing and costs the frame one Option test.
Sourcepub fn preview_readback_size(&self) -> Option<(u32, u32)>
pub fn preview_readback_size(&self) -> Option<(u32, u32)>
The size of the frames the readback yields, or None when it is closed.
The size the caller asked for, and not the output’s. The frames are a scaled, letterboxed copy of the intermediate rather than an exact one, so this is a fixed property of the open readback: it answers the same pair across every resize, and a consumer told it once never has to be told again.
Sourcepub fn take_preview_frame(&mut self) -> Option<CaptureImage>
pub fn take_preview_frame(&mut self) -> Option<CaptureImage>
Take the frame the readback produced, if one has landed.
A frame is available at most every other call in the steady state, and
None means “not yet” rather than “never”: the caller sends what it gets
and does not wait.
Source§impl Renderer
The roster-facing half of Renderer: replacing the preset set, moving the
active index, and applying the incoming preset’s structural config to its
scene. An impl Renderer continuation for the same reason tier_governor is
one – these read and write Roster and nothing else about the device.
impl Renderer
The roster-facing half of Renderer: replacing the preset set, moving the
active index, and applying the incoming preset’s structural config to its
scene. An impl Renderer continuation for the same reason tier_governor is
one – these read and write Roster and nothing else about the device.
Sourcepub fn set_presets(&mut self, presets: Vec<Preset>)
pub fn set_presets(&mut self, presets: Vec<Preset>)
Replace the preset roster (the standalone’s hot-reload path). An empty set is ignored so a preset directory that briefly reads empty — or whose files are all malformed — leaves the last good roster rendering (NFR 10).
Sourcepub fn set_param_override(
&mut self,
name: &str,
value: f32,
) -> Result<(), ParamError>
pub fn set_param_override( &mut self, name: &str, value: f32, ) -> Result<(), ParamError>
Hold value on name until it is cleared, shadowing whatever the active
preset’s binding for it evaluates to (ADR-0176).
The name does not have to be one the preset binds: anything the active
system’s vocabulary claims can be driven, and one it does not is refused
here rather than dropped silently at apply time. The override survives
every frame until clear_param_override,
and is dropped wholesale by a preset switch and by
set_presets.
Not eased. A sender moving a slider is already producing a continuous path, and a smoother between the two would make the picture lag the hand.
Sourcepub fn clear_param_override(&mut self, name: &str)
pub fn clear_param_override(&mut self, name: &str)
Drop name’s override; its binding resumes on the next frame, easing from
wherever the smoother left it rather than from the held value. A name that
holds no override is a no-op.
Sourcepub fn clear_param_overrides(&mut self)
pub fn clear_param_overrides(&mut self)
Drop every override at once.
Sourcepub fn cycle_preset(&mut self) -> &str
pub fn cycle_preset(&mut self) -> &str
Switch to the next preset; returns its name. Dissolves rather than cuts
(Plan 0023): the outgoing preset’s composite is captured on the next frame
and blended out over DEFAULT_DURATION_SECS while the incoming one
renders live. Every system is built at startup, so no scene is
constructed here; the dissolve’s opening frames do allocate its own
resources lazily — see begin_transition.
The returned name is the incoming preset’s, immediately — the frontend’s HUD should name where the show is going, not where it has been.
Sourcepub fn preset_names(&self) -> impl Iterator<Item = &str>
pub fn preset_names(&self) -> impl Iterator<Item = &str>
The loaded preset names in roster order — the browse overlay’s list source (Plan 0008). Selection addresses these by absolute index.
Sourcepub fn select_preset(&mut self, index: usize) -> &str
pub fn select_preset(&mut self, index: usize) -> &str
Switch to the preset at index (its absolute position in
preset_names); returns the incoming name. Like
cycle_preset this dissolves rather than cuts
(Plan 0023 Phase 5) — the browse overlay’s select is a switch the operator
watches, so it gets the same treatment as Space. An out-of-range index is
a no-op (never a panic, never a wrap), so a stale index from a shrunk
hot-reloaded roster is harmless.
Use select_preset_now where a blend would be
wrong rather than merely unwanted.
Sourcepub fn select_preset_now(&mut self, index: usize) -> &str
pub fn select_preset_now(&mut self, index: usize) -> &str
Jump to the preset at index with no dissolve — the instant-cut escape
for paths where a blend is wrong rather than unwanted: a capture, which must
stay a pure function of its inputs (NFR §6), or a test placing the roster on
a known preset before measuring. Returns the now-active name; an
out-of-range index is a no-op.
Sourcepub fn select_preset_by_name(&mut self, name: &str) -> bool
pub fn select_preset_by_name(&mut self, name: &str) -> bool
Make the preset named name active, returning whether it was found — the
by-name form of select_preset, and like it a
dissolve. An unknown name leaves the active preset unchanged.
Source§impl Renderer
impl Renderer
Sourcepub fn new(
target: impl Into<SurfaceTarget<'static>>,
width: u32,
height: u32,
opts: RendererOptions,
) -> Result<Self, RenderError>
pub fn new( target: impl Into<SurfaceTarget<'static>>, width: u32, height: u32, opts: RendererOptions, ) -> Result<Self, RenderError>
Build a renderer drawing into target (a safe window handle — the
standalone path). Starts with the embedded default presets.
opts carries the quality-tier pin and the adapter choice;
RendererOptions::default() is auto (rich, governed)
on whatever adapter wgpu picks for the surface.
Sourcepub fn new_headless(opts: HeadlessOptions) -> Result<Self, RenderError>
pub fn new_headless(opts: HeadlessOptions) -> Result<Self, RenderError>
Build a headless renderer that draws into offscreen textures instead of a window (Plan 0013 capture tooling). Same scenes, presets, and per-frame evaluation as the on-surface path — only the target differs. Starts with the embedded default presets.
Pinned Tier::Floor, and there is no argument to say otherwise
(ADR-0045). A capture is a pure function of its inputs (NFR §6), and a
baseline that moved because the machine that blessed it was fast is not a
baseline — so the floor is the default by construction here rather than by
every call site remembering to ask for it. Use
new_headless_tiered for a deliberate
rich-tier capture.
Sourcepub fn new_headless_tiered(
opts: HeadlessOptions,
tier: Tier,
) -> Result<Self, RenderError>
pub fn new_headless_tiered( opts: HeadlessOptions, tier: Tier, ) -> Result<Self, RenderError>
A headless renderer pinned to tier — the opt-in behind the shot CLI’s
--tier, for spot-checking that the rich tier’s raised budgets actually
render (Plan 0044 Phase 3). Always pinned, so a capture never demotes
mid-run and stays reproducible.
Sourcepub fn new_headless_on(
opts: HeadlessOptions,
tier: Tier,
adapter: &AdapterChoice,
) -> Result<Self, RenderError>
pub fn new_headless_on( opts: HeadlessOptions, tier: Tier, adapter: &AdapterChoice, ) -> Result<Self, RenderError>
A headless renderer pinned to tier, on a named adapter (ADR-0146).
The one real headless constructor; the two above delegate here with the
choice their prefer_software flag already implies, so every capture
path resolves exactly the adapter it resolved before.
A live video-out needs this because the adapter is not a performance preference there: a Spout receiver can only open a sender that lives on the GPU it renders with, and on a hybrid machine a console process is handed the power-saving one. The sender’s adapter is what that constrains; this one is the renderer’s, and the two are matched by name on each side rather than by a shared index.
Sourcepub fn new_headless_offline(
opts: HeadlessOptions,
tier: Tier,
) -> Result<Self, RenderError>
pub fn new_headless_offline( opts: HeadlessOptions, tier: Tier, ) -> Result<Self, RenderError>
A headless renderer pinned to tier and resolving the offline sample
ceiling (ADR-0140) — the one constructor shot --render reaches, and the
only one in the engine that does.
Separate from new_headless_tiered rather
than a flag on it, because the two answer to different bounds and only one
of them may ever produce a baseline: a render walks a clip with dt
injected and no display to miss, so its ceiling is memory; every other
headless path is a capture, and a capture takes the live ceiling so the
allocation — and with it the seeded scatter — is the one every committed
baseline was blessed against.
Sourcepub unsafe fn new_from_surface_target(
target: SurfaceTargetUnsafe,
width: u32,
height: u32,
) -> Result<Self, RenderError>
pub unsafe fn new_from_surface_target( target: SurfaceTargetUnsafe, width: u32, height: u32, ) -> Result<Self, RenderError>
Renderer targeting a surface the host owns and built the handle for — the C ABI path, and the only constructor that does not create its own surface. Starts with the embedded default presets (no ABI surface for preset selection yet).
The platform lives on the caller’s side of this seam, not here
(ADR-0001, ADR-0072): the host knows what kind of window it has and
builds the [wgpu::SurfaceTargetUnsafe] for it, so core stays
source-agnostic and platform-free. core-cabi is the one that knows
about HWND.
Auto tier: the plugin gets rich-with-governor, because the C ABI stays v4 and a plugin-side tier picker is a future ABI question rather than part of ADR-0045.
§Safety
target’s handles must be valid and must outlive this renderer.
Sourcepub fn tier(&self) -> Tier
pub fn tier(&self) -> Tier
The active quality tier (ADR-0045) — what the diagnostics overlay and the
shot report header name.
Sourcepub fn set_tier(&mut self, tier: Tier)
pub fn set_tier(&mut self, tier: Tier)
Change the quality tier on the running renderer (ADR-0054).
Rebuilds the tier-dependent GPU resources — the scene roster and the
composite side — against the new TierConfig, on the existing
RenderContext. The device, queue, surface, preset roster, active
preset, engine clock, text layer and diagnostics all survive, so the
operator stays on the preset they were watching. A dissolve in flight is
dropped: its two sides are GPU state built at the outgoing tier.
The visible cost is one re-accumulation of everything that accumulates — trails, reaction-diffusion state, the attractor’s deposit. That is the correct affordance rather than a defect: the operator asked for this and can see that it happened.
A no-op on a surface-less (headless) context, so ADR-0045’s
by-construction guarantee that a capture is Tier::Floor survives a
public mutator existing on this type at all. The condition is
tier::tier_change_permitted, which is a value rather than a comment.
An explicit call pins the tier and clears the governor’s demotion latch. The latch means “the governor took a decision the operator did not ask for, and must be told about it”; once the operator has asked for something, that history is spent. ADR-0045 says the latch is never cleared — ADR-0054 narrows that to “never cleared by the governor”, and is the correction of record.
Sourcepub fn active_index(&self) -> usize
pub fn active_index(&self) -> usize
The active preset’s index in the roster — what the browse overlay opens on, and what a caller checks a tier rebuild against.
Sourcepub fn target_preset_index(&self) -> usize
pub fn target_preset_index(&self) -> usize
The index the show is going to: the dissolve’s target while a
transition is in flight, and active_index
otherwise. This is the “where the show is going, not where it has been”
convention cycle_preset already returns a name by,
expressed as an index — a host checkmarking a menu wants the user’s most
recent choice to be ticked immediately, not a quarter-second later.
Meaningless on an empty roster (0, like active_index); a caller that
distinguishes that case reads preset_names first.
Sourcepub fn tier_demoted(&self) -> bool
pub fn tier_demoted(&self) -> bool
Whether the frame-time governor demoted this session’s tier.
The frontend reports the transition, so a demotion is announced once
rather than shouted every frame — the same pattern
cap_overflow is surfaced through. A pinned floor
answers false; only a governed demotion sets this.
Sourcepub fn set_display_hz(&mut self, hz: f32)
pub fn set_display_hz(&mut self, hz: f32)
Tell the renderer the display’s refresh rate, which sets the frame budget
the governor measures against (ADR-0045). Defaults to
DEFAULT_DISPLAY_HZ; a rate that is not usable
falls back to it rather than producing a degenerate budget.
Off the hot path — call it at startup and on a monitor change. The core
does not read this from the platform itself: a refresh rate is a shell
concern, and the whole point of the split is that core knows nothing
about windows.
Sourcepub fn resize(&mut self, width: u32, height: u32)
pub fn resize(&mut self, width: u32, height: u32)
Reconfigure the surface for a new window size.
Sourcepub fn open_preview(&mut self) -> Result<(), RenderError>
pub fn open_preview(&mut self) -> Result<(), RenderError>
Open the program preview: the frame starts being drawn into an intermediate and copied to its destination, so a second consumer can sample the same pixels the show is getting.
Fails when the destination surface does not accept COPY_DST, which is
the one thing that makes the copy exact. Reported rather than degraded
to a sampling blit: a preview is worth less than a show whose encoded
values silently changed.
Idempotent in effect — an already-open preview is rebuilt at the current size, which is also how a caller follows a resize it did not see.
Sourcepub fn close_preview(&mut self)
pub fn close_preview(&mut self)
Release the intermediate. Idempotent, and the frame path returns to drawing straight at its destination on the very next frame.
Takes any readback with it: the readback copies out of the intermediate, so one left behind would hold a staging buffer for a texture that no longer exists and never yield another frame.
Sourcepub fn preview_state(&self) -> Option<((u32, u32), u64)>
pub fn preview_state(&self) -> Option<((u32, u32), u64)>
The open preview’s size and identity, or None when closed.
Sourcepub fn pixel_order(&self) -> Result<PixelOrder, RenderError>
pub fn pixel_order(&self) -> Result<PixelOrder, RenderError>
The channel order every frame this renderer hands a consumer carries.
One answer for every path, because there is one source: the frame tap’s
target, the capture target and the preview’s intermediate are all built
at the context’s configured format, so a windowed run reports whatever
the swapchain negotiated and a headless one reports HEADLESS_FORMAT.
A caller announcing a frame pipe reads this instead of naming a constant
— a constant is true on one of those two paths and false on the other
(ADR-0187).
Err where that format has no name a consumer knows, which is a refusal
to publish bytes under a guess.
Sourcepub fn attach_aux(
&mut self,
target: impl Into<SurfaceTarget<'static>>,
width: u32,
height: u32,
frame_latency: u32,
) -> Result<AuxPresentMode, RenderError>
pub fn attach_aux( &mut self, target: impl Into<SurfaceTarget<'static>>, width: u32, height: u32, frame_latency: u32, ) -> Result<AuxPresentMode, RenderError>
Attach a secondary present target — a second window’s surface, on this renderer’s existing device (ADR-0143).
The core learns nothing about what that window is for. It presents the runs it is handed and nothing else; the shell decides their meaning. Returns the present mode the surface negotiated, so the caller can log which arm ran.
frame_latency is the secondary swapchain’s
desired_maximum_frame_latency; see AuxTarget::new for what it paces
and for the range it is clamped to.
An already-attached target is replaced. An Err means this adapter
cannot drive that surface — the dual-GPU case — and the caller is
expected to degrade rather than treat it as fatal: the show is on the
primary surface, which is unaffected.
Sourcepub fn aux_frame_latency(&self) -> Option<u32>
pub fn aux_frame_latency(&self) -> Option<u32>
The secondary target’s configured frame latency, or None when detached.
Sourcepub fn aux_counts(&self) -> Option<AuxCounts>
pub fn aux_counts(&self) -> Option<AuxCounts>
What the secondary target’s present path has done since it was attached,
or None when detached.
The counts live with the target and die with it, so a caller that wants
a session’s totals reads them before detach_aux.
Sourcepub fn detach_aux(&mut self)
pub fn detach_aux(&mut self)
Release the secondary target, its swapchain and its text atlas. Idempotent.
Sourcepub fn aux_attached(&self) -> bool
pub fn aux_attached(&self) -> bool
Whether a secondary target is currently attached.
Sourcepub fn resize_aux(&mut self, width: u32, height: u32)
pub fn resize_aux(&mut self, width: u32, height: u32)
Resize the secondary target’s swapchain. No-op with none attached.
Sourcepub fn aux_size(&self) -> Option<(u32, u32)>
pub fn aux_size(&self) -> Option<(u32, u32)>
The secondary target’s size in physical pixels, or None when detached.
Sourcepub fn present_aux(&mut self, runs: &[TextRun<'_>]) -> Result<(), RenderError>
pub fn present_aux(&mut self, runs: &[TextRun<'_>]) -> Result<(), RenderError>
Draw runs on the secondary target and present it. No-op with none
attached.
Deliberately not called from render: the two
surfaces present independently, so a frame the output drops does not
have to cost the console one, and neither surface’s state can reach the
other’s.
Independent is not free. The caller decides when this runs, and in
the standalone that is the display thread — so a console that stalls
stalls the loop that called it, whatever the two surfaces do
separately. The cost is a measurement: Plan 0147 Phase 4 put it inside
noise across three frame-time regimes on an integrated Radeon, and
aux_counts is what makes such a reading
distinguishable from a console that never presented at all.
Sourcepub fn queue_text(&mut self, runs: &[TextRun<'_>])
pub fn queue_text(&mut self, runs: &[TextRun<'_>])
Queue text runs to composite over the next rendered frame; the queue is
cleared after each render. The standalone fills it each frame with the
active preset name and, while the browse overlay is open, its rows. A
text-feature (standalone) path — the plugin/default build has no text.
Sourcepub fn set_now_playing(&mut self, text: &str)
pub fn set_now_playing(&mut self, text: &str)
Announce the currently playing track (ADR-0110). The banner fades in, holds, and fades out on its own; the caller only says what, never when to stop.
The string is artist - title, split on the first -. Setting the
string that is already set does nothing, so a metadata source may push on
every update it receives. An empty string clears the banner.
Source-agnostic by construction (ADR-0001): the argument carries no
evidence of whether it came from Windows SMTC or foobar’s titleformat.
Callers must not call this from an audio callback — the copy allocates.
Sourcepub fn enable_diagnostics(&mut self, on: bool)
pub fn enable_diagnostics(&mut self, on: bool)
Enable or disable rolling frame-time collection — the gated diagnostics clock read (Plan 0011). The standalone leaves this on so the title always shows live fps/p99; turning it off keeps the core fully clock-free.
Sourcepub fn set_overlay(&mut self, on: bool)
pub fn set_overlay(&mut self, on: bool)
Turn the on-screen debug overlay on or off (off by default). Independent of collection, so the plugin can log metrics without painting the overlay.
Sourcepub fn overlay_enabled(&self) -> bool
pub fn overlay_enabled(&self) -> bool
Whether the debug overlay is currently painted.
Sourcepub fn frame_ms_p50(&self) -> f32
pub fn frame_ms_p50(&self) -> f32
Median frame time over the diagnostics window, in milliseconds.
Native-only, beside the analysis snapshot below and for its reason:
Metrics mirrors the C ABI’s RlxMetrics, so a field added there
widens that surface (ADR-0052). A median beside the p99 is what makes a
frame-time reading legible — typical against worst — where the mean the
snapshot already carries sits between them saying neither.
Sourcepub fn analysis_metrics(&self) -> AnalysisMetrics
pub fn analysis_metrics(&self) -> AnalysisMetrics
The last drawn frame’s analysis snapshot — the levels and the downbeat lock state. Native-only: deliberately absent from the C ABI, so the foobar plugin has no counterpart (ADR-0052).
Sourcepub fn preset_name(&self) -> &str
pub fn preset_name(&self) -> &str
Name of the currently active preset.
Sourcepub fn adapter_is_software(&self) -> bool
pub fn adapter_is_software(&self) -> bool
Whether the active GPU adapter is a CPU/software rasterizer (WARP on DX12). Visual-QA tests read this to skip differential checks the software rasterizer can’t render faithfully — notably the fullscreen-scene + background-pipeline coexistence, which WARP mis-renders while real hardware renders it correctly (Plan 0025 / ADR-0026).
Sourcepub fn adapter_description(&self) -> &str
pub fn adapter_description(&self) -> &str
The active adapter’s description — name, backend, device type and driver.
For reports that have to name the machine they were taken on
(ADR-0071): a frame time is a fact about a GPU and a driver rather
than about the code, so a cost instrument that prints one has to say
which. Read by core/tests/collage_cost.rs; nothing on a render path
consults it.
Sourcepub fn active_system_name(&self) -> &'static str
pub fn active_system_name(&self) -> &'static str
Name of the built-in system the active preset drives (e.g. the frontend shows it next to the preset name).
This is the scene’s display string — "fragment field", with a
space. It is not the key anything looks a system up by; see
Renderer::active_system_key, which is a different string for every
system whose name is more than one word.
Sourcepub fn active_system_key(&self) -> &'static str
pub fn active_system_key(&self) -> &'static str
The canonical key of the system the active preset drives —
"fragment_field", the exact string a preset writes in its system
field and the schema export labels that system’s parameter roster with.
Distinct from Renderer::active_system_name and not interchangeable
with it: the two coincide on the four systems whose names are one word
(swarm, spectrum, emitter, attractor) and differ on every other,
so code that resolves a schema roster from the display name works for a
quarter of the systems and silently finds nothing for the rest. Anything
keyed by system takes this one (ADR-0184).
"" on an empty roster, matching its sibling.
Sourcepub fn active_preset_source(&self) -> Option<&Path>
pub fn active_preset_source(&self) -> Option<&Path>
The file the active preset was read from, or None when it came from
the embedded set and has no file on disk.
Absolute, as crate::preset::load_dir recorded it, so a consumer in
another process can act on it.
Sourcepub fn cap_overflow(&self) -> Option<&CapOverflow>
pub fn cap_overflow(&self) -> Option<&CapOverflow>
The segment-cap truncation from the active preset’s last configure, if
its geometry hit the fixed cap (ADR-0007: the cap is never a silent cut).
Refreshed on every active-preset change (select / cycle / hot-reload); the
standalone surfaces it at load. None in the normal case where geometry
fit — which is every shipped preset.
Sourcepub fn render(
&mut self,
frame: &AnalysisFrame,
dt: f32,
) -> Result<(), RenderError>
pub fn render( &mut self, frame: &AnalysisFrame, dt: f32, ) -> Result<(), RenderError>
Draw the current preset for this analysis frame, advancing all animation
by dt real seconds (Plan 0014 Phase 2). The frontend measures and
injects elapsed wall-clock time so the visuals run at the same speed on
any refresh rate; core never reads a clock. Lost/outdated surfaces
self-heal by reconfiguring; timeouts/occlusion skip the frame; only a
validation failure (a bug) bubbles up.