API Reference · Capturing a frame

SceneRenderer

Namespace: Ava3D

public sealed class SceneRenderer

Renders a Scene to pixels with no window and no view — on a GPU where one can be had without either, and on the CPU where it cannot.

What it is for. A thumbnail of a model for a catalogue, a picture at a size that has nothing to do with any window, a frame from a test or a build server, a probe capture from a command line — the things OfflineRenderer does, drawn by the same renderers a view on screen draws with, with the same pixels. Everything a view does per frame happens here per call: the same snapshot, the same backend, the same composite and the same readback as Ava3DView.CaptureAsync, so a picture from here and a capture of a view on the same device agree to the pixel.

Which device. This renderer makes its own, rather than borrowing a window's: MTLCreateSystemDefaultDevice and a command queue on macOS and iOS; a Vulkan device through MoltenVK there when asked for, or through the platform's own loader on Windows and Linux; an OpenGL context from the platform graphics the application was started with, when that is OpenGL; and the CPU renderer when none of those can be made, which includes always in a browser. SceneRenderer.Kind says which it got and SceneRenderer.Description names the card, and why anything else was declined.

What is shared. Resources live in a registry per device — see the guide — and this renderer joins the registry of the device it made. On Metal that is the system default device, which is the device every window on a one-GPU Mac or iPhone draws with: a texture uploaded for a thumbnail here is resident for the view that shows the model next, and the other way round. On the CPU it is the one software registry every view in the process shares. A Vulkan or OpenGL device made here is its own device, a second copy of everything it draws with, and shares nothing — as is a Metal device that is not the one a window happens to have, which a dual-GPU Intel Mac can produce.

Threads. One render thread per renderer, made with it and ended by SceneRenderer.Dispose. The device, the Skia context and the backend live on that thread and nowhere else; a call from any thread is queued to it and answered as a task. The scene is read on the thread that calls SceneRenderer.RenderAsync, which should be the thread that owns it, and never touched again; the pixels are converted on the thread pool.

One renderer serves any number of calls, in order. Make one and keep it: the device, the shaders and the registry it joined are the expensive part, and a second renderer of the same kind is a second context on the same device — cheap on Metal and the CPU, a whole second device on Vulkan.

Properties

MemberDescription
string Description { get; }

The graphics API and the card, as a diagnostics panel would name them — "Metal on Apple M3 Max", "Vulkan via MoltenVK on Apple M3 Max", "CPU" — and, after a dash, why each device that was asked for before this one could not be made.

RendererKind Kind { get; }

What was actually made, which is what was asked for only when the platform could oblige.

Methods

MemberDescription
static SceneRenderer Create(RendererPreference preference = RendererPreference.Gpu)

Makes a renderer on the best device preference allows, or on the CPU when no such device can be made here — never an exception for want of a GPU. SceneRenderer.Kind says which. The device, its Skia context and the renderer's shaders are all made before this returns, so a renderer that comes back can draw.

void Dispose()

Ends the render thread, taking the backend, the Skia context and the device down on it. What was uploaded to a shared registry stays for the views on that device; a device of this renderer's own goes with it. Renders still queued complete as cancelled or with ObjectDisposedException.

Threading.Tasks.Task PrepareAsync(Scene scene, IProgress<PrepareProgress> progress = null, Threading.CancellationToken cancellationToken = default)

Uploads everything scene draws with to this renderer's device ahead of drawing it, reporting how far it has got, and completes when it is all there. What is uploaded stays until the scene is rendered or the renderer is disposed, and is shared with every view on the same device — see the class remarks for which devices those are.

scene

The scene to make resident. Read on the calling thread and not retained.

progress

Told each time the count of resident resources changes. Null for nobody.

cancellationToken

Stops the wait and lets go of whatever was still on its way.

Threading.Tasks.Task<CapturedFrame> RenderAsync(Scene scene, Camera camera, PixelSize size, RenderOptions options = null, Threading.CancellationToken cancellationToken = default)

Draws scene as seen by camera, size pixels wide and high, and hands back the pixels.

scene

What to draw. Read on the calling thread before this returns its task, and not retained.

camera

Where to draw it from. Read on the calling thread, as the scene is.

size

The picture's size in pixels. The camera keeps its field of view, so the aspect ratio is this one.

options

Samples, supersampling, the composite and the format. Null for the defaults.

cancellationToken

Stops the render where it is: before it starts, while the scene's textures are on their way to the device, or while the frame is on its way back. Nothing already uploaded is undone.

returns

The picture as a CapturedFrame — straight alpha, rows top to bottom, in the format asked for — with a frame number that counts this renderer's own frames from one.

See also