LCOV - code coverage report
Current view: top level - src/shaders - deinterlacing.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 18 64 28.1 %
Date: 2025-03-29 09:04:10 Functions: 1 1 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 6 55 10.9 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * This file is part of libplacebo, but also based on vf_yadif_cuda.cu:
       3                 :            :  * Copyright (C) 2018 Philip Langdale <philipl@overt.org>
       4                 :            :  *
       5                 :            :  * libplacebo is free software; you can redistribute it and/or
       6                 :            :  * modify it under the terms of the GNU Lesser General Public
       7                 :            :  * License as published by the Free Software Foundation; either
       8                 :            :  * version 2.1 of the License, or (at your option) any later version.
       9                 :            :  *
      10                 :            :  * libplacebo is distributed in the hope that it will be useful,
      11                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      12                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13                 :            :  * GNU Lesser General Public License for more details.
      14                 :            :  *
      15                 :            :  * You should have received a copy of the GNU Lesser General Public
      16                 :            :  * License along with libplacebo. If not, see <http://www.gnu.org/licenses/>.
      17                 :            :  */
      18                 :            : 
      19                 :            : #include "shaders.h"
      20                 :            : 
      21                 :            : #include <libplacebo/shaders/deinterlacing.h>
      22                 :            : 
      23                 :            : const struct pl_deinterlace_params pl_deinterlace_default_params = { PL_DEINTERLACE_DEFAULTS };
      24                 :            : 
      25                 :          2 : void pl_shader_deinterlace(pl_shader sh, const struct pl_deinterlace_source *src,
      26                 :            :                            const struct pl_deinterlace_params *params)
      27                 :            : {
      28         [ +  - ]:          2 :     params = PL_DEF(params, &pl_deinterlace_default_params);
      29                 :            : 
      30                 :          2 :     const struct pl_tex_params *texparams = &src->cur.top->params;
      31         [ +  - ]:          2 :     if (!sh_require(sh, PL_SHADER_SIG_NONE, texparams->w, texparams->h))
      32                 :          0 :         return;
      33                 :            : 
      34                 :          2 :     sh_describe(sh, "deinterlacing");
      35                 :          2 :     GLSL("vec4 color = vec4(0,0,0,1);   \n"
      36                 :            :          "// pl_shader_deinterlace      \n"
      37                 :            :          "{                             \n");
      38                 :            : 
      39         [ -  + ]:          2 :     uint8_t comp_mask = PL_DEF(src->component_mask, 0xFu);
      40                 :          2 :     comp_mask &= (1u << texparams->format->num_components) - 1u;
      41         [ -  + ]:          2 :     if (!comp_mask) {
      42                 :          0 :         SH_FAIL(sh, "pl_shader_deinterlace: empty component mask?");
      43                 :          0 :         return;
      44                 :            :     }
      45                 :            : 
      46                 :            :     const uint8_t num_comps = sh_num_comps(comp_mask);
      47                 :            :     const char *swiz = sh_swizzle(comp_mask);
      48                 :          2 :     GLSL("#define T %s \n", sh_float_type(comp_mask));
      49                 :            : 
      50                 :            :     ident_t pos, pt;
      51                 :          2 :     ident_t cur = sh_bind(sh, src->cur.top, PL_TEX_ADDRESS_MIRROR,
      52                 :            :                           PL_TEX_SAMPLE_NEAREST, "cur", NULL, &pos, &pt);
      53         [ +  - ]:          2 :     if (!cur)
      54                 :            :         return;
      55                 :            : 
      56                 :          2 :     GLSL("#define GET(TEX, X, Y)                              \\\n"
      57                 :            :          "    (textureLod(TEX, pos + pt * vec2(X, Y), 0.0).%s)  \n"
      58                 :            :          "vec2 pos = "$";                                       \n"
      59                 :            :          "vec2 pt  = "$";                                       \n"
      60                 :            :          "T res;                                                \n",
      61                 :            :          swiz, pos, pt);
      62                 :            : 
      63         [ +  - ]:          2 :     if (src->field == PL_FIELD_NONE) {
      64                 :          2 :         GLSL("res = GET("$", 0, 0); \n", cur);
      65                 :          2 :         goto done;
      66                 :            :     }
      67                 :            : 
      68                 :            :     // Don't modify the primary field
      69                 :          0 :     GLSL("int yh = textureSize("$", 0).y;   \n"
      70                 :            :          "int yo = int("$".y * float(yh));  \n"
      71                 :            :          "if (yo %% 2 == %d) {              \n"
      72                 :            :          "    res = GET("$", 0, 0);         \n"
      73                 :            :          "} else {                          \n",
      74                 :            :          cur, pos,
      75                 :            :          src->field == PL_FIELD_TOP ? 0 : 1,
      76                 :            :          cur);
      77                 :            : 
      78   [ #  #  #  #  :          0 :     switch (params->algo) {
                      # ]
      79                 :            :     case PL_DEINTERLACE_WEAVE:
      80                 :          0 :         GLSL("res = GET("$", 0, 0); \n", cur);
      81                 :          0 :         break;
      82                 :            : 
      83                 :            :     case PL_DEINTERLACE_BOB:
      84         [ #  # ]:          0 :         GLSL("res = GET("$", 0, %d); \n", cur,
      85                 :            :              src->field == PL_FIELD_TOP ? -1 : 1);
      86                 :          0 :         break;
      87                 :            : 
      88                 :            : 
      89                 :          0 :     case PL_DEINTERLACE_YADIF: {
      90                 :            :         // Try using a compute shader for this, for the sole reason of
      91                 :            :         // optimizing for thread group synchronicity. Otherwise, because we
      92                 :            :         // alternate between lines output as-is and lines output deinterlaced,
      93                 :            :         // half of our thread group will be mostly idle at any point in time.
      94         [ #  # ]:          0 :         const int bw = PL_DEF(sh_glsl(sh).subgroup_size, 32);
      95                 :          0 :         sh_try_compute(sh, bw, 1, true, 0);
      96                 :            : 
      97                 :            :         // This magic constant is hard-coded in the original implementation as
      98                 :            :         // '1' on an 8-bit scale. Since we work with arbitrary bit depth
      99                 :            :         // floating point textures, we have to convert this somehow. Hard-code
     100                 :            :         // it as 1/255 under the assumption that the original intent was to be
     101                 :            :         // roughly 1 unit of brightness increment on an 8-bit source. This may
     102                 :            :         // or may not produce suboptimal results on higher-bit-depth content.
     103                 :            :         static const float spatial_bias = 1 / 255.0f;
     104                 :            : 
     105                 :            :         // Calculate spatial prediction
     106                 :          0 :         ident_t spatial_pred = sh_fresh(sh, "spatial_predictor");
     107                 :          0 :         GLSLH("float "$"(float a, float b, float c, float d, float e, float f, float g, \n"
     108                 :            :               "          float h, float i, float j, float k, float l, float m, float n) \n"
     109                 :            :               "{                                                                        \n"
     110                 :            :               "    float spatial_pred = (d + k) / 2.0;                                  \n"
     111                 :            :               "    float spatial_score = abs(c - j) + abs(d - k) + abs(e - l) - %f;     \n"
     112                 :            : 
     113                 :            :               "    float score = abs(b - k) + abs(c - l) + abs(d - m);                  \n"
     114                 :            :               "    if (score < spatial_score) {                                         \n"
     115                 :            :               "        spatial_pred = (c + l) / 2.0;                                    \n"
     116                 :            :               "        spatial_score = score;                                           \n"
     117                 :            :               "        score = abs(a - l) + abs(b - m) + abs(c - n);                    \n"
     118                 :            :               "        if (score < spatial_score) {                                     \n"
     119                 :            :               "          spatial_pred = (b + m) / 2.0;                                  \n"
     120                 :            :               "          spatial_score = score;                                         \n"
     121                 :            :               "        }                                                                \n"
     122                 :            :               "    }                                                                    \n"
     123                 :            :               "    score = abs(d - i) + abs(e - j) + abs(f - k);                        \n"
     124                 :            :               "    if (score < spatial_score) {                                         \n"
     125                 :            :               "        spatial_pred = (e + j) / 2.0;                                    \n"
     126                 :            :               "        spatial_score = score;                                           \n"
     127                 :            :               "        score = abs(e - h) + abs(f - i) + abs(g - j);                    \n"
     128                 :            :               "        if (score < spatial_score) {                                     \n"
     129                 :            :               "          spatial_pred = (f + i) / 2.0;                                  \n"
     130                 :            :               "          spatial_score = score;                                         \n"
     131                 :            :               "        }                                                                \n"
     132                 :            :               "    }                                                                    \n"
     133                 :            :               "    return spatial_pred;                                                 \n"
     134                 :            :               "}                                                                        \n",
     135                 :            :               spatial_pred, spatial_bias);
     136                 :            : 
     137                 :          0 :         GLSL("T a = GET("$", -3, -1); \n"
     138                 :            :              "T b = GET("$", -2, -1); \n"
     139                 :            :              "T c = GET("$", -1, -1); \n"
     140                 :            :              "T d = GET("$",  0, -1); \n"
     141                 :            :              "T e = GET("$", +1, -1); \n"
     142                 :            :              "T f = GET("$", +2, -1); \n"
     143                 :            :              "T g = GET("$", +3, -1); \n"
     144                 :            :              "T h = GET("$", -3, +1); \n"
     145                 :            :              "T i = GET("$", -2, +1); \n"
     146                 :            :              "T j = GET("$", -1, +1); \n"
     147                 :            :              "T k = GET("$",  0, +1); \n"
     148                 :            :              "T l = GET("$", +1, +1); \n"
     149                 :            :              "T m = GET("$", +2, +1); \n"
     150                 :            :              "T n = GET("$", +3, +1); \n",
     151                 :            :              cur, cur, cur, cur, cur, cur, cur, cur, cur, cur, cur, cur, cur, cur);
     152                 :            : 
     153         [ #  # ]:          0 :         if (num_comps == 1) {
     154                 :          0 :             GLSL("res = "$"(a, b, c, d, e, f, g, h, i, j, k, l, m, n); \n", spatial_pred);
     155                 :            :         } else {
     156         [ #  # ]:          0 :             for (uint8_t i = 0; i < num_comps; i++) {
     157                 :          0 :                 char c = "xyzw"[i];
     158                 :          0 :                 GLSL("res.%c = "$"(a.%c, b.%c, c.%c, d.%c, e.%c, f.%c, g.%c,  \n"
     159                 :            :                      "             h.%c, i.%c, j.%c, k.%c, l.%c, m.%c, n.%c); \n",
     160                 :            :                      c, spatial_pred, c, c, c, c, c, c, c, c, c, c, c, c, c, c);
     161                 :            :             }
     162                 :            :         }
     163                 :            : 
     164                 :            :         // Calculate temporal prediction
     165                 :          0 :         ident_t temporal_pred = sh_fresh(sh, "temporal_predictor");
     166                 :          0 :         GLSLH("float "$"(float A, float B, float C, float D, float E, float F,  \n"
     167                 :            :               "          float G, float H, float I, float J, float K, float L,  \n"
     168                 :            :               "          float spatial_pred)                                    \n"
     169                 :            :               "{                                                                \n"
     170                 :            :               "    float p0 = (C + H) / 2.0;                                    \n"
     171                 :            :               "    float p1 = F;                                                \n"
     172                 :            :               "    float p2 = (D + I) / 2.0;                                    \n"
     173                 :            :               "    float p3 = G;                                                \n"
     174                 :            :               "    float p4 = (E + J) / 2.0;                                    \n"
     175                 :            : 
     176                 :            :               "    float tdiff0 = abs(D - I) / 2.0;                             \n"
     177                 :            :               "    float tdiff1 = (abs(A - F) + abs(B - G)) / 2.0;              \n"
     178                 :            :               "    float tdiff2 = (abs(K - F) + abs(G - L)) / 2.0;              \n"
     179                 :            :               "    float diff = max(tdiff0, max(tdiff1, tdiff2));               \n",
     180                 :            :               temporal_pred);
     181         [ #  # ]:          0 :         if (!params->skip_spatial_check) {
     182                 :          0 :             GLSLH("float maxi = max(p2 - min(p3, p1), min(p0 - p1, p4 - p3));   \n"
     183                 :            :                   "float mini = min(p2 - max(p3, p1), max(p0 - p1, p4 - p3));   \n"
     184                 :            :                   "diff = max(diff, max(mini, -maxi));                          \n");
     185                 :            :         }
     186                 :          0 :         GLSLH("    if (spatial_pred > p2 + diff)                                \n"
     187                 :            :               "      spatial_pred = p2 + diff;                                  \n"
     188                 :            :               "    if (spatial_pred < p2 - diff)                                \n"
     189                 :            :               "      spatial_pred = p2 - diff;                                  \n"
     190                 :            :               "    return spatial_pred;                                         \n"
     191                 :            :               "}                                                                \n");
     192                 :            : 
     193                 :            :         ident_t prev2 = cur, next2 = cur;
     194   [ #  #  #  # ]:          0 :         if (src->prev.top && src->prev.top != src->cur.top) {
     195         [ #  # ]:          0 :             pl_assert(src->prev.top->params.w == texparams->w);
     196         [ #  # ]:          0 :             pl_assert(src->prev.top->params.h == texparams->h);
     197                 :          0 :             prev2 = sh_bind(sh, src->prev.top, PL_TEX_ADDRESS_MIRROR,
     198                 :            :                             PL_TEX_SAMPLE_NEAREST, "prev", NULL, NULL, NULL);
     199         [ #  # ]:          0 :             if (!prev2)
     200                 :            :                 return;
     201                 :            :         }
     202                 :            : 
     203   [ #  #  #  # ]:          0 :         if (src->next.top && src->next.top != src->cur.top) {
     204         [ #  # ]:          0 :             pl_assert(src->next.top->params.w == texparams->w);
     205         [ #  # ]:          0 :             pl_assert(src->next.top->params.h == texparams->h);
     206                 :          0 :             next2 = sh_bind(sh, src->next.top, PL_TEX_ADDRESS_MIRROR,
     207                 :            :                             PL_TEX_SAMPLE_NEAREST, "next", NULL, NULL, NULL);
     208         [ #  # ]:          0 :             if (!next2)
     209                 :            :                 return;
     210                 :            :         }
     211                 :            : 
     212                 :          0 :         enum pl_field first_field = PL_DEF(src->first_field, PL_FIELD_TOP);
     213         [ #  # ]:          0 :         ident_t prev1 = src->field == first_field ? prev2 : cur;
     214         [ #  # ]:          0 :         ident_t next1 = src->field == first_field ? cur : next2;
     215                 :            : 
     216                 :          0 :         GLSL("T A = GET("$", 0, -1); \n"
     217                 :            :              "T B = GET("$", 0,  1); \n"
     218                 :            :              "T C = GET("$", 0, -2); \n"
     219                 :            :              "T D = GET("$", 0,  0); \n"
     220                 :            :              "T E = GET("$", 0, +2); \n"
     221                 :            :              "T F = GET("$", 0, -1); \n"
     222                 :            :              "T G = GET("$", 0, +1); \n"
     223                 :            :              "T H = GET("$", 0, -2); \n"
     224                 :            :              "T I = GET("$", 0,  0); \n"
     225                 :            :              "T J = GET("$", 0, +2); \n"
     226                 :            :              "T K = GET("$", 0, -1); \n"
     227                 :            :              "T L = GET("$", 0, +1); \n",
     228                 :            :              prev2, prev2,
     229                 :            :              prev1, prev1, prev1,
     230                 :            :              cur, cur,
     231                 :            :              next1, next1, next1,
     232                 :            :              next2, next2);
     233                 :            : 
     234         [ #  # ]:          0 :         if (num_comps == 1) {
     235                 :          0 :             GLSL("res = "$"(A, B, C, D, E, F, G, H, I, J, K, L, res); \n", temporal_pred);
     236                 :            :         } else {
     237         [ #  # ]:          0 :             for (uint8_t i = 0; i < num_comps; i++) {
     238                 :          0 :                 char c = "xyzw"[i];
     239                 :          0 :                 GLSL("res.%c = "$"(A.%c, B.%c, C.%c, D.%c, E.%c, F.%c, \n"
     240                 :            :                      "             G.%c, H.%c, I.%c, J.%c, K.%c, L.%c, \n"
     241                 :            :                      "             res.%c);                            \n",
     242                 :            :                      c, temporal_pred, c, c, c, c, c, c, c, c, c, c, c, c, c);
     243                 :            :             }
     244                 :            :         }
     245                 :            :         break;
     246                 :            :     }
     247                 :            : 
     248                 :            :     case PL_DEINTERLACE_ALGORITHM_COUNT:
     249                 :          0 :         pl_unreachable();
     250                 :            :     }
     251                 :            : 
     252                 :          0 :     GLSL("}\n"); // End of primary/secondary field branch
     253                 :            : 
     254                 :          2 : done:
     255                 :          2 :     GLSL("color.%s = res;   \n"
     256                 :            :          "#undef T          \n"
     257                 :            :          "#undef GET        \n"
     258                 :            :          "}                 \n",
     259                 :            :          swiz);
     260                 :            : }

Generated by: LCOV version 1.16