Add libplacebo GPU module with render and shader filters#1201
Add libplacebo GPU module with render and shader filters#1201D-Ogi wants to merge 6 commits intomltframework:masterfrom
Conversation
|
How does this compare with using libplacebo through the existing avfilter? |
|
The main difference is the GPU context lifecycle. The avfilter wrapper creates a new AVFilterGraph per filter instance and vf_libplacebo initializes its own Vulkan device inside that graph. With multiple filters on a timeline you get multiple GPU contexts. The native module uses a process-wide singleton in gpu_context.c so one pl_gpu, one pl_renderer, one pl_dispatch shared across all instances. The frame path is also shorter. The avfilter wrapper does two memcpy round-trips between MLT buffers and AVFrames (line-by-line with linesize conversion), on top of whatever vf_libplacebo does internally for GPU transfer. The native module calls pl_tex_upload/pl_tex_download directly on the MLT image pointer, no intermediate AVFrame. A the most interesting part is the shader filter that doesn't have an avfilter equivalent. It loads mpv .hook files at runtime and checks file mtime on every frame so when the file changes on disk, it re-parses only the pl_hook object while keeping the GPU context alive. This is useful for iterative shader development in for instance an NLE where you want to edit a .hook file in a text editor and see the result on the timeline without restarting anything. The avfilter path would need a full graph rebuild to pick up a changed shader_path. The trade-off is a direct build dependency on libplacebo vs getting it through FFmpeg. The module could be optional so wouldn't affect builds where libplacebo isn't available. Regarding the tests, at this time I don't have solid Linux/MacOS environments to test all the dependencies, but as I read from failed tests the root cause seems to be easy to fix. |
518b737 to
67d2fa2
Compare
New module 'placebo' providing GPU-accelerated video processing via libplacebo. Includes two filters: - placebo.render: GPU scaling, debanding, dithering, and tonemapping with quality presets (fast/default/high_quality) - placebo.shader: Custom mpv-compatible .hook shader support Backend priority: D3D11 (Windows) -> Vulkan -> OpenGL. Vulkan loader is dynamically loaded on Windows when libplacebo is built without vk-proc-addr support. Features: - Singleton GPU context with thread-safe access - Shader cache persistence - Multiple scaling algorithms (ewa_lanczos, lanczos, mitchell, etc.) - Tone mapping (auto, clip, mobius, reinhard, hable, bt.2390, spline) - Graceful fallback to passthrough when no GPU is available The module is enabled by default but skipped automatically when libplacebo is not installed.
67d2fa2 to
ebe1cae
Compare
|
Fixed the MinGW build: |
|
You need to get at least some build workflows to actually build this (not all). For example,
Our codebase generally prefers the macros |
c5189b2 to
8129348
Compare
Use PRIu64/PRId64 from <inttypes.h> instead of %zu/%ld for size logging in the placebo module. Add libplacebo-dev packages to Ubuntu, Debian, and Fedora 42 CI workflows, and mingw-w64-x86_64-libplacebo to the MSYS2 MinGW64 workflow.
8129348 to
35e85eb
Compare
|
Done. Added libplacebo packages to the three workflows, switched to Verified on my fork - all green: MSYS2 MinGW64, Ubuntu 24.04, 22.04, Debian stable/testing/unstable, Fedora 42, 38. |
Break long mlt_log_info() call into multi-line format to match the project's clang-format rules (same style as load_cache above).
|
Hi Dan, could I ask if you have an estimated timeline for the next round of review? This PR is a key enabler for my downstream work. The shader filters support lets Kdenlive reproduce After Effects preset pipelines and opens the door for the community to write custom GPU shaders within MLT. |
|
In about 10 days as I’m on vacation |
|
Something for you to comment on or think about until then. I have not looked closely enough. What happens when multiple placebo MLT filters are used on a producer? Does it transfer the image from RAM to GPU and back to RAM for each filter? |
|
Currently each filter does a full RAM -> GPU -> RAM roundtrip per frame. The flow for N chained placebo filters looks like this: RAM (producer image) So with 3 filters that's 6 CPU <-> GPU transfers instead of the ideal 2 (one upload at the start, one download at the end). The reason is that MLT's mlt_frame_get_image() contract is fundamentally CPU-buffer-based. And I think no way for a filter to pass a GPU texture handle to the next filter in the chain. To eliminate the intermediate transfers, the frame would need to carry a "GPU-resident image" flag and a texture reference that downstream filters can reuse, with only the last filter in the chain (or the consumer) performing the final download. That's a non-trivial change to MLT's image passing architecture. I could attempt to implement this, for example by attaching a pl_tex to the frame via mlt_properties_set_data and having each placebo filter check for an existing GPU texture before uploading from RAM. The last consumer or non-placebo filter would trigger the download. But I'd rather hear your thoughts on the right approach before going down that path, since it touches assumptions about frame ownership and lifetime that you know much better than I do. |
When multiple placebo filters are stacked on one clip, each filter previously did a full RAM→GPU upload and GPU→RAM download. The intermediate uploads are redundant because the next placebo filter would re-upload the same pixels immediately. Each filter now attaches its output texture to the mlt_frame via placebo_frame_put_tex(). The next placebo filter calls placebo_frame_take_tex() to grab it directly as source, skipping the upload. The download to RAM still happens every time (MLT expects the image buffer to be current for non-GPU filters). Staleness detection: put_tex records the RAM buffer pointer, take_tex compares it against the current pointer. If a CPU filter ran in between and requested a writable buffer (triggering a copy and new allocation), the pointers differ and take_tex returns NULL, falling back to a fresh upload. Also cleans up internal ticket-style comments (C1/W2/etc.) with descriptions of actual logic and pitfalls.
84ee7e4 to
32f8a47
Compare
Add apply_shader_params() to override pl_hook DYNAMIC parameters from
MLT animated properties (shader_param.* prefix) on every frame. Uses
mlt_properties_anim_get_double/int to correctly resolve keyframe strings
("0=200;50=100") at the current frame position.
Add base64 decoding for shader_text values prefixed with "base64:" to
support inline shaders with characters that are problematic in MLT
property strings.
Run clang-format-14 (matching CI) on filter_placebo_shader.c and gpu_context.c to fix designated initializer spacing, ternary line breaks, and long argument lists.

Summary
New module
placeboproviding GPU-accelerated video processing via libplacebo:.hookshader support with hot-reload on file changeArchitecture
gpu_context.c) with thread-safe initialization and render lockingvk-proc-addrsupportBuild
Controlled by
MOD_PLACEBOCMake option (default ON). Requireslibplacebovia pkg-config. Optionally links D3D11/DXGI whenPL_HAVE_D3D11is detected at configure time. MSVC builds link PThreads4W.Files (10 total)
CMakeLists.txtMOD_PLACEBOoptionsrc/modules/CMakeLists.txtsrc/modules/placebo/CMakeLists.txtsrc/modules/placebo/factory.csrc/modules/placebo/gpu_context.hsrc/modules/placebo/gpu_context.csrc/modules/placebo/filter_placebo_render.csrc/modules/placebo/filter_placebo_render.ymlsrc/modules/placebo/filter_placebo_shader.csrc/modules/placebo/filter_placebo_shader.ymlTesting
Tested on Windows with D3D11 backend via Kdenlive. Verified:
.hookfiles