# Migrate to Emerge 0.4

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:

```elixir
# 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:

```elixir
# 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:

```elixir
# 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:

```elixir
# 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:

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

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

```elixir
: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:

```elixir
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`.

```elixir
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:

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

See [Use assets](../tutorials/use_assets.md) for configuration and memory
examples.

## Scope runtime font registration

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

```elixir
# 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.

```elixir
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`.
