Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,33 @@ jobs:
ERIKA_NATIVE_TARGET="${{ matrix.target }}"
cargo clippy --locked -p erika --all-targets --all-features
--no-deps
- name: Build wgpu_decode_png example
if: matrix.arch == 'arm64'
run: |
# Link against libclang_rt.osx to resolve __isPlatformVersionAtLeast,
# a symbol the sanitizer runtime needs but that may be weakly referenced.
# See commits acae10f and 3498b8e for context.
RT_DIR="$(dirname "$(find "$(dirname "$(xcrun -f clang)")/../lib/clang" -name libclang_rt.osx.a | head -1)")"
echo "clang_rt dir: $RT_DIR"
ERIKA_NATIVE_PROFILE="$PROFILE" \
ERIKA_NATIVE_TARGET="${{ matrix.target }}" \
RUSTFLAGS="-C link-arg=-L$RT_DIR -C link-arg=-lclang_rt.osx" \
cargo build --locked -p wgpu_decode_png --release --target "${{ matrix.target }}"
- name: Package erika-capi bundle
if: matrix.arch == 'arm64'
run: >-
bash packaging/bundle.sh erika-capi-macos-arm64
dist/erika-capi-macos-arm64.zip
"target/${{ matrix.target }}/release/liberika_capi.a"
- name: Upload test artifacts
if: matrix.arch == 'arm64'
uses: actions/upload-artifact@v4
with:
name: dv-test-macos-arm64
path: |
target/aarch64-apple-darwin/release/wgpu_decode_png
dist/erika-capi-macos-arm64.zip
if-no-files-found: error

ios:
name: iOS device staticlib
Expand Down
71 changes: 71 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,77 @@

## Unreleased

### Renderer

#### Dolby Vision and HDR tone mapping

- Dolby Vision RPU mapping via libplacebo-style piecewise polynomial/MMR
reshaping, followed by the RPU nonlinear matrix, PQ linearization, and
LMS→RGB composite. Profile 5/8 VUI tags are forced to BT.2020/PQ.
- Profile 5 falls back to software decode on mobile backends (MediaCodec /
generic AvCodec) that cannot attach RPU side data to hardware frames;
VideoToolbox and D3D11VA keep hardware decode.
- Profile 7 FEL/MEL RPUs are no longer rejected outright. An RPU whose
`disable_residual_flag` asks for an enhancement layer still applies its
base-layer reshaping curves, color matrices, and L1 trims — dropping the
whole RPU threw those away and left the frame as plain HDR10. The
un-composable residual is reported through the new `Frame::dovi_el_status()`
and one throttled `dovi_el_not_composed` diagnostic per stream, matching
libplacebo's split between `nlq_active` and "consumers that have not bound an
enhancement layer must not look at these fields".
- Per-frame Dolby Vision L1 brightness (`min_pq`/`max_pq`/`avg_pq`) drives the
tone-map source peak and scene-average pivot; the static mastering-display
(L0) peak still decides output-mode negotiation and whether tone mapping /
the perceptual gamut LUT are enabled.
- Tone mapping switched to a libplacebo-style IPT-domain color map with
**BT.2390 EETF as the default** operator, plus spline, BT.2446 method A,
SMPTE ST 2094-10, Mobius, Reinhard, and clip. Black-point compensation uses
`target black = target peak / contrast_ratio` (auto 1000:1 for SDR).
- Perceptual gamut mapping via a CPU-generated 48×32×256 IPT 3D LUT
(following libplacebo's lattice and index mapping) when a tone-mapped HDR
source is compressed into a smaller gamut; generated on a background thread
so the first wide-gamut frame does not stall the render thread. Chroma
rolloff is currently a simplified dead-zone blend rather than libplacebo's
full per-hue boundary search.
- Scene-adaptive HDR10: software-decoded PQ frames without DoVi L1 get a
CPU-measured scene-average luminance (`luma_stats`) that drives the
tone-map pivot, mirroring mpv's `--hdr-compute-peak`.
- Metal EDR negotiation reads the presenting screen rather than
`NSScreen.mainScreen`, and Auto HDR is decided from that display's
capability. Callers must set `CAMetalLayer.delegate` (examples updated).
- Metal: the perceptual gamut LUT is only marked active when the texture
resolved for the frame actually matches its (source, target, target-peak)
key. A headroom or primaries change while a new LUT generates used to bind
the 1x1x1 placeholder with `gamut_lut_enabled` still set, rendering black
frames until generation finished. D3D11 now masks the uniform the same way
while its LUT is still generating.
- ST 2094-10 tone mapping picks its knee in the PQ domain, matching
libplacebo's internal rescale, and `ToneMapConfig::curve_param` now tunes
its knee adaptation (default 0.70, libplacebo's `param_def`). Previously the
parameter had no effect and the knee was selected in linear nits.
- The tone map, its black-point compensation, and the perceptual gamut LUT are
enabled from the static mastering-display (L0) peak rather than the
per-frame Dolby Vision L1 peak, so dark scenes no longer drop them for a
frame and then restore them.
- Metal EDR / extended-linear output linearizes subtitle and danmaku colors
before compositing into the linear drawable; previously they were written
gamma-encoded, making mid-tones and colored text too bright.
- HDR10 scene-luma measurement honors the frame's color range: limited (TV)
range luma planes are expanded over the legal code span like the shaders'
`expand_ycbcr_range`, instead of being measured as full range. The
measurement also distinguishes `yuv420p10le` (10-bit code in bits `[9:0]`)
from P010 (left-aligned in `[15:6]`); treating both as P010 measured
software Main10 HDR10 as near-black.
- The perceptual gamut LUT's I axis now spans the target's `[black, peak]` in
PQ codes (libplacebo's `gamut.min_luma`/`max_luma`) instead of
`[0, peak]`; generation and all three backends' samplers agree, and the
cache key includes the target black, so a `contrast_ratio` change
regenerates the LUT. SDR targets shift their gamut mapping by up to ~3.5%
of the I axis; HDR/EDR targets are unchanged (black = 0).
- A degenerate ST 2094-10 anchor set (duplicated/non-finite points) falls back
to the identity curve (Clip) instead of zero coefficients, which the shader
would have rendered as a black frame.

## 0.1.8 - 2026-09-07

### Compatibility
Expand Down
5 changes: 5 additions & 0 deletions crates/erika/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,10 @@ pub struct PlayerVideoFrame {
pub media_time: Duration,
pub late_by: Option<Duration>,
pub generation: u64,
/// Measured scene-average luminance (nits) attached by the presenter for
/// HDR10 software frames without Dolby Vision L1 metadata. `None` for
/// every other source; renderers fold it into the tone-map pivot.
pub scene_avg_nits: Option<f32>,
}

impl PlayerVideoFrame {
Expand All @@ -481,6 +485,7 @@ impl PlayerVideoFrame {
media_time,
late_by,
generation,
scene_avg_nits: None,
})
}
}
Expand Down
Loading
Loading