Migrate to Emerge 0.4

Copy Markdown View Source

Emerge 0.4 unifies renderer selection, changes screenshot capture to retained renderer state, and moves video submission to viewport-local atom targets.

Use rendering_api

Replace macOS-specific selection:

# 0.3
EmergeSkia.start(otp_app: :my_app, backend: :macos, macos_backend: :metal)

# 0.4
EmergeSkia.start(otp_app: :my_app, backend: :macos, rendering_api: :metal)

Replace the compatibility renderer name:

# Deprecated in 0.4
backend_renderer: :gl

# Current
rendering_api: :opengl

backend_renderer and :gl still warn and work in 0.4, but should not be used in new configuration. macos_backend and dispatch_mode have been removed and raise during option validation.

See EmergeSkia.start/1 for supported backend/rendering API combinations and fallback rules.

Capture a retained renderer

In 0.3, render_to_pixels/2 and render_to_png/2 accepted a tree, created a one-shot raster surface, and returned a binary:

# 0.3
png =
  EmergeSkia.render_to_png(tree,
    otp_app: :my_app,
    width: 800,
    height: 600
  )

In 0.4, they capture the latest already-presented frame from a running renderer and return a result tuple:

# 0.4
{:ok, renderer} = EmergeSkia.start(otp_app: :my_app, backend: :wayland)
{_diff_state, _assigned_tree} = EmergeSkia.upload_tree(renderer, tree)

{:ok, png} = EmergeSkia.render_to_png(renderer, timeout: 5_000)

Update both the first argument and return-value matching. macOS host and headless PRIME sessions currently return a not-supported error for capture.

See EmergeSkia.render_to_pixels/2 and EmergeSkia.render_to_png/2 for the current capture options and backend support.

Submit video to an atom target

Renderer-owned target handles, direct viewport connections, and submit_prime/2 were removed. Describe the destination with an atom:

video([width(fill()), height(fill()), image_fit(:contain)], :camera)

Submit each owned binary or borrowed DMA-BUF frame to the viewport:

:ok = Emerge.submit_video_frame(viewport, :camera, frame)

Every normal return consumes the frame. Hidden targets drop it immediately; visible targets retain only the latest frame. Use VideoInterop.retain/2 first when another branch also needs the borrowed storage.

For Emerge-to-Emerge transport, configure the headless viewport to send %VideoInterop.Frame{} values into Membrane.VideoInterop.Source, then route them to Membrane.VideoInterop.Sink with an MFA that calls Emerge.submit_video_frame/3. Do not forward frames through an application handle_info/2 bridge.

The native video ownership contract changed in 0.4. Stop producers and consumers and cold-restart the BEAM operating-system process before loading upgraded Emerge, VideoInterop, or producer native libraries.

Handle renderer stop errors

EmergeSkia.stop/1 can now return {:error, reason} when a headless PRIME session cannot prove ownership-safe shutdown:

case EmergeSkia.stop(renderer) do
  :ok -> :ok
  {:error, reason} -> Logger.error("renderer did not stop cleanly: #{inspect(reason)}")
end

Do not treat an error as successful cleanup. Cold-restart the operating-system process before loading replacement native code.

Check stats schema version

The renderer stats payload changed with renderer, video, Vulkan, and cache diagnostics. The 0.4 schema version is 25.

with {:ok, %{version: 25} = snapshot} <- EmergeSkia.stats(renderer, :peek) do
  inspect_snapshot(snapshot)
end

Consumers that persist or transmit snapshots should branch on version rather than assuming the 0.3 map shape.

Review new memory defaults

Decoded raster assets now default to a 256-entry, 256 MiB cache. Constrained devices should set smaller limits and enable target-sized decode:

assets: [
  decode_at_size: true,
  cache: [max_entries: 8, max_bytes: 2 * 1024 * 1024]
]

See Use assets for configuration and memory examples.

Scope runtime font registration

Runtime font registration now requires the renderer that owns the font:

# 0.3
:ok = EmergeSkia.load_font_file("my-font", 400, false, path)

# 0.4
:ok = EmergeSkia.load_font_file(renderer, "my-font", 400, false, path)

The registration and its text-metrics cache are isolated from every other renderer. Fonts declared through assets.fonts are loaded into the renderer created by that start/1 call.

Review Vulkan configuration

Vulkan is supported and opt-in at build time. Existing OpenGL configurations do not switch to Vulkan automatically.

config :emerge,
  compiled_backends: [wayland: [:opengl, :vulkan]]

compiled_backends is a backend/API matrix. Use :all for every GPU API on a backend or list the exact APIs to include. For example, [wayland: [:vulkan]] contains Vulkan but no OpenGL. Atom entries retain their 0.3 behavior and select OpenGL. 64-bit Linux has precompiled Vulkan-only Wayland, DRM, and headless variants.

Explicit rendering_api: :vulkan does not fall back. DRM Vulkan also requires an exact vulkan_drm_node path separate from drm_card.