summaryrefslogtreecommitdiffstats
path: root/gfx/layers/d3d11/CompositorD3D11.hlsl
blob: c7406625485fd6953852145bcd9a3060ce7c4085 (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
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "BlendingHelpers.hlslh"
#include "BlendShaderConstants.h"

typedef float4 rect;

float4x4 mLayerTransform : register(vs, c0);
float4x4 mProjection : register(vs, c4);
float4 vRenderTargetOffset : register(vs, c8);
rect vTextureCoords : register(vs, c9);
rect vLayerQuad : register(vs, c10);
float4x4 mMaskTransform : register(vs, c11);
float4x4 mBackdropTransform : register(vs, c15);

float4 fLayerColor : register(ps, c0);
float fLayerOpacity : register(ps, c1);

// x = layer type
// y = mask type
// z = blend op
// w = is premultiplied
uint4 iBlendConfig : register(ps, c2);

float fCoefficient : register(ps, c3);

row_major float3x3 mYuvColorMatrix : register(ps, c4);

sampler sSampler : register(ps, s0);

// The mix-blend mega shader uses all variables, so we have to make sure they
// are assigned fixed slots.
Texture2D tRGB : register(ps, t0);
Texture2D tY : register(ps, t1);
Texture2D tCb : register(ps, t2);
Texture2D tCr : register(ps, t3);
Texture2D tRGBWhite : register(ps, t4);
Texture2D tMask : register(ps, t5);
Texture2D tBackdrop : register(ps, t6);

struct VS_INPUT {
  float2 vPosition : POSITION;
};

struct VS_TEX_INPUT {
  float2 vPosition : POSITION;
  float2 vTexCoords : TEXCOORD0;
};

struct VS_OUTPUT {
  float4 vPosition : SV_Position;
  float2 vTexCoords : TEXCOORD0;
};

struct VS_MASK_OUTPUT {
  float4 vPosition : SV_Position;
  float2 vTexCoords : TEXCOORD0;
  float3 vMaskCoords : TEXCOORD1;
};

// Combined struct for the mix-blend compatible vertex shaders.
struct VS_BLEND_OUTPUT {
  float4 vPosition : SV_Position;
  float2 vTexCoords : TEXCOORD0;
  float3 vMaskCoords : TEXCOORD1;
  float2 vBackdropCoords : TEXCOORD2;
};

struct PS_OUTPUT {
  float4 vSrc;
  float4 vAlpha;
};

float2 TexCoords(const float2 aPosition)
{
  float2 result;
  const float2 size = vTextureCoords.zw;
  result.x = vTextureCoords.x + aPosition.x * size.x;
  result.y = vTextureCoords.y + aPosition.y * size.y;

  return result;
}

SamplerState LayerTextureSamplerLinear
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = Clamp;
    AddressV = Clamp;
};

float4 TransformedPosition(float2 aInPosition)
{
  // the current vertex's position on the quad
  // [x,y,0,1] is mandated by the CSS Transforms spec as the point value to transform
  float4 position = float4(0, 0, 0, 1);

  // We use 4 component floats to uniquely describe a rectangle, by the structure
  // of x, y, width, height. This allows us to easily generate the 4 corners
  // of any rectangle from the 4 corners of the 0,0-1,1 quad that we use as the
  // stream source for our LayerQuad vertex shader. We do this by doing:
  // Xout = x + Xin * width
  // Yout = y + Yin * height
  float2 size = vLayerQuad.zw;
  position.x = vLayerQuad.x + aInPosition.x * size.x;
  position.y = vLayerQuad.y + aInPosition.y * size.y;

  position = mul(mLayerTransform, position);

  return position;
}

float4 VertexPosition(float4 aTransformedPosition)
{
  float4 result;
  result.w = aTransformedPosition.w;
  result.xyz = aTransformedPosition.xyz / aTransformedPosition.w;
  result -= vRenderTargetOffset;
  result.xyz *= result.w;

  result = mul(mProjection, result);

  return result;
}

float2 BackdropPosition(float4 aPosition)
{
  // Move the position from clip space (-1,1) into 0..1 space.
  float2 pos;
  pos.x = (aPosition.x + 1.0) / 2.0;
  pos.y = 1.0 - (aPosition.y + 1.0) / 2.0;

  return mul(mBackdropTransform, float4(pos.xy, 0, 1.0)).xy;
}

VS_OUTPUT LayerQuadVS(const VS_INPUT aVertex)
{
  VS_OUTPUT outp;
  float4 position = TransformedPosition(aVertex.vPosition);

  outp.vPosition = VertexPosition(position);
  outp.vTexCoords = TexCoords(aVertex.vPosition.xy);

  return outp;
}

