summaryrefslogtreecommitdiff
path: root/libweston/renderer-gl/fragment.glsl
blob: b19d042740328b40baa3ff0bb7aef9579ee4fcb9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
/*
 * Copyright 2012 Intel Corporation
 * Copyright 2015,2019,2021 Collabora, Ltd.
 * Copyright 2016 NVIDIA Corporation
 * Copyright 2021 Advanced Micro Devices, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/* GLSL version 1.00 ES, defined in gl-shaders.c */

/* For annotating shader compile-time constant arguments */
#define compile_const const

/*
 * Enumeration of shader variants, must match enum gl_shader_texture_variant.
 */
#define SHADER_VARIANT_RGBX     1
#define SHADER_VARIANT_RGBA     2
#define SHADER_VARIANT_Y_U_V    3
#define SHADER_VARIANT_Y_UV     4
#define SHADER_VARIANT_Y_XUXV   5
#define SHADER_VARIANT_XYUV     6
#define SHADER_VARIANT_SOLID    7
#define SHADER_VARIANT_EXTERNAL 8

/* enum gl_shader_color_curve */
#define SHADER_COLOR_CURVE_IDENTITY 0
#define SHADER_COLOR_CURVE_LUT_3x1D 1
#define SHADER_COLOR_CURVE_LINPOW 2
#define SHADER_COLOR_CURVE_POWLIN 3

/* enum gl_shader_color_mapping */
#define SHADER_COLOR_MAPPING_IDENTITY 0
#define SHADER_COLOR_MAPPING_3DLUT 1
#define SHADER_COLOR_MAPPING_MATRIX 2

#if DEF_VARIANT == SHADER_VARIANT_EXTERNAL
#extension GL_OES_EGL_image_external : require
#endif

#if DEF_COLOR_MAPPING == SHADER_COLOR_MAPPING_3DLUT
#extension GL_OES_texture_3D : require
#endif

#ifdef GL_FRAGMENT_PRECISION_HIGH
#define HIGHPRECISION highp
#else
#define HIGHPRECISION mediump
#endif

precision HIGHPRECISION float;

/*
 * These undeclared identifiers will be #defined by a runtime generated code
 * snippet.
 */
compile_const int c_variant = DEF_VARIANT;
compile_const bool c_input_is_premult = DEF_INPUT_IS_PREMULT;
compile_const bool c_green_tint = DEF_GREEN_TINT;
compile_const int c_color_pre_curve = DEF_COLOR_PRE_CURVE;
compile_const int c_color_mapping = DEF_COLOR_MAPPING;
compile_const int c_color_post_curve = DEF_COLOR_POST_CURVE;

compile_const bool c_need_color_pipeline =
	c_color_pre_curve != SHADER_COLOR_CURVE_IDENTITY ||
	c_color_mapping != SHADER_COLOR_MAPPING_IDENTITY ||
	c_color_post_curve != SHADER_COLOR_CURVE_IDENTITY;

vec4
yuva2rgba(vec4 yuva)
{
	vec4 color_out;
	float Y, su, sv;

	/* ITU-R BT.601 & BT.709 quantization (limited range) */

	/* Y = 255/219 * (x - 16/256) */
	Y = 1.16438356 * (yuva.x - 0.0625);

	/* Remove offset 128/256, but the 255/224 multiplier comes later */
	su = yuva.y - 0.5;
	sv = yuva.z - 0.5;

	/*
	 * ITU-R BT.601 encoding coefficients (inverse), with the
	 * 255/224 limited range multiplier already included in the
	 * factors for su (Cb) and sv (Cr).
	 */
	color_out.r = Y                   + 1.59602678 * sv;
	color_out.g = Y - 0.39176229 * su - 0.81296764 * sv;
	color_out.b = Y + 2.01723214 * su;

	color_out.a = yuva.w;

	return color_out;
}

#if DEF_VARIANT == SHADER_VARIANT_EXTERNAL
uniform samplerExternalOES tex;
#else
uniform sampler2D tex;
#endif

varying HIGHPRECISION vec2 v_texcoord;
uniform sampler2D tex1;
uniform sampler2D tex2;
uniform float view_alpha;
uniform vec4 unicolor;

uniform HIGHPRECISION sampler2D color_pre_curve_lut_2d;
uniform HIGHPRECISION vec2 color_pre_curve_lut_scale_offset;
uniform HIGHPRECISION float color_pre_curve_params[30];
uniform bool color_pre_curve_clamped_input;

uniform HIGHPRECISION sampler2D color_post_curve_lut_2d;
uniform HIGHPRECISION vec2 color_post_curve_lut_scale_offset;
uniform HIGHPRECISION float color_post_curve_params[30];
uniform bool color_post_curve_clamped_input;

#if DEF_COLOR_MAPPING == SHADER_COLOR_MAPPING_3DLUT
uniform HIGHPRECISION sampler3D color_mapping_lut_3d;
uniform HIGHPRECISION vec2 color_mapping_lut_scale_offset;
#endif
uniform HIGHPRECISION mat3 color_mapping_matrix;

vec4
sample_input_texture()
{
	vec4 yuva = vec4(0.0, 0.0, 0.0, 1.0);

	/* Producing RGBA directly */

	if (c_variant == SHADER_VARIANT_SOLID)
		return unicolor;

	if (c_variant == SHADER_VARIANT_RGBA ||
	    c_variant == SHADER_VARIANT_EXTERNAL) {
		return texture2D(tex, v_texcoord);
	}

	if (c_variant == SHADER_VARIANT_RGBX)
		return vec4(texture2D(tex, v_texcoord).rgb, 1.0);

	/* Requires conversion to RGBA */

	if (c_variant == SHADER_VARIANT_Y_U_V) {
		yuva.x = texture2D(tex, v_texcoord).x;
		yuva.y = texture2D(tex1, v_texcoord).x;
		yuva.z = texture2D(tex2, v_texcoord).x;

	} else if (c_variant == SHADER_VARIANT_Y_UV) {
		yuva.x = texture2D(tex, v_texcoord).x;
		yuva.yz = texture2D(tex1, v_texcoord).rg;

	} else if (c_variant == SHADER_VARIANT_Y_XUXV) {
		yuva.x = texture2D(tex, v_texcoord).x;
		yuva.yz = texture2D(tex1, v_texcoord).ga;

	} else if (c_variant == SHADER_VARIANT_XYUV) {
		yuva.xyz = texture2D(tex, v_texcoord).bgr;

	} else {
		/* Never reached, bad variant value. */
		return vec4(1.0, 0.3, 1.0, 1.0);
	}

	return yuva2rgba(yuva);
}

/*
 * Texture coordinates go from 0.0 to 1.0 corresponding to texture edges.
 * When we do LUT look-ups with linear filtering, the correct range to sample
 * from is not from edge to edge, but center of first texel to center of last
 * texel. This follows because with LUTs, you have the exact end points given,
 * you never extrapolate but only interpolate.
 * The scale and offset are precomputed to achieve this mapping.
 */
float
lut_texcoord(float x, vec2 scale_offset)
{
	return x * scale_offset.s + scale_offset.t;
}

vec3
lut_texcoord(vec3 pos, vec2 scale_offset)
{
	return pos * scale_offset.s + scale_offset.t;
}

/*
 * Sample a 1D LUT which is a single row of a 2D texture. The 2D texture has
 * four rows so that the centers of texels have precise y-coordinates.
 */
float
sample_color_pre_curve_lut_2d(float x, compile_const int row)
{
	float tx = lut_texcoord(x, color_pre_curve_lut_scale_offset);

	return texture2D(color_pre_curve_lut_2d,
			 vec2(tx, (float(row) + 0.5) / 4.0)).x;
}

float
linpow(float x, float g, float a, float b, float c, float d)
{
	/* See WESTON_COLOR_CURVE_TYPE_LINPOW for details about LINPOW. */

	if (x >= d)
		return pow((a * x) + b, g);

	return c * x;
}

float
powlin(float x, float g, float a, float b, float c, float d)
{
	/* See WESTON_COLOR_CURVE_TYPE_POWLIN for details about POWLIN. */

	if (x >= d)
		return a * pow(x, g) + b;

	return c * x;
}

