LCOV - code coverage report
Current view: top level - src/tests - gpu_tests.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 672 697 96.4 %
Date: 2025-03-29 09:04:10 Functions: 17 17 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 462 727 63.5 %

           Branch data     Line data    Source code
       1                 :            : #include "gpu_tests.h"
       2                 :            : #include "shaders.h"
       3                 :            : 
       4                 :            : #include <libplacebo/renderer.h>
       5                 :            : #include <libplacebo/utils/frame_queue.h>
       6                 :            : #include <libplacebo/utils/upload.h>
       7                 :            : 
       8                 :            : //#define PRINT_OUTPUT
       9                 :            : 
      10                 :          6 : void pl_buffer_tests(pl_gpu gpu)
      11                 :            : {
      12                 :            :     const size_t buf_size = 1024;
      13         [ +  + ]:          6 :     if (buf_size > gpu->limits.max_buf_size)
      14                 :          1 :         return;
      15                 :            :     printf("pl_buffer_tests:\n");
      16                 :            : 
      17                 :          5 :     uint8_t *test_src = malloc(buf_size * 2);
      18                 :          5 :     uint8_t *test_dst = test_src + buf_size;
      19         [ -  + ]:          5 :     assert(test_src && test_dst);
      20                 :            :     memset(test_dst, 0, buf_size);
      21         [ +  + ]:       5125 :     for (int i = 0; i < buf_size; i++)
      22                 :       5120 :         test_src[i] = RANDOM_U8;
      23                 :            : 
      24                 :          5 :     pl_buf buf = NULL, tbuf = NULL;
      25                 :            : 
      26                 :            :     printf("- test buffer static creation and readback\n");
      27                 :          5 :     buf = pl_buf_create(gpu, pl_buf_params(
      28                 :            :         .size = buf_size,
      29                 :            :         .host_readable = true,
      30                 :            :         .initial_data = test_src,
      31                 :            :     ));
      32                 :            : 
      33         [ -  + ]:          5 :     REQUIRE(buf);
      34         [ -  + ]:          5 :     REQUIRE(pl_buf_read(gpu, buf, 0, test_dst, buf_size));
      35                 :          5 :     REQUIRE_MEMEQ(test_src, test_dst, buf_size);
      36                 :          5 :     pl_buf_destroy(gpu, &buf);
      37                 :            : 
      38                 :            :     printf("- test buffer empty creation, update and readback\n");
      39                 :            :     memset(test_dst, 0, buf_size);
      40                 :          5 :     buf = pl_buf_create(gpu, pl_buf_params(
      41                 :            :         .size = buf_size,
      42                 :            :         .host_writable = true,
      43                 :            :         .host_readable = true,
      44                 :            :     ));
      45                 :            : 
      46         [ -  + ]:          5 :     REQUIRE(buf);
      47                 :          5 :     pl_buf_write(gpu, buf, 0, test_src, buf_size);
      48         [ -  + ]:          5 :     REQUIRE(pl_buf_read(gpu, buf, 0, test_dst, buf_size));
      49                 :          5 :     REQUIRE_MEMEQ(test_src, test_dst, buf_size);
      50                 :          5 :     pl_buf_destroy(gpu, &buf);
      51                 :            : 
      52                 :            :     printf("- test buffer-buffer copy and readback\n");
      53                 :            :     memset(test_dst, 0, buf_size);
      54                 :          5 :     buf = pl_buf_create(gpu, pl_buf_params(
      55                 :            :         .size = buf_size,
      56                 :            :         .initial_data = test_src,
      57                 :            :     ));
      58                 :            : 
      59                 :          5 :     tbuf = pl_buf_create(gpu, pl_buf_params(
      60                 :            :         .size = buf_size,
      61                 :            :         .host_readable = true,
      62                 :            :     ));
      63                 :            : 
      64   [ +  -  -  + ]:          5 :     REQUIRE(buf && tbuf);
      65                 :          5 :     pl_buf_copy(gpu, tbuf, 0, buf, 0, buf_size);
      66         [ -  + ]:          5 :     REQUIRE(pl_buf_read(gpu, tbuf, 0, test_dst, buf_size));
      67                 :          5 :     REQUIRE_MEMEQ(test_src, test_dst, buf_size);
      68                 :          5 :     pl_buf_destroy(gpu, &buf);
      69                 :          5 :     pl_buf_destroy(gpu, &tbuf);
      70                 :            : 
      71         [ +  - ]:          5 :     if (buf_size <= gpu->limits.max_mapped_size) {
      72                 :            :         printf("- test host mapped buffer readback\n");
      73                 :          5 :         buf = pl_buf_create(gpu, pl_buf_params(
      74                 :            :             .size = buf_size,
      75                 :            :             .host_mapped = true,
      76                 :            :             .initial_data = test_src,
      77                 :            :         ));
      78                 :            : 
      79         [ -  + ]:          5 :         REQUIRE(buf);
      80         [ -  + ]:          5 :         REQUIRE(!pl_buf_poll(gpu, buf, 0));
      81                 :          5 :         REQUIRE_MEMEQ(test_src, buf->data, buf_size);
      82                 :          5 :         pl_buf_destroy(gpu, &buf);
      83                 :            :     }
      84                 :            : 
      85                 :            :     // `compute_queues` check is to exclude dummy GPUs here
      86   [ +  +  +  + ]:          5 :     if (buf_size <= gpu->limits.max_ssbo_size && gpu->limits.compute_queues)
      87                 :            :     {
      88                 :            :         printf("- test endian swapping\n");
      89                 :          2 :         buf = pl_buf_create(gpu, pl_buf_params(
      90                 :            :             .size = buf_size,
      91                 :            :             .storable = true,
      92                 :            :             .initial_data = test_src,
      93                 :            :         ));
      94                 :            : 
      95                 :          2 :         tbuf = pl_buf_create(gpu, pl_buf_params(
      96                 :            :             .size = buf_size,
      97                 :            :             .storable = true,
      98                 :            :             .host_readable = true,
      99                 :            :         ));
     100                 :            : 
     101   [ +  -  -  + ]:          2 :         REQUIRE(buf && tbuf);
     102         [ -  + ]:          2 :         REQUIRE(pl_buf_copy_swap(gpu, &(struct pl_buf_copy_swap_params) {
     103                 :            :             .src = buf,
     104                 :            :             .dst = tbuf,
     105                 :            :             .size = buf_size,
     106                 :            :             .wordsize = 2,
     107                 :            :         }));
     108         [ -  + ]:          2 :         REQUIRE(pl_buf_read(gpu, tbuf, 0, test_dst, buf_size));
     109         [ +  + ]:       1026 :         for (int i = 0; i < buf_size / 2; i++) {
     110         [ -  + ]:       1024 :             REQUIRE_CMP(test_src[2 * i + 0], ==, test_dst[2 * i + 1], PRIu8);
     111         [ -  + ]:       1024 :             REQUIRE_CMP(test_src[2 * i + 1], ==, test_dst[2 * i + 0], PRIu8);
     112                 :            :         }
     113                 :            :         // test endian swap in-place
     114         [ -  + ]:          2 :         REQUIRE(pl_buf_copy_swap(gpu, &(struct pl_buf_copy_swap_params) {
     115                 :            :             .src = tbuf,
     116                 :            :             .dst = tbuf,
     117                 :            :             .size = buf_size,
     118                 :            :             .wordsize = 4,
     119                 :            :         }));
     120         [ -  + ]:          2 :         REQUIRE(pl_buf_read(gpu, tbuf, 0, test_dst, buf_size));
     121         [ +  + ]:        514 :         for (int i = 0; i < buf_size / 4; i++) {
     122         [ -  + ]:        512 :             REQUIRE_CMP(test_src[4 * i + 0], ==, test_dst[4 * i + 2], PRIu8);
     123         [ -  + ]:        512 :             REQUIRE_CMP(test_src[4 * i + 1], ==, test_dst[4 * i + 3], PRIu8);
     124         [ -  + ]:        512 :             REQUIRE_CMP(test_src[4 * i + 2], ==, test_dst[4 * i + 0], PRIu8);
     125         [ -  + ]:        512 :             REQUIRE_CMP(test_src[4 * i + 3], ==, test_dst[4 * i + 1], PRIu8);
     126                 :            :         }
     127                 :          2 :         pl_buf_destroy(gpu, &buf);
     128                 :          2 :         pl_buf_destroy(gpu, &tbuf);
     129                 :            :     }
     130                 :            : 
     131                 :          5 :     free(test_src);
     132                 :            : }
     133                 :            : 
     134                 :        912 : static void test_cb(void *priv)
     135                 :            : {
     136                 :            :     bool *flag = priv;
     137                 :        912 :     *flag = true;
     138                 :        912 : }
     139                 :            : 
     140                 :        763 : static void pl_test_roundtrip(pl_gpu gpu, pl_tex tex[2],
     141                 :            :                               uint8_t *src, uint8_t *dst)
     142                 :            : {
     143   [ +  +  -  + ]:        763 :     if (!tex[0] || !tex[1]) {
     144                 :            :         printf("failed creating test textures... skipping this test\n");
     145                 :         79 :         return;
     146                 :            :     }
     147                 :            : 
     148                 :        684 :     int texels = tex[0]->params.w;
     149         [ +  + ]:        684 :     texels *= tex[0]->params.h ? tex[0]->params.h : 1;
     150         [ +  + ]:        684 :     texels *= tex[0]->params.d ? tex[0]->params.d : 1;
     151                 :            : 
     152                 :        684 :     pl_fmt fmt = tex[0]->params.format;
     153                 :        684 :     size_t bytes = texels * fmt->texel_size;
     154                 :            :     memset(src, 0, bytes);
     155                 :            :     memset(dst, 0, bytes);
     156                 :            : 
     157         [ +  + ]:     978588 :     for (int i = 0; i < texels; i++) {
     158                 :     977904 :         uint8_t *data = &src[i * fmt->texel_size];
     159         [ +  + ]:     977904 :         if (fmt->type == PL_FMT_FLOAT) {
     160         [ +  + ]:     902368 :             for (int n = 0; n < fmt->num_components; n++) {
     161   [ +  +  +  - ]:     643888 :                 switch (fmt->component_depth[n]) {
     162                 :     381808 :                 case 16: *(uint16_t *) data = RANDOM_F16; data += 2; break;
     163                 :     218400 :                 case 32: *(float *)    data = RANDOM_F32; data += 4; break;
     164                 :      43680 :                 case 64: *(double *)   data = RANDOM_F64; data += 8; break;
     165                 :            :                 }
     166                 :            :             }
     167                 :            :         } else {
     168         [ +  + ]:    5176800 :             for (int n = 0; n < fmt->texel_size; n++)
     169                 :    4457376 :                 data[n] = RANDOM_U8;
     170                 :            :         }
     171                 :            :     }
     172                 :            : 
     173                 :            :     pl_timer ul, dl;
     174                 :        684 :     ul = pl_timer_create(gpu);
     175                 :        684 :     dl = pl_timer_create(gpu);
     176                 :            : 
     177                 :        684 :     bool ran_ul = false, ran_dl = false;
     178                 :            : 
     179   [ +  +  -  + ]:        912 :     REQUIRE(pl_tex_upload(gpu, &(struct pl_tex_transfer_params){
     180                 :            :         .tex = tex[0],
     181                 :            :         .ptr = src,
     182                 :            :         .timer = ul,
     183                 :            :         .callback = gpu->limits.callbacks ? test_cb : NULL,
     184                 :            :         .priv = &ran_ul,
     185                 :            :     }));
     186                 :            : 
     187                 :            :     // Test blitting, if possible for this format
     188                 :        684 :     pl_tex dst_tex = tex[0];
     189   [ +  +  +  - ]:        684 :     if (tex[0]->params.blit_src && tex[1]->params.blit_dst) {
     190                 :        252 :         pl_tex_clear_ex(gpu, tex[1], (union pl_clear_color){0}); // for testing
     191                 :        252 :         pl_tex_blit(gpu, &(struct pl_tex_blit_params) {
     192                 :        252 :             .src = tex[0],
     193                 :            :             .dst = tex[1],
     194                 :            :         });
     195                 :        252 :         dst_tex = tex[1];
     196                 :            :     }
     197                 :            : 
     198   [ +  +  -  + ]:        912 :     REQUIRE(pl_tex_download(gpu, &(struct pl_tex_transfer_params){
     199                 :            :         .tex = dst_tex,
     200                 :            :         .ptr = dst,
     201                 :            :         .timer = dl,
     202                 :            :         .callback = gpu->limits.callbacks ? test_cb : NULL,
     203                 :            :         .priv = &ran_dl,
     204                 :            :     }));
     205                 :            : 
     206                 :        684 :     pl_gpu_finish(gpu);
     207         [ +  + ]:        684 :     if (gpu->limits.callbacks)
     208   [ +  -  -  + ]:        456 :         REQUIRE(ran_ul && ran_dl);
     209                 :            : 
     210   [ +  +  +  + ]:        684 :     if (fmt->emulated && fmt->type == PL_FMT_FLOAT) {
     211                 :            :         // TODO: can't memcmp here because bits might be lost due to the
     212                 :            :         // emulated 16/32 bit upload paths, figure out a better way to
     213                 :            :         // generate data and verify the roundtrip!
     214                 :            :     } else {
     215                 :        621 :         REQUIRE_MEMEQ(src, dst, bytes);
     216                 :            :     }
     217                 :            : 
     218                 :            :     // Report timer results
     219                 :        684 :     printf("upload time: %"PRIu64", download time: %"PRIu64"\n",
     220                 :            :            pl_timer_query(gpu, ul), pl_timer_query(gpu, dl));
     221                 :            : 
     222                 :        684 :     pl_timer_destroy(gpu, &ul);
     223                 :        684 :     pl_timer_destroy(gpu, &dl);
     224                 :            : }
     225                 :            : 
     226                 :          6 : void pl_texture_tests(pl_gpu gpu)
     227                 :            : {
     228                 :            :     const size_t max_size = 16*16*16 * 4 *sizeof(double);
     229                 :          6 :     uint8_t *test_src = malloc(max_size * 2);
     230                 :          6 :     uint8_t *test_dst = test_src + max_size;
     231                 :            :     printf("pl_texture_tests:\n");
     232                 :            : 
     233         [ +  + ]:        300 :     for (int f = 0; f < gpu->num_formats; f++) {
     234                 :        294 :         pl_fmt fmt = gpu->formats[f];
     235   [ +  +  +  + ]:        294 :         if (fmt->opaque || !(fmt->caps & PL_FMT_CAP_HOST_READABLE))
     236                 :         33 :             continue;
     237                 :            : 
     238                 :        261 :         printf("- testing texture roundtrip for format %s\n", fmt->name);
     239         [ -  + ]:        261 :         assert(fmt->texel_size <= 4 * sizeof(double));
     240                 :            : 
     241                 :        261 :         struct pl_tex_params ref_params = {
     242                 :            :             .format        = fmt,
     243                 :        261 :             .blit_src      = (fmt->caps & PL_FMT_CAP_BLITTABLE),
     244                 :            :             .blit_dst      = (fmt->caps & PL_FMT_CAP_BLITTABLE),
     245                 :            :             .host_writable = true,
     246                 :            :             .host_readable = true,
     247                 :            :             .debug_tag     = PL_DEBUG_TAG,
     248                 :            :         };
     249                 :            : 
     250                 :            :         pl_tex tex[2];
     251                 :            : 
     252         [ +  + ]:        261 :         if (gpu->limits.max_tex_1d_dim >= 16) {
     253                 :            :             printf("-- 1D\n");
     254                 :        241 :             struct pl_tex_params params = ref_params;
     255                 :        241 :             params.w = 16;
     256         [ +  + ]:        241 :             if (!gpu->limits.blittable_1d_3d)
     257                 :        163 :                 params.blit_src = params.blit_dst = false;
     258         [ +  + ]:        723 :             for (int i = 0; i < PL_ARRAY_SIZE(tex); i++)
     259                 :        482 :                 tex[i] = pl_tex_create(gpu, &params);
     260                 :        241 :             pl_test_roundtrip(gpu, tex, test_src, test_dst);
     261         [ +  + ]:        723 :             for (int i = 0; i < PL_ARRAY_SIZE(tex); i++)
     262                 :        482 :                 pl_tex_destroy(gpu, &tex[i]);
     263                 :            :         }
     264                 :            : 
     265         [ +  - ]:        261 :         if (gpu->limits.max_tex_2d_dim >= 16) {
     266                 :            :             printf("-- 2D\n");
     267                 :        261 :             struct pl_tex_params params = ref_params;
     268                 :        261 :             params.w = params.h = 16;
     269         [ +  + ]:        783 :             for (int i = 0; i < PL_ARRAY_SIZE(tex); i++)
     270                 :        522 :                 tex[i] = pl_tex_create(gpu, &params);
     271                 :        261 :             pl_test_roundtrip(gpu, tex, test_src, test_dst);
     272         [ +  + ]:        783 :             for (int i = 0; i < PL_ARRAY_SIZE(tex); i++)
     273                 :        522 :                 pl_tex_destroy(gpu, &tex[i]);
     274                 :            :         }
     275                 :            : 
     276         [ +  - ]:        261 :         if (gpu->limits.max_tex_3d_dim >= 16) {
     277                 :            :             printf("-- 3D\n");
     278                 :        261 :             struct pl_tex_params params = ref_params;
     279                 :        261 :             params.w = params.h = params.d = 16;
     280         [ +  + ]:        261 :             if (!gpu->limits.blittable_1d_3d)
     281                 :        183 :                 params.blit_src = params.blit_dst = false;
     282         [ +  + ]:        783 :             for (int i = 0; i < PL_ARRAY_SIZE(tex); i++)
     283                 :        522 :                 tex[i] = pl_tex_create(gpu, &params);
     284                 :        261 :             pl_test_roundtrip(gpu, tex, test_src, test_dst);
     285         [ +  + ]:        783 :             for (int i = 0; i < PL_ARRAY_SIZE(tex); i++)
     286                 :        522 :                 pl_tex_destroy(gpu, &tex[i]);
     287                 :            :         }
     288                 :            :     }
     289                 :            : 
     290                 :          6 :     free(test_src);
     291                 :          6 : }
     292                 :            : 
     293                 :          5 : static void pl_planar_tests(pl_gpu gpu)
     294                 :          5 : {
     295                 :          5 :     pl_fmt fmt = pl_find_named_fmt(gpu, "g8_b8_r8_420");
     296         [ +  + ]:          5 :     if (!fmt)
     297                 :          5 :         return;
     298                 :            :     printf("pl_planar_tests:\n");
     299         [ -  + ]:          1 :     REQUIRE_CMP(fmt->num_planes, ==, 3, "d");
     300                 :            : 
     301                 :            :     const int width = 64, height = 32;
     302                 :          1 :     pl_tex tex = pl_tex_create(gpu, pl_tex_params(
     303                 :            :         .w              = width,
     304                 :            :         .h              = height,
     305                 :            :         .format         = fmt,
     306                 :            :         .blit_dst       = true,
     307                 :            :         .host_readable  = true,
     308                 :            :     ));
     309         [ -  + ]:          1 :     if (!tex)
     310                 :            :         return;
     311         [ #  # ]:          0 :     for (int i = 0; i < fmt->num_planes; i++)
     312         [ #  # ]:          0 :         REQUIRE(tex->planes[i]);
     313                 :            : 
     314                 :          0 :     pl_tex plane = tex->planes[1];
     315                 :          0 :     uint8_t data[(width * height) >> 2];
     316         [ #  # ]:          0 :     REQUIRE_CMP(plane->params.w * plane->params.h, ==, PL_ARRAY_SIZE(data), "d");
     317                 :            : 
     318                 :          0 :     pl_tex_clear(gpu, plane, (float[]){ (float) 0x80 / 0xFF, 0.0, 0.0, 1.0 });
     319         [ #  # ]:          0 :     REQUIRE(pl_tex_download(gpu, pl_tex_transfer_params(
     320                 :            :         .tex = plane,
     321                 :            :         .ptr = data,
     322                 :            :     )));
     323                 :            : 
     324                 :          0 :     uint8_t ref[PL_ARRAY_SIZE(data)];
     325                 :            :     memset(ref, 0x80, sizeof(ref));
     326                 :          0 :     REQUIRE_MEMEQ(data, ref, PL_ARRAY_SIZE(data));
     327                 :            : 
     328                 :          0 :     pl_tex_destroy(gpu, &tex);
     329                 :            : }
     330                 :            : 
     331                 :          5 : static void pl_shader_tests(pl_gpu gpu)
     332                 :            : {
     333         [ +  + ]:          5 :     if (gpu->glsl.version < 410)
     334                 :          3 :         return;
     335                 :            :     printf("pl_shader_tests:\n");
     336                 :            : 
     337                 :            :     const char *vert_shader =
     338                 :            :         "#version 410                               \n"
     339                 :            :         "layout(location=0) in vec2 vertex_pos;     \n"
     340                 :            :         "layout(location=1) in vec3 vertex_color;   \n"
     341                 :            :         "layout(location=0) out vec3 frag_color;    \n"
     342                 :            :         "void main() {                              \n"
     343                 :            :         "    gl_Position = vec4(vertex_pos, 0, 1);  \n"
     344                 :            :         "    frag_color = vertex_color;             \n"
     345                 :            :         "}";
     346                 :            : 
     347                 :            :     const char *frag_shader =
     348                 :            :         "#version 410                               \n"
     349                 :            :         "layout(location=0) in vec3 frag_color;     \n"
     350                 :            :         "layout(location=0) out vec4 out_color;     \n"
     351                 :            :         "void main() {                              \n"
     352                 :            :         "    out_color = vec4(frag_color, 1.0);     \n"
     353                 :            :         "}";
     354                 :            : 
     355                 :            :     pl_fmt fbo_fmt;
     356                 :            :     enum pl_fmt_caps caps = PL_FMT_CAP_RENDERABLE | PL_FMT_CAP_BLITTABLE |
     357                 :            :                             PL_FMT_CAP_LINEAR;
     358                 :            : 
     359                 :          2 :     fbo_fmt = pl_find_fmt(gpu, PL_FMT_FLOAT, 4, 16, 32, caps);
     360         [ +  - ]:          2 :     if (!fbo_fmt)
     361                 :            :         return;
     362                 :            : 
     363                 :            : #define FBO_W 16
     364                 :            : #define FBO_H 16
     365                 :            : 
     366                 :            :     pl_tex fbo;
     367                 :          4 :     fbo = pl_tex_create(gpu, &(struct pl_tex_params) {
     368                 :            :         .format         = fbo_fmt,
     369                 :            :         .w              = FBO_W,
     370                 :            :         .h              = FBO_H,
     371                 :            :         .renderable     = true,
     372                 :          2 :         .storable       = !!(fbo_fmt->caps & PL_FMT_CAP_STORABLE),
     373                 :            :         .host_readable  = true,
     374                 :            :         .blit_dst       = true,
     375                 :            :     });
     376         [ -  + ]:          2 :     REQUIRE(fbo);
     377                 :            : 
     378                 :          2 :     pl_tex_clear_ex(gpu, fbo, (union pl_clear_color){0});
     379                 :            : 
     380                 :            :     pl_fmt vert_fmt;
     381                 :          2 :     vert_fmt = pl_find_vertex_fmt(gpu, PL_FMT_FLOAT, 3);
     382         [ -  + ]:          2 :     REQUIRE(vert_fmt);
     383                 :            : 
     384                 :            :     static const struct vertex { float pos[2]; float color[3]; } vertices[] = {
     385                 :            :         {{-1.0, -1.0}, {0, 0, 0}},
     386                 :            :         {{ 1.0, -1.0}, {1, 0, 0}},
     387                 :            :         {{-1.0,  1.0}, {0, 1, 0}},
     388                 :            :         {{ 1.0,  1.0}, {1, 1, 0}},
     389                 :            :     };
     390                 :            : 
     391                 :            :     pl_pass pass;
     392                 :          6 :     pass = pl_pass_create(gpu, &(struct pl_pass_params) {
     393                 :            :         .type           = PL_PASS_RASTER,
     394                 :            :         .target_format  = fbo_fmt,
     395                 :            :         .vertex_shader  = vert_shader,
     396                 :            :         .glsl_shader    = frag_shader,
     397                 :            : 
     398                 :            :         .vertex_type    = PL_PRIM_TRIANGLE_STRIP,
     399                 :            :         .vertex_stride  = sizeof(struct vertex),
     400                 :            :         .num_vertex_attribs = 2,
     401                 :          2 :         .vertex_attribs = (struct pl_vertex_attrib[]) {{
     402                 :            :             .name     = "vertex_pos",
     403                 :          2 :             .fmt      = pl_find_vertex_fmt(gpu, PL_FMT_FLOAT, 2),
     404                 :            :             .location = 0,
     405                 :            :             .offset   = offsetof(struct vertex, pos),
     406                 :            :         }, {
     407                 :            :             .name     = "vertex_color",
     408                 :          2 :             .fmt      = pl_find_vertex_fmt(gpu, PL_FMT_FLOAT, 3),
     409                 :            :             .location = 1,
     410                 :            :             .offset   = offsetof(struct vertex, color),
     411                 :            :         }},
     412                 :            :     });
     413         [ -  + ]:          2 :     REQUIRE(pass);
     414   [ +  -  -  + ]:          2 :     if (pass->params.cached_program || pass->params.cached_program_len) {
     415                 :            :         // Ensure both are set if either one is set
     416         [ #  # ]:          0 :         REQUIRE(pass->params.cached_program);
     417         [ #  # ]:          0 :         REQUIRE(pass->params.cached_program_len);
     418                 :            :     }
     419                 :            : 
     420                 :          2 :     pl_timer timer = pl_timer_create(gpu);
     421                 :          2 :     pl_pass_run(gpu, &(struct pl_pass_run_params) {
     422                 :            :         .pass           = pass,
     423                 :            :         .target         = fbo,
     424                 :            :         .vertex_count   = PL_ARRAY_SIZE(vertices),
     425                 :            :         .vertex_data    = vertices,
     426                 :            :         .timer          = timer,
     427                 :            :     });
     428                 :            : 
     429                 :            :     // Wait until this pass is complete and report the timer result
     430                 :          2 :     pl_gpu_finish(gpu);
     431                 :          2 :     printf("timer query result: %"PRIu64"\n", pl_timer_query(gpu, timer));
     432                 :          2 :     pl_timer_destroy(gpu, &timer);
     433                 :            : 
     434                 :            :     static float test_data[FBO_H * FBO_W * 4] = {0};
     435                 :            : 
     436                 :            :     // Test against the known pattern of `src`, only useful for roundtrip tests
     437                 :            : #define TEST_FBO_PATTERN(eps, fmt, ...)                                     \
     438                 :            :     do {                                                                    \
     439                 :            :         printf("- testing pattern of " fmt "\n", __VA_ARGS__);                \
     440                 :            :         REQUIRE(pl_tex_download(gpu, &(struct pl_tex_transfer_params) {     \
     441                 :            :             .tex = fbo,                                                     \
     442                 :            :             .ptr = test_data,                                               \
     443                 :            :         }));                                                                \
     444                 :            :                                                                             \
     445                 :            :         for (int y = 0; y < FBO_H; y++) {                                   \
     446                 :            :             for (int x = 0; x < FBO_W; x++) {                               \
     447                 :            :                 float *color = &test_data[(y * FBO_W + x) * 4];             \
     448                 :            :                 REQUIRE_FEQ(color[0], (x + 0.5) / FBO_W, eps);              \
     449                 :            :                 REQUIRE_FEQ(color[1], (y + 0.5) / FBO_H, eps);              \
     450                 :            :                 REQUIRE_FEQ(color[2], 0.0, eps);                            \
     451                 :            :                 REQUIRE_FEQ(color[3], 1.0, eps);                            \
     452                 :            :             }                                                               \
     453                 :            :         }                                                                   \
     454                 :            :     } while (0)
     455                 :            : 
     456   [ -  +  -  +  :        546 :     TEST_FBO_PATTERN(1e-6, "%s", "initial rendering");
          -  +  -  +  -  
             +  +  +  +  
                      + ]
     457                 :            : 
     458         [ +  - ]:          2 :     if (sizeof(vertices) <= gpu->limits.max_vbo_size) {
     459                 :            :         // Test the use of an explicit vertex buffer
     460                 :          2 :         pl_buf vert = pl_buf_create(gpu, &(struct pl_buf_params) {
     461                 :            :             .size = sizeof(vertices),
     462                 :            :             .initial_data = vertices,
     463                 :            :             .drawable = true,
     464                 :            :         });
     465                 :            : 
     466         [ -  + ]:          2 :         REQUIRE(vert);
     467                 :          2 :         pl_pass_run(gpu, &(struct pl_pass_run_params) {
     468                 :            :             .pass           = pass,
     469                 :            :             .target         = fbo,
     470                 :            :             .vertex_count   = sizeof(vertices) / sizeof(struct vertex),
     471                 :            :             .vertex_buf     = vert,
     472                 :            :             .buf_offset     = 0,
     473                 :            :         });
     474                 :            : 
     475                 :          2 :         pl_buf_destroy(gpu, &vert);
     476   [ -  +  -  +  :        546 :         TEST_FBO_PATTERN(1e-6, "%s", "using vertex buffer");
          -  +  -  +  -  
             +  +  +  +  
                      + ]
     477                 :            :     }
     478                 :            : 
     479                 :            :     // Test the use of index buffers
     480                 :            :     static const uint16_t indices[] = { 3, 2, 1, 0 };
     481                 :          2 :     pl_pass_run(gpu, &(struct pl_pass_run_params) {
     482                 :            :         .pass           = pass,
     483                 :            :         .target         = fbo,
     484                 :            :         .vertex_count   = PL_ARRAY_SIZE(indices),
     485                 :            :         .vertex_data    = vertices,
     486                 :            :         .index_data     = indices,
     487                 :            :     });
     488                 :            : 
     489                 :          2 :     pl_pass_destroy(gpu, &pass);
     490   [ -  +  -  +  :        546 :     TEST_FBO_PATTERN(1e-6, "%s", "using indexed rendering");
          -  +  -  +  -  
             +  +  +  +  
                      + ]
     491                 :            : 
     492                 :            :     // Test the use of pl_dispatch
     493                 :          2 :     pl_dispatch dp = pl_dispatch_create(gpu->log, gpu);
     494                 :          2 :     pl_shader sh = pl_dispatch_begin(dp);
     495         [ -  + ]:          2 :     REQUIRE(pl_shader_custom(sh, &(struct pl_custom_shader) {
     496                 :            :         .body       = "color = vec4(col, 1.0);",
     497                 :            :         .input      = PL_SHADER_SIG_NONE,
     498                 :            :         .output     = PL_SHADER_SIG_COLOR,
     499                 :            :     }));
     500                 :            : 
     501         [ -  + ]:          2 :     REQUIRE(pl_dispatch_vertex(dp, &(struct pl_dispatch_vertex_params) {
     502                 :            :         .shader         = &sh,
     503                 :            :         .target         = fbo,
     504                 :            :         .vertex_stride  = sizeof(struct vertex),
     505                 :            :         .vertex_position_idx = 0,
     506                 :            :         .num_vertex_attribs = 2,
     507                 :            :         .vertex_attribs = (struct pl_vertex_attrib[]) {{
     508                 :            :             .name   = "pos",
     509                 :            :             .fmt    = pl_find_vertex_fmt(gpu, PL_FMT_FLOAT, 2),
     510                 :            :             .offset = offsetof(struct vertex, pos),
     511                 :            :         }, {
     512                 :            :             .name   = "col",
     513                 :            :             .fmt    = pl_find_vertex_fmt(gpu, PL_FMT_FLOAT, 3),
     514                 :            :             .offset = offsetof(struct vertex, color),
     515                 :            :         }},
     516                 :            : 
     517                 :            :         .vertex_type    = PL_PRIM_TRIANGLE_STRIP,
     518                 :            :         .vertex_coords  = PL_COORDS_NORMALIZED,
     519                 :            :         .vertex_count   = PL_ARRAY_SIZE(vertices),
     520                 :            :         .vertex_data    = vertices,
     521                 :            :     }));
     522                 :            : 
     523   [ -  +  -  +  :        546 :     TEST_FBO_PATTERN(1e-6, "%s", "using custom vertices");
          -  +  -  +  -  
             +  +  +  +  
                      + ]
     524                 :            : 
     525                 :            :     static float src_data[FBO_H * FBO_W * 4] = {0};
     526                 :            :     memcpy(src_data, test_data, sizeof(src_data));
     527                 :            : 
     528                 :            :     pl_tex src;
     529                 :          4 :     src = pl_tex_create(gpu, &(struct pl_tex_params) {
     530                 :            :         .format         = fbo_fmt,
     531                 :            :         .w              = FBO_W,
     532                 :            :         .h              = FBO_H,
     533                 :          2 :         .storable       = fbo->params.storable,
     534                 :            :         .sampleable     = true,
     535                 :            :         .initial_data   = src_data,
     536                 :            :     });
     537                 :            : 
     538         [ +  - ]:          2 :     if (fbo->params.storable) {
     539                 :            :         // Test 1x1 blit, to make sure the scaling code runs
     540         [ -  + ]:          2 :         REQUIRE(pl_tex_blit_compute(gpu, &(struct pl_tex_blit_params) {
     541                 :            :             .src = src,
     542                 :            :             .dst = fbo,
     543                 :            :             .src_rc = {0, 0, 0, 1, 1, 1},
     544                 :            :             .dst_rc = {0, 0, 0, FBO_W, FBO_H, 1},
     545                 :            :             .sample_mode = PL_TEX_SAMPLE_NEAREST,
     546                 :            :         }));
     547                 :            : 
     548                 :            :         // Test non-resizing blit, which uses the efficient imageLoad path
     549         [ -  + ]:          2 :         REQUIRE(pl_tex_blit_compute(gpu, &(struct pl_tex_blit_params) {
     550                 :            :             .src = src,
     551                 :            :             .dst = fbo,
     552                 :            :             .src_rc = {0, 0, 0, FBO_W, FBO_H, 1},
     553                 :            :             .dst_rc = {0, 0, 0, FBO_W, FBO_H, 1},
     554                 :            :             .sample_mode = PL_TEX_SAMPLE_NEAREST,
     555                 :            :         }));
     556                 :            : 
     557   [ -  +  -  +  :        546 :         TEST_FBO_PATTERN(1e-6, "%s", "pl_tex_blit_compute");
          -  +  -  +  -  
             +  +  +  +  
                      + ]
     558                 :            :     }
     559                 :            : 
     560                 :            :     // Test encoding/decoding of all gamma functions, color spaces, etc.
     561         [ +  + ]:         36 :     for (enum pl_color_transfer trc = 0; trc < PL_COLOR_TRC_COUNT; trc++) {
     562                 :         34 :         struct pl_color_space test_csp = {
     563                 :            :             .transfer = trc,
     564                 :            :             .hdr.min_luma = PL_COLOR_HDR_BLACK,
     565                 :            :         };
     566                 :         34 :         sh = pl_dispatch_begin(dp);
     567                 :         34 :         pl_shader_sample_nearest(sh, pl_sample_src( .tex = src ));
     568                 :         34 :         pl_shader_delinearize(sh, &test_csp);
     569                 :         34 :         pl_shader_linearize(sh, &test_csp);
     570         [ -  + ]:         34 :         REQUIRE(pl_dispatch_finish(dp, pl_dispatch_params(
     571                 :            :             .shader = &sh,
     572                 :            :             .target = fbo,
     573                 :            :         )));
     574                 :            : 
     575         [ +  + ]:         34 :         float epsilon = pl_color_transfer_is_hdr(trc) ? 1e-4 : 1e-6;
     576   [ -  +  -  +  :       9316 :         TEST_FBO_PATTERN(epsilon, "transfer function: %s", pl_color_transfer_name(trc));
          -  +  -  +  -  
             +  +  +  +  
                      + ]
     577                 :            :     }
     578                 :            : 
     579         [ +  + ]:         26 :     for (enum pl_color_system sys = 0; sys < PL_COLOR_SYSTEM_COUNT; sys++) {
     580         [ +  + ]:         24 :         if (sys == PL_COLOR_SYSTEM_DOLBYVISION)
     581                 :          6 :             continue; // requires metadata
     582                 :         22 :         sh = pl_dispatch_begin(dp);
     583                 :         22 :         pl_shader_sample_nearest(sh, pl_sample_src( .tex = src ));
     584                 :         22 :         pl_shader_encode_color(sh, &(struct pl_color_repr) { .sys = sys });
     585                 :         22 :         pl_shader_decode_color(sh, &(struct pl_color_repr) { .sys = sys }, NULL);
     586         [ -  + ]:         22 :         REQUIRE(pl_dispatch_finish(dp, &(struct pl_dispatch_params) {
     587                 :            :             .shader = &sh,
     588                 :            :             .target = fbo,
     589                 :            :         }));
     590                 :            : 
     591                 :            :         float epsilon;
     592      [ +  +  + ]:         22 :         switch (sys) {
     593                 :            :         case PL_COLOR_SYSTEM_BT_2020_C:
     594                 :            :         case PL_COLOR_SYSTEM_XYZ:
     595                 :            :             epsilon = 1e-5;
     596                 :            :             break;
     597                 :            : 
     598                 :          4 :         case PL_COLOR_SYSTEM_BT_2100_PQ:
     599                 :            :         case PL_COLOR_SYSTEM_BT_2100_HLG:
     600                 :            :             // These seem to be horrifically noisy and prone to breaking on
     601                 :            :             // edge cases for some reason
     602                 :            :             // TODO: figure out why!
     603                 :          4 :             continue;
     604                 :            : 
     605                 :         14 :         default: epsilon = 1e-6; break;
     606                 :            :         }
     607                 :            : 
     608   [ -  +  -  +  :       4932 :         TEST_FBO_PATTERN(epsilon, "color system: %s", pl_color_system_name(sys));
          -  +  -  +  -  
             +  +  +  +  
                      + ]
     609                 :            :     }
     610                 :            : 
     611                 :            :     // Repeat this a few times to test the caching
     612                 :          2 :     pl_cache cache = pl_cache_create(pl_cache_params( .log = gpu->log ));
     613                 :          2 :     pl_gpu_set_cache(gpu, cache);
     614         [ +  + ]:         22 :     for (int i = 0; i < 10; i++) {
     615         [ +  + ]:         20 :         if (i == 5) {
     616                 :            :             printf("Recreating pl_dispatch to test the caching\n");
     617                 :          2 :             size_t size = pl_dispatch_save(dp, NULL);
     618         [ -  + ]:          2 :             REQUIRE(size);
     619                 :          2 :             uint8_t *cache_data = malloc(size);
     620         [ -  + ]:          2 :             REQUIRE(cache_data);
     621         [ -  + ]:          2 :             REQUIRE_CMP(pl_dispatch_save(dp, cache_data), ==, size, "zu");
     622                 :            : 
     623                 :          2 :             pl_dispatch_destroy(&dp);
     624                 :          2 :             dp = pl_dispatch_create(gpu->log, gpu);
     625                 :          2 :             pl_dispatch_load(dp, cache_data);
     626                 :            : 
     627                 :            :             // Test to make sure the pass regenerates the same cache
     628                 :            :             uint64_t hash = pl_str_hash((pl_str) { cache_data, size });
     629         [ -  + ]:          2 :             REQUIRE_CMP(pl_dispatch_save(dp, NULL), ==, size, "zu");
     630         [ -  + ]:          2 :             REQUIRE_CMP(pl_dispatch_save(dp, cache_data), ==, size, "zu");
     631         [ -  + ]:          2 :             REQUIRE_CMP(pl_str_hash((pl_str) { cache_data, size }), ==, hash, PRIu64);
     632                 :          2 :             free(cache_data);
     633                 :            :         }
     634                 :            : 
     635                 :         20 :         sh = pl_dispatch_begin(dp);
     636                 :            : 
     637                 :            :         // For testing, force the use of CS if possible
     638         [ +  - ]:         20 :         if (gpu->glsl.compute) {
     639                 :         20 :             sh->type = SH_COMPUTE;
     640                 :         20 :             sh->group_size[0] = 8;
     641                 :         20 :             sh->group_size[1] = 8;
     642                 :            :         }
     643                 :            : 
     644                 :         20 :         pl_shader_deband(sh, pl_sample_src( .tex = src ), pl_deband_params(
     645                 :            :             .iterations     = 0,
     646                 :            :             .grain          = 0.0,
     647                 :            :         ));
     648                 :            : 
     649         [ -  + ]:         20 :         REQUIRE(pl_dispatch_finish(dp, &(struct pl_dispatch_params) {
     650                 :            :             .shader = &sh,
     651                 :            :             .target = fbo,
     652                 :            :         }));
     653   [ -  +  -  +  :       5460 :         TEST_FBO_PATTERN(1e-6, "deband iter %d", i);
          -  +  -  +  -  
             +  +  +  +  
                      + ]
     654                 :            :     }
     655                 :            : 
     656                 :          2 :     pl_gpu_set_cache(gpu, NULL);
     657                 :          2 :     pl_cache_destroy(&cache);
     658                 :            : 
     659                 :            :     // Test peak detection and readback if possible
     660                 :          2 :     sh = pl_dispatch_begin(dp);
     661                 :          2 :     pl_shader_sample_nearest(sh, pl_sample_src( .tex = src ));
     662                 :            : 
     663                 :          2 :     pl_shader_obj peak_state = NULL;
     664                 :          2 :     struct pl_color_space csp_gamma22 = { .transfer = PL_COLOR_TRC_GAMMA22 };
     665                 :          2 :     struct pl_peak_detect_params peak_params = { .minimum_peak = 0.01 };
     666         [ +  - ]:          2 :     if (pl_shader_detect_peak(sh, csp_gamma22, &peak_state, &peak_params)) {
     667         [ -  + ]:          2 :         REQUIRE(pl_dispatch_compute(dp, &(struct pl_dispatch_compute_params) {
     668                 :            :             .shader = &sh,
     669                 :            :             .width = fbo->params.w,
     670                 :            :             .height = fbo->params.h,
     671                 :            :         }));
     672                 :            : 
     673                 :            :         struct pl_hdr_metadata hdr;
     674         [ -  + ]:          2 :         REQUIRE(pl_get_detected_hdr_metadata(peak_state, &hdr));
     675                 :            : 
     676                 :            :         float real_peak = 0, real_avg = 0;
     677         [ +  + ]:         34 :         for (int y = 0; y < FBO_H; y++) {
     678         [ +  + ]:        544 :             for (int x = 0; x < FBO_W; x++) {
     679                 :        512 :                 float *color = &src_data[(y * FBO_W + x) * 4];
     680                 :        512 :                 float luma = 0.212639f * powf(color[0], 2.2f) +
     681                 :        512 :                              0.715169f * powf(color[1], 2.2f) +
     682                 :        512 :                              0.072192f * powf(color[2], 2.2f);
     683                 :        512 :                 luma = pl_hdr_rescale(PL_HDR_NORM, PL_HDR_PQ, luma);
     684         [ +  + ]:        512 :                 real_peak = PL_MAX(real_peak, luma);
     685                 :        512 :                 real_avg += luma;
     686                 :            :             }
     687                 :            :         }
     688                 :          2 :         real_avg = real_avg / (FBO_W * FBO_H);
     689         [ -  + ]:          2 :         REQUIRE_FEQ(hdr.max_pq_y, real_peak, 1e-4);
     690         [ -  + ]:          2 :         REQUIRE_FEQ(hdr.avg_pq_y, real_avg,  1e-3);
     691                 :            :     }
     692                 :            : 
     693                 :          2 :     pl_dispatch_abort(dp, &sh);
     694                 :          2 :     pl_shader_obj_destroy(&peak_state);
     695                 :            : 
     696                 :            :     // Test film grain synthesis
     697                 :          2 :     pl_shader_obj grain = NULL;
     698                 :          2 :     struct pl_film_grain_params grain_params = {
     699                 :            :         .tex = src,
     700                 :            :         .components = 3,
     701                 :            :         .component_mapping = { 0, 1, 2},
     702                 :          2 :         .repr = &(struct pl_color_repr) {
     703                 :            :             .sys = PL_COLOR_SYSTEM_BT_709,
     704                 :            :             .levels = PL_COLOR_LEVELS_LIMITED,
     705                 :            :             .bits = { .color_depth = 10, .sample_depth = 10 },
     706                 :            :         },
     707                 :            :     };
     708                 :            : 
     709         [ +  + ]:          6 :     for (int i = 0; i < 2; i++) {
     710                 :          4 :         grain_params.data.type = PL_FILM_GRAIN_AV1;
     711                 :          4 :         grain_params.data.params.av1 = av1_grain_data;
     712                 :          4 :         grain_params.data.params.av1.overlap = !!i;
     713                 :          4 :         grain_params.data.seed = rand();
     714                 :            : 
     715                 :          4 :         sh = pl_dispatch_begin(dp);
     716                 :          4 :         pl_shader_film_grain(sh, &grain, &grain_params);
     717         [ -  + ]:          4 :         REQUIRE(pl_dispatch_finish(dp, &(struct pl_dispatch_params) {
     718                 :            :             .shader = &sh,
     719                 :            :             .target = fbo,
     720                 :            :         }));
     721                 :            :     }
     722                 :            : 
     723         [ +  - ]:          2 :     if (gpu->glsl.compute) {
     724                 :          2 :         grain_params.data.type = PL_FILM_GRAIN_H274;
     725                 :          2 :         grain_params.data.params.h274 = h274_grain_data;
     726                 :          2 :         grain_params.data.seed = rand();
     727                 :            : 
     728                 :          2 :         sh = pl_dispatch_begin(dp);
     729                 :          2 :         pl_shader_film_grain(sh, &grain, &grain_params);
     730         [ -  + ]:          2 :         REQUIRE(pl_dispatch_finish(dp, &(struct pl_dispatch_params) {
     731                 :            :             .shader = &sh,
     732                 :            :             .target = fbo,
     733                 :            :         }));
     734                 :            :     }
     735                 :          2 :     pl_shader_obj_destroy(&grain);
     736                 :            : 
     737                 :            :     // Test custom shaders
     738                 :          4 :     struct pl_custom_shader custom = {
     739                 :            :         .header =
     740                 :            :             "vec3 invert(vec3 color)            \n"
     741                 :            :             "{                                  \n"
     742                 :            :             "    return vec3(1.0) - color;      \n"
     743                 :            :             "}                                  \n",
     744                 :            : 
     745                 :            :         .body =
     746                 :            :             "color = vec4(gl_FragCoord.xy, 0.0, 1.0);   \n"
     747                 :            :             "color.rgb = invert(color.rgb) + offset;    \n",
     748                 :            : 
     749                 :            :         .input = PL_SHADER_SIG_NONE,
     750                 :            :         .output = PL_SHADER_SIG_COLOR,
     751                 :            : 
     752                 :            :         .num_variables = 1,
     753                 :          4 :         .variables = &(struct pl_shader_var) {
     754                 :          2 :             .var = pl_var_float("offset"),
     755                 :          2 :             .data = &(float) { 0.1 },
     756                 :            :         },
     757                 :            :     };
     758                 :            : 
     759                 :          2 :     sh = pl_dispatch_begin(dp);
     760         [ -  + ]:          2 :     REQUIRE(pl_shader_custom(sh, &custom));
     761         [ -  + ]:          2 :     REQUIRE(pl_dispatch_finish(dp, &(struct pl_dispatch_params) {
     762                 :            :         .shader = &sh,
     763                 :            :         .target = fbo,
     764                 :            :     }));
     765                 :            : 
     766                 :            :     // Test dolbyvision
     767                 :          2 :     struct pl_color_repr repr = {
     768                 :            :         .sys = PL_COLOR_SYSTEM_DOLBYVISION,
     769                 :            :         .dovi = &dovi_meta,
     770                 :            :     };
     771                 :            : 
     772                 :          2 :     sh = pl_dispatch_begin(dp);
     773                 :          2 :     pl_shader_sample_direct(sh, pl_sample_src( .tex = src ));
     774                 :          2 :     pl_shader_decode_color(sh, &repr, NULL);
     775         [ -  + ]:          2 :     REQUIRE(pl_dispatch_finish(dp, &(struct pl_dispatch_params) {
     776                 :            :         .shader = &sh,
     777                 :            :         .target = fbo,
     778                 :            :     }));
     779                 :            : 
     780                 :            :     // Test deinterlacing
     781                 :          2 :     sh = pl_dispatch_begin(dp);
     782                 :          2 :     pl_shader_deinterlace(sh, pl_deinterlace_source( .cur = pl_field_pair(src) ), NULL);
     783         [ -  + ]:          2 :     REQUIRE(pl_dispatch_finish(dp, pl_dispatch_params(
     784                 :            :         .shader = &sh,
     785                 :            :         .target = fbo,
     786                 :            :     )));
     787                 :            : 
     788                 :            :     // Test error diffusion
     789         [ +  - ]:          2 :     if (fbo->params.storable) {
     790         [ +  + ]:         22 :         for (int i = 0; i < pl_num_error_diffusion_kernels; i++) {
     791                 :         20 :             const struct pl_error_diffusion_kernel *k = pl_error_diffusion_kernels[i];
     792                 :         20 :             printf("- testing error diffusion kernel '%s'\n", k->name);
     793                 :         20 :             sh = pl_dispatch_begin(dp);
     794                 :         20 :             bool ok = pl_shader_error_diffusion(sh, pl_error_diffusion_params(
     795                 :            :                 .input_tex  = src,
     796                 :            :                 .output_tex = fbo,
     797                 :            :                 .new_depth  = 8,
     798                 :            :                 .kernel     = k,
     799                 :            :             ));
     800                 :            : 
     801         [ -  + ]:         20 :             if (!ok) {
     802                 :          0 :                 fprintf(stderr, "kernel '%s' exceeds GPU limits, skipping...\n", k->name);
     803                 :          0 :                 continue;
     804                 :            :             }
     805                 :            : 
     806         [ -  + ]:         20 :             REQUIRE(pl_dispatch_compute(dp, pl_dispatch_compute_params(
     807                 :            :                 .shader = &sh,
     808                 :            :                 .dispatch_size = {1, 1, 1},
     809                 :            :             )));
     810                 :            :         }
     811                 :            :     }
     812                 :            : 
     813                 :          2 :     pl_dispatch_destroy(&dp);
     814                 :          2 :     pl_tex_destroy(gpu, &src);
     815                 :          2 :     pl_tex_destroy(gpu, &fbo);
     816                 :            : }
     817                 :            : 
     818                 :          5 : static void pl_scaler_tests(pl_gpu gpu)
     819                 :            : {
     820                 :          5 :     pl_fmt src_fmt = pl_find_fmt(gpu, PL_FMT_FLOAT, 1, 16, 32, PL_FMT_CAP_LINEAR);
     821                 :          5 :     pl_fmt fbo_fmt = pl_find_fmt(gpu, PL_FMT_FLOAT, 1, 16, 32, PL_FMT_CAP_RENDERABLE);
     822         [ -  + ]:          5 :     if (!src_fmt || !fbo_fmt)
     823                 :          0 :         return;
     824                 :            :     printf("pl_scaler_tests:\n");
     825                 :            : 
     826                 :            :     float *fbo_data = NULL;
     827                 :          5 :     pl_shader_obj lut = NULL;
     828                 :            : 
     829                 :            :     static float data_5x5[5][5] = {
     830                 :            :         { 0, 0, 0, 0, 0 },
     831                 :            :         { 0, 0, 0, 0, 0 },
     832                 :            :         { 0, 0, 1, 0, 0 },
     833                 :            :         { 0, 0, 0, 0, 0 },
     834                 :            :         { 0, 0, 0, 0, 0 },
     835                 :            :     };
     836                 :            : 
     837                 :          5 :     pl_tex dot5x5 = pl_tex_create(gpu, &(struct pl_tex_params) {
     838                 :            :         .w              = 5,
     839                 :            :         .h              = 5,
     840                 :            :         .format         = src_fmt,
     841                 :            :         .sampleable     = true,
     842                 :            :         .initial_data   = &data_5x5[0][0],
     843                 :            :     });
     844                 :            : 
     845                 :          5 :     struct pl_tex_params fbo_params = {
     846                 :            :         .w              = 100,
     847                 :            :         .h              = 100,
     848                 :            :         .format         = fbo_fmt,
     849                 :            :         .renderable     = true,
     850                 :          5 :         .storable       = fbo_fmt->caps & PL_FMT_CAP_STORABLE,
     851                 :          5 :         .host_readable  = fbo_fmt->caps & PL_FMT_CAP_HOST_READABLE,
     852                 :            :     };
     853                 :            : 
     854                 :          5 :     pl_tex fbo = pl_tex_create(gpu, &fbo_params);
     855                 :          5 :     pl_dispatch dp = pl_dispatch_create(gpu->log, gpu);
     856   [ +  +  -  + ]:          5 :     if (!dot5x5 || !fbo || !dp)
     857                 :          1 :         goto error;
     858                 :            : 
     859                 :          4 :     pl_shader sh = pl_dispatch_begin(dp);
     860         [ -  + ]:          4 :     REQUIRE(pl_shader_sample_polar(sh,
     861                 :            :         pl_sample_src(
     862                 :            :             .tex        = dot5x5,
     863                 :            :             .new_w      = fbo->params.w,
     864                 :            :             .new_h      = fbo->params.h,
     865                 :            :         ),
     866                 :            :         pl_sample_filter_params(
     867                 :            :             .filter     = pl_filter_ewa_lanczos,
     868                 :            :             .lut        = &lut,
     869                 :            :             .no_compute = !fbo->params.storable,
     870                 :            :         )
     871                 :            :     ));
     872         [ -  + ]:          4 :     REQUIRE(pl_dispatch_finish(dp, &(struct pl_dispatch_params) {
     873                 :            :         .shader = &sh,
     874                 :            :         .target = fbo,
     875                 :            :     }));
     876                 :            : 
     877         [ -  + ]:          4 :     if (fbo->params.host_readable) {
     878                 :          4 :         fbo_data = malloc(fbo->params.w * fbo->params.h * sizeof(float));
     879         [ -  + ]:          4 :         REQUIRE(pl_tex_download(gpu, &(struct pl_tex_transfer_params) {
     880                 :            :             .tex            = fbo,
     881                 :            :             .ptr            = fbo_data,
     882                 :            :         }));
     883                 :            : 
     884                 :            : #ifdef PRINT_OUTPUT
     885                 :            :         int max = 255;
     886                 :            :         printf("P2\n%d %d\n%d\n", fbo->params.w, fbo->params.h, max);
     887                 :            :         for (int y = 0; y < fbo->params.h; y++) {
     888                 :            :             for (int x = 0; x < fbo->params.w; x++) {
     889                 :            :                 float v = fbo_data[y * fbo->params.h + x];
     890                 :            :                 printf("%d ", (int) round(fmin(fmax(v, 0.0), 1.0) * max));
     891                 :            :             }
     892                 :            :             printf("\n");
     893                 :            :         }
     894                 :            : #endif
     895                 :            :     }
     896                 :            : 
     897                 :          0 : error:
     898                 :          5 :     free(fbo_data);
     899                 :          5 :     pl_shader_obj_destroy(&lut);
     900                 :          5 :     pl_dispatch_destroy(&dp);
     901                 :          5 :     pl_tex_destroy(gpu, &dot5x5);
     902                 :          5 :     pl_tex_destroy(gpu, &fbo);
     903                 :            : }
     904                 :            : 
     905                 :            : static const char *user_shader_tests[] = {
     906                 :            :     // Test hooking, saving and loading
     907                 :            :     "// Example of a comment at the beginning                               \n"
     908                 :            :     "                                                                       \n"
     909                 :            :     "//!HOOK NATIVE                                                         \n"
     910                 :            :     "//!DESC upscale image                                                  \n"
     911                 :            :     "//!BIND HOOKED                                                         \n"
     912                 :            :     "//!WIDTH HOOKED.w 10 *                                                 \n"
     913                 :            :     "//!HEIGHT HOOKED.h 10 *                                                \n"
     914                 :            :     "//!SAVE NATIVEBIG                                                      \n"
     915                 :            :     "//!WHEN NATIVE.w 500 <                                                 \n"
     916                 :            :     "                                                                       \n"
     917                 :            :     "vec4 hook()                                                            \n"
     918                 :            :     "{                                                                      \n"
     919                 :            :     "    return HOOKED_texOff(0);                                           \n"
     920                 :            :     "}                                                                      \n"
     921                 :            :     "                                                                       \n"
     922                 :            :     "//!HOOK MAIN                                                           \n"
     923                 :            :     "//!DESC downscale bigger image                                         \n"
     924                 :            :     "//!WHEN NATIVE.w 500 <                                                 \n"
     925                 :            :     "//!BIND NATIVEBIG                                                      \n"
     926                 :            :     "                                                                       \n"
     927                 :            :     "vec4 hook()                                                            \n"
     928                 :            :     "{                                                                      \n"
     929                 :            :     "    return NATIVEBIG_texOff(0);                                        \n"
     930                 :            :     "}                                                                      \n",
     931                 :            : 
     932                 :            :     // Test use of textures
     933                 :            :     "//!HOOK MAIN                                                           \n"
     934                 :            :     "//!DESC turn everything into colorful pixels                           \n"
     935                 :            :     "//!BIND HOOKED                                                         \n"
     936                 :            :     "//!BIND DISCO                                                          \n"
     937                 :            :     "//!COMPONENTS 3                                                        \n"
     938                 :            :     "                                                                       \n"
     939                 :            :     "vec4 hook()                                                            \n"
     940                 :            :     "{                                                                      \n"
     941                 :            :     "    return vec4(DISCO_tex(HOOKED_pos * 10.0).rgb, 1);                  \n"
     942                 :            :     "}                                                                      \n"
     943                 :            :     "                                                                       \n"
     944                 :            :     "//!TEXTURE DISCO                                                       \n"
     945                 :            :     "//!SIZE 3 3                                                            \n"
     946                 :            :     "//!FORMAT rgba8                                                        \n"
     947                 :            :     "//!FILTER NEAREST                                                      \n"
     948                 :            :     "//!BORDER REPEAT                                                       \n"
     949                 :            :     "ff0000ff00ff00ff0000ffff00ffffffff00ffffffff00ff4c4c4cff999999ffffffffff\n"
     950                 :            : 
     951                 :            :     // Test custom parameters
     952                 :            :     "//!PARAM test                                                          \n"
     953                 :            :     "//!DESC test parameter                                                 \n"
     954                 :            :     "//!TYPE DYNAMIC float                                                  \n"
     955                 :            :     "//!MINIMUM 0.0                                                         \n"
     956                 :            :     "//!MAXIMUM 100.0                                                       \n"
     957                 :            :     "1.0                                                                    \n"
     958                 :            :     "                                                                       \n"
     959                 :            :     "//!PARAM testconst                                                     \n"
     960                 :            :     "//!TYPE CONSTANT uint                                                  \n"
     961                 :            :     "//!MAXIMUM 16                                                          \n"
     962                 :            :     "3                                                                      \n"
     963                 :            :     "                                                                       \n"
     964                 :            :     "//!PARAM testdefine                                                    \n"
     965                 :            :     "//!TYPE DEFINE                                                         \n"
     966                 :            :     "100                                                                    \n"
     967                 :            :     "                                                                       \n"
     968                 :            :     "//!PARAM testenum                                                      \n"
     969                 :            :     "//!TYPE ENUM DEFINE                                                    \n"
     970                 :            :     "FOO                                                                    \n"
     971                 :            :     "BAR                                                                    \n"
     972                 :            :     "                                                                       \n"
     973                 :            :     "//!HOOK MAIN                                                           \n"
     974                 :            :     "//!WHEN testconst 30 >                                                 \n"
     975                 :            :     "#error should not be run                                               \n"
     976                 :            :     "                                                                       \n"
     977                 :            :     "//!HOOK MAIN                                                           \n"
     978                 :            :     "//!WHEN testenum FOO =                                                 \n"
     979                 :            :     "#if testenum == BAR                                                    \n"
     980                 :            :     " #error bad                                                            \n"
     981                 :            :     "#endif                                                                 \n"
     982                 :            :     "vec4 hook() { return vec4(0.0); }                                      \n"
     983                 :            : };
     984                 :            : 
     985                 :            : static const char *compute_shader_tests[] = {
     986                 :            :     // Test use of storage/buffer resources
     987                 :            :     "//!HOOK MAIN                                                           \n"
     988                 :            :     "//!DESC attach some storage objects                                    \n"
     989                 :            :     "//!BIND tex_storage                                                    \n"
     990                 :            :     "//!BIND buf_uniform                                                    \n"
     991                 :            :     "//!BIND buf_storage                                                    \n"
     992                 :            :     "//!COMPONENTS 4                                                        \n"
     993                 :            :     "                                                                       \n"
     994                 :            :     "vec4 hook()                                                            \n"
     995                 :            :     "{                                                                      \n"
     996                 :            :     "    return vec4(foo, bar, bat);                                        \n"
     997                 :            :     "}                                                                      \n"
     998                 :            :     "                                                                       \n"
     999                 :            :     "//!TEXTURE tex_storage                                                 \n"
    1000                 :            :     "//!SIZE 100 100                                                        \n"
    1001                 :            :     "//!FORMAT r32f                                                         \n"
    1002                 :            :     "//!STORAGE                                                             \n"
    1003                 :            :     "                                                                       \n"
    1004                 :            :     "//!BUFFER buf_uniform                                                  \n"
    1005                 :            :     "//!VAR float foo                                                       \n"
    1006                 :            :     "//!VAR float bar                                                       \n"
    1007                 :            :     "0000000000000000                                                       \n"
    1008                 :            :     "                                                                       \n"
    1009                 :            :     "//!BUFFER buf_storage                                                  \n"
    1010                 :            :     "//!VAR vec2 bat                                                        \n"
    1011                 :            :     "//!VAR int big[32];                                                    \n"
    1012                 :            :     "//!STORAGE                                                             \n",
    1013                 :            : 
    1014                 :            : };
    1015                 :            : 
    1016                 :            : static const char *test_luts[] = {
    1017                 :            : 
    1018                 :            :     "TITLE \"1D identity\"  \n"
    1019                 :            :     "LUT_1D_SIZE 2          \n"
    1020                 :            :     "0.0 0.0 0.0            \n"
    1021                 :            :     "1.0 1.0 1.0            \n",
    1022                 :            : 
    1023                 :            :     "TITLE \"3D identity\"  \n"
    1024                 :            :     "LUT_3D_SIZE 2          \n"
    1025                 :            :     "0.0 0.0 0.0            \n"
    1026                 :            :     "1.0 0.0 0.0            \n"
    1027                 :            :     "0.0 1.0 0.0            \n"
    1028                 :            :     "1.0 1.0 0.0            \n"
    1029                 :            :     "0.0 0.0 1.0            \n"
    1030                 :            :     "1.0 0.0 1.0            \n"
    1031                 :            :     "0.0 1.0 1.0            \n"
    1032                 :            :     "1.0 1.0 1.0            \n"
    1033                 :            : 
    1034                 :            : };
    1035                 :            : 
    1036                 :        240 : static bool frame_passthrough(pl_gpu gpu, pl_tex *tex,
    1037                 :            :                               const struct pl_source_frame *src, struct pl_frame *out_frame)
    1038                 :            : {
    1039                 :        240 :     const struct pl_frame *frame = src->frame_data;
    1040                 :        240 :     *out_frame = *frame;
    1041                 :        240 :     return true;
    1042                 :            : }
    1043                 :            : 
    1044                 :         88 : static enum pl_queue_status get_frame_ptr(struct pl_source_frame *out_frame,
    1045                 :            :                                           const struct pl_queue_params *qparams)
    1046                 :            : {
    1047                 :         88 :     const struct pl_source_frame **pframe = qparams->priv;
    1048         [ +  + ]:         88 :     if (!(*pframe)->frame_data)
    1049                 :            :         return PL_QUEUE_EOF;
    1050                 :            : 
    1051                 :         80 :     *out_frame = *(*pframe)++;
    1052                 :         80 :     return PL_QUEUE_OK;
    1053                 :            : }
    1054                 :            : 
    1055                 :        928 : static void render_info_cb(void *priv, const struct pl_render_info *info)
    1056                 :            : {
    1057                 :        928 :     printf("{%d} Executed shader: %s\n", info->index,
    1058                 :        928 :            info->pass->shader->description);
    1059                 :        928 : }
    1060                 :            : 
    1061                 :          5 : static void pl_render_tests(pl_gpu gpu)
    1062                 :            : {
    1063                 :          5 :     pl_tex img_tex = NULL, fbo = NULL;
    1064                 :          5 :     pl_renderer rr = NULL;
    1065                 :            :     printf("pl_render_tests:\n");
    1066                 :            : 
    1067                 :            :     enum { width = 50, height = 50 };
    1068                 :            :     static float data[width][height];
    1069         [ +  + ]:        255 :     for (int y = 0; y < height; y++) {
    1070         [ +  + ]:      12750 :         for (int x = 0; x < width; x++)
    1071                 :      12500 :             data[y][x] = RANDOM;
    1072                 :            :     }
    1073                 :            : 
    1074                 :          5 :     struct pl_plane img_plane = {0};
    1075                 :          5 :     struct pl_plane_data plane_data = {
    1076                 :            :         .type = PL_FMT_FLOAT,
    1077                 :            :         .width = width,
    1078                 :            :         .height = height,
    1079                 :            :         .component_size = { 8 * sizeof(float) },
    1080                 :            :         .component_map  = { 0 },
    1081                 :            :         .pixel_stride = sizeof(float),
    1082                 :            :         .pixels = data,
    1083                 :            :     };
    1084                 :            : 
    1085         [ +  + ]:          5 :     if (!pl_recreate_plane(gpu, NULL, &fbo, &plane_data))
    1086                 :          1 :         return;
    1087                 :            : 
    1088         [ -  + ]:          4 :     if (!pl_upload_plane(gpu, &img_plane, &img_tex, &plane_data))
    1089                 :          0 :         goto error;
    1090                 :            : 
    1091                 :          4 :     rr = pl_renderer_create(gpu->log, gpu);
    1092                 :          4 :     pl_tex_clear_ex(gpu, fbo, (union pl_clear_color){0});
    1093                 :            : 
    1094                 :          4 :     struct pl_frame image = {
    1095                 :            :         .num_planes     = 1,
    1096                 :            :         .planes         = { img_plane },
    1097                 :            :         .repr = {
    1098                 :            :             .sys        = PL_COLOR_SYSTEM_BT_709,
    1099                 :            :             .levels     = PL_COLOR_LEVELS_FULL,
    1100                 :            :         },
    1101                 :            :         .color          = pl_color_space_srgb,
    1102                 :            :     };
    1103                 :            : 
    1104                 :          4 :     struct pl_frame target = {
    1105                 :            :         .num_planes     = 1,
    1106                 :            :         .planes         = {{
    1107                 :            :             .texture            = fbo,
    1108                 :            :             .components         = 3,
    1109                 :            :             .component_mapping  = {0, 1, 2},
    1110                 :            :         }},
    1111                 :            :         .repr = {
    1112                 :            :             .sys        = PL_COLOR_SYSTEM_RGB,
    1113                 :            :             .levels     = PL_COLOR_LEVELS_FULL,
    1114                 :            :             .bits.color_depth = 32,
    1115                 :            :         },
    1116                 :            :         .color          = pl_color_space_srgb,
    1117                 :            :     };
    1118                 :            : 
    1119         [ -  + ]:          4 :     REQUIRE(pl_render_image(rr, &image, &target, NULL));
    1120         [ -  + ]:          4 :     REQUIRE(pl_renderer_get_errors(rr).errors == PL_RENDER_ERR_NONE);
    1121                 :            : 
    1122                 :            :     // TODO: embed a reference texture and ensure it matches
    1123                 :            : 
    1124                 :            :     // Test a bunch of different params
    1125                 :            : #define TEST(SNAME, STYPE, DEFAULT, FIELD, LIMIT)                       \
    1126                 :            :     do {                                                                \
    1127                 :            :         for (int i = 0; i <= LIMIT; i++) {                              \
    1128                 :            :             printf("- testing `" #STYPE "." #FIELD " = %d`\n", i);        \
    1129                 :            :             struct pl_render_params params = pl_render_default_params;  \
    1130                 :            :             params.force_dither = true;                                 \
    1131                 :            :             struct STYPE tmp = DEFAULT;                                 \
    1132                 :            :             tmp.FIELD = i;                                              \
    1133                 :            :             params.SNAME = &tmp;                                        \
    1134                 :            :             REQUIRE(pl_render_image(rr, &image, &target, &params));     \
    1135                 :            :             pl_gpu_flush(gpu);                                          \
    1136                 :            :             REQUIRE(pl_renderer_get_errors(rr).errors == PL_RENDER_ERR_NONE); \
    1137                 :            :         }                                                               \
    1138                 :            :     } while (0)
    1139                 :            : 
    1140                 :            : #define TEST_PARAMS(NAME, FIELD, LIMIT) \
    1141                 :            :     TEST(NAME##_params, pl_##NAME##_params, pl_##NAME##_default_params, FIELD, LIMIT)
    1142                 :            : 
    1143                 :          4 :     image.crop.x1 = width / 2.0;
    1144                 :          4 :     image.crop.y1 = height / 2.0;
    1145         [ +  + ]:        112 :     for (int i = 0; i < pl_num_scale_filters; i++) {
    1146                 :        108 :         struct pl_render_params params = pl_render_default_params;
    1147                 :        108 :         params.upscaler = pl_scale_filters[i].filter;
    1148                 :        108 :         printf("- testing `params.upscaler = /* %s */`\n", pl_scale_filters[i].name);
    1149         [ -  + ]:        108 :         REQUIRE(pl_render_image(rr, &image, &target, &params));
    1150                 :        108 :         pl_gpu_flush(gpu);
    1151         [ -  + ]:        108 :         REQUIRE(pl_renderer_get_errors(rr).errors == PL_RENDER_ERR_NONE);
    1152                 :            :     }
    1153                 :          4 :     image.crop.x1 = image.crop.y1 = 0;
    1154                 :            : 
    1155                 :          4 :     target.crop.x1 = width / 2.0;
    1156                 :          4 :     target.crop.y1 = height / 2.0;
    1157         [ +  + ]:        112 :     for (int i = 0; i < pl_num_scale_filters; i++) {
    1158                 :        108 :         struct pl_render_params params = pl_render_default_params;
    1159                 :        108 :         params.downscaler = pl_scale_filters[i].filter;
    1160                 :        108 :         printf("- testing `params.downscaler = /* %s */`\n", pl_scale_filters[i].name);
    1161         [ -  + ]:        108 :         REQUIRE(pl_render_image(rr, &image, &target, &params));
    1162                 :        108 :         pl_gpu_flush(gpu);
    1163         [ -  + ]:        108 :         REQUIRE(pl_renderer_get_errors(rr).errors == PL_RENDER_ERR_NONE);
    1164                 :            :     }
    1165                 :          4 :     target.crop.x1 = target.crop.y1 = 0;
    1166                 :            : 
    1167   [ -  +  -  +  :         20 :     TEST_PARAMS(deband, iterations, 3);
                   +  + ]
    1168   [ -  +  -  +  :         12 :     TEST_PARAMS(sigmoid, center, 1);
                   +  + ]
    1169   [ -  +  -  +  :         20 :     TEST_PARAMS(color_map, intent, PL_INTENT_ABSOLUTE_COLORIMETRIC);
                   +  + ]
    1170   [ -  +  -  +  :         20 :     TEST_PARAMS(dither, method, PL_DITHER_WHITE_NOISE);
                   +  + ]
    1171   [ -  +  -  +  :         12 :     TEST_PARAMS(dither, temporal, true);
                   +  + ]
    1172   [ -  +  -  +  :         12 :     TEST_PARAMS(distort, alpha_mode, PL_ALPHA_INDEPENDENT);
                   +  + ]
    1173   [ -  +  -  +  :         12 :     TEST_PARAMS(distort, constrain, true);
                   +  + ]
    1174   [ -  +  -  +  :         12 :     TEST_PARAMS(distort, bicubic, true);
                   +  + ]
    1175   [ -  +  -  +  :         12 :     TEST(cone_params, pl_cone_params, pl_vision_deuteranomaly, strength, 0);
                   +  + ]
    1176                 :            : 
    1177                 :            :     // Test gamma-correct dithering
    1178                 :          4 :     target.repr.bits.color_depth = 2;
    1179   [ -  +  -  +  :         32 :     TEST_PARAMS(dither, transfer, PL_COLOR_TRC_GAMMA22);
                   +  + ]
    1180                 :          4 :     target.repr.bits.color_depth = 32;
    1181                 :            : 
    1182                 :            :     // Test HDR tone mapping
    1183                 :          4 :     image.color = pl_color_space_hdr10;
    1184   [ -  +  -  +  :         12 :     TEST_PARAMS(color_map, visualize_lut, true);
                   +  + ]
    1185         [ +  + ]:          4 :     if (gpu->limits.max_ssbo_size)
    1186   [ -  +  -  +  :          9 :         TEST_PARAMS(peak_detect, allow_delayed, true);
                   +  + ]
    1187                 :            : 
    1188                 :            :     // Test inverse tone-mapping and pure BPC
    1189                 :          4 :     image.color.hdr.max_luma = 1000;
    1190                 :          4 :     target.color.hdr.max_luma = 4000;
    1191                 :          4 :     target.color.hdr.min_luma = 0.02;
    1192   [ -  +  -  +  :         12 :     TEST_PARAMS(color_map, inverse_tone_mapping, true);
                   +  + ]
    1193                 :            : 
    1194                 :          4 :     image.color = pl_color_space_srgb;
    1195                 :          4 :     target.color = pl_color_space_srgb;
    1196                 :            : 
    1197                 :            :     // Test some misc stuff
    1198                 :          4 :     struct pl_render_params params = pl_render_default_params;
    1199                 :          4 :     params.color_adjustment = &(struct pl_color_adjustment) {
    1200                 :            :         .brightness = 0.1,
    1201                 :            :         .contrast = 0.9,
    1202                 :            :         .saturation = 1.5,
    1203                 :            :         .gamma = 0.8,
    1204                 :            :         .temperature = 0.3,
    1205                 :            :     };
    1206         [ -  + ]:          4 :     REQUIRE(pl_render_image(rr, &image, &target, &params));
    1207         [ -  + ]:          4 :     REQUIRE(pl_renderer_get_errors(rr).errors == PL_RENDER_ERR_NONE);
    1208                 :          4 :     params = pl_render_default_params;
    1209                 :            : 
    1210                 :          4 :     struct pl_frame inferred_image = image, inferred_target = target;
    1211                 :          4 :     pl_frames_infer(rr, &inferred_image, &inferred_target);
    1212         [ -  + ]:          4 :     REQUIRE(pl_render_image(rr, &inferred_image, &inferred_target, &params));
    1213         [ -  + ]:          4 :     REQUIRE(pl_renderer_get_errors(rr).errors == PL_RENDER_ERR_NONE);
    1214                 :            : 
    1215                 :            :     // Test background blending and alpha transparency
    1216                 :          4 :     params.blend_against_tiles = true;
    1217                 :          4 :     params.corner_rounding = 0.25f;
    1218         [ -  + ]:          4 :     REQUIRE(pl_render_image(rr, &image, &target, &params));
    1219         [ -  + ]:          4 :     REQUIRE(pl_renderer_get_errors(rr).errors == PL_RENDER_ERR_NONE);
    1220                 :          4 :     params = pl_render_default_params;
    1221                 :            : 
    1222                 :            :     // Test film grain synthesis
    1223                 :          4 :     image.film_grain.type = PL_FILM_GRAIN_AV1;
    1224                 :          4 :     image.film_grain.params.av1 = av1_grain_data;
    1225         [ -  + ]:          4 :     REQUIRE(pl_render_image(rr, &image, &target, &params));
    1226         [ -  + ]:          4 :     REQUIRE(pl_renderer_get_errors(rr).errors == PL_RENDER_ERR_NONE);
    1227                 :            : 
    1228                 :          4 :     image.film_grain.type = PL_FILM_GRAIN_H274;
    1229                 :          4 :     image.film_grain.params.h274 = h274_grain_data;
    1230         [ -  + ]:          4 :     REQUIRE(pl_render_image(rr, &image, &target, &params));
    1231                 :            :     // H.274 film grain synthesis requires compute shaders
    1232         [ +  + ]:          4 :     if (gpu->glsl.compute) {
    1233         [ -  + ]:          2 :         REQUIRE(pl_renderer_get_errors(rr).errors == PL_RENDER_ERR_NONE);
    1234                 :            :     } else {
    1235                 :          2 :         const struct pl_render_errors rr_err = pl_renderer_get_errors(rr);
    1236         [ -  + ]:          2 :         REQUIRE(pl_renderer_get_errors(rr).errors == PL_RENDER_ERR_FILM_GRAIN);
    1237                 :          2 :         pl_renderer_reset_errors(rr, &rr_err);
    1238                 :            :     }
    1239                 :          4 :     image.film_grain = (struct pl_film_grain_data) {0};
    1240                 :            : 
    1241                 :            :     // Test mpv-style custom shaders
    1242         [ +  + ]:         12 :     for (int i = 0; i < PL_ARRAY_SIZE(user_shader_tests); i++) {
    1243                 :          8 :         printf("- testing user shader:\n\n%s\n", user_shader_tests[i]);
    1244                 :            :         const struct pl_hook *hook;
    1245                 :          8 :         hook = pl_mpv_user_shader_parse(gpu, user_shader_tests[i],
    1246                 :            :                                         strlen(user_shader_tests[i]));
    1247         [ -  + ]:          8 :         REQUIRE(hook);
    1248                 :            : 
    1249                 :          8 :         params.hooks = &hook;
    1250                 :          8 :         params.num_hooks = 1;
    1251         [ -  + ]:          8 :         REQUIRE(pl_render_image(rr, &image, &target, &params));
    1252         [ -  + ]:          8 :         REQUIRE(pl_renderer_get_errors(rr).errors == PL_RENDER_ERR_NONE);
    1253                 :            : 
    1254                 :          8 :         pl_mpv_user_shader_destroy(&hook);
    1255                 :            :     }
    1256                 :            : 
    1257   [ +  +  +  - ]:          4 :     if (gpu->glsl.compute && gpu->limits.max_ssbo_size) {
    1258         [ +  + ]:          4 :         for (int i = 0; i < PL_ARRAY_SIZE(compute_shader_tests); i++) {
    1259                 :          2 :             printf("- testing user shader:\n\n%s\n", compute_shader_tests[i]);
    1260                 :            :             const struct pl_hook *hook;
    1261                 :          2 :             hook = pl_mpv_user_shader_parse(gpu, compute_shader_tests[i],
    1262                 :            :                                             strlen(compute_shader_tests[i]));
    1263         [ -  + ]:          2 :             REQUIRE(hook);
    1264                 :            : 
    1265                 :          2 :             params.hooks = &hook;
    1266                 :          2 :             params.num_hooks = 1;
    1267         [ -  + ]:          2 :             REQUIRE(pl_render_image(rr, &image, &target, &params));
    1268         [ -  + ]:          2 :             REQUIRE(pl_renderer_get_errors(rr).errors == PL_RENDER_ERR_NONE);
    1269                 :            : 
    1270                 :          2 :             pl_mpv_user_shader_destroy(&hook);
    1271                 :            :         }
    1272                 :            :     }
    1273                 :          4 :     params = pl_render_default_params;
    1274                 :            : 
    1275                 :            :     // Test custom LUTs
    1276         [ +  + ]:         12 :     for (int i = 0; i < PL_ARRAY_SIZE(test_luts); i++) {
    1277                 :            :         printf("- testing custom lut %d\n", i);
    1278                 :            :         struct pl_custom_lut *lut;
    1279                 :          8 :         lut = pl_lut_parse_cube(gpu->log, test_luts[i], strlen(test_luts[i]));
    1280         [ -  + ]:          8 :         REQUIRE(lut);
    1281                 :            : 
    1282   [ +  -  -  + ]:          8 :         bool has_3dlut = gpu->limits.max_tex_3d_dim && gpu->glsl.version > 100;
    1283   [ +  +  -  + ]:          8 :         if (lut->size[2] && !has_3dlut) {
    1284                 :          0 :             pl_lut_free(&lut);
    1285                 :          0 :             continue;
    1286                 :            :         }
    1287                 :            : 
    1288                 :            :         // Test all three at the same time to reduce the number of tests
    1289                 :          8 :         image.lut = target.lut = params.lut = lut;
    1290                 :            : 
    1291         [ +  + ]:         40 :         for (enum pl_lut_type t = PL_LUT_UNKNOWN; t <= PL_LUT_CONVERSION; t++) {
    1292                 :            :             printf("- testing LUT method %d\n", t);
    1293                 :         32 :             image.lut_type = target.lut_type = params.lut_type = t;
    1294         [ -  + ]:         32 :             REQUIRE(pl_render_image(rr, &image, &target, &params));
    1295         [ -  + ]:         32 :             REQUIRE(pl_renderer_get_errors(rr).errors == PL_RENDER_ERR_NONE);
    1296                 :            :         }
    1297                 :            : 
    1298                 :          8 :         image.lut = target.lut = params.lut = NULL;
    1299                 :          8 :         pl_lut_free(&lut);
    1300                 :            :     }
    1301                 :            : 
    1302                 :            : #ifdef PL_HAVE_LCMS
    1303                 :            : 
    1304                 :            :     // It doesn't fit without use of 3D textures on GLES2
    1305         [ +  - ]:          4 :     if (gpu->glsl.version > 100) {
    1306                 :            :         // Test ICC profiles
    1307                 :          4 :         image.profile = TEST_PROFILE(sRGB_v2_nano_icc);
    1308         [ -  + ]:          4 :         REQUIRE(pl_render_image(rr, &image, &target, &params));
    1309         [ -  + ]:          4 :         REQUIRE(pl_renderer_get_errors(rr).errors == PL_RENDER_ERR_NONE);
    1310                 :          4 :         image.profile = (struct pl_icc_profile) {0};
    1311                 :            : 
    1312                 :          4 :         target.profile = TEST_PROFILE(sRGB_v2_nano_icc);
    1313         [ -  + ]:          4 :         REQUIRE(pl_render_image(rr, &image, &target, &params));
    1314         [ -  + ]:          4 :         REQUIRE(pl_renderer_get_errors(rr).errors == PL_RENDER_ERR_NONE);
    1315                 :            :         target.profile = (struct pl_icc_profile) {0};
    1316                 :            : 
    1317                 :          4 :         image.profile = TEST_PROFILE(sRGB_v2_nano_icc);
    1318                 :          4 :         target.profile = image.profile;
    1319         [ -  + ]:          4 :         REQUIRE(pl_render_image(rr, &image, &target, &params));
    1320         [ -  + ]:          4 :         REQUIRE(pl_renderer_get_errors(rr).errors == PL_RENDER_ERR_NONE);
    1321                 :          4 :         image.profile = (struct pl_icc_profile) {0};
    1322                 :          4 :         target.profile = (struct pl_icc_profile) {0};
    1323                 :            :     }
    1324                 :            : 
    1325                 :            : #endif
    1326                 :            : 
    1327                 :            :     // Test overlays
    1328                 :          4 :     image.num_overlays = 1;
    1329                 :          4 :     image.overlays = &(struct pl_overlay) {
    1330                 :          4 :         .tex = img_plane.texture,
    1331                 :            :         .mode = PL_OVERLAY_NORMAL,
    1332                 :            :         .num_parts = 2,
    1333                 :          4 :         .parts = (struct pl_overlay_part[]) {{
    1334                 :            :             .src = {0, 0, 2, 2},
    1335                 :            :             .dst = {30, 100, 40, 200},
    1336                 :            :         }, {
    1337                 :            :             .src = {2, 2, 5, 5},
    1338                 :            :             .dst = {1000, -1, 3, 5},
    1339                 :            :         }},
    1340                 :            :     };
    1341         [ -  + ]:          4 :     REQUIRE(pl_render_image(rr, &image, &target, &params));
    1342         [ -  + ]:          4 :     REQUIRE(pl_renderer_get_errors(rr).errors == PL_RENDER_ERR_NONE);
    1343                 :          4 :     params.disable_fbos = true;
    1344         [ -  + ]:          4 :     REQUIRE(pl_render_image(rr, &image, &target, &params));
    1345         [ -  + ]:          4 :     REQUIRE(pl_renderer_get_errors(rr).errors == PL_RENDER_ERR_NONE);
    1346                 :          4 :     image.num_overlays = 0;
    1347                 :          4 :     params = pl_render_default_params;
    1348                 :            : 
    1349                 :          4 :     target.num_overlays = 1;
    1350                 :          4 :     target.overlays = &(struct pl_overlay) {
    1351                 :          4 :         .tex = img_plane.texture,
    1352                 :            :         .mode = PL_OVERLAY_MONOCHROME,
    1353                 :            :         .num_parts = 1,
    1354                 :          4 :         .parts = &(struct pl_overlay_part) {
    1355                 :            :             .src = {5, 5, 15, 15},
    1356                 :            :             .dst = {5, 5, 15, 15},
    1357                 :            :             .color = {1.0, 0.5, 0.0},
    1358                 :            :         },
    1359                 :            :     };
    1360         [ -  + ]:          4 :     REQUIRE(pl_render_image(rr, &image, &target, &params));
    1361         [ -  + ]:          4 :     REQUIRE(pl_renderer_get_errors(rr).errors == PL_RENDER_ERR_NONE);
    1362         [ -  + ]:          4 :     REQUIRE(pl_render_image(rr, NULL, &target, &params));
    1363         [ -  + ]:          4 :     REQUIRE(pl_renderer_get_errors(rr).errors == PL_RENDER_ERR_NONE);
    1364                 :          4 :     target.num_overlays = 0;
    1365                 :            : 
    1366                 :            :     // Test rotation
    1367         [ +  + ]:         20 :     for (pl_rotation rot = 0; rot < PL_ROTATION_360; rot += PL_ROTATION_90) {
    1368                 :         16 :         image.rotation = rot;
    1369         [ -  + ]:         16 :         REQUIRE(pl_render_image(rr, &image, &target, &params));
    1370         [ -  + ]:         16 :         REQUIRE(pl_renderer_get_errors(rr).errors == PL_RENDER_ERR_NONE);
    1371                 :            :     }
    1372                 :            : 
    1373                 :            :     // Attempt frame mixing, using the mixer queue helper
    1374                 :            :     printf("- testing frame mixing\n");
    1375                 :          4 :     struct pl_render_params mix_params = {
    1376                 :            :         .frame_mixer = &pl_filter_mitchell_clamp,
    1377                 :            :         .info_callback = render_info_cb,
    1378                 :            :     };
    1379                 :            : 
    1380         [ +  - ]:          4 :     struct pl_queue_params qparams = {
    1381                 :            :         .radius = pl_frame_mix_radius(&mix_params),
    1382                 :            :         .vsync_duration = 1.0 / 60.0,
    1383                 :            :     };
    1384                 :            : 
    1385                 :            :     // Test large PTS jumps in frame mix
    1386                 :          4 :     struct pl_frame_mix mix = (struct pl_frame_mix) {
    1387                 :            :         .num_frames = 2,
    1388                 :          4 :         .frames = (const struct pl_frame *[]) { &image, &image },
    1389                 :          4 :         .signatures = (uint64_t[]) { 0xFFF1, 0xFFF2 },
    1390                 :          4 :         .timestamps = (float[]) { -100, 100 },
    1391                 :            :         .vsync_duration = 1.6,
    1392                 :            :     };
    1393         [ -  + ]:          4 :     REQUIRE(pl_render_image_mix(rr, &mix, &target, &mix_params));
    1394                 :            : 
    1395                 :            :     // Test inferring frame mix
    1396                 :          4 :     inferred_target = target;
    1397                 :          4 :     pl_frames_infer_mix(rr, &mix, &inferred_target, &inferred_image);
    1398         [ -  + ]:          4 :     REQUIRE(pl_render_image_mix(rr, &mix, &target, &mix_params));
    1399                 :            : 
    1400                 :            :     // Test empty frame mix
    1401                 :          4 :     mix = (struct pl_frame_mix) {0};
    1402         [ -  + ]:          4 :     REQUIRE(pl_render_image_mix(rr, &mix, &target, &mix_params));
    1403                 :            : 
    1404                 :            :     // Test inferring empty frame mix
    1405                 :          4 :     inferred_target = target;
    1406                 :          4 :     pl_frames_infer_mix(rr, &mix, &inferred_target, &inferred_image);
    1407         [ -  + ]:          4 :     REQUIRE(pl_render_image_mix(rr, &mix, &target, &mix_params));
    1408                 :            : 
    1409                 :            :     // Test mixer queue
    1410                 :            : #define NUM_MIX_FRAMES 20
    1411                 :            :     const float frame_duration = 1.0 / 24.0;
    1412                 :            :     struct pl_source_frame srcframes[NUM_MIX_FRAMES+1];
    1413                 :          4 :     srcframes[NUM_MIX_FRAMES] = (struct pl_source_frame) {0};
    1414         [ +  + ]:         84 :     for (int i = 0; i < NUM_MIX_FRAMES; i++) {
    1415                 :         80 :         srcframes[i] = (struct pl_source_frame) {
    1416                 :         80 :             .pts = i * frame_duration,
    1417                 :            :             .duration = frame_duration,
    1418                 :            :             .map = frame_passthrough,
    1419                 :            :             .frame_data = &image,
    1420                 :            :         };
    1421                 :            :     }
    1422                 :            : 
    1423                 :          4 :     pl_queue queue = pl_queue_create(gpu);
    1424                 :            :     enum pl_queue_status ret;
    1425                 :            : 
    1426                 :            :     // Test pre-pushing all frames, with delayed EOF.
    1427         [ +  + ]:         84 :     for (int i = 0; i < NUM_MIX_FRAMES; i++) {
    1428                 :         80 :         const struct pl_source_frame *src = &srcframes[i];
    1429         [ +  + ]:         80 :         if (i > 10) // test pushing in reverse order
    1430                 :         36 :             src = &srcframes[NUM_MIX_FRAMES + 10 - i];
    1431         [ +  + ]:         80 :         if (!pl_queue_push_block(queue, 1, src)) // mini-sleep
    1432                 :         72 :             pl_queue_push(queue, src); // push it anyway, for testing
    1433                 :            :     }
    1434                 :            : 
    1435         [ +  + ]:        208 :     while ((ret = pl_queue_update(queue, &mix, &qparams)) != PL_QUEUE_EOF) {
    1436         [ +  + ]:        204 :         if (ret == PL_QUEUE_MORE) {
    1437         [ -  + ]:          4 :             REQUIRE_CMP(qparams.pts, >, 0.0f, "f");
    1438                 :          4 :             pl_queue_push(queue, NULL); // push delayed EOF
    1439                 :          4 :             continue;
    1440                 :            :         }
    1441                 :            : 
    1442         [ -  + ]:        200 :         REQUIRE_CMP(ret, ==, PL_QUEUE_OK, "u");
    1443         [ -  + ]:        200 :         REQUIRE(pl_render_image_mix(rr, &mix, &target, &mix_params));
    1444                 :            : 
    1445                 :            :         // Simulate advancing vsync
    1446                 :        200 :         qparams.pts += qparams.vsync_duration;
    1447                 :            :     }
    1448                 :            : 
    1449                 :            :     // Test dynamically pulling all frames, with oversample mixer
    1450                 :          4 :     const struct pl_source_frame *frame_ptr = &srcframes[0];
    1451         [ +  - ]:          4 :     mix_params.frame_mixer = &pl_oversample_frame_mixer;
    1452                 :            : 
    1453                 :          4 :     qparams = (struct pl_queue_params) {
    1454                 :            :         .radius = pl_frame_mix_radius(&mix_params),
    1455                 :          4 :         .vsync_duration = qparams.vsync_duration,
    1456                 :            :         .get_frame = get_frame_ptr,
    1457                 :            :         .priv = &frame_ptr,
    1458                 :            :     };
    1459                 :            : 
    1460                 :          4 :     pl_queue_reset(queue);
    1461         [ +  + ]:        204 :     while ((ret = pl_queue_update(queue, &mix, &qparams)) != PL_QUEUE_EOF) {
    1462         [ -  + ]:        200 :         REQUIRE_CMP(ret, ==, PL_QUEUE_OK, "u");
    1463         [ -  + ]:        200 :         REQUIRE_CMP(mix.num_frames, <=, 2, "d");
    1464         [ -  + ]:        200 :         REQUIRE(pl_render_image_mix(rr, &mix, &target, &mix_params));
    1465                 :        200 :         qparams.pts += qparams.vsync_duration;
    1466                 :            :     }
    1467                 :            : 
    1468                 :            :     // Test large PTS jump
    1469                 :          4 :     pl_queue_reset(queue);
    1470         [ -  + ]:          4 :     REQUIRE(pl_queue_update(queue, &mix, &qparams) == PL_QUEUE_EOF);
    1471                 :            : 
    1472                 :            :     // Test deinterlacing
    1473                 :          4 :     pl_queue_reset(queue);
    1474                 :            :     printf("- testing deinterlacing\n");
    1475         [ +  + ]:         84 :     for (int i = 0; i < NUM_MIX_FRAMES; i++) {
    1476                 :         80 :         struct pl_source_frame *src = &srcframes[i];
    1477         [ +  + ]:         80 :         if (i > 10)
    1478                 :         36 :             src = &srcframes[NUM_MIX_FRAMES + 10 - i];
    1479                 :         80 :         src->first_field = PL_FIELD_EVEN;
    1480                 :         80 :         pl_queue_push(queue, src);
    1481                 :            :     }
    1482                 :          4 :     pl_queue_push(queue, NULL);
    1483                 :            : 
    1484                 :          4 :     qparams.pts = 0;
    1485                 :          4 :     qparams.get_frame = NULL;
    1486         [ +  + ]:        204 :     while ((ret = pl_queue_update(queue, &mix, &qparams)) != PL_QUEUE_EOF) {
    1487         [ -  + ]:        200 :         REQUIRE_CMP(ret, ==, PL_QUEUE_OK, "u");
    1488         [ -  + ]:        200 :         REQUIRE(pl_render_image_mix(rr, &mix, &target, &mix_params));
    1489                 :        200 :         qparams.pts += qparams.vsync_duration;
    1490                 :            :     }
    1491                 :            : 
    1492                 :          4 :     pl_queue_destroy(&queue);
    1493                 :            : 
    1494                 :          4 : error:
    1495                 :          4 :     pl_renderer_destroy(&rr);
    1496                 :          4 :     pl_tex_destroy(gpu, &img_tex);
    1497                 :          4 :     pl_tex_destroy(gpu, &fbo);
    1498                 :            : }
    1499                 :            : 
    1500                 :          5 : static struct pl_hook_res noop_hook(void *priv, const struct pl_hook_params *params)
    1501                 :            : {
    1502                 :          5 :     return (struct pl_hook_res) {0};
    1503                 :            : }
    1504                 :            : 
    1505                 :          5 : static void pl_ycbcr_tests(pl_gpu gpu)
    1506                 :            : {
    1507                 :            :     struct pl_plane_data data[3];
    1508         [ +  + ]:         20 :     for (int i = 0; i < 3; i++) {
    1509                 :         15 :         const int sub = i > 0 ? 1 : 0;
    1510                 :         15 :         const int width = (323 + sub) >> sub;
    1511                 :         15 :         const int height = (255 + sub) >> sub;
    1512                 :            : 
    1513                 :         15 :         data[i] = (struct pl_plane_data) {
    1514                 :            :             .type = PL_FMT_UNORM,
    1515                 :            :             .width = width,
    1516                 :            :             .height = height,
    1517                 :            :             .component_size = {16},
    1518                 :            :             .component_map = {i},
    1519                 :            :             .pixel_stride = sizeof(uint16_t),
    1520                 :         15 :             .row_stride = PL_ALIGN2(width * sizeof(uint16_t),
    1521                 :            :                                     gpu->limits.align_tex_xfer_pitch),
    1522                 :            :         };
    1523                 :            :     }
    1524                 :            : 
    1525                 :          5 :     pl_fmt fmt = pl_plane_find_fmt(gpu, NULL, &data[0]);
    1526                 :            :     enum pl_fmt_caps caps = PL_FMT_CAP_RENDERABLE | PL_FMT_CAP_HOST_READABLE;
    1527   [ +  -  +  - ]:          5 :     if (!fmt || (fmt->caps & caps) != caps)
    1528                 :          0 :         return;
    1529                 :            : 
    1530                 :          5 :     printf("pl_ycbcr_tests:\nfmt=%s\n", fmt->name);
    1531                 :            : 
    1532                 :          5 :     pl_renderer rr = pl_renderer_create(gpu->log, gpu);
    1533         [ +  - ]:          5 :     if (!rr)
    1534                 :            :         return;
    1535                 :            : 
    1536                 :          5 :     pl_tex src_tex[3] = {0};
    1537                 :          5 :     pl_tex dst_tex[3] = {0};
    1538                 :          5 :     struct pl_frame img = {
    1539                 :            :         .num_planes = 3,
    1540                 :            :         .repr = pl_color_repr_hdtv,
    1541                 :            :         .color = pl_color_space_bt709,
    1542                 :            :     };
    1543                 :            : 
    1544                 :          5 :     struct pl_frame target = {
    1545                 :            :         .num_planes = 3,
    1546                 :            :         .repr = pl_color_repr_hdtv,
    1547                 :            :         .color = pl_color_space_bt709,
    1548                 :            :     };
    1549                 :            : 
    1550                 :          5 :     uint8_t *src_buffer[3] = {0};
    1551                 :            :     uint8_t *dst_buffer = NULL;
    1552         [ +  + ]:         20 :     for (int i = 0; i < 3; i++) {
    1553                 :            :         // Generate some arbitrary data for the buffer
    1554                 :         15 :         src_buffer[i] = malloc(data[i].height * data[i].row_stride);
    1555         [ -  + ]:         15 :         if (!src_buffer[i])
    1556                 :          0 :             goto error;
    1557                 :            : 
    1558                 :         15 :         data[i].pixels = src_buffer[i];
    1559         [ +  + ]:       2570 :         for (int y = 0; y < data[i].height; y++) {
    1560         [ +  + ]:     621740 :             for (int x = 0; x < data[i].width; x++) {
    1561                 :     619185 :                 size_t off = y * data[i].row_stride + x * data[i].pixel_stride;
    1562                 :     619185 :                 uint16_t *pixel = (uint16_t *) &src_buffer[i][off];
    1563                 :     619185 :                 int gx = 200 + 100 * i, gy = 300 + 150 * i;
    1564                 :     619185 :                 *pixel = (gx * x) ^ (gy * y); // whatever
    1565                 :            :             }
    1566                 :            :         }
    1567                 :            : 
    1568         [ -  + ]:         15 :         REQUIRE(pl_upload_plane(gpu, &img.planes[i], &src_tex[i], &data[i]));
    1569                 :            :     }
    1570                 :            : 
    1571                 :            :     // This co-sites chroma pixels with pixels in the RGB image, meaning we
    1572                 :            :     // get an exact round-trip when sampling both ways. This makes it useful
    1573                 :            :     // as a test case, even though it's not common in the real world.
    1574                 :          5 :     pl_frame_set_chroma_location(&img, PL_CHROMA_TOP_LEFT);
    1575                 :            : 
    1576         [ +  + ]:         20 :     for (int i = 0; i < 3; i++) {
    1577                 :         30 :         dst_tex[i] = pl_tex_create(gpu, &(struct pl_tex_params) {
    1578                 :            :             .format = fmt,
    1579                 :         15 :             .w = data[i].width,
    1580                 :         15 :             .h = data[i].height,
    1581                 :            :             .renderable = true,
    1582                 :            :             .host_readable = true,
    1583                 :         15 :             .storable = fmt->caps & PL_FMT_CAP_STORABLE,
    1584                 :         15 :             .blit_dst = fmt->caps & PL_FMT_CAP_BLITTABLE,
    1585                 :            :         });
    1586                 :            : 
    1587         [ -  + ]:         15 :         if (!dst_tex[i])
    1588                 :          0 :             goto error;
    1589                 :            : 
    1590                 :         15 :         target.planes[i] = img.planes[i];
    1591                 :         15 :         target.planes[i].texture = dst_tex[i];
    1592                 :            :     }
    1593                 :            : 
    1594         [ -  + ]:          5 :     REQUIRE(pl_render_image(rr, &img, &target, &(struct pl_render_params) {
    1595                 :            :         .num_hooks = 1,
    1596                 :            :         .hooks = &(const struct pl_hook *){&(struct pl_hook) {
    1597                 :            :             // Forces chroma merging, to test the chroma merging code
    1598                 :            :             .stages = PL_HOOK_CHROMA_INPUT,
    1599                 :            :             .hook = noop_hook,
    1600                 :            :         }},
    1601                 :            :     }));
    1602         [ -  + ]:          5 :     REQUIRE(pl_renderer_get_errors(rr).errors == PL_RENDER_ERR_NONE);
    1603                 :            : 
    1604                 :          5 :     size_t buf_size = data[0].height * data[0].row_stride;
    1605                 :          5 :     dst_buffer = malloc(buf_size);
    1606         [ -  + ]:          5 :     if (!dst_buffer)
    1607                 :          0 :         goto error;
    1608                 :            : 
    1609         [ +  + ]:         20 :     for (int i = 0; i < 3; i++) {
    1610                 :            :         memset(dst_buffer, 0xAA, buf_size);
    1611         [ -  + ]:         15 :         REQUIRE(pl_tex_download(gpu, &(struct pl_tex_transfer_params) {
    1612                 :            :             .tex = dst_tex[i],
    1613                 :            :             .ptr = dst_buffer,
    1614                 :            :             .row_pitch = data[i].row_stride,
    1615                 :            :         }));
    1616                 :            : 
    1617         [ +  + ]:       2570 :         for (int y = 0; y < data[i].height; y++) {
    1618         [ +  + ]:     621740 :             for (int x = 0; x < data[i].width; x++) {
    1619                 :     619185 :                 size_t off = y * data[i].row_stride + x * data[i].pixel_stride;
    1620                 :     619185 :                 uint16_t *src_pixel = (uint16_t *) &src_buffer[i][off];
    1621                 :     619185 :                 uint16_t *dst_pixel = (uint16_t *) &dst_buffer[off];
    1622                 :     619185 :                 int diff = abs((int) *src_pixel - (int) *dst_pixel);
    1623         [ -  + ]:     619185 :                 REQUIRE_CMP(diff, <=, 150, "d"); // a little over 0.2%
    1624                 :            :             }
    1625                 :            :         }
    1626                 :            :     }
    1627                 :            : 
    1628                 :          5 : error:
    1629                 :          5 :     pl_renderer_destroy(&rr);
    1630                 :          5 :     free(dst_buffer);
    1631         [ +  + ]:         20 :     for (int i = 0; i < 3; i++) {
    1632                 :         15 :         free(src_buffer[i]);
    1633                 :         15 :         pl_tex_destroy(gpu, &src_tex[i]);
    1634                 :         15 :         pl_tex_destroy(gpu, &dst_tex[i]);
    1635                 :            :     }
    1636                 :            : }
    1637                 :            : 
    1638                 :          6 : static void pl_test_export_import(pl_gpu gpu,
    1639                 :            :                                   enum pl_handle_type handle_type)
    1640                 :            : {
    1641                 :            :     printf("pl_test_export_import:\n");
    1642                 :            :     // Test texture roundtrip
    1643                 :            : 
    1644         [ +  - ]:          6 :     if (!(gpu->export_caps.tex & handle_type) ||
    1645         [ -  + ]:          6 :         !(gpu->import_caps.tex & handle_type))
    1646                 :          0 :         goto skip_tex;
    1647                 :            : 
    1648                 :          6 :     pl_fmt fmt = pl_find_fmt(gpu, PL_FMT_UNORM, 4, 0, 0, PL_FMT_CAP_BLITTABLE);
    1649         [ -  + ]:          6 :     if (!fmt)
    1650                 :          0 :         goto skip_tex;
    1651                 :            : 
    1652                 :          6 :     printf("- testing texture import/export with fmt %s\n", fmt->name);
    1653                 :            : 
    1654                 :          6 :     pl_tex export = pl_tex_create(gpu, &(struct pl_tex_params) {
    1655                 :            :         .w = 32,
    1656                 :            :         .h = 32,
    1657                 :            :         .format = fmt,
    1658                 :            :         .export_handle = handle_type,
    1659                 :            :     });
    1660         [ -  + ]:          6 :     REQUIRE(export);
    1661   [ +  -  -  -  :          6 :     REQUIRE_HANDLE(export->shared_mem, handle_type);
          -  -  +  -  -  
             -  -  -  - ]
    1662                 :            : 
    1663                 :         12 :     pl_tex import = pl_tex_create(gpu, &(struct pl_tex_params) {
    1664                 :          6 :         .w = export->params.w,
    1665                 :          6 :         .h = export->params.h,
    1666                 :            :         .format = fmt,
    1667                 :            :         .import_handle = handle_type,
    1668                 :            :         .shared_mem = export->shared_mem,
    1669                 :            :     });
    1670         [ -  + ]:          6 :     REQUIRE(import);
    1671                 :            : 
    1672                 :          6 :     pl_tex_destroy(gpu, &import);
    1673                 :          6 :     pl_tex_destroy(gpu, &export);
    1674                 :            : 
    1675                 :          6 : skip_tex: ;
    1676                 :            : 
    1677                 :            :     // Test buffer roundtrip
    1678                 :            : 
    1679         [ +  + ]:          6 :     if (!(gpu->export_caps.buf & handle_type) ||
    1680         [ -  + ]:          2 :         !(gpu->import_caps.buf & handle_type))
    1681                 :          4 :         return;
    1682                 :            : 
    1683                 :            :     printf("- testing buffer import/export\n");
    1684                 :            : 
    1685                 :          2 :     pl_buf exp_buf = pl_buf_create(gpu, &(struct pl_buf_params) {
    1686                 :            :         .size = 32,
    1687                 :            :         .export_handle = handle_type,
    1688                 :            :     });
    1689         [ -  + ]:          2 :     REQUIRE(exp_buf);
    1690   [ +  -  -  -  :          2 :     REQUIRE_HANDLE(exp_buf->shared_mem, handle_type);
          -  -  +  -  -  
             -  -  -  - ]
    1691                 :            : 
    1692                 :          2 :     pl_buf imp_buf = pl_buf_create(gpu, &(struct pl_buf_params) {
    1693                 :            :         .size = 32,
    1694                 :            :         .import_handle = handle_type,
    1695                 :            :         .shared_mem = exp_buf->shared_mem,
    1696                 :            :     });
    1697         [ -  + ]:          2 :     REQUIRE(imp_buf);
    1698                 :            : 
    1699                 :          2 :     pl_buf_destroy(gpu, &imp_buf);
    1700                 :          2 :     pl_buf_destroy(gpu, &exp_buf);
    1701                 :            : }
    1702                 :            : 
    1703                 :          6 : static void pl_test_host_ptr(pl_gpu gpu)
    1704                 :            : {
    1705         [ +  + ]:          6 :     if (!(gpu->import_caps.buf & PL_HANDLE_HOST_PTR))
    1706                 :          4 :         return;
    1707                 :            : 
    1708                 :            :     printf("pl_test_host_ptr:\n");
    1709                 :            : 
    1710                 :            : #ifdef __unix__
    1711                 :            : 
    1712                 :            :     printf("- testing host ptr\n");
    1713         [ -  + ]:          2 :     REQUIRE(gpu->limits.max_mapped_size);
    1714                 :            : 
    1715                 :            :     const size_t size = 2 << 20;
    1716                 :            :     const size_t offset = 2 << 10;
    1717                 :            :     const size_t slice = 2 << 16;
    1718                 :            : 
    1719                 :          2 :     uint8_t *data = aligned_alloc(0x1000, size);
    1720         [ +  + ]:    4194306 :     for (int i = 0; i < size; i++)
    1721                 :    4194304 :         data[i] = (uint8_t) i;
    1722                 :            : 
    1723                 :          2 :     pl_buf buf = pl_buf_create(gpu, &(struct pl_buf_params) {
    1724                 :            :         .size = slice,
    1725                 :            :         .import_handle = PL_HANDLE_HOST_PTR,
    1726                 :            :         .shared_mem = {
    1727                 :            :             .handle.ptr = data,
    1728                 :            :             .size = size,
    1729                 :            :             .offset = offset,
    1730                 :            :         },
    1731                 :            :         .host_mapped = true,
    1732                 :            :     });
    1733                 :            : 
    1734         [ -  + ]:          2 :     REQUIRE(buf);
    1735                 :          2 :     REQUIRE_MEMEQ(data + offset, buf->data, slice);
    1736                 :            : 
    1737                 :          2 :     pl_buf_destroy(gpu, &buf);
    1738                 :          2 :     free(data);
    1739                 :            : 
    1740                 :            : #endif // unix
    1741                 :            : }
    1742                 :            : 
    1743                 :          5 : void gpu_shader_tests(pl_gpu gpu)
    1744                 :            : {
    1745                 :          5 :     pl_buffer_tests(gpu);
    1746                 :          5 :     pl_texture_tests(gpu);
    1747                 :          5 :     pl_planar_tests(gpu);
    1748                 :          5 :     pl_shader_tests(gpu);
    1749                 :          5 :     pl_scaler_tests(gpu);
    1750                 :          5 :     pl_render_tests(gpu);
    1751                 :          5 :     pl_ycbcr_tests(gpu);
    1752                 :            : 
    1753         [ -  + ]:          5 :     REQUIRE(!pl_gpu_is_failed(gpu));
    1754                 :          5 : }
    1755                 :            : 
    1756                 :          6 : void gpu_interop_tests(pl_gpu gpu)
    1757                 :            : {
    1758                 :          6 :     pl_test_export_import(gpu, PL_HANDLE_DMA_BUF);
    1759                 :          6 :     pl_test_host_ptr(gpu);
    1760                 :            : 
    1761         [ -  + ]:          6 :     REQUIRE(!pl_gpu_is_failed(gpu));
    1762                 :          6 : }

Generated by: LCOV version 1.16