float3 MaskCoords(float4 aPosition)
{
  // We use the w coord to do non-perspective correct interpolation:
  // the quad might be transformed in 3D, in which case it will have some
  // perspective. The graphics card will do perspective-correct interpolation
  // of the texture, but our mask is already transformed and so we require
  // linear interpolation. Therefore, we must correct the interpolation
  // ourselves, we do this by multiplying all coords by w here, and dividing by
  // w in the pixel shader (post-interpolation), we pass w in outp.vMaskCoords.z.
  // See http://en.wikipedia.org/wiki/Texture_mapping#Perspective_correctness
  return float3(mul(mMaskTransform, (aPosition / aPosition.w)).xy, 1.0) * aPosition.w;
}

VS_MASK_OUTPUT LayerQuadMaskVS(const VS_INPUT aVertex)
{
  float4 position = TransformedPosition(aVertex.vPosition);

  VS_MASK_OUTPUT outp;
  outp.vPosition = VertexPosition(position);
  outp.vMaskCoords = MaskCoords(position);
  outp.vTexCoords = TexCoords(aVertex.vPosition.xy);
  return outp;
}

VS_OUTPUT LayerDynamicVS(const VS_TEX_INPUT aVertex)
{
  VS_OUTPUT outp;

  float4 position = float4(aVertex.vPosition, 0, 1);
  position = mul(mLayerTransform, position);
  outp.vPosition = VertexPosition(position);

  outp.vTexCoords = aVertex.vTexCoords;

  return outp;
}

VS_MASK_OUTPUT LayerDynamicMaskVS(const VS_TEX_INPUT aVertex)
{
  VS_MASK_OUTPUT outp;

  float4 position = float4(aVertex.vPosition, 0, 1);
  position = mul(mLayerTransform, position);
  outp.vPosition = VertexPosition(position);

  // calculate the position on the mask texture
  outp.vMaskCoords = MaskCoords(position);
  outp.vTexCoords = aVertex.vTexCoords;
  return outp;
}

float4 RGBAShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target
{
  float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z;
  float mask = tMask.Sample(sSampler, maskCoords).r;
  return tRGB.Sample(sSampler, aVertex.vTexCoords) * fLayerOpacity * mask;
}

float4 RGBShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target
{
  float4 result;
  result = tRGB.Sample(sSampler, aVertex.vTexCoords) * fLayerOpacity;
  result.a = fLayerOpacity;

  float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z;
  float mask = tMask.Sample(sSampler, maskCoords).r;
  return result * mask;
}

/* From Rec601:
[R]   [1.1643835616438356,  0.0,                 1.5960267857142858]      [ Y -  16]
[G] = [1.1643835616438358, -0.3917622900949137, -0.8129676472377708]    x [Cb - 128]
[B]   [1.1643835616438356,  2.017232142857143,   8.862867620416422e-17]   [Cr - 128]

For [0,1] instead of [0,255], and to 5 places:
[R]   [1.16438,  0.00000,  1.59603]   [ Y - 0.06275]
[G] = [1.16438, -0.39176, -0.81297] x [Cb - 0.50196]
[B]   [1.16438,  2.01723,  0.00000]   [Cr - 0.50196]

From Rec709:
[R]   [1.1643835616438356,  4.2781193979771426e-17, 1.7927410714285714]     [ Y -  16]
[G] = [1.1643835616438358, -0.21324861427372963,   -0.532909328559444]    x [Cb - 128]
[B]   [1.1643835616438356,  2.1124017857142854,     0.0]                    [Cr - 128]

For [0,1] instead of [0,255], and to 5 places:
[R]   [1.16438,  0.00000,  1.79274]   [ Y - 0.06275]
[G] = [1.16438, -0.21325, -0.53291] x [Cb - 0.50196]
[B]   [1.16438,  2.11240,  0.00000]   [Cr - 0.50196]
*/
float4 CalculateYCbCrColor(const float2 aTexCoords)
{
  float3 yuv = float3(
    tY.Sample(sSampler, aTexCoords).r,
    tCb.Sample(sSampler, aTexCoords).r,
    tCr.Sample(sSampler, aTexCoords).r);
  yuv = yuv * fCoefficient - float3(0.06275, 0.50196, 0.50196);

  return float4(mul(mYuvColorMatrix, yuv), 1.0);
}

float4 CalculateNV12Color(const float2 aTexCoords)
{
  float3 yuv = float3(
    tY.Sample(sSampler, aTexCoords).r,
    tCb.Sample(sSampler, aTexCoords).r,
    tCb.Sample(sSampler, aTexCoords).g);
  yuv = yuv * fCoefficient - float3(0.06275, 0.50196, 0.50196);

  return float4(mul(mYuvColorMatrix, yuv), 1.0);
}