float
sample_color_pre_curve_linpow(float x, compile_const int color_channel)
{
	float g, a, b, c, d;

	/* For each color channel we have 10 parameters. The params are
	 * linearized in an array of size 30, in RGB order. */
	g = color_pre_curve_params[0 + (color_channel * 10)];
	a = color_pre_curve_params[1 + (color_channel * 10)];
	b = color_pre_curve_params[2 + (color_channel * 10)];
	c = color_pre_curve_params[3 + (color_channel * 10)];
	d = color_pre_curve_params[4 + (color_channel * 10)];

	if (color_pre_curve_clamped_input)
		x = clamp(x, 0.0, 1.0);

	/* We use mirroring for negative input values. */
	if (x < 0.0)
		return -linpow(-x, g, a, b, c, d);

	return linpow(x, g, a, b, c, d);
}

float
sample_color_pre_curve_powlin(float x, compile_const int color_channel)
{
	float g, a, b, c, d;

	/* For each color channel we have 10 parameters. The params are
	 * linearized in an array of size 30, in RGB order. */
	g = color_pre_curve_params[0 + (color_channel * 10)];
	a = color_pre_curve_params[1 + (color_channel * 10)];
	b = color_pre_curve_params[2 + (color_channel * 10)];
	c = color_pre_curve_params[3 + (color_channel * 10)];
	d = color_pre_curve_params[4 + (color_channel * 10)];

	if (color_pre_curve_clamped_input)
		x = clamp(x, 0.0, 1.0);

	/* We use mirroring for negative input values. */
	if (x < 0.0)
		return -powlin(-x, g, a, b, c, d);

	return powlin(x, g, a, b, c, d);
}

float
sample_color_post_curve_linpow(float x, compile_const int color_channel)
{
	float g, a, b, c, d;

	/* For each color channel we have 10 parameters. The params are
	 * linearized in an array of size 30, in RGB order. */
	g = color_post_curve_params[0 + (color_channel * 10)];
	a = color_post_curve_params[1 + (color_channel * 10)];
	b = color_post_curve_params[2 + (color_channel * 10)];
	c = color_post_curve_params[3 + (color_channel * 10)];
	d = color_post_curve_params[4 + (color_channel * 10)];

	if (color_post_curve_clamped_input)
		x = clamp(x, 0.0, 1.0);

	/* We use mirroring for negative input values. */
	if (x < 0.0)
		return -linpow(-x, g, a, b, c, d);

	return linpow(x, g, a, b, c, d);
}

float
sample_color_post_curve_powlin(float x, compile_const int color_channel)
{
	float g, a, b, c, d;

	/* For each color channel we have 10 parameters. The params are
	 * linearized in an array of size 30, in RGB order. */
	g = color_post_curve_params[0 + (color_channel * 10)];
	a = color_post_curve_params[1 + (color_channel * 10)];
	b = color_post_curve_params[2 + (color_channel * 10)];
	c = color_post_curve_params[3 + (color_channel * 10)];
	d = color_post_curve_params[4 + (color_channel * 10)];

	if (color_post_curve_clamped_input)
		x = clamp(x, 0.0, 1.0);

	/* We use mirroring for negative input values. */
	if (x < 0.0)
		return -powlin(-x, g, a, b, c, d);

	return powlin(x, g, a, b, c, d);
}

vec3
color_pre_curve(vec3 color)
{
	vec3 ret;

	if (c_color_pre_curve == SHADER_COLOR_CURVE_IDENTITY) {
		return color;
	} else if (c_color_pre_curve == SHADER_COLOR_CURVE_LUT_3x1D) {
		ret.r = sample_color_pre_curve_lut_2d(color.r, 0);
		ret.g = sample_color_pre_curve_lut_2d(color.g, 1);
		ret.b = sample_color_pre_curve_lut_2d(color.b, 2);
		return ret;
	} else if (c_color_pre_curve == SHADER_COLOR_CURVE_LINPOW) {
		ret.r = sample_color_pre_curve_linpow(color.r, 0);
		ret.g = sample_color_pre_curve_linpow(color.g, 1);
		ret.b = sample_color_pre_curve_linpow(color.b, 2);
		return ret;
	} else if (c_color_pre_curve == SHADER_COLOR_CURVE_POWLIN) {
		ret.r = sample_color_pre_curve_powlin(color.r, 0);
		ret.g = sample_color_pre_curve_powlin(color.g, 1);
		ret.b = sample_color_pre_curve_powlin(color.b, 2);
		return ret;
	} else {
		/* Never reached, bad c_color_pre_curve. */
		return vec3(1.0, 0.3, 1.0);
	}
}

