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 : : }
|