float4 YCbCrShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target
{
  float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z;
  float mask = tMask.Sample(sSampler, maskCoords).r;

  return CalculateYCbCrColor(aVertex.vTexCoords) * fLayerOpacity * mask;
}

float4 NV12ShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target
{
  float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z;
  float mask = tMask.Sample(sSampler, maskCoords).r;

  return CalculateNV12Color(aVertex.vTexCoords) * fLayerOpacity * mask;
}

PS_OUTPUT ComponentAlphaShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target
{
  PS_OUTPUT result;

  result.vSrc = tRGB.Sample(sSampler, aVertex.vTexCoords);
  result.vAlpha = 1.0 - tRGBWhite.Sample(sSampler, aVertex.vTexCoords) + result.vSrc;
  result.vSrc.a = result.vAlpha.g;

  float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z;
  float mask = tMask.Sample(sSampler, maskCoords).r;
  result.vSrc *= fLayerOpacity * mask;
  result.vAlpha *= fLayerOpacity * mask;

  return result;
}

float4 SolidColorShaderMask(const VS_MASK_OUTPUT aVertex) : SV_Target
{
  float2 maskCoords = aVertex.vMaskCoords.xy / aVertex.vMaskCoords.z;
  float mask = tMask.Sample(sSampler, maskCoords).r;
  return fLayerColor * mask;
}

/*
 *  Un-masked versions
 *************************************************************
 */
float4 RGBAShader(const VS_OUTPUT aVertex) : SV_Target
{
  return tRGB.Sample(sSampler, aVertex.vTexCoords) * fLayerOpacity;
}

float4 RGBShader(const VS_OUTPUT aVertex) : SV_Target
{
  float4 result;
  result = tRGB.Sample(sSampler, aVertex.vTexCoords) * fLayerOpacity;
  result.a = fLayerOpacity;
  return result;
}

float4 YCbCrShader(const VS_OUTPUT aVertex) : SV_Target
{
  return CalculateYCbCrColor(aVertex.vTexCoords) * fLayerOpacity;
}

float4 NV12Shader(const VS_OUTPUT aVertex) : SV_Target
{
  return CalculateNV12Color(aVertex.vTexCoords) * fLayerOpacity;
}

PS_OUTPUT ComponentAlphaShader(const VS_OUTPUT aVertex) : SV_Target
{
  PS_OUTPUT result;

  result.vSrc = tRGB.Sample(sSampler, aVertex.vTexCoords);
  result.vAlpha = 1.0 - tRGBWhite.Sample(sSampler, aVertex.vTexCoords) + result.vSrc;
  result.vSrc.a = result.vAlpha.g;
  result.vSrc *= fLayerOpacity;
  result.vAlpha *= fLayerOpacity;
  return result;
}

float4 SolidColorShader(const VS_OUTPUT aVertex) : SV_Target
{
  return fLayerColor;
}

// Mix-blend compatible vertex shaders.
VS_BLEND_OUTPUT LayerQuadBlendVS(const VS_INPUT aVertex)
{
  VS_OUTPUT v = LayerQuadVS(aVertex);

  VS_BLEND_OUTPUT o;
  o.vPosition = v.vPosition;
  o.vTexCoords = v.vTexCoords;
  o.vMaskCoords = float3(0, 0, 0);
  o.vBackdropCoords = BackdropPosition(v.vPosition);
  return o;
}

VS_BLEND_OUTPUT LayerQuadBlendMaskVS(const VS_INPUT aVertex)
{
  VS_MASK_OUTPUT v = LayerQuadMaskVS(aVertex);

  VS_BLEND_OUTPUT o;
  o.vPosition = v.vPosition;
  o.vTexCoords = v.vTexCoords;
  o.vMaskCoords = v.vMaskCoords;
  o.vBackdropCoords = BackdropPosition(v.vPosition);
  return o;
}

VS_BLEND_OUTPUT LayerDynamicBlendVS(const VS_TEX_INPUT aVertex)
{
  VS_OUTPUT v = LayerDynamicVS(aVertex);

  VS_BLEND_OUTPUT o;
  o.vPosition = v.vPosition;
  o.vTexCoords = v.vTexCoords;
  o.vMaskCoords = float3(0, 0, 0);
  o.vBackdropCoords = BackdropPosition(v.vPosition);
  return o;
}

VS_BLEND_OUTPUT LayerDynamicBlendMaskVS(const VS_TEX_INPUT aVertex)
{
  VS_MASK_OUTPUT v = LayerDynamicMaskVS(aVertex);

  VS_BLEND_OUTPUT o;
  o.vPosition = v.vPosition;
  o.vTexCoords = v.vTexCoords;
  o.vMaskCoords = v.vMaskCoords;
  o.vBackdropCoords = BackdropPosition(v.vPosition);
  return o;
}

// The layer type and mask type are specified as constants. We use these to
// call the correct pixel shader to determine the source color for blending.
// Unfortunately this also requires some boilerplate to convert VS_BLEND_OUTPUT
// to a compatible pixel shader input.
float4 ComputeBlendSourceColor(const VS_BLEND_OUTPUT aVertex)
{
  if (iBlendConfig.y == PS_MASK_NONE) {
    VS_OUTPUT tmp;
    tmp.vPosition = aVertex.vPosition;
    tmp.vTexCoords = aVertex.vTexCoords;
    if (iBlendConfig.x == PS_LAYER_RGB) {
      return RGBShader(tmp);
    } else if (iBlendConfig.x == PS_LAYER_RGBA) {
      return RGBAShader(tmp);
    } else if (iBlendConfig.x == PS_LAYER_YCBCR) {
      return YCbCrShader(tmp);
    } else if (iBlendConfig.x == PS_LAYER_NV12) {
      return NV12Shader(tmp);
    } else {
      return SolidColorShader(tmp);
    }
  } else if (iBlendConfig.y == PS_MASK) {
    VS_MASK_OUTPUT tmp;
    tmp.vPosition = aVertex.vPosition;
    tmp.vTexCoords = aVertex.vTexCoords;
    tmp.vMaskCoords = aVertex.vMaskCoords;

    if (iBlendConfig.x == PS_LAYER_RGB) {
      return RGBShaderMask(tmp);
    } else if (iBlendConfig.x == PS_LAYER_RGBA) {
      return RGBAShaderMask(tmp);
    } else if (iBlendConfig.x == PS_LAYER_YCBCR) {
      return YCbCrShaderMask(tmp);
    } else if (iBlendConfig.x == PS_LAYER_NV12) {
      return NV12ShaderMask(tmp);
    } else {
      return SolidColorShaderMask(tmp);
    }
  } else {
    return float4(0.0, 0.0, 0.0, 1.0);
  }
}

float3 ChooseBlendFunc(float3 dest, float3 src)
{
  [flatten] switch (iBlendConfig.z) {
    case PS_BLEND_MULTIPLY:
      return BlendMultiply(dest, src);
    case PS_BLEND_SCREEN:
      return BlendScreen(dest, src);
    case PS_BLEND_OVERLAY:
      return BlendOverlay(dest, src);
    case PS_BLEND_DARKEN:
      return BlendDarken(dest, src);
    case PS_BLEND_LIGHTEN:
      return BlendLighten(dest, src);
    case PS_BLEND_COLOR_DODGE:
      return BlendColorDodge(dest, src);
    case PS_BLEND_COLOR_BURN:
      return BlendColorBurn(dest, src);
    case PS_BLEND_HARD_LIGHT:
      return BlendHardLight(dest, src);
    case PS_BLEND_SOFT_LIGHT:
      return BlendSoftLight(dest, src);
    case PS_BLEND_DIFFERENCE:
      return BlendDifference(dest, src);
    case PS_BLEND_EXCLUSION:
      return BlendExclusion(dest, src);
    case PS_BLEND_HUE:
      return BlendHue(dest, src);
    case PS_BLEND_SATURATION:
      return BlendSaturation(dest, src);
    case PS_BLEND_COLOR:
      return BlendColor(dest, src);
    case PS_BLEND_LUMINOSITY:
      return BlendLuminosity(dest, src);
    default:
      return float3(0, 0, 0);
  }
}

float4 BlendShader(const VS_BLEND_OUTPUT aVertex) : SV_Target
{
  float4 backdrop = tBackdrop.Sample(sSampler, aVertex.vBackdropCoords.xy);
  float4 source = ComputeBlendSourceColor(aVertex);

  // Shortcut when the backdrop or source alpha is 0, otherwise we may leak
  // infinity into the blend function and return incorrect results.
  if (backdrop.a == 0.0) {
    return source;
  }
  if (source.a == 0.0) {
    return float4(0, 0, 0, 0);
  }

  // The spec assumes there is no premultiplied alpha. The backdrop is always
  // premultiplied, so undo the premultiply. If the source is premultiplied we
  // must fix that as well.
  backdrop.rgb /= backdrop.a;
  if (iBlendConfig.w) {
    source.rgb /= source.a;
  }

  float4 result;
  result.rgb = ChooseBlendFunc(backdrop.rgb, source.rgb);
  result.a = source.a;

  // Factor backdrop alpha, then premultiply for the final OP_OVER.
  result.rgb = (1.0 - backdrop.a) * source.rgb + backdrop.a * result.rgb;
  result.rgb *= result.a;
  return result;
}