vec3
sample_color_mapping_lut_3d(vec3 color)
{
	vec3 pos, ret = vec3(0.0, 0.0, 0.0);
#if DEF_COLOR_MAPPING == SHADER_COLOR_MAPPING_3DLUT
	pos = lut_texcoord(color, color_mapping_lut_scale_offset);
	ret = texture3D(color_mapping_lut_3d, pos).rgb;
#endif
	return ret;
}

vec3
color_mapping(vec3 color)
{
	if (c_color_mapping == SHADER_COLOR_MAPPING_IDENTITY)
		return color;
	else if (c_color_mapping == SHADER_COLOR_MAPPING_3DLUT)
		return sample_color_mapping_lut_3d(color);
	else if (c_color_mapping == SHADER_COLOR_MAPPING_MATRIX)
		return color_mapping_matrix * color.rgb;
	else /* Never reached, bad c_color_mapping. */
		return vec3(1.0, 0.3, 1.0);
}

float
sample_color_post_curve_lut_2d(float x, compile_const int row)
{
	float tx = lut_texcoord(x, color_post_curve_lut_scale_offset);

	return texture2D(color_post_curve_lut_2d,
			 vec2(tx, (float(row) + 0.5) / 4.0)).x;
}

vec3
color_post_curve(vec3 color)
{
	vec3 ret;

	if (c_color_post_curve == SHADER_COLOR_CURVE_IDENTITY) {
		return color;
	} else if (c_color_post_curve == SHADER_COLOR_CURVE_LUT_3x1D) {
		ret.r = sample_color_post_curve_lut_2d(color.r, 0);
		ret.g = sample_color_post_curve_lut_2d(color.g, 1);
		ret.b = sample_color_post_curve_lut_2d(color.b, 2);
		return ret;
	} else if (c_color_post_curve == SHADER_COLOR_CURVE_LINPOW) {
		ret.r = sample_color_post_curve_linpow(color.r, 0);
		ret.g = sample_color_post_curve_linpow(color.g, 1);
		ret.b = sample_color_post_curve_linpow(color.b, 2);
		return ret;
	} else if (c_color_post_curve == SHADER_COLOR_CURVE_POWLIN) {
		ret.r = sample_color_post_curve_powlin(color.r, 0);
		ret.g = sample_color_post_curve_powlin(color.g, 1);
		ret.b = sample_color_post_curve_powlin(color.b, 2);
		return ret;
	} else {
		/* Never reached, bad c_color_post_curve. */
		return vec3(1.0, 0.3, 1.0);
	}
}

vec4
color_pipeline(vec4 color)
{
	/* Ensure straight alpha */
	if (c_input_is_premult) {
		if (color.a == 0.0)
			color.rgb = vec3(0, 0, 0);
		else
			color.rgb *= 1.0 / color.a;
	}

	color.rgb = color_pre_curve(color.rgb);
	color.rgb = color_mapping(color.rgb);
	color.rgb = color_post_curve(color.rgb);

	return color;
}

void
main()
{
	vec4 color;

	/* Electrical (non-linear) RGBA values, may be premult or not */
	color = sample_input_texture();

	if (c_need_color_pipeline)
		color = color_pipeline(color); /* Produces straight alpha */

	/* Ensure pre-multiplied for blending */
	if (!c_input_is_premult || c_need_color_pipeline)
		color.rgb *= color.a;

	color *= view_alpha;

	if (c_green_tint)
		color = vec4(0.0, 0.3, 0.0, 0.2) + color * 0.8;

	gl_FragColor = color;
}