summaryrefslogtreecommitdiffstats
path: root/src/multimedia/shaders
diff options
context:
space:
mode:
Diffstat (limited to 'src/multimedia/shaders')
-rw-r--r--src/multimedia/shaders/abgr.frag19
-rw-r--r--src/multimedia/shaders/argb.frag19
-rw-r--r--src/multimedia/shaders/ayuv.frag25
-rw-r--r--src/multimedia/shaders/bgra.frag19
-rw-r--r--src/multimedia/shaders/colorconvert.glsl19
-rw-r--r--src/multimedia/shaders/colortransfer.glsl118
-rwxr-xr-xsrc/multimedia/shaders/compile.bat16
-rw-r--r--src/multimedia/shaders/externalsampler.frag14
-rw-r--r--src/multimedia/shaders/externalsampler.vert16
-rw-r--r--src/multimedia/shaders/externalsampler_gles.frag28
-rw-r--r--src/multimedia/shaders/hdrtonemapper.glsl38
-rw-r--r--src/multimedia/shaders/imc2.frag29
-rw-r--r--src/multimedia/shaders/imc4.frag29
-rw-r--r--src/multimedia/shaders/nv12.frag27
-rw-r--r--src/multimedia/shaders/nv12_bt2020_hlg.frag42
-rw-r--r--src/multimedia/shaders/nv12_bt2020_pq.frag48
-rw-r--r--src/multimedia/shaders/nv21.frag27
-rw-r--r--src/multimedia/shaders/rectsampler.vert16
-rw-r--r--src/multimedia/shaders/rectsampler_bgra.frag19
-rw-r--r--src/multimedia/shaders/rgba.frag19
-rw-r--r--src/multimedia/shaders/uniformbuffer.glsl11
-rw-r--r--src/multimedia/shaders/uyvy.frag27
-rw-r--r--src/multimedia/shaders/vertex.vert16
-rw-r--r--src/multimedia/shaders/y.frag25
-rw-r--r--src/multimedia/shaders/yuv_triplanar.frag29
-rw-r--r--src/multimedia/shaders/yuv_triplanar_p10.frag29
-rw-r--r--src/multimedia/shaders/yuyv.frag27
-rw-r--r--src/multimedia/shaders/yvu_triplanar.frag29
28 files changed, 780 insertions, 0 deletions
diff --git a/src/multimedia/shaders/abgr.frag b/src/multimedia/shaders/abgr.frag
new file mode 100644
index 000000000..7eadb092d
--- /dev/null
+++ b/src/multimedia/shaders/abgr.frag
@@ -0,0 +1,19 @@
+#version 440
+#extension GL_GOOGLE_include_directive : enable
+
+#include "uniformbuffer.glsl"
+#include "colortransfer.glsl"
+
+layout(location = 0) in vec2 texCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D rgbTexture;
+
+void main()
+{
+ fragColor = ubuf.colorMatrix * texture(rgbTexture, texCoord).abgr * ubuf.opacity;
+
+#ifdef QMM_OUTPUTSURFACE_LINEAR
+ fragColor = convertSRGBToLinear(fragColor);
+#endif
+}
diff --git a/src/multimedia/shaders/argb.frag b/src/multimedia/shaders/argb.frag
new file mode 100644
index 000000000..1adbc8dba
--- /dev/null
+++ b/src/multimedia/shaders/argb.frag
@@ -0,0 +1,19 @@
+#version 440
+#extension GL_GOOGLE_include_directive : enable
+
+#include "uniformbuffer.glsl"
+#include "colortransfer.glsl"
+
+layout(location = 0) in vec2 texCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D rgbTexture;
+
+void main()
+{
+ fragColor = ubuf.colorMatrix * texture(rgbTexture, texCoord).gbar * ubuf.opacity;
+
+#ifdef QMM_OUTPUTSURFACE_LINEAR
+ fragColor = convertSRGBToLinear(fragColor);
+#endif
+}
diff --git a/src/multimedia/shaders/ayuv.frag b/src/multimedia/shaders/ayuv.frag
new file mode 100644
index 000000000..92c3f71fe
--- /dev/null
+++ b/src/multimedia/shaders/ayuv.frag
@@ -0,0 +1,25 @@
+#version 440
+#extension GL_GOOGLE_include_directive : enable
+
+#include "uniformbuffer.glsl"
+#include "colortransfer.glsl"
+
+layout(location = 0) in vec2 texCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D plane1Texture;
+
+void main()
+{
+ vec3 YUV = texture(plane1Texture, texCoord).gba;
+ float A = texture(plane1Texture, texCoord).r;
+ fragColor = ubuf.colorMatrix * vec4(YUV, 1.0) * A * ubuf.opacity;
+
+#ifdef QMM_OUTPUTSURFACE_LINEAR
+ fragColor = convertSRGBToLinear(fragColor);
+#endif
+
+ // Clamp output to valid range to account for out-of-range
+ // input values and numerical inaccuracies in YUV->RGB conversion
+ fragColor = clamp(fragColor, 0.0, 1.0);
+}
diff --git a/src/multimedia/shaders/bgra.frag b/src/multimedia/shaders/bgra.frag
new file mode 100644
index 000000000..0ba0e4c9e
--- /dev/null
+++ b/src/multimedia/shaders/bgra.frag
@@ -0,0 +1,19 @@
+#version 440
+#extension GL_GOOGLE_include_directive : enable
+
+#include "uniformbuffer.glsl"
+#include "colortransfer.glsl"
+
+layout(location = 0) in vec2 texCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D rgbTexture;
+
+void main()
+{
+ fragColor = ubuf.colorMatrix * texture(rgbTexture, texCoord).bgra * ubuf.opacity;
+
+#ifdef QMM_OUTPUTSURFACE_LINEAR
+ fragColor = convertSRGBToLinear(fragColor);
+#endif
+}
diff --git a/src/multimedia/shaders/colorconvert.glsl b/src/multimedia/shaders/colorconvert.glsl
new file mode 100644
index 000000000..f28a0902e
--- /dev/null
+++ b/src/multimedia/shaders/colorconvert.glsl
@@ -0,0 +1,19 @@
+#ifndef COLORCONVERT
+#define COLORCONVERT
+
+// Convert the Rec2020 RGB colorspace to sRGB using:
+// https://www.itu.int/dms_pub/itu-r/opb/rep/R-REP-BT.2407-2017-PDF-E.pdf
+// We can use the simple matrix defined there for a conversion between BT2020
+// and sRGB. Conversion has to be done in linear color space.
+vec4 convertRec2020ToSRGB(vec4 rgba)
+{
+ const mat4 mat =
+ mat4(1.6605f, -0.5876f, -0.0728f, 0.0000f,
+ -0.1246f, 1.1329f, -0.0083f, 0.0000f,
+ -0.0182f, -0.1006f, 1.1187f, 0.0000f,
+ 0.0000f, 0.0000f, 0.0000f, 1.0000f);
+
+ return rgba * mat;
+}
+
+#endif
diff --git a/src/multimedia/shaders/colortransfer.glsl b/src/multimedia/shaders/colortransfer.glsl
new file mode 100644
index 000000000..1d70ae9eb
--- /dev/null
+++ b/src/multimedia/shaders/colortransfer.glsl
@@ -0,0 +1,118 @@
+#ifndef PERCEPTUAL_QUANTIZER
+#define PERCEPTUAL_QUANTIZER
+
+// Convert Rec.709 to linear
+vec4 convertRec709ToLinear(vec4 rgba)
+{
+ const vec4 alphaMinusOne = vec4(0.099, 0.099, 0.099, 0);
+ const vec4 alpha = alphaMinusOne + 1.;
+ bvec4 cutoff = lessThan(rgba, vec4(0.081));
+ vec4 high = pow((rgba + alphaMinusOne)/alpha, vec4(1./0.45));
+ vec4 low = rgba/4.5;
+ return mix(high, low, cutoff);
+}
+
+vec4 convertSRGBToLinear(vec4 sRGB)
+{
+ // https://en.wikipedia.org/wiki/SRGB
+ const bvec3 cutoff = lessThanEqual(sRGB.rgb, vec3(0.04045));
+ const vec3 low = sRGB.rgb / 12.92;
+ const vec3 high = pow((sRGB.rgb + 0.055) / 1.055, vec3(2.4));
+ vec3 linear = mix(high, low, cutoff);
+ return vec4(linear, sRGB.a);
+}
+
+vec4 convertSRGBFromLinear(vec4 linear)
+{
+ // https://en.wikipedia.org/wiki/SRGB
+ const bvec3 cutoff = lessThanEqual(linear.rgb, vec3(0.0031308));
+ const vec3 low = linear.rgb * 12.92;
+ const vec3 high = 1.055 * pow(linear.rgb, vec3(1.0 / 2.4f)) - 0.055;
+ vec3 sRGB = mix(high, low, cutoff);
+ return vec4(sRGB, linear.a);
+}
+
+// This uses the PQ transfer function, see also https://en.wikipedia.org/wiki/Perceptual_quantizer
+// or https://ieeexplore.ieee.org/document/7291452
+const float SDR_LEVEL = 100.;
+
+vec4 convertPQToLinear(vec4 rgba)
+{
+ const vec4 one_over_m1 = vec4(8192./1305.);
+ const vec4 one_over_m2 = vec4(32./2523.);
+ const float c1 = 107./128.;
+ const float c2 = 2413./128;
+ const float c3 = 2392./128.;
+
+ vec4 e = pow(rgba, one_over_m2);
+ vec4 num = max(e - c1, 0);
+ vec4 den = c2 - c3*e;
+ return pow(num/den, one_over_m1)*10000./SDR_LEVEL;
+}
+
+vec4 convertPQFromLinear(vec4 rgba)
+{
+ const float m1 = float(1305./8192.);
+ const float m2 = float(2523./32.);
+ const float c1 = 107./128.;
+ const float c2 = 2413./128;
+ const float c3 = 2392./128.;
+
+ rgba *= SDR_LEVEL/10000.;
+ vec4 p = pow(rgba, vec4(m1));
+ vec4 num = c1 + c2*p;
+ vec4 den = 1. + c3*p;
+ return pow(num/den, vec4(m2));
+}
+
+float convertPQToLinear(float sig)
+{
+ const float one_over_m1 = float(8192./1305.);
+ const float one_over_m2 = float(32./2523.);
+ const float c1 = 107./128.;
+ const float c2 = 2413./128;
+ const float c3 = 2392./128.;
+
+ float e = pow(sig, one_over_m2);
+ float num = max(e - c1, 0);
+ float den = c2 - c3*e;
+ return pow(num/den, one_over_m1)*10000./SDR_LEVEL;
+}
+
+float convertPQFromLinear(float sig)
+{
+ const float m1 = float(1305./8192.);
+ const float m2 = float(2523./32.);
+ const float c1 = 107./128.;
+ const float c2 = 2413./128;
+ const float c3 = 2392./128.;
+
+ sig *= SDR_LEVEL/10000.;
+ float p = pow(sig, m1);
+ float num = c1 + c2*p;
+ float den = 1. + c3*p;
+ return pow(num/den, m2);
+}
+
+// This implements support for HLG transfer functions, see also https://en.wikipedia.org/wiki/Hybrid_log–gamma
+
+
+vec4 convertHLGToLinear(vec4 rgba, float maxLum)
+{
+ const float a = 0.17883277;
+ const float b = 0.28466892; // = 1 - 4a
+ const float c = 0.55991073; // = 0.5 - a ln(4a)
+
+ bvec4 cutoff = lessThan(rgba, vec4(0.5));
+ vec4 low = rgba*rgba/3;
+ vec4 high = (exp((rgba - c)/a) + b)/12.;
+ rgba = mix(high, low, cutoff);
+
+ float lum = dot(rgba, vec4(0.2627, 0.6780, 0.0593, 0.));
+ float y = pow(lum, 0.2); // gamma-1 with gamma = 1.2
+
+ rgba *= y*maxLum;
+ return rgba;
+}
+
+#endif
diff --git a/src/multimedia/shaders/compile.bat b/src/multimedia/shaders/compile.bat
new file mode 100755
index 000000000..8a012d265
--- /dev/null
+++ b/src/multimedia/shaders/compile.bat
@@ -0,0 +1,16 @@
+:: Copyright (C) 2019 The Qt Company Ltd.
+:: SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o rgba.vert.qsb rgba.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o rgba.frag.qsb rgba.frag
+
+qsb -b --glsl "150,120,100 es" --hlsl 50 --msl 12 -o yuv.vert.qsb yuv.vert
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o yuv_yv.frag.qsb yuv_yv.frag
+
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o nv12.frag.qsb nv12.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o nv21.frag.qsb nv21.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o uyvy.frag.qsb uyvy.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o yuyv.frag.qsb yuyv.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o ayuv.frag.qsb ayuv.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o p010be.frag.qsb p010be.frag
+qsb --glsl "150,120,100 es" --hlsl 50 --msl 12 -o p010be.frag.qsb p010be.frag
diff --git a/src/multimedia/shaders/externalsampler.frag b/src/multimedia/shaders/externalsampler.frag
new file mode 100644
index 000000000..f786bc4e1
--- /dev/null
+++ b/src/multimedia/shaders/externalsampler.frag
@@ -0,0 +1,14 @@
+#version 440
+#extension GL_GOOGLE_include_directive : enable
+
+#include "uniformbuffer.glsl"
+
+layout(location = 0) in vec2 texCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D plane1Texture;
+
+void main()
+{
+ fragColor = vec4(1., 0., 0., 0.);
+}
diff --git a/src/multimedia/shaders/externalsampler.vert b/src/multimedia/shaders/externalsampler.vert
new file mode 100644
index 000000000..e693370dc
--- /dev/null
+++ b/src/multimedia/shaders/externalsampler.vert
@@ -0,0 +1,16 @@
+#version 440
+#extension GL_GOOGLE_include_directive : enable
+
+#include "uniformbuffer.glsl"
+
+layout(location = 0) in vec4 vertexPosition;
+layout(location = 1) in vec2 vertexTexCoord;
+
+layout(location = 0) out vec2 texCoord;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main() {
+ texCoord = (ubuf.colorMatrix * vec4(vertexTexCoord, 0.0, 1.0)).xy;
+ gl_Position = ubuf.matrix * vertexPosition;
+}
diff --git a/src/multimedia/shaders/externalsampler_gles.frag b/src/multimedia/shaders/externalsampler_gles.frag
new file mode 100644
index 000000000..1292acebc
--- /dev/null
+++ b/src/multimedia/shaders/externalsampler_gles.frag
@@ -0,0 +1,28 @@
+#extension GL_OES_EGL_image_external : require
+precision highp float;
+precision highp int;
+
+struct buf
+{
+ mat4 matrix;
+ mat4 colorMatrix;
+ float opacity;
+ float width;
+ float masteringWhite;
+ float maxLum;
+};
+
+uniform buf ubuf;
+
+uniform samplerExternalOES plane1Texture;
+
+varying vec2 texCoord;
+
+void main()
+{
+ gl_FragColor = texture2D(plane1Texture, texCoord).rgba * ubuf.opacity;
+
+#ifdef QMM_OUTPUTSURFACE_LINEAR
+ fragColor = pow(fragColor, 2.2);
+#endif
+}
diff --git a/src/multimedia/shaders/hdrtonemapper.glsl b/src/multimedia/shaders/hdrtonemapper.glsl
new file mode 100644
index 000000000..e10b6eca3
--- /dev/null
+++ b/src/multimedia/shaders/hdrtonemapper.glsl
@@ -0,0 +1,38 @@
+#ifndef HDR_TONEMAPPER
+#define HDR_TONEMAPPER
+
+#include "colortransfer.glsl"
+
+// See https://www.itu.int/dms_pub/itu-r/opb/rep/R-REP-BT.2390-6-2019-PDF-E.pdf, section 5.4
+//
+// masteringWhite in PQ values, not in linear. maxOutLum as defined in the doc above
+// we assume that masteringBlack == 0, and minLum == 0 to simplify the calculations
+//
+// The HDR tonemapping operates in non linear space (PQ or HLG), taking the
+// non linear luminosity as input and returning a factor by which to scale
+// the RGB signal so it fits within the outbut devices range.
+//
+// We're using the same algorithm for HLG, and do the equivalent mapping in
+// HLG space.
+float tonemapScaleForLuminosity(float y, float masteringWhite, float maxLum)
+{
+ float p = y/masteringWhite;
+
+ float ks = 1.5*maxLum - 0.5;
+
+ if (p < ks)
+ return 1.;
+
+ float t = (p - ks)/(1 - ks);
+ float t2 = t*t;
+ float t3 = t*t2;
+
+ p = (2*t3 - 3*t2 + 1)*ks + (t3 - 2*t2 + t)*(1. - ks) + (-2*t3 + 3*t2)*maxLum;
+
+ // get the linear new luminosity
+ float newY = p*masteringWhite;
+
+ return newY/y;
+}
+
+#endif
diff --git a/src/multimedia/shaders/imc2.frag b/src/multimedia/shaders/imc2.frag
new file mode 100644
index 000000000..18401d420
--- /dev/null
+++ b/src/multimedia/shaders/imc2.frag
@@ -0,0 +1,29 @@
+#version 440
+#extension GL_GOOGLE_include_directive : enable
+
+#include "uniformbuffer.glsl"
+#include "colortransfer.glsl"
+
+layout(location = 0) in vec2 texCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D plane1Texture;
+layout(binding = 2) uniform sampler2D plane2Texture;
+
+void main()
+{
+ float Y = texture(plane1Texture, texCoord).r;
+ float x = texCoord.x/2.;
+ float U = texture(plane2Texture, vec2(x + .5, texCoord.y)).r;
+ float V = texture(plane2Texture, vec2(x, texCoord.y)).r;
+ vec4 color = vec4(Y, U, V, 1.);
+ fragColor = ubuf.colorMatrix * color * ubuf.opacity;
+
+#ifdef QMM_OUTPUTSURFACE_LINEAR
+ fragColor = convertSRGBToLinear(fragColor);
+#endif
+
+ // Clamp output to valid range to account for out-of-range
+ // input values and numerical inaccuracies in YUV->RGB conversion
+ fragColor = clamp(fragColor, 0.0, 1.0);
+}
diff --git a/src/multimedia/shaders/imc4.frag b/src/multimedia/shaders/imc4.frag
new file mode 100644
index 000000000..574a5f49b
--- /dev/null
+++ b/src/multimedia/shaders/imc4.frag
@@ -0,0 +1,29 @@
+#version 440
+#extension GL_GOOGLE_include_directive : enable
+
+#include "uniformbuffer.glsl"
+#include "colortransfer.glsl"
+
+layout(location = 0) in vec2 texCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D plane1Texture;
+layout(binding = 2) uniform sampler2D plane2Texture;
+
+void main()
+{
+ float Y = texture(plane1Texture, texCoord).r;
+ float x = texCoord.x/2.;
+ float U = texture(plane2Texture, vec2(x, texCoord.y)).r;
+ float V = texture(plane2Texture, vec2(x + .5, texCoord.y)).r;
+ vec4 color = vec4(Y, U, V, 1.);
+ fragColor = ubuf.colorMatrix * color * ubuf.opacity;
+
+#ifdef QMM_OUTPUTSURFACE_LINEAR
+ fragColor = convertSRGBToLinear(fragColor);
+#endif
+
+ // Clamp output to valid range to account for out-of-range
+ // input values and numerical inaccuracies in YUV->RGB conversion
+ fragColor = clamp(fragColor, 0.0, 1.0);
+}
diff --git a/src/multimedia/shaders/nv12.frag b/src/multimedia/shaders/nv12.frag
new file mode 100644
index 000000000..20399d9f0
--- /dev/null
+++ b/src/multimedia/shaders/nv12.frag
@@ -0,0 +1,27 @@
+#version 440
+#extension GL_GOOGLE_include_directive : enable
+
+#include "uniformbuffer.glsl"
+#include "colortransfer.glsl"
+
+layout(location = 0) in vec2 texCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D plane1Texture;
+layout(binding = 2) uniform sampler2D plane2Texture;
+
+void main()
+{
+ float Y = texture(plane1Texture, texCoord).r;
+ vec2 UV = texture(plane2Texture, texCoord).rg;
+ vec4 color = vec4(Y, UV.x, UV.y, 1.);
+ fragColor = ubuf.colorMatrix * color * ubuf.opacity;
+
+#ifdef QMM_OUTPUTSURFACE_LINEAR
+ fragColor = convertSRGBToLinear(fragColor);
+#endif
+
+ // Clamp output to valid range to account for out-of-range
+ // input values and numerical inaccuracies in YUV->RGB conversion
+ fragColor = clamp(fragColor, 0.0, 1.0);
+}
diff --git a/src/multimedia/shaders/nv12_bt2020_hlg.frag b/src/multimedia/shaders/nv12_bt2020_hlg.frag
new file mode 100644
index 000000000..25560c9b7
--- /dev/null
+++ b/src/multimedia/shaders/nv12_bt2020_hlg.frag
@@ -0,0 +1,42 @@
+#version 440
+#extension GL_GOOGLE_include_directive : enable
+
+#include "uniformbuffer.glsl"
+#include "colortransfer.glsl"
+#include "colorconvert.glsl"
+#include "hdrtonemapper.glsl"
+
+layout(location = 0) in vec2 texCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D plane1Texture;
+layout(binding = 2) uniform sampler2D plane2Texture;
+
+// This implements support HDR video using the HLG transfer functions, see also
+// https://en.wikipedia.org/wiki/Hybrid_log–gamma
+// https://www.itu.int/dms_pub/itu-r/opb/rep/R-REP-BT.2390-6-2019-PDF-E.pdf
+// https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.2100-2-201807-I!!PDF-E.pdf
+//
+// Tonemapping is done using the same algorithm as for the PQ transfer function, but we
+// operate in HLG space here.
+void main()
+{
+ float Y = texture(plane1Texture, texCoord).r;
+ vec2 UV = texture(plane2Texture, texCoord).rg;
+ // map to Rec.2020 color space
+ fragColor = vec4(Y, UV.x, UV.y, 1.);
+ fragColor = ubuf.colorMatrix * fragColor;
+
+ // tonemap
+ float y = (Y - 16./256.)*256./219.; // Video range (16...235)
+ float scale = tonemapScaleForLuminosity(y, ubuf.masteringWhite, ubuf.maxLum);
+ fragColor *= scale;
+
+ fragColor = convertHLGToLinear(fragColor, ubuf.maxLum);
+ fragColor = convertRec2020ToSRGB(fragColor);
+ fragColor *= ubuf.opacity;
+
+#ifndef QMM_OUTPUTSURFACE_LINEAR
+ fragColor = convertSRGBFromLinear(fragColor);
+#endif
+}
diff --git a/src/multimedia/shaders/nv12_bt2020_pq.frag b/src/multimedia/shaders/nv12_bt2020_pq.frag
new file mode 100644
index 000000000..96aef068e
--- /dev/null
+++ b/src/multimedia/shaders/nv12_bt2020_pq.frag
@@ -0,0 +1,48 @@
+#version 440
+#extension GL_GOOGLE_include_directive : enable
+
+#include "uniformbuffer.glsl"
+#include "colortransfer.glsl"
+#include "colorconvert.glsl"
+#include "hdrtonemapper.glsl"
+
+layout(location = 0) in vec2 texCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D plane1Texture;
+layout(binding = 2) uniform sampler2D plane2Texture;
+
+// This uses the PQ transfer function, see also https://en.wikipedia.org/wiki/Perceptual_quantizer
+// or https://ieeexplore.ieee.org/document/7291452
+//
+// Tonemapping into the RGB range supported by the output is done using
+// https://www.itu.int/dms_pub/itu-r/opb/rep/R-REP-BT.2390-6-2019-PDF-E.pdf, section 5.4
+//
+// masteringWhite in PQ values, not in linear. maxOutLum as defined in the doc above
+// we assume that masteringBlack == 0, and minLum == 0 to simplify the calculations
+//
+// The calculation calculates a new luminosity in non linear space and scales the UV
+// components before linearizing. This corresponds to option (2) at the end of section 5.4.
+// This option was chosen as it keeps the colors correct while as well as being computationally
+// cheapest.
+void main()
+{
+ float Y = texture(plane1Texture, texCoord).r;
+ vec2 UV = texture(plane2Texture, texCoord).rg;
+ // map to Rec.2020 color space
+ fragColor = vec4(Y, UV.x, UV.y, 1.);
+ fragColor = ubuf.colorMatrix * fragColor;
+
+ // tonemap
+ float y = (Y - 16./256.)*256./219.; // Video range (16...235)
+ float scale = tonemapScaleForLuminosity(y, ubuf.masteringWhite, ubuf.maxLum);
+ fragColor *= scale;
+
+ fragColor = convertPQToLinear(fragColor);
+ fragColor = convertRec2020ToSRGB(fragColor);
+ fragColor *= ubuf.opacity;
+
+#ifndef QMM_OUTPUTSURFACE_LINEAR
+ fragColor = convertSRGBFromLinear(fragColor);
+#endif
+}
diff --git a/src/multimedia/shaders/nv21.frag b/src/multimedia/shaders/nv21.frag
new file mode 100644
index 000000000..c3f641be5
--- /dev/null
+++ b/src/multimedia/shaders/nv21.frag
@@ -0,0 +1,27 @@
+#version 440
+#extension GL_GOOGLE_include_directive : enable
+
+#include "uniformbuffer.glsl"
+#include "colortransfer.glsl"
+
+layout(location = 0) in vec2 texCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D plane1Texture;
+layout(binding = 2) uniform sampler2D plane2Texture;
+
+void main()
+{
+ float Y = texture(plane1Texture, texCoord).r;
+ vec2 UV = texture(plane2Texture, texCoord).gr;
+ vec4 color = vec4(Y, UV.x, UV.y, 1.);
+ fragColor = ubuf.colorMatrix * color * ubuf.opacity;
+
+#ifdef QMM_OUTPUTSURFACE_LINEAR
+ fragColor = convertSRGBToLinear(fragColor);
+#endif
+
+ // Clamp output to valid range to account for out-of-range
+ // input values and numerical inaccuracies in YUV->RGB conversion
+ fragColor = clamp(fragColor, 0.0, 1.0);
+}
diff --git a/src/multimedia/shaders/rectsampler.vert b/src/multimedia/shaders/rectsampler.vert
new file mode 100644
index 000000000..e693370dc
--- /dev/null
+++ b/src/multimedia/shaders/rectsampler.vert
@@ -0,0 +1,16 @@
+#version 440
+#extension GL_GOOGLE_include_directive : enable
+
+#include "uniformbuffer.glsl"
+
+layout(location = 0) in vec4 vertexPosition;
+layout(location = 1) in vec2 vertexTexCoord;
+
+layout(location = 0) out vec2 texCoord;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main() {
+ texCoord = (ubuf.colorMatrix * vec4(vertexTexCoord, 0.0, 1.0)).xy;
+ gl_Position = ubuf.matrix * vertexPosition;
+}
diff --git a/src/multimedia/shaders/rectsampler_bgra.frag b/src/multimedia/shaders/rectsampler_bgra.frag
new file mode 100644
index 000000000..8b1b9a4ad
--- /dev/null
+++ b/src/multimedia/shaders/rectsampler_bgra.frag
@@ -0,0 +1,19 @@
+#version 440
+#extension GL_GOOGLE_include_directive : enable
+
+#include "uniformbuffer.glsl"
+#include "colortransfer.glsl"
+
+layout(location = 0) in vec2 texCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2DRect rgbTexture;
+
+void main()
+{
+ fragColor = texture(rgbTexture, texCoord).rgba * ubuf.opacity;
+
+#ifdef QMM_OUTPUTSURFACE_LINEAR
+ fragColor = convertSRGBToLinear(fragColor);
+#endif
+}
diff --git a/src/multimedia/shaders/rgba.frag b/src/multimedia/shaders/rgba.frag
new file mode 100644
index 000000000..b53e0dc71
--- /dev/null
+++ b/src/multimedia/shaders/rgba.frag
@@ -0,0 +1,19 @@
+#version 440
+#extension GL_GOOGLE_include_directive : enable
+
+#include "uniformbuffer.glsl"
+#include "colortransfer.glsl"
+
+layout(location = 0) in vec2 texCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D rgbTexture;
+
+void main()
+{
+ fragColor = ubuf.colorMatrix * texture(rgbTexture, texCoord).rgba * ubuf.opacity;
+
+#ifdef QMM_OUTPUTSURFACE_LINEAR
+ fragColor = convertSRGBToLinear(fragColor);
+#endif
+}
diff --git a/src/multimedia/shaders/uniformbuffer.glsl b/src/multimedia/shaders/uniformbuffer.glsl
new file mode 100644
index 000000000..c74859c5d
--- /dev/null
+++ b/src/multimedia/shaders/uniformbuffer.glsl
@@ -0,0 +1,11 @@
+// Make sure to also modify externalsampler_gles.frag when modifying this
+
+layout(std140, binding = 0) uniform buf {
+ mat4 matrix;
+ mat4 colorMatrix;
+ float opacity;
+ float width;
+ // HDR metadata required for tonemapping
+ float masteringWhite; // in PQ or HLG values
+ float maxLum; // in PQ or HLG values
+} ubuf;
diff --git a/src/multimedia/shaders/uyvy.frag b/src/multimedia/shaders/uyvy.frag
new file mode 100644
index 000000000..26a83da3f
--- /dev/null
+++ b/src/multimedia/shaders/uyvy.frag
@@ -0,0 +1,27 @@
+#version 440
+#extension GL_GOOGLE_include_directive : enable
+
+#include "uniformbuffer.glsl"
+#include "colortransfer.glsl"
+
+layout(location = 0) in vec2 texCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D plane1Texture;
+
+void main()
+{
+ int x = int(floor(texCoord.x * ubuf.width));
+ bool rightSubPixel = (x/2*2 != x);
+ float Y = rightSubPixel ? texture(plane1Texture, texCoord).a : texture(plane1Texture, texCoord).g;
+ vec2 UV = texture(plane1Texture, texCoord).rb;
+ fragColor = ubuf.colorMatrix * vec4(Y, UV, 1.0) * ubuf.opacity;
+
+#ifdef QMM_OUTPUTSURFACE_LINEAR
+ fragColor = convertSRGBToLinear(fragColor);
+#endif
+
+ // Clamp output to valid range to account for out-of-range
+ // input values and numerical inaccuracies in YUV->RGB conversion
+ fragColor = clamp(fragColor, 0.0, 1.0);
+}
diff --git a/src/multimedia/shaders/vertex.vert b/src/multimedia/shaders/vertex.vert
new file mode 100644
index 000000000..ce170824e
--- /dev/null
+++ b/src/multimedia/shaders/vertex.vert
@@ -0,0 +1,16 @@
+#version 440
+#extension GL_GOOGLE_include_directive : enable
+
+#include "uniformbuffer.glsl"
+
+layout(location = 0) in vec4 vertexPosition;
+layout(location = 1) in vec2 vertexTexCoord;
+
+layout(location = 0) out vec2 texCoord;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+void main() {
+ texCoord = vertexTexCoord;
+ gl_Position = ubuf.matrix * vertexPosition;
+}
diff --git a/src/multimedia/shaders/y.frag b/src/multimedia/shaders/y.frag
new file mode 100644
index 000000000..8c25fc762
--- /dev/null
+++ b/src/multimedia/shaders/y.frag
@@ -0,0 +1,25 @@
+#version 440
+#extension GL_GOOGLE_include_directive : enable
+
+#include "uniformbuffer.glsl"
+#include "colortransfer.glsl"
+
+layout(location = 0) in vec2 texCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D plane1Texture;
+
+void main()
+{
+ float Y = texture(plane1Texture, texCoord).r;
+ vec4 color = vec4(Y, Y, Y, 1.);
+ fragColor = ubuf.colorMatrix * color * ubuf.opacity;
+
+#ifdef QMM_OUTPUTSURFACE_LINEAR
+ fragColor = convertSRGBToLinear(fragColor);
+#endif
+
+ // Clamp output to valid range to account for out-of-range
+ // input values and numerical inaccuracies in YUV->RGB conversion
+ fragColor = clamp(fragColor, 0.0, 1.0);
+}
diff --git a/src/multimedia/shaders/yuv_triplanar.frag b/src/multimedia/shaders/yuv_triplanar.frag
new file mode 100644
index 000000000..228147943
--- /dev/null
+++ b/src/multimedia/shaders/yuv_triplanar.frag
@@ -0,0 +1,29 @@
+#version 440
+#extension GL_GOOGLE_include_directive : enable
+
+#include "uniformbuffer.glsl"
+#include "colortransfer.glsl"
+
+layout(location = 0) in vec2 texCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D plane1Texture;
+layout(binding = 2) uniform sampler2D plane2Texture;
+layout(binding = 3) uniform sampler2D plane3Texture;
+
+void main()
+{
+ float Y = texture(plane1Texture, texCoord).r;
+ float U = texture(plane2Texture, texCoord).r;
+ float V = texture(plane3Texture, texCoord).r;
+ vec4 color = vec4(Y, U, V, 1.);
+ fragColor = ubuf.colorMatrix * color * ubuf.opacity;
+
+#ifdef QMM_OUTPUTSURFACE_LINEAR
+ fragColor = convertSRGBToLinear(fragColor);
+#endif
+
+ // Clamp output to valid range to account for out-of-range
+ // input values and numerical inaccuracies in YUV->RGB conversion
+ fragColor = clamp(fragColor, 0.0, 1.0);
+}
diff --git a/src/multimedia/shaders/yuv_triplanar_p10.frag b/src/multimedia/shaders/yuv_triplanar_p10.frag
new file mode 100644
index 000000000..5da0ed339
--- /dev/null
+++ b/src/multimedia/shaders/yuv_triplanar_p10.frag
@@ -0,0 +1,29 @@
+#version 440
+#extension GL_GOOGLE_include_directive : enable
+
+#include "uniformbuffer.glsl"
+#include "colortransfer.glsl"
+
+layout(location = 0) in vec2 texCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D plane1Texture;
+layout(binding = 2) uniform sampler2D plane2Texture;
+layout(binding = 3) uniform sampler2D plane3Texture;
+
+void main()
+{
+ float Y = texture(plane1Texture, texCoord).r * 64;
+ float U = texture(plane2Texture, texCoord).r * 64;
+ float V = texture(plane3Texture, texCoord).r * 64;
+ vec4 color = vec4(Y, U, V, 1.);
+ fragColor = ubuf.colorMatrix * color * ubuf.opacity;
+
+#ifdef QMM_OUTPUTSURFACE_LINEAR
+ fragColor = convertSRGBToLinear(fragColor);
+#endif
+
+ // Clamp output to valid range to account for out-of-range
+ // input values and numerical inaccuracies in YUV->RGB conversion
+ fragColor = clamp(fragColor, 0.0, 1.0);
+}
diff --git a/src/multimedia/shaders/yuyv.frag b/src/multimedia/shaders/yuyv.frag
new file mode 100644
index 000000000..0f6e65b6d
--- /dev/null
+++ b/src/multimedia/shaders/yuyv.frag
@@ -0,0 +1,27 @@
+#version 440
+#extension GL_GOOGLE_include_directive : enable
+
+#include "uniformbuffer.glsl"
+#include "colortransfer.glsl"
+
+layout(location = 0) in vec2 texCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D plane1Texture;
+
+void main()
+{
+ int x = int(floor(texCoord.x * ubuf.width));
+ bool rightSubPixel = (x/2*2 != x);
+ float Y = rightSubPixel ? texture(plane1Texture, texCoord).b : texture(plane1Texture, texCoord).r;
+ vec2 UV = texture(plane1Texture, texCoord).ga;
+ fragColor = ubuf.colorMatrix * vec4(Y, UV, 1.0) * ubuf.opacity;
+
+#ifdef QMM_OUTPUTSURFACE_LINEAR
+ fragColor = convertSRGBToLinear(fragColor);
+#endif
+
+ // Clamp output to valid range to account for out-of-range
+ // input values and numerical inaccuracies in YUV->RGB conversion
+ fragColor = clamp(fragColor, 0.0, 1.0);
+}
diff --git a/src/multimedia/shaders/yvu_triplanar.frag b/src/multimedia/shaders/yvu_triplanar.frag
new file mode 100644
index 000000000..ac2cbdf63
--- /dev/null
+++ b/src/multimedia/shaders/yvu_triplanar.frag
@@ -0,0 +1,29 @@
+#version 440
+#extension GL_GOOGLE_include_directive : enable
+
+#include "uniformbuffer.glsl"
+#include "colortransfer.glsl"
+
+layout(location = 0) in vec2 texCoord;
+layout(location = 0) out vec4 fragColor;
+
+layout(binding = 1) uniform sampler2D plane1Texture;
+layout(binding = 2) uniform sampler2D plane2Texture;
+layout(binding = 3) uniform sampler2D plane3Texture;
+
+void main()
+{
+ float Y = texture(plane1Texture, texCoord).r;
+ float V = texture(plane2Texture, texCoord).r;
+ float U = texture(plane3Texture, texCoord).r;
+ vec4 color = vec4(Y, U, V, 1.);
+ fragColor = ubuf.colorMatrix * color * ubuf.opacity;
+
+#ifdef QMM_OUTPUTSURFACE_LINEAR
+ fragColor = convertSRGBToLinear(fragColor);
+#endif
+
+ // Clamp output to valid range to account for out-of-range
+ // input values and numerical inaccuracies in YUV->RGB conversion
+ fragColor = clamp(fragColor, 0.0, 1.0);
+}