Skip to content

Prepare for multi decoder support - #492

Merged
JeromeMartinez merged 15 commits into
MediaArea:mainfrom
JeromeMartinez:MultiDecoder_Prepare
Oct 12, 2026
Merged

JeromeMartinez merged 15 commits into
MediaArea:mainfrom
JeromeMartinez:MultiDecoder_Prepare

Conversation

@JeromeMartinez

Copy link
Copy Markdown
Member

No description provided.

Preparation for other FFV1 decoders: --decoder selects the FFV1 decoder
used for decoding and checking. Only our decoder is available for the
moment, so internal and auto use it.
If the video encoder is ffv1_vulkan, Vulkan is wanted, so the decoder
used for checking (encoding with --check, or checking of a Matroska file)
is auto if no decoder is provided.
auto is kept for being resolved when a decoder is needed, it is our
decoder only for the moment.
Each mkv file is decoded by our decoder, then by the decoder selected by
--decoder auto with the Vulkan encoder (-c:v ffv1_vulkan), our decoder
only for the moment.
Preparation for decoders using the frame data after the call:
- Hold() keeps the current memory map until Release(), also after a
  Remap() or a Close() (memory map method only),
- Readable_End() is the end of the bytes readable from the data, which
  may be after the end of the file with the other methods.
Preparation for decoders using the frame data after the call (decoding
in other threads, data imported in GPU memory space) and reading after
the frame data (FFmpeg), without copy in the decoders:
- a wrapper tells the count of bytes its decoder reads after the frame
  data (Padding()),
- the decoder receives a holder of the frame data, Hold() keeps the data
  until Release(),
- the Matroska parser gives the data in the file map, kept as long as
  needed (memory map method), or a copy in reused buffers if the padding
  is not readable (end of the file), if the file map can not be kept
  (other methods) or if allocated memory is requested.
Our decoder does not use them.
…ames

Preparation for decoders running in their own threads: End() is called
when there are no more frames, so a decoder may release its resources
during the output of the last frames, and Remaining is the count of
frames after the frame given to Decode() (if known).
…anges

Planes were never created again, even if the frame size changed.
Preparation for frames reused by decoders using different slice counts:
planes are reused only if the frame size is the same and there is enough
room for the line slices.
Preparation for thread pools with a lower priority.
Preparation for decoding frames in parallel by several decoders: keyframes
can be decoded by any decoder.
Preparation for decoders outputting planar R, G, B, (A) samples (e.g.
FFmpeg): Transform_Direct_Init() returns a function transforming a line
of planar samples, not JPEG2000-RCT coded, to a line of the frame, for
DPX RGB(A) 8-bit, RGB 10-bit method A, RGB(A) 12-bit method A and RGB(A)
16-bit flavors, and TIFF RGB(A) 8-bit, RGB 16-bit and RGBA 16-bit LE
flavors (AVX2 for RGB 10-bit, 12-bit and 16-bit).
Transforms are deleted through a pointer to transform_base (e.g. by the
FFV1 decoder), so its destructor is virtual (no undefined behavior, no
compiler warning).
If the RAWCOOKED_TRACE environment variable is set, the processing stages
of each thread (parsing, copies of frame data, decoding, outputs,
hashes...) are recorded, with the CPU time of the thread during each stage,
and counters (e.g. frames in flight).
At the end, the trace is written in Chrome trace event format (JSON, e.g.
for https://ui.perfetto.dev) to the file named by the variable, and a
summary (count, wall and CPU time of each stage, busy and CPU time of each
thread) is written to stderr.
Without the variable, each instrumentation point only tests a boolean.
With a thread pool, our decoder returns when all slices of a frame are
being decoded, and a callback is called at the end of the decoding of the
frame, so the threads which would wait for the last slices of a frame
decode the slices of the next frame, and the parsing of the next frame
(including the remapping of the file) is done during the decoding.
A keyframe uses a free set of slices, other frames use the set of the
previous frame after its end (decoding states), and all previous frames
are decoded first if there is no configuration record (parameters in
slices).
The frame data is kept by its holder until the end of the decoding.
3% faster with s1.mkv (i7-7800X).
If the frame is bigger than the cache (32 MiB or more), it would be read
again from memory anyway, so the direct transforms of frames decoded by
FFmpeg write it with non-temporal stores (after a scalar head for the
alignment), without reading the destination in the cache first.
Transform of 6464x4852 RGB 16-bit planes: 23% less CPU time (35% in a
micro-benchmark, 8.2 ms instead of 12.6 ms with 4 threads).
With the microcode update for the Jump Conditional Code erratum (Intel
Skylake and derived CPUs), jumps crossing or ending on 32-byte boundaries
are slower, so the speed of the decoding depended on the code layout
(variations of up to 10% between builds of the same decoder code).
Jumps are now aligned (MSVC x64 Release: /QIntel-jcc-erratum, GNU:
-Wa,-mbranches-within-32B-boundaries if the assembler supports it).
Our decoder with s1.mkv on i7-7800X: 10% faster with MSVC, 7% with GCC,
internal+vulkan 4% faster.
@digitensions

Copy link
Copy Markdown
Contributor

Exciting!!

@JeromeMartinez

Copy link
Copy Markdown
Member Author

Exciting!!

@digitensions unfortunately still no help about AI GPUs, sorry... It will be a supporting in the same decoding CPU and general purpose GPU.

@JeromeMartinez
JeromeMartinez merged commit 957192e into MediaArea:main Oct 12, 2026
3 of 4 checks passed
@JeromeMartinez
JeromeMartinez deleted the MultiDecoder_Prepare branch October 12, 2026 15:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants