summaryrefslogtreecommitdiffstats
path: root/src/include/libplacebo/renderer.h
blob: d2e01e49ee304868b54e2264294ed2a8c8a87432 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
/*
 * This file is part of libplacebo.
 *
 * libplacebo is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * libplacebo is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with libplacebo.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef LIBPLACEBO_RENDERER_H_
#define LIBPLACEBO_RENDERER_H_

#include <libplacebo/config.h>
#include <libplacebo/colorspace.h>
#include <libplacebo/filters.h>
#include <libplacebo/gpu.h>
#include <libplacebo/shaders/colorspace.h>
#include <libplacebo/shaders/deinterlacing.h>
#include <libplacebo/shaders/dithering.h>
#include <libplacebo/shaders/film_grain.h>
#include <libplacebo/shaders/icc.h>
#include <libplacebo/shaders/lut.h>
#include <libplacebo/shaders/sampling.h>
#include <libplacebo/shaders/custom.h>
#include <libplacebo/swapchain.h>

PL_API_BEGIN

// Thread-safety: Unsafe
typedef struct pl_renderer_t *pl_renderer;

// Enum values used in pl_renderer_errors_t as a bit positions for error flags
enum pl_render_error {
    PL_RENDER_ERR_NONE              = 0,
    PL_RENDER_ERR_FBO               = 1 << 0,
    PL_RENDER_ERR_SAMPLING          = 1 << 1,
    PL_RENDER_ERR_DEBANDING         = 1 << 2,
    PL_RENDER_ERR_BLENDING          = 1 << 3,
    PL_RENDER_ERR_OVERLAY           = 1 << 4,
    PL_RENDER_ERR_PEAK_DETECT       = 1 << 5,
    PL_RENDER_ERR_FILM_GRAIN        = 1 << 6,
    PL_RENDER_ERR_FRAME_MIXING      = 1 << 7,
    PL_RENDER_ERR_DEINTERLACING     = 1 << 8,
    PL_RENDER_ERR_ERROR_DIFFUSION   = 1 << 9,
    PL_RENDER_ERR_HOOKS             = 1 << 10,
    PL_RENDER_ERR_CONTRAST_RECOVERY = 1 << 11,
};

// Struct describing current renderer state, including internal processing errors,
// as well as list of signatures of disabled hooks.
struct pl_render_errors {
    enum pl_render_error errors;
    // List containing signatures of disabled hooks
    const uint64_t *disabled_hooks;
    int num_disabled_hooks;
};

// Creates a new renderer object, which is backed by a GPU context. This is a
// high-level object that takes care of the rendering chain as a whole, from
// the source textures to the finished frame.
PL_API pl_renderer pl_renderer_create(pl_log log, pl_gpu gpu);
PL_API void pl_renderer_destroy(pl_renderer *rr);

// Returns current renderer state, see pl_render_errors.
PL_API struct pl_render_errors pl_renderer_get_errors(pl_renderer rr);

// Clears errors state of renderer. If `errors` is NULL, all render errors will
// be cleared. Otherwise only selected errors/hooks will be cleared.
// If `PL_RENDER_ERR_HOOKS` is set and `num_disabled_hooks` is 0, clear all hooks.
// Otherwise only selected hooks will be cleard based on `disabled_hooks` array.
PL_API void pl_renderer_reset_errors(pl_renderer rr,
                                     const struct pl_render_errors *errors);

enum pl_lut_type {
    PL_LUT_UNKNOWN = 0,
    PL_LUT_NATIVE,      // applied to raw image contents (after fixing bit depth)
    PL_LUT_NORMALIZED,  // applied to normalized (HDR) RGB values
    PL_LUT_CONVERSION,  // LUT fully replaces color conversion

    // Note: When using a PL_LUT_CONVERSION to replace the YUV->RGB conversion,
    // `pl_render_params.color_adjustment` is no longer applied. Similarly,
    // when using a PL_LUT_CONVERSION to replace the image->target color space
    // conversion, `pl_render_params.color_map_params` are ignored.
    //
    // Note: For LUTs attached to the output frame, PL_LUT_CONVERSION should
    // instead perform the inverse (RGB->native) conversion.
    //
    // Note: PL_LUT_UNKNOWN tries inferring the meaning of the LUT from the
    // LUT's tagged metadata, and otherwise falls back to PL_LUT_NATIVE.
};

enum pl_render_stage {
    PL_RENDER_STAGE_FRAME,  // full frame redraws, for fresh/uncached frames
    PL_RENDER_STAGE_BLEND,  // the output blend pass (only for pl_render_image_mix)
    PL_RENDER_STAGE_COUNT,
};

struct pl_render_info {
    const struct pl_dispatch_info *pass;    // information about the shader
    enum pl_render_stage stage;             // the associated render stage

    // This specifies the chronological index of this pass within the frame and
    // stage (starting at `index == 0`).
    int index;

    // For PL_RENDER_STAGE_BLEND, this specifies the number of frames
    // being blended (since that results in a different shader).
    int count;
};

// Represents the options used for rendering. These affect the quality of
// the result.
struct pl_render_params {
    // Configures the algorithms used for upscaling and downscaling,
    // respectively. If left as NULL, then libplacebo will only use inexpensive
    // sampling (bilinear or nearest neighbour depending on the capabilities
    // of the hardware / texture).
    //
    // Note: Setting `downscaler` to NULL also implies `skip_anti_aliasing`,
    // since the built-in GPU sampling algorithms can't anti-alias.
    //
    // Note: If set to the same address as the built-in `pl_filter_bicubic`,
    // `pl_filter_nearest` etc.; libplacebo will also use the more efficient
    // direct sampling algorithm where possible without quality loss.
    const struct pl_filter_config *upscaler;
    const struct pl_filter_config *downscaler;

    // If set, this overrides the value of `upscaler`/`downscaling` for
    // subsampled (chroma) planes. These scalers are used whenever the size of
    // multiple different `pl_plane`s in a single `pl_frame` differ, requiring
    // adaptation when converting to/from RGB. Note that a value of NULL simply
    // means "no override". To force built-in scaling explicitly, set this to
    // `&pl_filter_bilinear`.
    const struct pl_filter_config *plane_upscaler;
    const struct pl_filter_config *plane_downscaler;

    // The anti-ringing strength to apply to filters. See the equivalent option
    // in `pl_sample_filter_params` for more information.
    float antiringing_strength;

    // Configures the algorithm used for frame mixing (when using
    // `pl_render_image_mix`). Ignored otherwise. As a special requirement,
    // this must be a filter config with `polar` set to false, since it's only
    // used for 1D mixing and thus only 1D filters are compatible.
    //
    // If set to NULL, frame mixing is disabled, in which case
    // `pl_render_image_mix` will use nearest-neighbour semantics. (Note that
    // this still goes through the redraw cache, unless you also enable
    // `skip_caching_single_frame`)
    const struct pl_filter_config *frame_mixer;

    // Configures the settings used to deband source textures. Leaving this as
    // NULL disables debanding.
    //
    // Note: The `deband_params.grain` setting is automatically adjusted to
    // prevent blowing up on HDR sources. The user need not account for this.
    const struct pl_deband_params *deband_params;

    // Configures the settings used to sigmoidize the image before upscaling.
    // This is not always used. If NULL, disables sigmoidization.
    const struct pl_sigmoid_params *sigmoid_params;

    // Configures the color adjustment parameters used to decode the color.
    // This can be used to apply additional artistic settings such as
    // desaturation, etc. If NULL, defaults to &pl_color_adjustment_neutral.
    const struct pl_color_adjustment *color_adjustment;

    // Configures the settings used to detect the peak of the source content,
    // for HDR sources. Has no effect on SDR content. If NULL, peak detection
    // is disabled.
    const struct pl_peak_detect_params *peak_detect_params;

    // Configures the settings used to tone map from HDR to SDR, or from higher
    // gamut to standard gamut content. If NULL, defaults to
    // `&pl_color_map_default_params`.
    const struct pl_color_map_params *color_map_params;

    // Configures the settings used to dither to the output depth. Leaving this
    // as NULL disables dithering.
    const struct pl_dither_params *dither_params;

    // Configures the error diffusion kernel to use for error diffusion
    // dithering. If set, this will be used instead of `dither_params` whenever
    // possible. Leaving this as NULL disables error diffusion.
    const struct pl_error_diffusion_kernel *error_diffusion;

    // Configures the settings used to simulate color blindness, if desired.
    // If NULL, this feature is disabled.
    const struct pl_cone_params *cone_params;

    // Configures output blending. When rendering to the final target, the
    // framebuffer contents will be blended using this blend mode. Requires
    // that the target format has PL_FMT_CAP_BLENDABLE. NULL disables blending.
    const struct pl_blend_params *blend_params;

    // Configures the settings used to deinterlace frames (see
    // `pl_frame.field`), if required.. If NULL, deinterlacing is "disabled",
    // meaning interlaced frames are rendered as weaved frames instead.
    //
    // Note: As a consequence of how `pl_frame` represents individual fields,
    // and especially when using the `pl_queue`, this will still result in
    // frames being redundantly rendered twice. As such, it's highly
    // recommended to, instead, fully disable deinterlacing by not marking
    // source frames as interlaced in the first place.
    const struct pl_deinterlace_params *deinterlace_params;

    // If set, applies an extra distortion matrix to the image, after
    // scaling and before presenting it to the screen. Can be used for e.g.
    // fractional rotation.
    //
    // Note: The distortion canvas will be set to the size of `target->crop`,
    // so this cannot effectively draw outside the specified target area,
    // nor change the aspect ratio of the image.
    const struct pl_distort_params *distort_params;

    // List of custom user shaders / hooks.
    // See <libplacebo/shaders/custom.h> for more information.
    const struct pl_hook * const *hooks;
    int num_hooks;

    // Color mapping LUT. If present, this will be applied as part of the
    // image being rendered, in normalized RGB space.
    //
    // Note: In this context, PL_LUT_NATIVE means "gamma light" and
    // PL_LUT_NORMALIZED means "linear light". For HDR signals, normalized LUTs
    // are scaled so 1.0 corresponds to the `pl_color_transfer_nominal_peak`.
    //
    // Note: A PL_LUT_CONVERSION fully replaces the color adaptation from
    // `image` to `target`, including any tone-mapping (if necessary) and ICC
    // profiles. It has the same representation as PL_LUT_NATIVE, so in this
    // case the input and output are (respectively) non-linear light RGB.
    const struct pl_custom_lut *lut;
    enum pl_lut_type lut_type;

    // If the image being rendered does not span the entire size of the target,
    // it will be cleared explicitly using this background color (RGB). To
    // disable this logic, set `skip_target_clearing`.
    float background_color[3];
    float background_transparency; // 0.0 for opaque, 1.0 for fully transparent
    bool skip_target_clearing;

    // If set to a value above 0.0, the output will be rendered with rounded
    // corners, as if an alpha transparency mask had been applied. The value
    // indicates the relative fraction of the side length to round - a value
    // of 1.0 rounds the corners as much as possible.
    float corner_rounding;

    // If true, then transparent images will made opaque by painting them
    // against a checkerboard pattern consisting of alternating colors. If both
    // colors are left as {0}, they default respectively to 93% and 87% gray.
    bool blend_against_tiles;
    float tile_colors[2][3];
    int tile_size;

    // --- Performance / quality trade-off options:
    // These should generally be left off where quality is desired, as they can
    // degrade the result quite noticeably; but may be useful for older or
    // slower hardware. Note that libplacebo will automatically disable
    // advanced features on hardware where they are unsupported, regardless of
    // these settings. So only enable them if you need a performance bump.

    // Disables anti-aliasing on downscaling. This will result in moiré
    // artifacts and nasty, jagged pixels when downscaling, except for some
    // very limited special cases (e.g. bilinear downsampling to exactly 0.5x).
    //
    // Significantly speeds up downscaling with high downscaling ratios.
    bool skip_anti_aliasing;

    // Normally, when the size of the `target` used with `pl_render_image_mix`
    // changes, or the render parameters are updated, the internal cache of
    // mixed frames must be discarded in order to re-render all required
    // frames. Setting this option to `true` will skip the cache invalidation
    // and instead re-use the existing frames (with bilinear scaling to the new
    // size if necessary), which comes at a quality loss shortly after a
    // resize, but should make it much more smooth.
    bool preserve_mixing_cache;

    // --- Performance tuning / debugging options
    // These may affect performance or may make debugging problems easier,
    // but shouldn't have any effect on the quality.

    // Normally, `pl_render_image_mix` will also push single frames through the
    // mixer cache, in order to speed up re-draws. Enabling this option
    // disables that logic, causing single frames to bypass the cache. (Though
    // it will still read from, if they happen to already be cached)
    bool skip_caching_single_frame;

    // Disables linearization / sigmoidization before scaling. This might be
    // useful when tracking down unexpected image artifacts or excessing
    // ringing, but it shouldn't normally be necessary.
    bool disable_linear_scaling;

    // Forces the use of the "general" scaling algorithms even when using the
    // special-cased built-in presets like `pl_filter_bicubic`. Basically, this
    // disables the more efficient implementations in favor of the slower,
    // general-purpose ones.
    bool disable_builtin_scalers;

    // Forces correction of subpixel offsets (using the configured `upscaler`).
    bool correct_subpixel_offsets;

    // Forces the use of dithering, even when rendering to 16-bit FBOs. This is
    // generally pretty pointless because most 16-bit FBOs have high enough
    // depth that rounding errors are below the human perception threshold,
    // but this can be used to test the dither code.
    bool force_dither;

    // Disables the gamma-correct dithering logic which normally applies when
    // dithering to low bit depths. No real use, outside of testing.
    bool disable_dither_gamma_correction;

    // Completely overrides the use of FBOs, as if there were no renderable
    // texture format available. This disables most features.
    bool disable_fbos;

    // Use only low-bit-depth FBOs (8 bits). Note that this also implies
    // disabling linear scaling and sigmoidization.
    bool force_low_bit_depth_fbos;

    // If this is true, all shaders will be generated as "dynamic" shaders,
    // with any compile-time constants being replaced by runtime-adjustable
    // values. This is generally a performance loss, but has the advantage of
    // being able to freely change parameters without triggering shader
    // recompilations.
    //
    // It's a good idea to enable while presenting configurable settings to the
    // user, but it should be set to false once those values are "dialed in".
    bool dynamic_constants;

    // This callback is invoked for every pass successfully executed in the
    // process of rendering a frame. Optional.
    //
    // Note: `info` is only valid until this function returns.
    void (*info_callback)(void *priv, const struct pl_render_info *info);
    void *info_priv;

    // --- Deprecated/removed fields
    bool allow_delayed_peak_detect PL_DEPRECATED; // moved to pl_peak_detect_params
    const struct pl_icc_params *icc_params PL_DEPRECATED; // use pl_frame.icc
    bool ignore_icc_profiles PL_DEPRECATED; // non-functional, just set pl_frame.icc to NULL
    int lut_entries PL_DEPRECATED; // hard-coded as 256
    float polar_cutoff PL_DEPRECATED; // hard-coded as 1e-3
};

// Bare minimum parameters, with no features enabled. This is the fastest
// possible configuration, and should therefore be fine on any system.
#define PL_RENDER_DEFAULTS                              \
    .color_map_params   = &pl_color_map_default_params, \
    .color_adjustment   = &pl_color_adjustment_neutral, \
    .tile_colors        = {{0.93, 0.93, 0.93},          \
                           {0.87, 0.87, 0.87}},         \
    .tile_size          = 32,

#define pl_render_params(...) (&(struct pl_render_params) { PL_RENDER_DEFAULTS __VA_ARGS__ })
PL_API extern const struct pl_render_params pl_render_fast_params;

// This contains the default/recommended options for reasonable image quality,
// while also not being too terribly slow. All of the *_params structs are
// defaulted to the corresponding *_default_params, except for deband_params,
// which is disabled by default.
//
// This should be fine on most integrated GPUs, but if it's too slow,
// consider using `pl_render_fast_params` instead.
PL_API extern const struct pl_render_params pl_render_default_params;

// This contains a higher quality preset for better image quality at the cost
// of quite a bit of performance. In addition to the settings implied by
// `pl_render_default_params`, it enables debanding, sets the upscaler to
// `pl_filter_ewa_lanczossharp`, and uses pl_*_high_quality_params structs where
// available. This should only really be used with a discrete GPU and where
// maximum image quality is desired.
PL_API extern const struct pl_render_params pl_render_high_quality_params;

#define PL_MAX_PLANES 4

// High level description of a single slice of an image. This basically
// represents a single 2D plane, with any number of components
struct pl_plane {
    // The texture underlying this plane. The texture must be 2D, and must
    // have specific parameters set depending on what the plane is being used
    // for (see `pl_render_image`).
    pl_tex texture;

    // The preferred behaviour when sampling outside of this texture. Optional,
    // since the default (PL_TEX_ADDRESS_CLAMP) is very reasonable.
    enum pl_tex_address_mode address_mode;

    // Controls whether or not the `texture` will be considered flipped
    // vertically with respect to the overall image dimensions. It's generally
    // preferable to flip planes using this setting instead of the crop in
    // cases where the flipping is the result of e.g. negative plane strides or
    // flipped framebuffers (OpenGL).
    //
    // Note that any planar padding (due to e.g. size mismatch or misalignment
    // of subsampled planes) is always at the physical end of the texture
    // (highest y coordinate) - even if this bool is true. However, any
    // subsampling shift (`shift_y`) is applied with respect to the flipped
    // direction. This ensures the correct interpretation when e.g. vertically
    // flipping 4:2:0 sources by flipping all planes.
    bool flipped;

    // Describes the number and interpretation of the components in this plane.
    // This defines the mapping from component index to the canonical component
    // order (RGBA, YCbCrA or XYZA). It's worth pointing out that this is
    // completely separate from `texture->format.sample_order`. The latter is
    // essentially irrelevant/transparent for the API user, since it just
    // determines which order the texture data shows up as inside the GLSL
    // shader; whereas this field controls the actual meaning of the component.
    //
    // Example; if the user has a plane with just {Y} and a plane with just
    // {Cb Cr}, and a GPU that only supports bgra formats, you would still
    // specify the component mapping as {0} and {1 2} respectively, even though
    // the GPU is sampling the data in the order BGRA. Use -1 for "ignored"
    // components.
    int components;           // number of relevant components
    int component_mapping[4]; // semantic index of each component

    // Controls the sample offset, relative to the "reference" dimensions. For
    // an example of what to set here, see `pl_chroma_location_offset`. Note
    // that this is given in unit of reference pixels. For a graphical example,
    // imagine you have a 2x2 image with a 1x1 (subsampled) plane. Without any
    // shift (0.0), the situation looks like this:
    //
    // X-------X  X = reference pixel
    // |       |  P = plane pixel
    // |   P   |
    // |       |
    // X-------X
    //
    // For 4:2:0 subsampling, this corresponds to PL_CHROMA_CENTER. If the
    // shift_x was instead set to -0.5, the `P` pixel would be offset to the
    // left by half the separation between the reference (`X` pixels), resulting
    // in the following:
    //
    // X-------X  X = reference pixel
    // |       |  P = plane pixel
    // P       |
    // |       |
    // X-------X
    //
    // For 4:2:0 subsampling, this corresponds to PL_CHROMA_LEFT.
    //
    // Note: It's recommended to fill this using `pl_chroma_location_offset` on
    // the chroma planes.
    float shift_x, shift_y;
};

enum pl_overlay_mode {
    PL_OVERLAY_NORMAL = 0, // treat the texture as a normal, full-color texture
    PL_OVERLAY_MONOCHROME, // treat the texture as a single-component alpha map
    PL_OVERLAY_MODE_COUNT,
};

enum pl_overlay_coords {
    PL_OVERLAY_COORDS_AUTO = 0,  // equal to SRC/DST_FRAME, respectively
    PL_OVERLAY_COORDS_SRC_FRAME, // relative to the raw src frame
    PL_OVERLAY_COORDS_SRC_CROP,  // relative to the src frame crop
    PL_OVERLAY_COORDS_DST_FRAME, // relative to the raw dst frame
    PL_OVERLAY_COORDS_DST_CROP,  // relative to the dst frame crop
    PL_OVERLAY_COORDS_COUNT,

    // Note on rotations: If there is an end-to-end rotation between `src` and
    // `dst`, then any overlays relative to SRC_FRAME or SRC_CROP will be
    // rotated alongside the image, while overlays relative to DST_FRAME or
    // DST_CROP will not.
};

struct pl_overlay_part {
    pl_rect2df src; // source coordinate with respect to `pl_overlay.tex`
    pl_rect2df dst; // target coordinates with respect to `pl_overlay.coords`

    // If `mode` is PL_OVERLAY_MONOCHROME, then this specifies the color of
    // this overlay part. The color is multiplied into the sampled texture's
    // first channel.
    float color[4];
};

// A struct representing an image overlay (e.g. for subtitles or on-screen
// status messages, controls, ...)
struct pl_overlay {
    // The texture containing the backing data for overlay parts. Must have
    // `params.sampleable` set.
    pl_tex tex;

    // This controls the coloring mode of this overlay.
    enum pl_overlay_mode mode;

    // Controls which coordinates this overlay is addressed relative to.
    enum pl_overlay_coords coords;

    // This controls the colorspace information for this overlay. The contents
    // of the texture / the value of `color` are interpreted according to this.
    struct pl_color_repr repr;
    struct pl_color_space color;

    // The number of parts for this overlay.
    const struct pl_overlay_part *parts;
    int num_parts;
};

// High-level description of a complete frame, including metadata and planes
struct pl_frame {
    // Each frame is split up into some number of planes, each of which may
    // carry several components and be of any size / offset.
    int num_planes;
    struct pl_plane planes[PL_MAX_PLANES];

    // For interlaced frames. If set, this `pl_frame` corresponds to a single
    // field of the underlying source textures. `first_field` indicates which
    // of these fields is ordered first in time. `prev` and `next` should point
    // to the previous/next frames in the file, or NULL if there are none.
    //
    // Note: Setting these fields on the render target has no meaning and will
    // be ignored.
    enum pl_field field;
    enum pl_field first_field;
    const struct pl_frame *prev, *next;

    // If set, will be called immediately before GPU access to this frame. This
    // function *may* be used to, for example, perform synchronization with
    // external APIs (e.g. `pl_vulkan_hold/release`). If your mapping requires
    // a memcpy of some sort (e.g. pl_tex_transfer), users *should* instead do
    // the memcpy up-front and avoid the use of these callbacks - because they
    // might be called multiple times on the same frame.
    //
    // This function *may* arbitrarily mutate the `pl_frame`, but it *should*
    // ideally only update `planes` - in particular, color metadata and so
    // forth should be provided up-front as best as possible. Note that changes
    // here will not be reflected back to the structs provided in the original
    // `pl_render_*` call (e.g. via `pl_frame_mix`).
    //
    // Note: Unless dealing with interlaced frames, only one frame will ever be
    // acquired at a time per `pl_render_*` call. So users *can* safely use
    // this with, for example, hwdec mappers that can only map a single frame
    // at a time. When using this with, for example, `pl_render_image_mix`,
    // each frame to be blended is acquired and release in succession, before
    // moving on to the next frame. For interlaced frames, the previous and
    // next frames must also be acquired simultaneously.
    bool (*acquire)(pl_gpu gpu, struct pl_frame *frame);

    // If set, will be called after a plane is done being used by the GPU,
    // *including* after any errors (e.g. `acquire` returning false).
    void (*release)(pl_gpu gpu, struct pl_frame *frame);

    // Color representation / encoding / semantics of this frame.
    struct pl_color_repr repr;
    struct pl_color_space color;

    // Optional ICC profile associated with this frame.
    pl_icc_object icc;

    // Alternative to `icc`, this can be used in cases where allocating and
    // tracking an pl_icc_object externally may be inconvenient. The resulting
    // profile will be managed internally by the pl_renderer.
    struct pl_icc_profile profile;

    // Optional LUT associated with this frame.
    const struct pl_custom_lut *lut;
    enum pl_lut_type lut_type;

    // The logical crop / rectangle containing the valid information, relative
    // to the reference plane's dimensions (e.g. luma). Pixels outside of this
    // rectangle will ostensibly be ignored, but note that this is not a hard
    // guarantee. In particular, scaler filters may end up sampling outside of
    // this crop. This rect may be flipped, and may be partially or wholly
    // outside the bounds of the underlying textures. (Optional)
    //
    // Note that `pl_render_image` will map the input crop directly to the
    // output crop, stretching and scaling as needed. If you wish to preserve
    // the aspect ratio, use a dedicated function like pl_rect2df_aspect_copy.
    pl_rect2df crop;

    // Logical rotation of the image, with respect to the underlying planes.
    // For example, if this is PL_ROTATION_90, then the image will be rotated
    // to the right by 90° when mapping to `crop`. The actual position on-screen
    // is unaffected, so users should ensure that the (rotated) aspect ratio
    // matches the source. (Or use a helper like `pl_rect2df_aspect_set_rot`)
    //
    // Note: For `target` frames, this corresponds to a rotation of the
    // display, for `image` frames, this corresponds to a rotation of the
    // camera.
    //
    // So, as an example, target->rotation = PL_ROTATE_90 means the end user
    // has rotated the display to the right by 90° (meaning rendering will be
    // rotated 90° to the *left* to compensate), and image->rotation =
    // PL_ROTATE_90 means the video provider has rotated the camera to the
    // right by 90° (so rendering will be rotated 90° to the *right* to
    // compensate).
    pl_rotation rotation;

    // A list of additional overlays associated with this frame. Note that will
    // be rendered directly onto intermediate/cache frames, so changing any of
    // these overlays may require flushing the renderer cache.
    const struct pl_overlay *overlays;
    int num_overlays;

    // Note on subsampling and plane correspondence: All planes belonging to
    // the same frame will only be stretched by an integer multiple (or inverse
    // thereof) in order to match the reference dimensions of this image. For
    // example, suppose you have an 8x4 image. A valid plane scaling would be
    // 4x2 -> 8x4 or 4x4 -> 4x4, but not 6x4 -> 8x4. So if a 6x4 plane is
    // given, then it would be treated like a cropped 8x4 plane (since 1.0 is
    // the closest scaling ratio to the actual ratio of 1.3).
    //
    // For an explanation of why this makes sense, consider the relatively
    // common example of a subsampled, oddly sized (e.g. jpeg) image. In such
    // cases, for example a 35x23 image, the 4:2:0 subsampled chroma plane
    // would have to end up as 17.5x11.5, which gets rounded up to 18x12 by
    // implementations. So in this example, the 18x12 chroma plane would get
    // treated by libplacebo as an oversized chroma plane - i.e. the plane
    // would get sampled as if it was 17.5 pixels wide and 11.5 pixels large.

    // Associated film grain data (see <libplacebo/shaders/film_grain.h>).
    //
    // Note: This is ignored for the `target` of `pl_render_image`, since
    // un-applying grain makes little sense.
    struct pl_film_grain_data film_grain;

    // Ignored by libplacebo. May be useful for users.
    void *user_data;
};

// Helper function to infer the chroma location offset for each plane in a
// frame. This is equivalent to calling `pl_chroma_location_offset` on all
// subsampled planes' shift_x/shift_y variables.
PL_API void pl_frame_set_chroma_location(struct pl_frame *frame,
                                         enum pl_chroma_location chroma_loc);

// Fills in a `pl_frame` based on a swapchain frame's FBO and metadata.
PL_API void pl_frame_from_swapchain(struct pl_frame *out_frame,
                                    const struct pl_swapchain_frame *frame);

// Helper function to determine if a frame is logically cropped or not. In
// particular, this is useful in determining whether or not an output frame
// needs to be cleared before rendering or not.
PL_API bool pl_frame_is_cropped(const struct pl_frame *frame);

// Helper function to reset a frame to a given RGB color. If the frame's
// color representation is something other than RGB, the clear color will
// be adjusted accordingly. `clear_color` should be non-premultiplied.
PL_API void pl_frame_clear_rgba(pl_gpu gpu, const struct pl_frame *frame,
                                const float clear_color[4]);

// Like `pl_frame_clear_rgba` but without an alpha channel.
static inline void pl_frame_clear(pl_gpu gpu, const struct pl_frame *frame,
                                  const float clear_color[3])
{
    const float clear_color_rgba[4] = { clear_color[0], clear_color[1], clear_color[2], 1.0 };
    pl_frame_clear_rgba(gpu, frame, clear_color_rgba);
}

// Helper functions to return the fixed/inferred pl_frame parameters used
// for rendering internally. Mutates `image` and `target` in-place to hold
// the modified values, which are what will actually be used for rendering.
//
// This currently includes:
// - Defaulting all missing pl_color_space/repr parameters
// - Coalescing all rotation to the target
// - Rounding and clamping the target crop to pixel boundaries and adjusting the
//   image crop correspondingly
//
// Note: This is idempotent and does not generally alter the effects of a
// subsequent `pl_render_image` on the same pl_frame pair. (But see the
// following warning)
//
// Warning: This does *not* call pl_frame.acquire/release, and so the returned
// metadata *may* be incorrect if the acquire callback mutates the pl_frame in
// nontrivial ways, in particular the crop and color space fields.
PL_API void pl_frames_infer(pl_renderer rr, struct pl_frame *image,
                            struct pl_frame *target);


// Render a single image to a target using the given parameters. This is
// fully dynamic, i.e. the params can change at any time. libplacebo will
// internally detect and flush whatever caches are invalidated as a result of
// changing colorspace, size etc.
//
// Required plane capabilities:
// - Planes in `image` must be `sampleable`
// - Planes in `target` must be `renderable`
//
// Recommended plane capabilities: (Optional, but good for performance)
// - Planes in `image` should have `sample_mode` PL_TEX_SAMPLE_LINEAR
// - Planes in `target` should be `storable`
// - Planes in `target` should have `blit_dst`
//
// Note on lifetime: Once this call returns, the passed structures may be
// freely overwritten or discarded by the caller, even the referenced
// `pl_tex` objects may be freely reused.
//
// Note: `image` may be NULL, in which case `target.overlays` will still be
// rendered, but nothing else.
PL_API bool pl_render_image(pl_renderer rr, const struct pl_frame *image,
                            const struct pl_frame *target,
                            const struct pl_render_params *params);

// Flushes the internal state of this renderer. This is normally not needed,
// even if the image parameters, colorspace or target configuration change,
// since libplacebo will internally detect such circumstances and recreate
// outdated resources automatically. Doing this explicitly *may* be useful to
// purge some state related to things like HDR peak detection or frame mixing,
// so calling it is a good idea if the content source is expected to change
// dramatically (e.g. when switching to a different file).
PL_API void pl_renderer_flush_cache(pl_renderer rr);

// Mirrors `pl_get_detected_hdr_metadata`, giving you the current internal peak
// detection HDR metadata (when peak detection is active). Returns false if no
// information is available (e.g. not HDR source, peak detection disabled).
PL_API bool pl_renderer_get_hdr_metadata(pl_renderer rr,
                                         struct pl_hdr_metadata *metadata);

// Represents a mixture of input frames, distributed temporally.
//
// NOTE: Frames must be sorted by timestamp, i.e. `timestamps` must be
// monotonically increasing.
struct pl_frame_mix {
    // The number of frames in this mixture. The number of frames should be
    // sufficient to meet the needs of the configured frame mixer. See the
    // section below for more information.
    //
    // If the number of frames is 0, this call will be equivalent to
    // `pl_render_image` with `image == NULL`.
    int num_frames;

    // A list of the frames themselves. The frames can have different
    // colorspaces, configurations of planes, or even sizes.
    //
    // Note: This is a list of pointers, to avoid users having to copy
    // around `pl_frame` structs when re-organizing this array.
    const struct pl_frame **frames;

    // A list of unique signatures, one for each frame. These are used to
    // identify frames across calls to this function, so it's crucial that they
    // be both unique per-frame but also stable across invocations of
    // `pl_render_frame_mix`.
    const uint64_t *signatures;

    // A list of relative timestamps for each frame. These are relative to the
    // time of the vsync being drawn, i.e. this function will render the frame
    // that will be made visible at timestamp 0.0. The values are expected to
    // be normalized such that a separation of 1.0 corresponds to roughly one
    // nominal source frame duration. So a constant framerate video file will
    // always have timestamps like e.g. {-2.3, -1.3, -0.3, 0.7, 1.7, 2.7},
    // using an example radius of 3.
    //
    // In cases where the framerate is variable (e.g. VFR video), the choice of
    // what to scale to use can be difficult to answer. A typical choice would
    // be either to use the canonical (container-tagged) framerate, or the
    // highest momentary framerate, as a reference. If all else fails, you
    // could also use the display's framerate.
    //
    // Note: This function assumes zero-order-hold semantics, i.e. the frame at
    // timestamp 0.7 is intended to remain visible until timestamp 1.7, when
    // the next frame replaces it.
    const float *timestamps;

    // The duration for which the vsync being drawn will be held, using the
    // same scale as `timestamps`. If the display has an unknown or variable
    // frame-rate (e.g. Adaptive Sync), then you're probably better off not
    // using this function and instead just painting the frames directly using
    // `pl_render_frame` at the correct PTS.
    //
    // As an example, if `vsync_duration` is 0.4, then it's assumed that the
    // vsync being painted is visible for the period [0.0, 0.4].
    float vsync_duration;

    // Explanation of the frame mixing radius: The algorithm chosen in
    // `pl_render_params.frame_mixer` has a canonical radius equal to
    // `pl_filter_config.kernel->radius`. This means that the frame mixing
    // algorithm will (only) need to consult all of the frames that have a
    // distance within the interval [-radius, radius]. As such, the user should
    // include all such frames in `frames`, but may prune or omit frames that
    // lie outside it.
    //
    // The built-in frame mixing (`pl_render_params.frame_mixer == NULL`) has
    // no concept of radius, it just always needs access to the "current" and
    // "next" frames.
};

// Helper function to calculate the base frame mixing radius.
//
// Note: When the source FPS exceeds the display FPS, this radius must be
// increased by the corresponding ratio.
static inline float pl_frame_mix_radius(const struct pl_render_params *params)
{
    // For backwards compatibility, allow !frame_mixer->kernel
    if (!params->frame_mixer || !params->frame_mixer->kernel)
        return 0.0;

    return params->frame_mixer->kernel->radius;
}

// Find closest frame to current PTS by zero-order hold semantics, or NULL.
PL_API const struct pl_frame *pl_frame_mix_current(const struct pl_frame_mix *mix);

// Find closest frame to current PTS by nearest neighbour semantics, or NULL.
PL_API const struct pl_frame *pl_frame_mix_nearest(const struct pl_frame_mix *mix);

// Render a mixture of images to the target using the given parameters. This
// functions much like a generalization of `pl_render_image`, for when the API
// user has more control over the frame queue / vsync loop, and can provide a
// few frames from the past and future + timestamp information.
//
// This allows libplacebo to perform rudimentary frame mixing / interpolation,
// in order to eliminate judder artifacts typically associated with
// source/display frame rate mismatch.
PL_API bool pl_render_image_mix(pl_renderer rr, const struct pl_frame_mix *images,
                                const struct pl_frame *target,
                                const struct pl_render_params *params);

// Analog of `pl_frame_infer` corresponding to `pl_render_image_mix`. This
// function will *not* mutate the frames contained in `mix`, and instead
// return an adjusted copy of the "reference" frame for that image mix in
// `out_refimage`, or {0} if the mix is empty.
PL_API void pl_frames_infer_mix(pl_renderer rr, const struct pl_frame_mix *mix,
                                struct pl_frame *target, struct pl_frame *out_ref);

// Backwards compatibility with old filters API, may be deprecated.
// Redundant with pl_filter_configs and masking `allowed` for
// PL_FILTER_SCALING and PL_FILTER_FRAME_MIXING respectively.

// A list of recommended frame mixer presets, terminated by {0}
PL_API extern const struct pl_filter_preset pl_frame_mixers[];
PL_API extern const int pl_num_frame_mixers; // excluding trailing {0}

// A list of recommended scaler presets, terminated by {0}. This is almost
// equivalent to `pl_filter_presets` with the exception of including extra
// built-in filters that don't map to the `pl_filter` architecture.
PL_API extern const struct pl_filter_preset pl_scale_filters[];
PL_API extern const int pl_num_scale_filters; // excluding trailing {0}

// Deprecated in favor of `pl_cache_save/pl_cache_load` on the `pl_cache`
// associated with the `pl_gpu` this renderer is using.
PL_DEPRECATED PL_API size_t pl_renderer_save(pl_renderer rr, uint8_t *out_cache);
PL_DEPRECATED PL_API void pl_renderer_load(pl_renderer rr, const uint8_t *cache);

PL_API_END

#endif // LIBPLACEBO_RENDERER_H_