tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

GLBlitHelper.cpp (73784B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "GLBlitHelper.h"
      8 
      9 #include "gfxEnv.h"
     10 #include "gfxUtils.h"
     11 #include "GLContext.h"
     12 #include "GLScreenBuffer.h"
     13 #include "GPUVideoImage.h"
     14 #include "HeapCopyOfStackArray.h"
     15 #include "ImageContainer.h"
     16 #include "ScopedGLHelpers.h"
     17 #include "GLUploadHelpers.h"
     18 #include "mozilla/Casting.h"
     19 #include "mozilla/Preferences.h"
     20 #include "mozilla/StaticPrefs_gfx.h"
     21 #include "mozilla/UniquePtr.h"
     22 #include "mozilla/gfx/BuildConstants.h"
     23 #include "mozilla/gfx/Logging.h"
     24 #include "mozilla/gfx/Matrix.h"
     25 #include "mozilla/layers/ImageDataSerializer.h"
     26 #include "mozilla/layers/LayersSurfaces.h"
     27 
     28 #ifdef MOZ_WIDGET_ANDROID
     29 #  include "AndroidSurfaceTexture.h"
     30 #  include "GLImages.h"
     31 #  include "GLLibraryEGL.h"
     32 #endif
     33 
     34 #ifdef XP_MACOSX
     35 #  include "GLContextCGL.h"
     36 #  include "MacIOSurfaceImage.h"
     37 #endif
     38 
     39 #ifdef XP_WIN
     40 #  include "mozilla/layers/D3D11ShareHandleImage.h"
     41 #  include "mozilla/layers/D3D11ZeroCopyTextureImage.h"
     42 #  include "mozilla/layers/D3D11YCbCrImage.h"
     43 #endif
     44 
     45 #ifdef MOZ_WIDGET_GTK
     46 #  include "mozilla/layers/DMABUFSurfaceImage.h"
     47 #  include "mozilla/widget/DMABufSurface.h"
     48 #  include "mozilla/widget/DMABufDevice.h"
     49 #endif
     50 
     51 using mozilla::layers::PlanarYCbCrData;
     52 using mozilla::layers::PlanarYCbCrImage;
     53 
     54 namespace mozilla {
     55 namespace gl {
     56 
     57 // --
     58 
     59 static const char kFragPreprocHeader[] = R"(
     60  #ifdef GL_ES
     61    #ifdef GL_FRAGMENT_PRECISION_HIGH
     62      #define MAXP highp
     63    #endif
     64  #else
     65    #define MAXP highp
     66  #endif
     67  #ifndef MAXP
     68    #define MAXP mediump
     69  #endif
     70 
     71  #if __VERSION__ >= 130
     72    #define VARYING in
     73  #else
     74    #define VARYING varying
     75  #endif
     76  #if __VERSION__ >= 120
     77    #define MAT4X3 mat4x3
     78  #else
     79    #define MAT4X3 mat4
     80  #endif
     81 )";
     82 
     83 // -
     84 
     85 const char* const kFragHeader_Tex2D = R"(
     86    #define SAMPLER sampler2D
     87    #if __VERSION__ >= 130
     88        #define TEXTURE texture
     89    #else
     90        #define TEXTURE texture2D
     91    #endif
     92 )";
     93 const char* const kFragHeader_Tex2DRect = R"(
     94    #define SAMPLER sampler2DRect
     95    #if __VERSION__ >= 130
     96        #define TEXTURE texture
     97    #else
     98        #define TEXTURE texture2DRect
     99    #endif
    100 )";
    101 const char* const kFragHeader_TexExt = R"(
    102    #extension GL_OES_EGL_image_external : enable
    103    #extension GL_OES_EGL_image_external_essl3 : enable
    104    #if __VERSION__ >= 130
    105        #define TEXTURE texture
    106    #else
    107        #define TEXTURE texture2D
    108    #endif
    109    #define SAMPLER samplerExternalOES
    110 )";
    111 
    112 // -
    113 
    114 static const char kFragDeclHeader[] = R"(
    115  precision PRECISION float;
    116  #if __VERSION__ >= 130
    117    #define FRAG_COLOR oFragColor
    118    out vec4 FRAG_COLOR;
    119  #else
    120    #define FRAG_COLOR gl_FragColor
    121  #endif
    122 )";
    123 
    124 // -
    125 
    126 const char* const kFragSample_OnePlane = R"(
    127  VARYING mediump vec2 vTexCoord0;
    128  uniform PRECISION SAMPLER uTex0;
    129 
    130  vec4 metaSample() {
    131    vec4 src = TEXTURE(uTex0, vTexCoord0);
    132    return src;
    133  }
    134 )";
    135 // Ideally this would just change the color-matrix it uses, but this is
    136 // acceptable debt for now.
    137 // `extern` so that we don't get ifdef-dependent const-var-unused Werrors.
    138 extern const char* const kFragSample_OnePlane_YUV_via_GBR = R"(
    139  VARYING mediump vec2 vTexCoord0;
    140  uniform PRECISION SAMPLER uTex0;
    141 
    142  vec4 metaSample() {
    143    vec4 yuva = TEXTURE(uTex0, vTexCoord0).gbra;
    144    return yuva;
    145  }
    146 )";
    147 const char* const kFragSample_TwoPlane = R"(
    148  VARYING mediump vec2 vTexCoord0;
    149  VARYING mediump vec2 vTexCoord1;
    150  uniform PRECISION SAMPLER uTex0;
    151  uniform PRECISION SAMPLER uTex1;
    152 
    153  vec4 metaSample() {
    154    vec4 src = TEXTURE(uTex0, vTexCoord0); // Keep r and a.
    155    src.gb = TEXTURE(uTex1, vTexCoord1).rg;
    156    return src;
    157  }
    158 )";
    159 const char* const kFragSample_ThreePlane = R"(
    160  VARYING mediump vec2 vTexCoord0;
    161  VARYING mediump vec2 vTexCoord1;
    162  uniform PRECISION SAMPLER uTex0;
    163  uniform PRECISION SAMPLER uTex1;
    164  uniform PRECISION SAMPLER uTex2;
    165 
    166  vec4 metaSample() {
    167    vec4 src = TEXTURE(uTex0, vTexCoord0); // Keep r and a.
    168    src.g = TEXTURE(uTex1, vTexCoord1).r;
    169    src.b = TEXTURE(uTex2, vTexCoord1).r;
    170    return src;
    171  }
    172 )";
    173 
    174 extern const char* const kFragSample_TwoPlaneUV = R"(
    175  VARYING mediump vec2 vTexCoord0;
    176  uniform PRECISION SAMPLER uTex1;
    177  uniform PRECISION SAMPLER uTex2;
    178 
    179  vec4 metaSample() {
    180    vec4 src = TEXTURE(uTex1, vTexCoord0);
    181    src.g = TEXTURE(uTex2, vTexCoord0).r;
    182    return src;
    183  }
    184 )";
    185 
    186 // -
    187 
    188 const char* const kFragConvert_None = R"(
    189  vec3 metaConvert(vec3 src) {
    190    return src;
    191  }
    192 )";
    193 const char* const kFragConvert_BGR = R"(
    194  vec3 metaConvert(vec3 src) {
    195    return src.bgr;
    196  }
    197 )";
    198 const char* const kFragConvert_ColorMatrix = R"(
    199  uniform mediump MAT4X3 uColorMatrix;
    200 
    201  vec3 metaConvert(vec3 src) {
    202    return (uColorMatrix * vec4(src, 1)).rgb;
    203  }
    204 )";
    205 const char* const kFragConvert_ColorLut3d = R"(
    206  uniform PRECISION sampler3D uColorLut;
    207 
    208  vec3 metaConvert(vec3 src) {
    209    // Half-texel filtering hazard!
    210    // E.g. For texture size of 2,
    211    // E.g. x=0.25 is still sampling 100% of texel x=0, 0% of texel x=1.
    212    // For the LUT, we need r=0.25 to filter 75/25 from texel 0 and 1.
    213    // That is, we need to adjust our sampling point such that it starts in the
    214    // center of texel 0, and ends in the center of texel N-1.
    215    // We need, for N=2:
    216    // v=0.0|N=2 => v'=0.5/2
    217    // v=1.0|N=2 => v'=1.5/2
    218    // For N=3:
    219    // v=0.0|N=3 => v'=0.5/3
    220    // v=1.0|N=3 => v'=2.5/3
    221    // => v' = ( 0.5 + v * (3 - 1) )/3
    222    vec3 size = vec3(textureSize(uColorLut, 0));
    223    src = (0.5 + src * (size - 1.0)) / size;
    224    return texture(uColorLut, src).rgb;
    225  }
    226 )";
    227 // Delete if unused after 2024-10-01:
    228 const char* const kFragConvert_ColorLut2d = R"(
    229  uniform PRECISION sampler2D uColorLut;
    230  uniform mediump vec3 uColorLut3dSize;
    231 
    232  vec3 metaConvert(vec3 src) {
    233    // Half-texel filtering hazard!
    234    // E.g. For texture size of 2,
    235    // E.g. x=0.25 is still sampling 100% of texel x=0, 0% of texel x=1.
    236    // For the LUT, we need r=0.25 to filter 75/25 from texel 0 and 1.
    237    // That is, we need to adjust our sampling point such that it starts in the
    238    // center of texel 0, and ends in the center of texel N-1.
    239    // We need, for N=2:
    240    // v=0.0|N=2 => v'=0.5/2
    241    // v=1.0|N=2 => v'=1.5/2
    242    // For N=3:
    243    // v=0.0|N=3 => v'=0.5/3
    244    // v=1.0|N=3 => v'=2.5/3
    245    // => v' = ( 0.5 + v * (3 - 1) )/3
    246    src = clamp(src, vec3(0,0,0), vec3(1,1,1));
    247    vec3 lut3dSize = uColorLut3dSize;
    248    vec2 lut2dSize = vec2(lut3dSize.x, lut3dSize.y * lut3dSize.z);
    249    vec3 texelSrc3d = 0.5 + src * (lut3dSize - 1.0);
    250 
    251    vec3 texelSrc3d_zFloor = texelSrc3d;
    252    texelSrc3d_zFloor.z = floor(texelSrc3d_zFloor.z);
    253    vec3 texelSrc3d_zNext = texelSrc3d_zFloor + vec3(0,0,1);
    254    texelSrc3d_zNext.z = min(texelSrc3d_zNext.z, lut3dSize.z - 1.0);
    255 
    256    vec2 texelSrc2d_zFloor = texelSrc3d_zFloor.xy + vec2(0, texelSrc3d_zFloor.z * lut3dSize.y);
    257    vec2 texelSrc2d_zNext  = texelSrc3d_zNext.xy  + vec2(0, texelSrc3d_zNext.z  * lut3dSize.y);
    258 
    259    vec4 dst_zFloor = texture(uColorLut, texelSrc2d_zFloor / lut2dSize);
    260    vec4 dst_zNext = texture(uColorLut, texelSrc2d_zNext / lut2dSize);
    261 
    262    return mix(dst_zFloor, dst_zNext, texelSrc3d.z - texelSrc3d_zFloor.z);
    263  }
    264 )";
    265 
    266 extern const char* const kFragConvertYUVP010 = R"(
    267  vec3 metaConvert(vec3 src) {
    268    // YUV420P10 and P010 are both 10-bit formats stored in 16-bit integer.
    269    // P010 has 6 lower bits 0 (value is shifted to upper bits)
    270    // while YUV420P10 has upper 6 bites zeroed.
    271    src *= 64.0;
    272    return src;
    273  }
    274 )";
    275 
    276 // -
    277 
    278 const char* const kFragMixin_AlphaMultColors = R"(
    279  #define MIXIN_ALPHA_MULT_COLORS
    280 )";
    281 const char* const kFragMixin_AlphaUnpremultColors = R"(
    282  #define MIXIN_ALPHA_UNPREMULT_COLORS
    283 )";
    284 const char* const kFragMixin_AlphaClampColors = R"(
    285  #define MIXIN_ALPHA_CLAMP_COLORS
    286 )";
    287 const char* const kFragMixin_AlphaOne = R"(
    288  #define MIXIN_ALPHA_ONE
    289 )";
    290 
    291 // -
    292 
    293 static const char kFragBody[] = R"(
    294  void main(void) {
    295    vec4 src = metaSample();
    296    vec4 dst = vec4(metaConvert(src.rgb), src.a);
    297 
    298  #ifdef MIXIN_ALPHA_MULT_COLORS
    299    dst.rgb *= dst.a;
    300  #endif
    301  #ifdef MIXIN_ALPHA_UNPREMULT_COLORS
    302    dst.rgb = dst.a != 0.0 ? dst.rgb / dst.a : dst.rgb;
    303  #endif
    304  #ifdef MIXIN_ALPHA_CLAMP_COLORS
    305    dst.rgb = min(dst.rgb, vec3(dst.a)); // Ensure valid premult-alpha colors.
    306  #endif
    307  #ifdef MIXIN_ALPHA_ONE
    308    dst.a = 1.0;
    309  #endif
    310 
    311    FRAG_COLOR = dst;
    312  }
    313 )";
    314 
    315 // --
    316 
    317 Mat3 SubRectMat3(const float x, const float y, const float w, const float h) {
    318  auto ret = Mat3{};
    319  ret.at(0, 0) = w;
    320  ret.at(1, 1) = h;
    321  ret.at(2, 0) = x;
    322  ret.at(2, 1) = y;
    323  ret.at(2, 2) = 1.0f;
    324  return ret;
    325 }
    326 
    327 Mat3 SubRectMat3(const gfx::IntRect& subrect, const gfx::IntSize& size) {
    328  return SubRectMat3(float(subrect.X()) / size.width,
    329                     float(subrect.Y()) / size.height,
    330                     float(subrect.Width()) / size.width,
    331                     float(subrect.Height()) / size.height);
    332 }
    333 
    334 Mat3 SubRectMat3(const gfx::IntRect& bigSubrect, const gfx::IntSize& smallSize,
    335                 const gfx::IntSize& divisors) {
    336  const float x = float(bigSubrect.X()) / divisors.width;
    337  const float y = float(bigSubrect.Y()) / divisors.height;
    338  const float w = float(bigSubrect.Width()) / divisors.width;
    339  const float h = float(bigSubrect.Height()) / divisors.height;
    340  return SubRectMat3(x / smallSize.width, y / smallSize.height,
    341                     w / smallSize.width, h / smallSize.height);
    342 }
    343 
    344 Mat3 MatrixToMat3(const gfx::Matrix& aMatrix) {
    345  auto ret = Mat3();
    346  ret.at(0, 0) = aMatrix._11;
    347  ret.at(1, 0) = aMatrix._21;
    348  ret.at(2, 0) = aMatrix._31;
    349  ret.at(0, 1) = aMatrix._12;
    350  ret.at(1, 1) = aMatrix._22;
    351  ret.at(2, 1) = aMatrix._32;
    352  ret.at(0, 2) = 0.0f;
    353  ret.at(1, 2) = 0.0f;
    354  ret.at(2, 2) = 1.0f;
    355  return ret;
    356 }
    357 
    358 // --
    359 
    360 ScopedSaveMultiTex::ScopedSaveMultiTex(GLContext* const gl,
    361                                       const size_t texUnits,
    362                                       const GLenum texTarget)
    363    : mGL(*gl),
    364      mTexUnits(texUnits),
    365      mTexTarget(texTarget),
    366      mOldTexUnit(mGL.GetIntAs<GLenum>(LOCAL_GL_ACTIVE_TEXTURE)) {
    367  MOZ_RELEASE_ASSERT(texUnits >= 1);
    368 
    369  GLenum texBinding;
    370  switch (mTexTarget) {
    371    case LOCAL_GL_TEXTURE_2D:
    372      texBinding = LOCAL_GL_TEXTURE_BINDING_2D;
    373      break;
    374    case LOCAL_GL_TEXTURE_3D:
    375      texBinding = LOCAL_GL_TEXTURE_BINDING_3D;
    376      break;
    377    case LOCAL_GL_TEXTURE_RECTANGLE:
    378      texBinding = LOCAL_GL_TEXTURE_BINDING_RECTANGLE;
    379      break;
    380    case LOCAL_GL_TEXTURE_EXTERNAL:
    381      texBinding = LOCAL_GL_TEXTURE_BINDING_EXTERNAL;
    382      break;
    383    default:
    384      gfxCriticalError() << "Unhandled texTarget: " << texTarget;
    385      MOZ_CRASH();
    386  }
    387 
    388  for (const auto i : IntegerRange(mTexUnits)) {
    389    mGL.fActiveTexture(LOCAL_GL_TEXTURE0 + i);
    390    if (mGL.IsSupported(GLFeature::sampler_objects)) {
    391      mOldTexSampler[i] = mGL.GetIntAs<GLuint>(LOCAL_GL_SAMPLER_BINDING);
    392      mGL.fBindSampler(i, 0);
    393    }
    394    mOldTex[i] = mGL.GetIntAs<GLuint>(texBinding);
    395  }
    396 }
    397 
    398 ScopedSaveMultiTex::~ScopedSaveMultiTex() {
    399  // Unbind in reverse order, in case we have repeats.
    400  // Order matters because we unbound samplers during ctor, so now we have to
    401  // make sure we rebind them in the right order.
    402  for (const auto i : Reversed(IntegerRange(mTexUnits))) {
    403    mGL.fActiveTexture(LOCAL_GL_TEXTURE0 + i);
    404    if (mGL.IsSupported(GLFeature::sampler_objects)) {
    405      mGL.fBindSampler(i, mOldTexSampler[i]);
    406    }
    407    mGL.fBindTexture(mTexTarget, mOldTex[i]);
    408  }
    409  mGL.fActiveTexture(mOldTexUnit);
    410 }
    411 
    412 // --
    413 
    414 class ScopedBindArrayBuffer final {
    415 public:
    416  GLContext& mGL;
    417  const GLuint mOldVBO;
    418 
    419  ScopedBindArrayBuffer(GLContext* const gl, const GLuint vbo)
    420      : mGL(*gl), mOldVBO(mGL.GetIntAs<GLuint>(LOCAL_GL_ARRAY_BUFFER_BINDING)) {
    421    mGL.fBindBuffer(LOCAL_GL_ARRAY_BUFFER, vbo);
    422  }
    423 
    424  ~ScopedBindArrayBuffer() { mGL.fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mOldVBO); }
    425 };
    426 
    427 // --
    428 
    429 class ScopedShader final {
    430  GLContext& mGL;
    431  const GLuint mName;
    432 
    433 public:
    434  ScopedShader(GLContext* const gl, const GLenum shaderType)
    435      : mGL(*gl), mName(mGL.fCreateShader(shaderType)) {}
    436 
    437  ~ScopedShader() { mGL.fDeleteShader(mName); }
    438 
    439  operator GLuint() const { return mName; }
    440 };
    441 
    442 // --
    443 
    444 class SaveRestoreCurrentProgram final {
    445  GLContext& mGL;
    446  const GLuint mOld;
    447 
    448 public:
    449  explicit SaveRestoreCurrentProgram(GLContext* const gl)
    450      : mGL(*gl), mOld(mGL.GetIntAs<GLuint>(LOCAL_GL_CURRENT_PROGRAM)) {}
    451 
    452  ~SaveRestoreCurrentProgram() { mGL.fUseProgram(mOld); }
    453 };
    454 
    455 // --
    456 
    457 class ScopedDrawBlitState final {
    458  GLContext& mGL;
    459 
    460  const bool blend;
    461  const bool cullFace;
    462  const bool depthTest;
    463  const bool dither;
    464  const bool polyOffsFill;
    465  const bool sampleAToC;
    466  const bool sampleCover;
    467  const bool scissor;
    468  const bool stencil;
    469  Maybe<bool> rasterizerDiscard;
    470 
    471  realGLboolean colorMask[4];
    472  GLint viewport[4];
    473 
    474 public:
    475  ScopedDrawBlitState(GLContext* const gl, const gfx::IntSize& fbSize)
    476      : mGL(*gl),
    477        blend(mGL.PushEnabled(LOCAL_GL_BLEND, false)),
    478        cullFace(mGL.PushEnabled(LOCAL_GL_CULL_FACE, false)),
    479        depthTest(mGL.PushEnabled(LOCAL_GL_DEPTH_TEST, false)),
    480        dither(mGL.PushEnabled(LOCAL_GL_DITHER, true)),
    481        polyOffsFill(mGL.PushEnabled(LOCAL_GL_POLYGON_OFFSET_FILL, false)),
    482        sampleAToC(mGL.PushEnabled(LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE, false)),
    483        sampleCover(mGL.PushEnabled(LOCAL_GL_SAMPLE_COVERAGE, false)),
    484        scissor(mGL.PushEnabled(LOCAL_GL_SCISSOR_TEST, false)),
    485        stencil(mGL.PushEnabled(LOCAL_GL_STENCIL_TEST, false)) {
    486    if (mGL.IsSupported(GLFeature::transform_feedback2)) {
    487      // Technically transform_feedback2 requires transform_feedback, which
    488      // actually adds RASTERIZER_DISCARD.
    489      rasterizerDiscard =
    490          Some(mGL.PushEnabled(LOCAL_GL_RASTERIZER_DISCARD, false));
    491    }
    492 
    493    mGL.fGetBooleanv(LOCAL_GL_COLOR_WRITEMASK, colorMask);
    494    if (mGL.IsSupported(GLFeature::draw_buffers_indexed)) {
    495      mGL.fColorMaski(0, true, true, true, true);
    496    } else {
    497      mGL.fColorMask(true, true, true, true);
    498    }
    499 
    500    mGL.fGetIntegerv(LOCAL_GL_VIEWPORT, viewport);
    501    MOZ_ASSERT(fbSize.width && fbSize.height);
    502    mGL.fViewport(0, 0, fbSize.width, fbSize.height);
    503  }
    504 
    505  ~ScopedDrawBlitState() {
    506    mGL.SetEnabled(LOCAL_GL_BLEND, blend);
    507    mGL.SetEnabled(LOCAL_GL_CULL_FACE, cullFace);
    508    mGL.SetEnabled(LOCAL_GL_DEPTH_TEST, depthTest);
    509    mGL.SetEnabled(LOCAL_GL_DITHER, dither);
    510    mGL.SetEnabled(LOCAL_GL_POLYGON_OFFSET_FILL, polyOffsFill);
    511    mGL.SetEnabled(LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE, sampleAToC);
    512    mGL.SetEnabled(LOCAL_GL_SAMPLE_COVERAGE, sampleCover);
    513    mGL.SetEnabled(LOCAL_GL_SCISSOR_TEST, scissor);
    514    mGL.SetEnabled(LOCAL_GL_STENCIL_TEST, stencil);
    515    if (rasterizerDiscard) {
    516      mGL.SetEnabled(LOCAL_GL_RASTERIZER_DISCARD, rasterizerDiscard.value());
    517    }
    518 
    519    if (mGL.IsSupported(GLFeature::draw_buffers_indexed)) {
    520      mGL.fColorMaski(0, colorMask[0], colorMask[1], colorMask[2],
    521                      colorMask[3]);
    522    } else {
    523      mGL.fColorMask(colorMask[0], colorMask[1], colorMask[2], colorMask[3]);
    524    }
    525    mGL.fViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
    526  }
    527 };
    528 
    529 // --
    530 
    531 DrawBlitProg::DrawBlitProg(const GLBlitHelper* const parent, const GLuint prog)
    532    : mParent(*parent),
    533      mProg(prog),
    534      mLoc_uDestMatrix(mParent.mGL->fGetUniformLocation(mProg, "uDestMatrix")),
    535      mLoc_uTexMatrix0(mParent.mGL->fGetUniformLocation(mProg, "uTexMatrix0")),
    536      mLoc_uTexMatrix1(mParent.mGL->fGetUniformLocation(mProg, "uTexMatrix1")),
    537      mLoc_uColorLut(mParent.mGL->fGetUniformLocation(mProg, "uColorLut")),
    538      mLoc_uColorMatrix(
    539          mParent.mGL->fGetUniformLocation(mProg, "uColorMatrix")) {
    540  const auto& gl = mParent.mGL;
    541  MOZ_GL_ASSERT(gl, mLoc_uDestMatrix != -1);  // Required
    542  MOZ_GL_ASSERT(gl, mLoc_uTexMatrix0 != -1);  // Required
    543  if (mLoc_uColorMatrix != -1) {
    544    MOZ_GL_ASSERT(gl, mLoc_uTexMatrix1 != -1);
    545 
    546    int32_t numActiveUniforms = 0;
    547    gl->fGetProgramiv(mProg, LOCAL_GL_ACTIVE_UNIFORMS, &numActiveUniforms);
    548 
    549    const size_t kMaxNameSize = 32;
    550    char name[kMaxNameSize] = {0};
    551    GLint size = 0;
    552    GLenum type = 0;
    553    for (int32_t i = 0; i < numActiveUniforms; i++) {
    554      gl->fGetActiveUniform(mProg, i, kMaxNameSize, nullptr, &size, &type,
    555                            name);
    556      if (strcmp("uColorMatrix", name) == 0) {
    557        mType_uColorMatrix = type;
    558        break;
    559      }
    560    }
    561    MOZ_GL_ASSERT(gl, mType_uColorMatrix);
    562  }
    563 }
    564 
    565 DrawBlitProg::~DrawBlitProg() {
    566  const auto& gl = mParent.mGL;
    567  if (!gl->MakeCurrent()) return;
    568 
    569  gl->fDeleteProgram(mProg);
    570 }
    571 
    572 void DrawBlitProg::Draw(const BaseArgs& args,
    573                        const YUVArgs* const argsYUV) const {
    574  const auto& gl = mParent.mGL;
    575 
    576  const SaveRestoreCurrentProgram oldProg(gl);
    577  gl->fUseProgram(mProg);
    578 
    579  // --
    580 
    581  gfx::IntSize fbSize = args.fbSize;
    582  Mat3 destMatrix;
    583  if (!args.destRect.IsEmpty()) {
    584    const gfx::IntRect& destRect = args.destRect;
    585    if (fbSize.IsEmpty()) {
    586      fbSize = destRect.Size();
    587    }
    588    destMatrix = SubRectMat3(float(destRect.X()) / fbSize.width,
    589                             float(destRect.Y()) / fbSize.height,
    590                             float(destRect.Width()) / fbSize.width,
    591                             float(destRect.Height()) / fbSize.height);
    592  } else {
    593    MOZ_ASSERT(!fbSize.IsEmpty());
    594    destMatrix = Mat3::I();
    595  }
    596 
    597  if (args.yFlip) {
    598    // Apply the y-flip matrix before the destMatrix.
    599    // That is, flip y=[0-1] to y=[1-0] before we restrict to the destRect.
    600    destMatrix.at(2, 1) += destMatrix.at(1, 1);
    601    destMatrix.at(1, 1) *= -1.0f;
    602  }
    603 
    604  Mat3 texScale = Mat3::I();
    605  if (!args.texSize.IsEmpty()) {
    606    const gfx::IntSize clipSize =
    607        args.destRect.IsEmpty() ? fbSize : args.destRect.Size();
    608    const gfx::IntSize& texSize = args.texSize;
    609    texScale = SubRectMat3(
    610        gfx::IntRect(
    611            gfx::IntPoint(0, args.yFlip ? texSize.height - clipSize.height : 0),
    612            clipSize),
    613        texSize);
    614  }
    615 
    616  gl->fUniformMatrix3fv(mLoc_uDestMatrix, 1, false, destMatrix.m);
    617 
    618  Mat3 texMatrix0 = args.texMatrix0 * texScale;
    619  gl->fUniformMatrix3fv(mLoc_uTexMatrix0, 1, false, texMatrix0.m);
    620 
    621  MOZ_ASSERT(bool(argsYUV) == (mLoc_uColorMatrix != -1));
    622  if (argsYUV) {
    623    Mat3 texMatrix1 = argsYUV->texMatrix1 * texScale;
    624    gl->fUniformMatrix3fv(mLoc_uTexMatrix1, 1, false, texMatrix1.m);
    625 
    626    if (mLoc_uColorMatrix != -1) {
    627      const auto& colorMatrix =
    628          gfxUtils::YuvToRgbMatrix4x4ColumnMajor(*argsYUV->colorSpaceForMatrix);
    629      float mat4x3[4 * 3];
    630      switch (mType_uColorMatrix) {
    631        case LOCAL_GL_FLOAT_MAT4:
    632          gl->fUniformMatrix4fv(mLoc_uColorMatrix, 1, false, colorMatrix);
    633          break;
    634        case LOCAL_GL_FLOAT_MAT4x3:
    635          for (int x = 0; x < 4; x++) {
    636            for (int y = 0; y < 3; y++) {
    637              mat4x3[3 * x + y] = colorMatrix[4 * x + y];
    638            }
    639          }
    640          gl->fUniformMatrix4x3fv(mLoc_uColorMatrix, 1, false, mat4x3);
    641          break;
    642        default:
    643          gfxCriticalError()
    644              << "Bad mType_uColorMatrix: " << gfx::hexa(mType_uColorMatrix);
    645      }
    646    }
    647  }
    648 
    649  // --
    650 
    651  const ScopedDrawBlitState drawState(gl, fbSize);
    652 
    653  GLuint oldVAO;
    654  GLint vaa0Enabled;
    655  GLint vaa0Size;
    656  GLenum vaa0Type;
    657  GLint vaa0Normalized;
    658  GLsizei vaa0Stride;
    659  GLvoid* vaa0Pointer;
    660  GLuint vaa0Buffer;
    661  if (mParent.mQuadVAO) {
    662    oldVAO = gl->GetIntAs<GLuint>(LOCAL_GL_VERTEX_ARRAY_BINDING);
    663    gl->fBindVertexArray(mParent.mQuadVAO);
    664  } else {
    665    // clang-format off
    666    gl->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, (GLint*)&vaa0Buffer);
    667    gl->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED, &vaa0Enabled);
    668    gl->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_SIZE, &vaa0Size);
    669    gl->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_TYPE, (GLint*)&vaa0Type);
    670    gl->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &vaa0Normalized);
    671    gl->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_STRIDE, (GLint*)&vaa0Stride);
    672    gl->fGetVertexAttribPointerv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER, &vaa0Pointer);
    673    // clang-format on
    674 
    675    gl->fEnableVertexAttribArray(0);
    676    const ScopedBindArrayBuffer bindVBO(gl, mParent.mQuadVBO);
    677    gl->fVertexAttribPointer(0, 2, LOCAL_GL_FLOAT, false, 0, 0);
    678  }
    679 
    680  gl->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
    681 
    682  if (mParent.mQuadVAO) {
    683    gl->fBindVertexArray(oldVAO);
    684  } else {
    685    if (vaa0Enabled) {
    686      gl->fEnableVertexAttribArray(0);
    687    } else {
    688      gl->fDisableVertexAttribArray(0);
    689    }
    690    // The current VERTEX_ARRAY_BINDING is not necessarily the same as the
    691    // buffer set for vaa0Buffer.
    692    const ScopedBindArrayBuffer bindVBO(gl, vaa0Buffer);
    693    gl->fVertexAttribPointer(0, vaa0Size, vaa0Type, bool(vaa0Normalized),
    694                             vaa0Stride, vaa0Pointer);
    695  }
    696 }
    697 
    698 // --
    699 
    700 GLBlitHelper::GLBlitHelper(GLContext* const gl)
    701    : mGL(gl),
    702      mDrawBlitProg_VertShader(mGL->fCreateShader(LOCAL_GL_VERTEX_SHADER))
    703 //, mYuvUploads_YSize(0, 0)
    704 //, mYuvUploads_UVSize(0, 0)
    705 {
    706  mGL->fGenBuffers(1, &mQuadVBO);
    707  {
    708    const ScopedBindArrayBuffer bindVBO(mGL, mQuadVBO);
    709 
    710    const float quadData[] = {0, 0, 1, 0, 0, 1, 1, 1};
    711    const HeapCopyOfStackArray<float> heapQuadData(quadData);
    712    mGL->fBufferData(LOCAL_GL_ARRAY_BUFFER, heapQuadData.ByteLength(),
    713                     heapQuadData.Data(), LOCAL_GL_STATIC_DRAW);
    714 
    715    if (mGL->IsSupported(GLFeature::vertex_array_object)) {
    716      const auto prev = mGL->GetIntAs<GLuint>(LOCAL_GL_VERTEX_ARRAY_BINDING);
    717 
    718      mGL->fGenVertexArrays(1, &mQuadVAO);
    719      mGL->fBindVertexArray(mQuadVAO);
    720      mGL->fEnableVertexAttribArray(0);
    721      mGL->fVertexAttribPointer(0, 2, LOCAL_GL_FLOAT, false, 0, 0);
    722 
    723      mGL->fBindVertexArray(prev);
    724    }
    725  }
    726 
    727  // --
    728 
    729  const auto glslVersion = mGL->ShadingLanguageVersion();
    730 
    731  if (mGL->IsGLES()) {
    732    // If you run into problems on old android devices, it might be because some
    733    // devices have OES_EGL_image_external but not OES_EGL_image_external_essl3.
    734    // We could just use 100 in that particular case, but then we lose out on
    735    // e.g. sampler3D. Let's just try 300 for now, and if we get regressions
    736    // we'll add an essl100 fallback.
    737    if (glslVersion >= 300) {
    738      mDrawBlitProg_VersionLine = nsCString("#version 300 es\n");
    739    } else {
    740      mDrawBlitProg_VersionLine = nsCString("#version 100\n");
    741    }
    742  } else if (glslVersion >= 130) {
    743    mDrawBlitProg_VersionLine = nsPrintfCString("#version %u\n", glslVersion);
    744  }
    745 
    746  const char kVertSource[] =
    747      "\
    748        #if __VERSION__ >= 130                                               \n\
    749            #define ATTRIBUTE in                                             \n\
    750            #define VARYING out                                              \n\
    751        #else                                                                \n\
    752            #define ATTRIBUTE attribute                                      \n\
    753            #define VARYING varying                                          \n\
    754        #endif                                                               \n\
    755                                                                             \n\
    756        ATTRIBUTE vec2 aVert; // [0.0-1.0]                                   \n\
    757                                                                             \n\
    758        uniform mat3 uDestMatrix;                                            \n\
    759        uniform mat3 uTexMatrix0;                                            \n\
    760        uniform mat3 uTexMatrix1;                                            \n\
    761                                                                             \n\
    762        VARYING vec2 vTexCoord0;                                             \n\
    763        VARYING vec2 vTexCoord1;                                             \n\
    764                                                                             \n\
    765        void main(void)                                                      \n\
    766        {                                                                    \n\
    767            vec2 destPos = (uDestMatrix * vec3(aVert, 1.0)).xy;              \n\
    768            gl_Position = vec4(destPos * 2.0 - 1.0, 0.0, 1.0);               \n\
    769                                                                             \n\
    770            vTexCoord0 = (uTexMatrix0 * vec3(aVert, 1.0)).xy;                \n\
    771            vTexCoord1 = (uTexMatrix1 * vec3(aVert, 1.0)).xy;                \n\
    772        }                                                                    \n\
    773    ";
    774  const char* const parts[] = {mDrawBlitProg_VersionLine.get(), kVertSource};
    775  mGL->fShaderSource(mDrawBlitProg_VertShader, std::size(parts), parts,
    776                     nullptr);
    777  mGL->fCompileShader(mDrawBlitProg_VertShader);
    778 }
    779 
    780 GLBlitHelper::~GLBlitHelper() {
    781  mDrawBlitProgs.clear();
    782 
    783  if (!mGL->MakeCurrent()) return;
    784 
    785  mGL->fDeleteShader(mDrawBlitProg_VertShader);
    786  mGL->fDeleteBuffers(1, &mQuadVBO);
    787 
    788  if (mQuadVAO) {
    789    mGL->fDeleteVertexArrays(1, &mQuadVAO);
    790  }
    791 }
    792 
    793 // --
    794 
    795 const DrawBlitProg& GLBlitHelper::GetDrawBlitProg(
    796    const DrawBlitProg::Key& key) const {
    797  auto& ret = mDrawBlitProgs[key];
    798  if (!ret) {
    799    ret = CreateDrawBlitProg(key);
    800  }
    801  return *ret;
    802 }
    803 
    804 std::unique_ptr<const DrawBlitProg> GLBlitHelper::CreateDrawBlitProg(
    805    const DrawBlitProg::Key& key) const {
    806  const auto precisionPref = StaticPrefs::gfx_blithelper_precision();
    807  const char* precision;
    808  switch (precisionPref) {
    809    case 0:
    810      precision = "lowp";
    811      break;
    812    case 1:
    813      precision = "mediump";
    814      break;
    815    default:
    816      if (precisionPref != 2) {
    817        NS_WARNING("gfx.blithelper.precision clamped to 2.");
    818      }
    819      precision = "MAXP";
    820      break;
    821  }
    822 
    823  nsPrintfCString precisionLine("\n#define PRECISION %s\n", precision);
    824 
    825  // -
    826 
    827  const ScopedShader fs(mGL, LOCAL_GL_FRAGMENT_SHADER);
    828 
    829  std::vector<const char*> parts;
    830  {
    831    parts.push_back(mDrawBlitProg_VersionLine.get());
    832    parts.push_back(kFragPreprocHeader);
    833    if (key.fragHeader) {
    834      parts.push_back(key.fragHeader);
    835    }
    836    parts.push_back(precisionLine.BeginReading());
    837    parts.push_back(kFragDeclHeader);
    838    for (const auto& part : key.fragParts) {
    839      if (part) {
    840        parts.push_back(part);
    841      }
    842    }
    843    parts.push_back(kFragBody);
    844  }
    845 
    846  const auto PrintFragSource = [&]() {
    847    printf_stderr("Frag source:\n");
    848    int i = 0;
    849    for (const auto& part : parts) {
    850      printf_stderr("// parts[%i]:\n%s\n", i, part);
    851      i += 1;
    852    }
    853  };
    854  if (gfxEnv::MOZ_DUMP_GLBLITHELPER()) {
    855    PrintFragSource();
    856  }
    857 
    858  mGL->fShaderSource(fs, AssertedCast<GLint>(parts.size()), parts.data(),
    859                     nullptr);
    860  mGL->fCompileShader(fs);
    861 
    862  const auto prog = mGL->fCreateProgram();
    863  mGL->fAttachShader(prog, mDrawBlitProg_VertShader);
    864  mGL->fAttachShader(prog, fs);
    865 
    866  mGL->fBindAttribLocation(prog, 0, "aVert");
    867  mGL->fLinkProgram(prog);
    868 
    869  GLenum status = 0;
    870  mGL->fGetProgramiv(prog, LOCAL_GL_LINK_STATUS, (GLint*)&status);
    871  if (status == LOCAL_GL_TRUE || mGL->CheckContextLost()) {
    872    const SaveRestoreCurrentProgram oldProg(mGL);
    873    mGL->fUseProgram(prog);
    874    const char* samplerNames[] = {"uTex0", "uTex1", "uTex2"};
    875    for (int i = 0; i < 3; i++) {
    876      const auto loc = mGL->fGetUniformLocation(prog, samplerNames[i]);
    877      if (loc == -1) continue;
    878      mGL->fUniform1i(loc, i);
    879    }
    880 
    881    return std::make_unique<DrawBlitProg>(this, prog);
    882  }
    883 
    884  GLuint progLogLen = 0;
    885  mGL->fGetProgramiv(prog, LOCAL_GL_INFO_LOG_LENGTH, (GLint*)&progLogLen);
    886  const UniquePtr<char[]> progLog(new char[progLogLen + 1]);
    887  mGL->fGetProgramInfoLog(prog, progLogLen, nullptr, progLog.get());
    888  progLog[progLogLen] = 0;
    889 
    890  const auto& vs = mDrawBlitProg_VertShader;
    891  GLuint vsLogLen = 0;
    892  mGL->fGetShaderiv(vs, LOCAL_GL_INFO_LOG_LENGTH, (GLint*)&vsLogLen);
    893  const UniquePtr<char[]> vsLog(new char[vsLogLen + 1]);
    894  mGL->fGetShaderInfoLog(vs, vsLogLen, nullptr, vsLog.get());
    895  vsLog[vsLogLen] = 0;
    896 
    897  GLuint fsLogLen = 0;
    898  mGL->fGetShaderiv(fs, LOCAL_GL_INFO_LOG_LENGTH, (GLint*)&fsLogLen);
    899  const UniquePtr<char[]> fsLog(new char[fsLogLen + 1]);
    900  mGL->fGetShaderInfoLog(fs, fsLogLen, nullptr, fsLog.get());
    901  fsLog[fsLogLen] = 0;
    902 
    903  const auto logs =
    904      std::string("DrawBlitProg link failed:\n") + "progLog: " + progLog.get() +
    905      "\n" + "vsLog: " + vsLog.get() + "\n" + "fsLog: " + fsLog.get() + "\n";
    906  gfxCriticalError() << logs;
    907 
    908  PrintFragSource();
    909 
    910  MOZ_CRASH("DrawBlitProg link failed");
    911 }
    912 
    913 // -----------------------------------------------------------------------------
    914 
    915 #ifdef XP_MACOSX
    916 static RefPtr<MacIOSurface> LookupSurface(
    917    const layers::SurfaceDescriptorMacIOSurface& sd) {
    918  return MacIOSurface::LookupSurface(sd.surfaceId(), !sd.isOpaque(),
    919                                     sd.yUVColorSpace());
    920 }
    921 #endif
    922 
    923 bool GLBlitHelper::BlitSdToFramebuffer(const layers::SurfaceDescriptor& asd,
    924                                       const gfx::IntRect& destRect,
    925                                       const OriginPos destOrigin,
    926                                       const gfx::IntSize& fbSize,
    927                                       Maybe<gfxAlphaType> convertAlpha) {
    928  const auto sdType = asd.type();
    929  switch (sdType) {
    930    case layers::SurfaceDescriptor::TSurfaceDescriptorBuffer: {
    931      const auto& sd = asd.get_SurfaceDescriptorBuffer();
    932      const auto yuvData = PlanarYCbCrData::From(sd);
    933      if (!yuvData) {
    934        gfxCriticalNote << "[GLBlitHelper::BlitSdToFramebuffer] "
    935                           "PlanarYCbCrData::From failed";
    936        return false;
    937      }
    938      return BlitPlanarYCbCr(*yuvData, destRect, destOrigin, fbSize,
    939                             convertAlpha);
    940    }
    941 #ifdef XP_WIN
    942    case layers::SurfaceDescriptor::TSurfaceDescriptorD3D10: {
    943      const auto& sd = asd.get_SurfaceDescriptorD3D10();
    944      return BlitDescriptor(sd, destRect, destOrigin, fbSize, convertAlpha);
    945    }
    946    case layers::SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr: {
    947      const auto& sd = asd.get_SurfaceDescriptorDXGIYCbCr();
    948      return BlitDescriptor(sd, destRect, destOrigin, fbSize, convertAlpha);
    949    }
    950 #endif
    951 #ifdef XP_MACOSX
    952    case layers::SurfaceDescriptor::TSurfaceDescriptorMacIOSurface: {
    953      const auto& sd = asd.get_SurfaceDescriptorMacIOSurface();
    954      const auto surf = LookupSurface(sd);
    955      if (!surf) {
    956        NS_WARNING("LookupSurface(MacIOSurface) failed");
    957        // Sometimes that frame for our handle gone already. That's life, for
    958        // now.
    959        return false;
    960      }
    961      return BlitImage(surf, destRect, destOrigin, fbSize, convertAlpha);
    962    }
    963 #endif
    964 #ifdef MOZ_WIDGET_ANDROID
    965    case layers::SurfaceDescriptor::TSurfaceTextureDescriptor: {
    966      const auto& sd = asd.get_SurfaceTextureDescriptor();
    967      auto surfaceTexture = java::GeckoSurfaceTexture::Lookup(sd.handle());
    968      return Blit(surfaceTexture, sd.size(), destRect, destOrigin, fbSize,
    969                  convertAlpha);
    970    }
    971    case layers::SurfaceDescriptor::TEGLImageDescriptor: {
    972      const auto& sd = asd.get_EGLImageDescriptor();
    973      return Blit((EGLImage)sd.image(), (EGLSync)sd.fence(), sd.size(),
    974                  destRect, destOrigin, fbSize, convertAlpha);
    975    }
    976 #endif
    977 #ifdef MOZ_WIDGET_GTK
    978    case layers::SurfaceDescriptor::TSurfaceDescriptorDMABuf: {
    979      const auto& sd = asd.get_SurfaceDescriptorDMABuf();
    980      RefPtr<DMABufSurface> surface = DMABufSurface::CreateDMABufSurface(sd);
    981      return Blit(surface, destRect, destOrigin, fbSize, convertAlpha);
    982    }
    983 #endif
    984    default:
    985      return false;
    986  }
    987 }
    988 
    989 bool GLBlitHelper::BlitImageToFramebuffer(layers::Image* const srcImage,
    990                                          const gfx::IntRect& destRect,
    991                                          const OriginPos destOrigin,
    992                                          const gfx::IntSize& fbSize) {
    993  switch (srcImage->GetFormat()) {
    994    case ImageFormat::PLANAR_YCBCR: {
    995      const auto srcImage2 = static_cast<PlanarYCbCrImage*>(srcImage);
    996      const auto data = srcImage2->GetData();
    997      return BlitPlanarYCbCr(*data, destRect, destOrigin, fbSize);
    998    }
    999 
   1000    case ImageFormat::SURFACE_TEXTURE: {
   1001 #ifdef MOZ_WIDGET_ANDROID
   1002      auto* image = srcImage->AsSurfaceTextureImage();
   1003      MOZ_ASSERT(image);
   1004      auto surfaceTexture =
   1005          java::GeckoSurfaceTexture::Lookup(image->GetHandle());
   1006      return Blit(surfaceTexture, image->GetSize(), destRect, destOrigin,
   1007                  fbSize);
   1008 #else
   1009      MOZ_ASSERT(false);
   1010      return false;
   1011 #endif
   1012    }
   1013    case ImageFormat::MAC_IOSURFACE:
   1014 #ifdef XP_MACOSX
   1015      return BlitImage(srcImage->AsMacIOSurfaceImage(), destRect, destOrigin,
   1016                       fbSize);
   1017 #else
   1018      MOZ_ASSERT(false);
   1019      return false;
   1020 #endif
   1021 
   1022    case ImageFormat::GPU_VIDEO:
   1023      return BlitImage(static_cast<layers::GPUVideoImage*>(srcImage), destRect,
   1024                       destOrigin, fbSize);
   1025 #ifdef XP_WIN
   1026    case ImageFormat::D3D11_SHARE_HANDLE_TEXTURE:
   1027      return BlitImage(static_cast<layers::D3D11ShareHandleImage*>(srcImage),
   1028                       destRect, destOrigin, fbSize);
   1029    case ImageFormat::D3D11_TEXTURE_ZERO_COPY:
   1030      return BlitImage(
   1031          static_cast<layers::D3D11ZeroCopyTextureImage*>(srcImage), destRect,
   1032          destOrigin, fbSize);
   1033    case ImageFormat::D3D9_RGB32_TEXTURE:
   1034      return false;  // todo
   1035    case ImageFormat::DCOMP_SURFACE:
   1036      return false;
   1037 #else
   1038    case ImageFormat::D3D11_SHARE_HANDLE_TEXTURE:
   1039    case ImageFormat::D3D11_TEXTURE_ZERO_COPY:
   1040    case ImageFormat::D3D9_RGB32_TEXTURE:
   1041    case ImageFormat::DCOMP_SURFACE:
   1042      MOZ_ASSERT(false);
   1043      return false;
   1044 #endif
   1045    case ImageFormat::DMABUF:
   1046 #ifdef MOZ_WIDGET_GTK
   1047      return BlitImage(static_cast<layers::DMABUFSurfaceImage*>(srcImage),
   1048                       destRect, destOrigin, fbSize);
   1049 #else
   1050      return false;
   1051 #endif
   1052    case ImageFormat::MOZ2D_SURFACE:
   1053    case ImageFormat::NV_IMAGE:
   1054    case ImageFormat::OVERLAY_IMAGE:
   1055    case ImageFormat::SHARED_RGB:
   1056    case ImageFormat::TEXTURE_WRAPPER:
   1057      return false;  // todo
   1058  }
   1059  return false;
   1060 }
   1061 
   1062 // -------------------------------------
   1063 
   1064 const char* GLBlitHelper::GetAlphaMixin(
   1065    Maybe<gfxAlphaType> convertAlpha) const {
   1066  if (!convertAlpha) {
   1067    return nullptr;
   1068  }
   1069  switch (convertAlpha.value()) {
   1070    case gfxAlphaType::Premult:
   1071      return kFragMixin_AlphaMultColors;
   1072    case gfxAlphaType::NonPremult:
   1073      return kFragMixin_AlphaUnpremultColors;
   1074    case gfxAlphaType::Opaque:
   1075      return kFragMixin_AlphaOne;
   1076    default:
   1077      return nullptr;
   1078  }
   1079 }
   1080 
   1081 #ifdef MOZ_WIDGET_ANDROID
   1082 bool GLBlitHelper::Blit(const java::GeckoSurfaceTexture::Ref& surfaceTexture,
   1083                        const gfx::IntSize& texSize,
   1084                        const gfx::IntRect& destRect,
   1085                        const OriginPos destOrigin, const gfx::IntSize& fbSize,
   1086                        Maybe<gfxAlphaType> convertAlpha) const {
   1087  if (!surfaceTexture) {
   1088    return false;
   1089  }
   1090 
   1091  const ScopedBindTextureUnit boundTU(mGL, LOCAL_GL_TEXTURE0);
   1092 
   1093  if (!surfaceTexture->IsAttachedToGLContext((int64_t)mGL)) {
   1094    GLuint tex;
   1095    mGL->MakeCurrent();
   1096    mGL->fGenTextures(1, &tex);
   1097 
   1098    if (NS_FAILED(surfaceTexture->AttachToGLContext((int64_t)mGL, tex))) {
   1099      mGL->fDeleteTextures(1, &tex);
   1100      return false;
   1101    }
   1102  }
   1103 
   1104  const ScopedBindTexture savedTex(mGL, surfaceTexture->GetTexName(),
   1105                                   LOCAL_GL_TEXTURE_EXTERNAL);
   1106  surfaceTexture->UpdateTexImage();
   1107 
   1108  gfx::Matrix4x4 transform;
   1109  const auto surf = java::sdk::SurfaceTexture::LocalRef::From(surfaceTexture);
   1110  gl::AndroidSurfaceTexture::GetTransformMatrix(surf, &transform);
   1111  // SurfaceTexture transforms should always be 2D
   1112  MOZ_DIAGNOSTIC_ASSERT(transform.Is2D());
   1113  const auto transform3 = MatrixToMat3(transform.As2D());
   1114 
   1115  // The origin is top-left here due to the transform supplied in the surface.
   1116  const auto srcOrigin = OriginPos::TopLeft;
   1117  const bool yFlip = (srcOrigin != destOrigin);
   1118  const auto& prog = GetDrawBlitProg(
   1119      {kFragHeader_TexExt,
   1120       {kFragSample_OnePlane, kFragConvert_None, GetAlphaMixin(convertAlpha)}});
   1121  const DrawBlitProg::BaseArgs baseArgs = {transform3, yFlip, fbSize, destRect,
   1122                                           texSize};
   1123  prog.Draw(baseArgs, nullptr);
   1124 
   1125  if (surfaceTexture->IsSingleBuffer()) {
   1126    surfaceTexture->ReleaseTexImage();
   1127  }
   1128 
   1129  return true;
   1130 }
   1131 
   1132 bool GLBlitHelper::Blit(EGLImage image, EGLSync fence,
   1133                        const gfx::IntSize& texSize,
   1134                        const gfx::IntRect& destRect,
   1135                        const OriginPos destOrigin, const gfx::IntSize& fbSize,
   1136                        Maybe<gfxAlphaType> convertAlpha) const {
   1137  if (!image) {
   1138    return false;
   1139  }
   1140 
   1141  GLenum target = mGL->GetPreferredEGLImageTextureTarget();
   1142  const ScopedBindTextureUnit boundTU(mGL, LOCAL_GL_TEXTURE0);
   1143  ScopedTexture tex(mGL);
   1144  ScopedBindTexture bindTex(mGL, tex.Texture(), target);
   1145 
   1146  mGL->TexParams_SetClampNoMips(target);
   1147  mGL->fEGLImageTargetTexture2D(target, image);
   1148 
   1149  const auto srcOrigin = OriginPos::BottomLeft;
   1150  const bool yFlip = (srcOrigin != destOrigin);
   1151  const auto& prog = GetDrawBlitProg(
   1152      {target == LOCAL_GL_TEXTURE_2D ? kFragHeader_Tex2D : kFragHeader_TexExt,
   1153       {kFragSample_OnePlane, kFragConvert_None, GetAlphaMixin(convertAlpha)}});
   1154  const DrawBlitProg::BaseArgs baseArgs = {SubRectMat3(0, 0, 1, 1), yFlip,
   1155                                           fbSize, destRect, texSize};
   1156  prog.Draw(baseArgs);
   1157 
   1158  return true;
   1159 }
   1160 #endif
   1161 
   1162 // -------------------------------------
   1163 
   1164 bool GuessDivisors(const gfx::IntSize& ySize, const gfx::IntSize& uvSize,
   1165                   gfx::IntSize* const out_divisors) {
   1166  const gfx::IntSize divisors((ySize.width == uvSize.width) ? 1 : 2,
   1167                              (ySize.height == uvSize.height) ? 1 : 2);
   1168  if (uvSize.width * divisors.width != ySize.width ||
   1169      uvSize.height * divisors.height != ySize.height) {
   1170    return false;
   1171  }
   1172  *out_divisors = divisors;
   1173  return true;
   1174 }
   1175 
   1176 bool GLBlitHelper::BlitPlanarYCbCr(const PlanarYCbCrData& yuvData,
   1177                                   const gfx::IntRect& destRect,
   1178                                   const OriginPos destOrigin,
   1179                                   const gfx::IntSize& fbSize,
   1180                                   Maybe<gfxAlphaType> convertAlpha) {
   1181  const auto& prog =
   1182      GetDrawBlitProg({kFragHeader_Tex2D,
   1183                       {kFragSample_ThreePlane, kFragConvert_ColorMatrix,
   1184                        GetAlphaMixin(convertAlpha)}});
   1185 
   1186  if (!mYuvUploads[0]) {
   1187    mGL->fGenTextures(3, mYuvUploads);
   1188    const ScopedBindTexture bindTex(mGL, mYuvUploads[0], LOCAL_GL_TEXTURE_2D);
   1189    mGL->TexParams_SetClampNoMips(LOCAL_GL_TEXTURE_2D);
   1190    mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[1]);
   1191    mGL->TexParams_SetClampNoMips(LOCAL_GL_TEXTURE_2D);
   1192    mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[2]);
   1193    mGL->TexParams_SetClampNoMips(LOCAL_GL_TEXTURE_2D);
   1194  }
   1195 
   1196  // --
   1197 
   1198  auto ySize = yuvData.YDataSize();
   1199  auto cbcrSize = yuvData.CbCrDataSize();
   1200  if (yuvData.mYSkip || yuvData.mCbSkip || yuvData.mCrSkip || ySize.width < 0 ||
   1201      ySize.height < 0 || cbcrSize.width < 0 || cbcrSize.height < 0 ||
   1202      yuvData.mYStride < 0 || yuvData.mCbCrStride < 0) {
   1203    gfxCriticalError() << "Unusual PlanarYCbCrData: " << yuvData.mYSkip << ","
   1204                       << yuvData.mCbSkip << "," << yuvData.mCrSkip << ", "
   1205                       << ySize.width << "," << ySize.height << ", "
   1206                       << cbcrSize.width << "," << cbcrSize.height << ", "
   1207                       << yuvData.mYStride << "," << yuvData.mCbCrStride;
   1208    return false;
   1209  }
   1210 
   1211  gfx::IntSize divisors;
   1212  switch (yuvData.mChromaSubsampling) {
   1213    case gfx::ChromaSubsampling::FULL:
   1214      divisors = gfx::IntSize(1, 1);
   1215      break;
   1216    case gfx::ChromaSubsampling::HALF_WIDTH:
   1217      divisors = gfx::IntSize(2, 1);
   1218      break;
   1219    case gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT:
   1220      divisors = gfx::IntSize(2, 2);
   1221      break;
   1222    default:
   1223      gfxCriticalError() << "Unknown chroma subsampling:"
   1224                         << int(yuvData.mChromaSubsampling);
   1225      return false;
   1226  }
   1227 
   1228  // --
   1229 
   1230  // RED textures aren't valid in GLES2, and ALPHA textures are not valid in
   1231  // desktop GL Core Profiles. So use R8 textures on GL3.0+ and GLES3.0+, but
   1232  // LUMINANCE/LUMINANCE/UNSIGNED_BYTE otherwise.
   1233  GLenum internalFormat;
   1234  GLenum unpackFormat;
   1235  if (mGL->IsAtLeast(gl::ContextProfile::OpenGLCore, 300) ||
   1236      mGL->IsAtLeast(gl::ContextProfile::OpenGLES, 300)) {
   1237    internalFormat = LOCAL_GL_R8;
   1238    unpackFormat = LOCAL_GL_RED;
   1239  } else {
   1240    internalFormat = LOCAL_GL_LUMINANCE;
   1241    unpackFormat = LOCAL_GL_LUMINANCE;
   1242  }
   1243 
   1244  // --
   1245 
   1246  const ScopedSaveMultiTex saveTex(mGL, 3, LOCAL_GL_TEXTURE_2D);
   1247  const ResetUnpackState reset(mGL);
   1248  const gfx::IntSize yTexSize(yuvData.mYStride, yuvData.YDataSize().height);
   1249  const gfx::IntSize uvTexSize(yuvData.mCbCrStride,
   1250                               yuvData.CbCrDataSize().height);
   1251 
   1252  if (yTexSize != mYuvUploads_YSize || uvTexSize != mYuvUploads_UVSize) {
   1253    mYuvUploads_YSize = yTexSize;
   1254    mYuvUploads_UVSize = uvTexSize;
   1255 
   1256    mGL->fActiveTexture(LOCAL_GL_TEXTURE0);
   1257    mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[0]);
   1258    mGL->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, internalFormat, yTexSize.width,
   1259                     yTexSize.height, 0, unpackFormat, LOCAL_GL_UNSIGNED_BYTE,
   1260                     nullptr);
   1261    for (int i = 1; i < 3; i++) {
   1262      mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
   1263      mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[i]);
   1264      mGL->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, internalFormat, uvTexSize.width,
   1265                       uvTexSize.height, 0, unpackFormat,
   1266                       LOCAL_GL_UNSIGNED_BYTE, nullptr);
   1267    }
   1268  }
   1269 
   1270  // --
   1271 
   1272  mGL->fActiveTexture(LOCAL_GL_TEXTURE0);
   1273  mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[0]);
   1274  mGL->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0, 0, 0, yTexSize.width,
   1275                      yTexSize.height, unpackFormat, LOCAL_GL_UNSIGNED_BYTE,
   1276                      yuvData.mYChannel);
   1277  mGL->fActiveTexture(LOCAL_GL_TEXTURE1);
   1278  mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[1]);
   1279  mGL->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0, 0, 0, uvTexSize.width,
   1280                      uvTexSize.height, unpackFormat, LOCAL_GL_UNSIGNED_BYTE,
   1281                      yuvData.mCbChannel);
   1282  mGL->fActiveTexture(LOCAL_GL_TEXTURE2);
   1283  mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[2]);
   1284  mGL->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0, 0, 0, uvTexSize.width,
   1285                      uvTexSize.height, unpackFormat, LOCAL_GL_UNSIGNED_BYTE,
   1286                      yuvData.mCrChannel);
   1287 
   1288  // --
   1289 
   1290  const auto& clipRect = yuvData.mPictureRect;
   1291  const auto srcOrigin = OriginPos::BottomLeft;
   1292  const bool yFlip = (destOrigin != srcOrigin);
   1293 
   1294  const DrawBlitProg::BaseArgs baseArgs = {SubRectMat3(clipRect, yTexSize),
   1295                                           yFlip, fbSize, destRect,
   1296                                           clipRect.Size()};
   1297  const DrawBlitProg::YUVArgs yuvArgs = {
   1298      SubRectMat3(clipRect, uvTexSize, divisors), Some(yuvData.mYUVColorSpace)};
   1299  prog.Draw(baseArgs, &yuvArgs);
   1300  return true;
   1301 }
   1302 
   1303 // -------------------------------------
   1304 
   1305 #ifdef XP_MACOSX
   1306 bool GLBlitHelper::BlitImage(layers::MacIOSurfaceImage* const srcImage,
   1307                             const gfx::IntRect& destRect,
   1308                             const OriginPos destOrigin,
   1309                             const gfx::IntSize& fbSize) const {
   1310  return BlitImage(srcImage->GetSurface(), destRect, destOrigin, fbSize);
   1311 }
   1312 
   1313 static std::string IntAsAscii(const int x) {
   1314  std::string str;
   1315  str.reserve(6);
   1316  auto u = static_cast<unsigned int>(x);
   1317  while (u) {
   1318    str.insert(str.begin(), u & 0xff);
   1319    u >>= 8;
   1320  }
   1321  str.insert(str.begin(), '\'');
   1322  str.push_back('\'');
   1323  return str;
   1324 }
   1325 
   1326 bool GLBlitHelper::BlitImage(MacIOSurface* const iosurf,
   1327                             const gfx::IntRect& destRect,
   1328                             const OriginPos destOrigin,
   1329                             const gfx::IntSize& fbSize,
   1330                             Maybe<gfxAlphaType> convertAlpha) const {
   1331  if (!iosurf) {
   1332    gfxCriticalError() << "Null MacIOSurface for GLBlitHelper::BlitImage";
   1333    return false;
   1334  }
   1335  if (mGL->GetContextType() != GLContextType::CGL) {
   1336    MOZ_ASSERT(false);
   1337    return false;
   1338  }
   1339 
   1340  const auto& srcOrigin = OriginPos::BottomLeft;
   1341 
   1342  DrawBlitProg::BaseArgs baseArgs;
   1343  baseArgs.yFlip = (destOrigin != srcOrigin);
   1344  baseArgs.fbSize = fbSize;
   1345  baseArgs.destRect = destRect;
   1346 
   1347  // TODO: The colorspace is known by the IOSurface, why override it?
   1348  // See GetYUVColorSpace/GetFullRange()
   1349  DrawBlitProg::YUVArgs yuvArgs;
   1350  yuvArgs.colorSpaceForMatrix = Some(iosurf->GetYUVColorSpace());
   1351 
   1352  auto planes = iosurf->GetPlaneCount();
   1353  if (!planes) {
   1354    planes = 1;  // Bad API. No cookie.
   1355  }
   1356 
   1357  const auto pixelFormat = iosurf->GetPixelFormat();
   1358  if (mGL->ShouldSpew()) {
   1359    const auto formatStr = IntAsAscii(pixelFormat);
   1360    printf_stderr("iosurf format: %s (0x%08x)\n", formatStr.c_str(),
   1361                  pixelFormat);
   1362  }
   1363 
   1364  const char* fragSample = nullptr;
   1365  bool yuv = true;
   1366  switch (planes) {
   1367    case 1:
   1368      switch (pixelFormat) {
   1369        case kCVPixelFormatType_24RGB:
   1370        case kCVPixelFormatType_24BGR:
   1371        case kCVPixelFormatType_32ARGB:
   1372        case kCVPixelFormatType_32BGRA:
   1373        case kCVPixelFormatType_32ABGR:
   1374        case kCVPixelFormatType_32RGBA:
   1375        case kCVPixelFormatType_64ARGB:
   1376        case kCVPixelFormatType_48RGB:
   1377          fragSample = kFragSample_OnePlane;
   1378          yuv = false;
   1379          break;
   1380        case kCVPixelFormatType_422YpCbCr8:
   1381        case kCVPixelFormatType_422YpCbCr8_yuvs:
   1382          fragSample = kFragSample_OnePlane_YUV_via_GBR;
   1383          break;
   1384        default: {
   1385          std::string str;
   1386          if (pixelFormat <= 0xff) {
   1387            str = std::to_string(pixelFormat);
   1388          } else {
   1389            str = IntAsAscii(pixelFormat);
   1390          }
   1391          gfxCriticalError() << "Unhandled kCVPixelFormatType_*: " << str;
   1392          // Probably YUV though
   1393          fragSample = kFragSample_OnePlane_YUV_via_GBR;
   1394          break;
   1395        }
   1396      }
   1397      break;
   1398    case 2:
   1399      fragSample = kFragSample_TwoPlane;
   1400      break;
   1401    case 3:
   1402      fragSample = kFragSample_ThreePlane;
   1403      break;
   1404    default:
   1405      gfxCriticalError() << "Unexpected plane count: " << planes;
   1406      return false;
   1407  }
   1408 
   1409  const GLenum texTarget = LOCAL_GL_TEXTURE_RECTANGLE;
   1410  const ScopedSaveMultiTex saveTex(mGL, planes, texTarget);
   1411  Maybe<ScopedTexture> texs[3];
   1412 
   1413  for (uint32_t p = 0; p < planes; p++) {
   1414    texs[p].emplace(mGL);
   1415    mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + p);
   1416    mGL->fBindTexture(texTarget, texs[p]->Texture());
   1417    mGL->TexParams_SetClampNoMips(texTarget);
   1418 
   1419    if (!iosurf->BindTexImage(mGL, p)) {
   1420      return false;
   1421    }
   1422 
   1423    if (p == 0) {
   1424      const auto width = iosurf->GetDevicePixelWidth(p);
   1425      const auto height = iosurf->GetDevicePixelHeight(p);
   1426      baseArgs.texMatrix0 = SubRectMat3(0, 0, width, height);
   1427      baseArgs.texSize = gfx::IntSize(width, height);
   1428      yuvArgs.texMatrix1 = SubRectMat3(0, 0, width / 2.0, height / 2.0);
   1429    }
   1430  }
   1431 
   1432  const char* fragConvert = yuv ? kFragConvert_ColorMatrix : kFragConvert_None;
   1433  const auto& prog = GetDrawBlitProg({
   1434      kFragHeader_Tex2DRect,
   1435      {fragSample, fragConvert, GetAlphaMixin(convertAlpha)},
   1436  });
   1437  prog.Draw(baseArgs, yuv ? &yuvArgs : nullptr);
   1438  return true;
   1439 }
   1440 #endif
   1441 
   1442 // -----------------------------------------------------------------------------
   1443 
   1444 void GLBlitHelper::DrawBlitTextureToFramebuffer(
   1445    const GLuint srcTex, const gfx::IntSize& srcSize,
   1446    const gfx::IntSize& destSize, const GLenum srcTarget, const bool srcIsBGRA,
   1447    const bool yFlip, const Maybe<gfxAlphaType> convertAlpha) const {
   1448  const char* fragHeader = nullptr;
   1449  Mat3 texMatrix0;
   1450  switch (srcTarget) {
   1451    case LOCAL_GL_TEXTURE_2D:
   1452      fragHeader = kFragHeader_Tex2D;
   1453      texMatrix0 = Mat3::I();
   1454      break;
   1455    case LOCAL_GL_TEXTURE_RECTANGLE_ARB:
   1456      fragHeader = kFragHeader_Tex2DRect;
   1457      texMatrix0 = SubRectMat3(0, 0, srcSize.width, srcSize.height);
   1458      break;
   1459    default:
   1460      gfxCriticalError() << "Unexpected srcTarget: " << srcTarget;
   1461      return;
   1462  }
   1463  const auto fragConvert = srcIsBGRA ? kFragConvert_BGR : kFragConvert_None;
   1464  const auto& prog = GetDrawBlitProg(
   1465      {fragHeader,
   1466       {kFragSample_OnePlane, fragConvert, GetAlphaMixin(convertAlpha)}});
   1467 
   1468  const ScopedSaveMultiTex saveTex(mGL, 1, srcTarget);
   1469  mGL->fActiveTexture(LOCAL_GL_TEXTURE0);
   1470  mGL->fBindTexture(srcTarget, srcTex);
   1471 
   1472  const DrawBlitProg::BaseArgs baseArgs = {texMatrix0, yFlip, destSize, {}};
   1473  prog.Draw(baseArgs);
   1474 }
   1475 
   1476 // -----------------------------------------------------------------------------
   1477 
   1478 void GLBlitHelper::BlitFramebuffer(const gfx::IntRect& srcRect,
   1479                                   const gfx::IntRect& destRect,
   1480                                   GLuint filter) const {
   1481  MOZ_ASSERT(mGL->IsSupported(GLFeature::framebuffer_blit));
   1482 
   1483  const ScopedGLState scissor(mGL, LOCAL_GL_SCISSOR_TEST, false);
   1484  mGL->fBlitFramebuffer(srcRect.x, srcRect.y, srcRect.XMost(), srcRect.YMost(),
   1485                        destRect.x, destRect.y, destRect.XMost(),
   1486                        destRect.YMost(), LOCAL_GL_COLOR_BUFFER_BIT, filter);
   1487 }
   1488 
   1489 // --
   1490 
   1491 void GLBlitHelper::BlitFramebufferToFramebuffer(const GLuint srcFB,
   1492                                                const GLuint destFB,
   1493                                                const gfx::IntRect& srcRect,
   1494                                                const gfx::IntRect& destRect,
   1495                                                GLuint filter) const {
   1496  MOZ_ASSERT(mGL->IsSupported(GLFeature::framebuffer_blit));
   1497  MOZ_GL_ASSERT(mGL, !srcFB || mGL->fIsFramebuffer(srcFB));
   1498  MOZ_GL_ASSERT(mGL, !destFB || mGL->fIsFramebuffer(destFB));
   1499 
   1500  const ScopedBindFramebuffer boundFB(mGL);
   1501  mGL->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, srcFB);
   1502  mGL->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, destFB);
   1503 
   1504  BlitFramebuffer(srcRect, destRect, filter);
   1505 }
   1506 
   1507 void GLBlitHelper::BlitTextureToFramebuffer(GLuint srcTex,
   1508                                            const gfx::IntSize& srcSize,
   1509                                            const gfx::IntSize& destSize,
   1510                                            GLenum srcTarget) const {
   1511  MOZ_GL_ASSERT(mGL, mGL->fIsTexture(srcTex));
   1512 
   1513  if (mGL->IsSupported(GLFeature::framebuffer_blit)) {
   1514    const ScopedFramebufferForTexture srcWrapper(mGL, srcTex, srcTarget);
   1515    const ScopedBindFramebuffer bindFB(mGL);
   1516    mGL->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, srcWrapper.FB());
   1517    BlitFramebuffer(gfx::IntRect({}, srcSize), gfx::IntRect({}, destSize));
   1518    return;
   1519  }
   1520 
   1521  DrawBlitTextureToFramebuffer(srcTex, srcSize, destSize, srcTarget);
   1522 }
   1523 
   1524 void GLBlitHelper::BlitFramebufferToTexture(GLuint destTex,
   1525                                            const gfx::IntSize& srcSize,
   1526                                            const gfx::IntSize& destSize,
   1527                                            GLenum destTarget) const {
   1528  MOZ_GL_ASSERT(mGL, mGL->fIsTexture(destTex));
   1529 
   1530  if (mGL->IsSupported(GLFeature::framebuffer_blit)) {
   1531    const ScopedFramebufferForTexture destWrapper(mGL, destTex, destTarget);
   1532    const ScopedBindFramebuffer bindFB(mGL);
   1533    mGL->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, destWrapper.FB());
   1534    BlitFramebuffer(gfx::IntRect({}, srcSize), gfx::IntRect({}, destSize));
   1535    return;
   1536  }
   1537 
   1538  ScopedBindTexture autoTex(mGL, destTex, destTarget);
   1539  ScopedGLState scissor(mGL, LOCAL_GL_SCISSOR_TEST, false);
   1540  mGL->fCopyTexSubImage2D(destTarget, 0, 0, 0, 0, 0, srcSize.width,
   1541                          srcSize.height);
   1542 }
   1543 
   1544 void GLBlitHelper::BlitTextureToTexture(GLuint srcTex, GLuint destTex,
   1545                                        const gfx::IntSize& srcSize,
   1546                                        const gfx::IntSize& destSize,
   1547                                        GLenum srcTarget,
   1548                                        GLenum destTarget) const {
   1549  MOZ_GL_ASSERT(mGL, mGL->fIsTexture(srcTex));
   1550  MOZ_GL_ASSERT(mGL, mGL->fIsTexture(destTex));
   1551 
   1552  // Start down the CopyTexSubImage path, not the DrawBlit path.
   1553  const ScopedFramebufferForTexture srcWrapper(mGL, srcTex, srcTarget);
   1554  const ScopedBindFramebuffer bindFB(mGL, srcWrapper.FB());
   1555  BlitFramebufferToTexture(destTex, srcSize, destSize, destTarget);
   1556 }
   1557 
   1558 // -------------------------------------
   1559 
   1560 bool GLBlitHelper::BlitImage(layers::GPUVideoImage* const srcImage,
   1561                             const gfx::IntRect& destRect,
   1562                             const OriginPos destOrigin,
   1563                             const gfx::IntSize& fbSize) const {
   1564  const auto& data = srcImage->GetData();
   1565  if (!data) return false;
   1566 
   1567  const auto& desc = data->SD();
   1568 
   1569  MOZ_ASSERT(
   1570      desc.type() ==
   1571      layers::SurfaceDescriptorGPUVideo::TSurfaceDescriptorRemoteDecoder);
   1572  const auto& subdescUnion =
   1573      desc.get_SurfaceDescriptorRemoteDecoder().subdesc();
   1574  switch (subdescUnion.type()) {
   1575 #ifdef MOZ_WIDGET_GTK
   1576    case layers::RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorDMABuf: {
   1577      const auto& subdesc = subdescUnion.get_SurfaceDescriptorDMABuf();
   1578      RefPtr<DMABufSurface> surface =
   1579          DMABufSurface::CreateDMABufSurface(subdesc);
   1580      return Blit(surface, destRect, destOrigin, fbSize);
   1581    }
   1582 #endif
   1583 #ifdef XP_WIN
   1584    case layers::RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorD3D10: {
   1585      const auto& subdesc = subdescUnion.get_SurfaceDescriptorD3D10();
   1586      return BlitDescriptor(subdesc, destRect, destOrigin, fbSize);
   1587    }
   1588    case layers::RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorDXGIYCbCr: {
   1589      const auto& subdesc = subdescUnion.get_SurfaceDescriptorDXGIYCbCr();
   1590      return BlitDescriptor(subdesc, destRect, destOrigin, fbSize);
   1591    }
   1592 #endif
   1593 #ifdef XP_MACOSX
   1594    case layers::RemoteDecoderVideoSubDescriptor::
   1595        TSurfaceDescriptorMacIOSurface: {
   1596      const auto& subdesc = subdescUnion.get_SurfaceDescriptorMacIOSurface();
   1597      RefPtr<MacIOSurface> surface = MacIOSurface::LookupSurface(
   1598          subdesc.surfaceId(), !subdesc.isOpaque(), subdesc.yUVColorSpace());
   1599      MOZ_ASSERT(surface);
   1600      if (!surface) {
   1601        return false;
   1602      }
   1603      return BlitImage(surface, destRect, destOrigin, fbSize);
   1604    }
   1605 #endif
   1606    case layers::RemoteDecoderVideoSubDescriptor::Tnull_t:
   1607      // This GPUVideoImage isn't directly readable outside the GPU process.
   1608      // Abort.
   1609      return false;
   1610    default:
   1611      gfxCriticalError() << "Unhandled subdesc type: "
   1612                         << uint32_t(subdescUnion.type());
   1613      return false;
   1614  }
   1615 }
   1616 
   1617 // -------------------------------------
   1618 #ifdef MOZ_WIDGET_GTK
   1619 bool GLBlitHelper::Blit(DMABufSurface* surface, const gfx::IntRect& destRect,
   1620                        OriginPos destOrigin, const gfx::IntSize& fbSize,
   1621                        Maybe<gfxAlphaType> convertAlpha) const {
   1622  const auto& srcOrigin = OriginPos::BottomLeft;
   1623 
   1624  DrawBlitProg::BaseArgs baseArgs;
   1625  baseArgs.yFlip = (destOrigin != srcOrigin);
   1626  baseArgs.fbSize = fbSize;
   1627  baseArgs.destRect = destRect;
   1628 
   1629  // TODO: The colorspace is known by the DMABUFSurface, why override it?
   1630  // See GetYUVColorSpace/GetFullRange()
   1631  DrawBlitProg::YUVArgs yuvArgs;
   1632  yuvArgs.colorSpaceForMatrix = Some(surface->GetYUVColorSpace());
   1633 
   1634  const DrawBlitProg::YUVArgs* pYuvArgs = nullptr;
   1635  const auto planes = surface->GetTextureCount();
   1636 
   1637  // -
   1638  // Ensure textures for all planes have been created.
   1639 
   1640  const bool createTextures = [&]() {
   1641    for (int i = 0; i < planes; i++) {
   1642      if (!surface->GetTexture(i)) {
   1643        return true;
   1644      }
   1645    }
   1646    return false;
   1647  }();
   1648 
   1649  bool didCreateTexture = false;
   1650  auto releaseTextures = mozilla::MakeScopeExit([&] {
   1651    if (didCreateTexture) {
   1652      surface->ReleaseTextures();
   1653    }
   1654  });
   1655 
   1656  if (createTextures) {
   1657    for (int i = 0; i < planes; i++) {
   1658      if (surface->GetTexture(i)) {
   1659        continue;
   1660      }
   1661      if (!surface->CreateTexture(mGL, i)) {
   1662        LOGDMABUF(("GLBlitHelper::Blit(): Failed to create DMABuf textures."));
   1663        return false;
   1664      }
   1665      didCreateTexture = true;
   1666    }
   1667  }
   1668 
   1669  // -
   1670 
   1671  const GLenum texTarget = LOCAL_GL_TEXTURE_2D;
   1672 
   1673  const ScopedSaveMultiTex saveTex(mGL, planes, texTarget);
   1674  const auto pixelFormat = surface->GetSurfaceType();
   1675 
   1676  const char* fragSample = nullptr;
   1677  auto fragConvert = kFragConvert_None;
   1678  switch (pixelFormat) {
   1679    case DMABufSurface::SURFACE_RGBA:
   1680      fragSample = kFragSample_OnePlane;
   1681      break;
   1682    case DMABufSurface::SURFACE_YUV:
   1683      if (surface->GetTextureCount() == 2) {
   1684        fragSample = kFragSample_TwoPlane;
   1685      } else if (surface->GetTextureCount() == 3) {
   1686        fragSample = kFragSample_ThreePlane;
   1687      } else {
   1688        gfxCriticalError() << "Unexpected planes count: "
   1689                           << surface->GetTextureCount();
   1690        return false;
   1691      }
   1692      pYuvArgs = &yuvArgs;
   1693      fragConvert = kFragConvert_ColorMatrix;
   1694      break;
   1695    default:
   1696      return false;
   1697  }
   1698 
   1699  for (const auto p : IntegerRange(planes)) {
   1700    mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + p);
   1701    mGL->fBindTexture(texTarget, surface->GetTexture(p));
   1702    mGL->TexParams_SetClampNoMips(texTarget);
   1703  }
   1704 
   1705  // We support only NV12/YUV420 formats only with 1/2 texture scale.
   1706  // We don't set cliprect as DMABus textures are created without padding.
   1707  baseArgs.texMatrix0 = SubRectMat3(0, 0, 1, 1);
   1708  baseArgs.texSize = gfx::IntSize(surface->GetWidth(), surface->GetHeight());
   1709  yuvArgs.texMatrix1 = SubRectMat3(0, 0, 1, 1);
   1710 
   1711  const auto& prog =
   1712      GetDrawBlitProg({kFragHeader_Tex2D,
   1713                       {fragSample, fragConvert, GetAlphaMixin(convertAlpha)}});
   1714  prog.Draw(baseArgs, pYuvArgs);
   1715 
   1716  return true;
   1717 }
   1718 
   1719 bool GLBlitHelper::BlitImage(layers::DMABUFSurfaceImage* srcImage,
   1720                             const gfx::IntRect& destRect, OriginPos destOrigin,
   1721                             const gfx::IntSize& fbSize) const {
   1722  DMABufSurface* surface = srcImage->GetSurface();
   1723  if (!surface) {
   1724    gfxCriticalError() << "Null DMABUFSurface for GLBlitHelper::BlitImage";
   1725    return false;
   1726  }
   1727  return Blit(surface, destRect, destOrigin, fbSize);
   1728 }
   1729 
   1730 bool GLBlitHelper::BlitYCbCrImageToDMABuf(const PlanarYCbCrData& yuvData,
   1731                                          DMABufSurface* surface) {
   1732  if ((!mGL->IsAtLeast(gl::ContextProfile::OpenGLCore, 300) &&
   1733       !mGL->IsAtLeast(gl::ContextProfile::OpenGLES, 300)) ||
   1734      !mGL->HasPBOState()) {
   1735    gfxCriticalError() << "BlitYCbCrImageToDMABuf: old GL version";
   1736    return false;
   1737  }
   1738 
   1739  auto ySize = yuvData.YDataSize();
   1740  auto cbcrSize = yuvData.CbCrDataSize();
   1741  if (yuvData.mYSkip || yuvData.mCbSkip || yuvData.mCrSkip || ySize.width < 0 ||
   1742      ySize.height < 0 || cbcrSize.width < 0 || cbcrSize.height < 0 ||
   1743      yuvData.mYStride < 0 || yuvData.mCbCrStride < 0) {
   1744    gfxCriticalError() << "Unusual PlanarYCbCrData: " << yuvData.mYSkip << ","
   1745                       << yuvData.mCbSkip << "," << yuvData.mCrSkip << ", "
   1746                       << ySize.width << "," << ySize.height << ", "
   1747                       << cbcrSize.width << "," << cbcrSize.height << ", "
   1748                       << yuvData.mYStride << "," << yuvData.mCbCrStride;
   1749    return false;
   1750  }
   1751 
   1752  GLenum internalFormat;
   1753  GLenum unpackFormat;
   1754  GLenum sizeFormat;
   1755  switch (yuvData.mColorDepth) {
   1756    case gfx::ColorDepth::COLOR_8:
   1757      internalFormat = LOCAL_GL_R8;
   1758      unpackFormat = LOCAL_GL_RED;
   1759      sizeFormat = LOCAL_GL_UNSIGNED_BYTE;
   1760      break;
   1761    case gfx::ColorDepth::COLOR_10:
   1762      internalFormat = LOCAL_GL_R16;
   1763      unpackFormat = LOCAL_GL_RED;
   1764      sizeFormat = LOCAL_GL_UNSIGNED_SHORT;
   1765      break;
   1766    default:
   1767      gfxCriticalError() << "BlitYCbCrImageToDMABuf: Unsupported color depth";
   1768      return false;
   1769  }
   1770 
   1771  if (!mYuvUploads[0]) {
   1772    mGL->fGenTextures(3, mYuvUploads);
   1773    const ScopedBindTexture bindTex(mGL, mYuvUploads[0], LOCAL_GL_TEXTURE_2D);
   1774    mGL->TexParams_SetClampNoMips(LOCAL_GL_TEXTURE_2D);
   1775    mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[1]);
   1776    mGL->TexParams_SetClampNoMips(LOCAL_GL_TEXTURE_2D);
   1777    mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[2]);
   1778    mGL->TexParams_SetClampNoMips(LOCAL_GL_TEXTURE_2D);
   1779  }
   1780 
   1781  // --
   1782 
   1783  const ScopedSaveMultiTex saveTex(mGL, 3, LOCAL_GL_TEXTURE_2D);
   1784  const ResetUnpackState reset(mGL);
   1785 
   1786  gfx::IntSize yTexSize(yuvData.mYStride, yuvData.YDataSize().height);
   1787  gfx::IntSize uvTexSize(yuvData.mCbCrStride, yuvData.CbCrDataSize().height);
   1788 
   1789  // If we upload short integer type (16bit per pixel),
   1790  // we need to divide texture width as it's derived from stride in bytes.
   1791  if (sizeFormat == LOCAL_GL_UNSIGNED_SHORT) {
   1792    yTexSize.width >>= 1;
   1793    uvTexSize.width >>= 1;
   1794  }
   1795 
   1796  if (yTexSize != mYuvUploads_YSize || uvTexSize != mYuvUploads_UVSize) {
   1797    mYuvUploads_YSize = yTexSize;
   1798    mYuvUploads_UVSize = uvTexSize;
   1799 
   1800    mGL->fActiveTexture(LOCAL_GL_TEXTURE0);
   1801    mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[0]);
   1802    mGL->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, internalFormat, yTexSize.width,
   1803                     yTexSize.height, 0, unpackFormat, sizeFormat, nullptr);
   1804    for (int i = 1; i < 3; i++) {
   1805      mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
   1806      mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[i]);
   1807      mGL->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, internalFormat, uvTexSize.width,
   1808                       uvTexSize.height, 0, unpackFormat, sizeFormat, nullptr);
   1809    }
   1810  }
   1811 
   1812  // --
   1813 
   1814  mGL->fActiveTexture(LOCAL_GL_TEXTURE0);
   1815  mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[0]);
   1816  mGL->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0, 0, 0, yTexSize.width,
   1817                      yTexSize.height, unpackFormat, sizeFormat,
   1818                      yuvData.mYChannel);
   1819  mGL->fActiveTexture(LOCAL_GL_TEXTURE1);
   1820  mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[1]);
   1821  mGL->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0, 0, 0, uvTexSize.width,
   1822                      uvTexSize.height, unpackFormat, sizeFormat,
   1823                      yuvData.mCbChannel);
   1824  mGL->fActiveTexture(LOCAL_GL_TEXTURE2);
   1825  mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[2]);
   1826  mGL->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0, 0, 0, uvTexSize.width,
   1827                      uvTexSize.height, unpackFormat, sizeFormat,
   1828                      yuvData.mCrChannel);
   1829 
   1830  // --
   1831 
   1832  DrawBlitProg::BaseArgs baseArgs;
   1833  baseArgs.yFlip = false;
   1834  const auto& clipRect = yuvData.mPictureRect;
   1835  baseArgs.texMatrix0 = SubRectMat3(clipRect, yTexSize);
   1836 
   1837  const char* fragConvert = yuvData.mColorDepth == gfx::ColorDepth::COLOR_10
   1838                                ? kFragConvertYUVP010
   1839                                : kFragConvert_None;
   1840 
   1841  // Blit Y plane
   1842  {
   1843    ScopedFramebufferForTexture autoFBForTex(mGL, surface->GetTexture(0));
   1844    if (!autoFBForTex.IsComplete()) {
   1845      gfxCriticalError() << "GLBlitHelper::BlitYCbCrImageToDMABuf: "
   1846                            "ScopedFramebufferForTexture failed.";
   1847      return false;
   1848    }
   1849    const ScopedBindFramebuffer bindFB(mGL, autoFBForTex.FB());
   1850 
   1851    baseArgs.fbSize = gfx::IntSize(surface->GetWidth(), surface->GetHeight());
   1852    const auto& prog = GetDrawBlitProg(
   1853        {kFragHeader_Tex2D, {kFragSample_OnePlane, fragConvert}});
   1854    prog.Draw(baseArgs);
   1855  }
   1856 
   1857  // Blit UV planes
   1858  {
   1859    ScopedFramebufferForTexture autoFBForTex(mGL, surface->GetTexture(1));
   1860    if (!autoFBForTex.IsComplete()) {
   1861      gfxCriticalError() << "GLBlitHelper::BlitYCbCrImageToDMABuf: "
   1862                            "ScopedFramebufferForTexture failed.";
   1863      return false;
   1864    }
   1865    const ScopedBindFramebuffer bindFB(mGL, autoFBForTex.FB());
   1866 
   1867    baseArgs.fbSize = gfx::IntSize(surface->GetWidth(1), surface->GetHeight(1));
   1868    const auto& prog = GetDrawBlitProg(
   1869        {kFragHeader_Tex2D, {kFragSample_TwoPlaneUV, fragConvert}});
   1870    prog.Draw(baseArgs);
   1871  }
   1872 
   1873  return true;
   1874 }
   1875 #endif
   1876 
   1877 // -
   1878 
   1879 template <size_t N>
   1880 static void PushUnorm(uint32_t* const out, const float inVal) {
   1881  const uint32_t mask = (1 << N) - 1;
   1882  auto fval = inVal;
   1883  fval = std::clamp(fval, 0.0f, 1.0f);
   1884  fval *= mask;
   1885  fval = roundf(fval);
   1886  auto ival = static_cast<uint32_t>(fval);
   1887  ival &= mask;
   1888 
   1889  *out <<= N;
   1890  *out |= ival;
   1891 }
   1892 
   1893 static uint32_t toRgb10A2(const color::vec4& val) {
   1894  // R in LSB
   1895  uint32_t ret = 0;
   1896  PushUnorm<2>(&ret, val.w());
   1897  PushUnorm<10>(&ret, val.z());
   1898  PushUnorm<10>(&ret, val.y());
   1899  PushUnorm<10>(&ret, val.x());
   1900  return ret;
   1901 }
   1902 
   1903 // -
   1904 
   1905 color::ColorspaceDesc ToColorspaceDesc(const gfx::YUVRangedColorSpace cs) {
   1906  switch (cs) {
   1907    case gfx::YUVRangedColorSpace::BT601_Narrow:
   1908      return {
   1909          .chrom = color::Chromaticities::Rec601_525_Ntsc(),
   1910          .tf = color::PiecewiseGammaDesc::Rec709(),
   1911          .yuv =
   1912              color::YuvDesc{
   1913                  .yCoeffs = color::YuvLumaCoeffs::Rec709(),
   1914                  .ycbcr = color::YcbcrDesc::Narrow8(),
   1915              },
   1916      };
   1917    case gfx::YUVRangedColorSpace::BT601_Full:
   1918      return {
   1919          .chrom = color::Chromaticities::Rec601_525_Ntsc(),
   1920          .tf = color::PiecewiseGammaDesc::Rec709(),
   1921          .yuv =
   1922              color::YuvDesc{
   1923                  .yCoeffs = color::YuvLumaCoeffs::Rec709(),
   1924                  .ycbcr = color::YcbcrDesc::Full8(),
   1925              },
   1926      };
   1927    case gfx::YUVRangedColorSpace::BT709_Narrow:
   1928      return {
   1929          .chrom = color::Chromaticities::Rec709(),
   1930          .tf = color::PiecewiseGammaDesc::Rec709(),
   1931          .yuv =
   1932              color::YuvDesc{
   1933                  .yCoeffs = color::YuvLumaCoeffs::Rec709(),
   1934                  .ycbcr = color::YcbcrDesc::Narrow8(),
   1935              },
   1936      };
   1937    case gfx::YUVRangedColorSpace::BT709_Full:
   1938      return {
   1939          .chrom = color::Chromaticities::Rec709(),
   1940          .tf = color::PiecewiseGammaDesc::Rec709(),
   1941          .yuv =
   1942              color::YuvDesc{
   1943                  .yCoeffs = color::YuvLumaCoeffs::Rec709(),
   1944                  .ycbcr = color::YcbcrDesc::Full8(),
   1945              },
   1946      };
   1947    case gfx::YUVRangedColorSpace::BT2020_Narrow:
   1948      return {
   1949          .chrom = color::Chromaticities::Rec2020(),
   1950          .tf = color::PiecewiseGammaDesc::Rec2020_12bit(),
   1951          .yuv =
   1952              color::YuvDesc{
   1953                  .yCoeffs = color::YuvLumaCoeffs::Rec709(),
   1954                  .ycbcr = color::YcbcrDesc::Narrow8(),
   1955              },
   1956      };
   1957    case gfx::YUVRangedColorSpace::BT2020_Full:
   1958      return {
   1959          .chrom = color::Chromaticities::Rec2020(),
   1960          .tf = color::PiecewiseGammaDesc::Rec2020_12bit(),
   1961          .yuv =
   1962              color::YuvDesc{
   1963                  .yCoeffs = color::YuvLumaCoeffs::Rec2020(),
   1964                  .ycbcr = color::YcbcrDesc::Full8(),
   1965              },
   1966      };
   1967    case gfx::YUVRangedColorSpace::GbrIdentity:
   1968      return {
   1969          .chrom = color::Chromaticities::Rec709(),
   1970          .tf = color::PiecewiseGammaDesc::Rec709(),
   1971          .yuv =
   1972              color::YuvDesc{
   1973                  .yCoeffs = color::YuvLumaCoeffs::Gbr(),
   1974                  .ycbcr = color::YcbcrDesc::Full8(),
   1975              },
   1976      };
   1977  }
   1978  MOZ_CRASH("Bad YUVRangedColorSpace.");
   1979 }
   1980 
   1981 }  // namespace gl
   1982 namespace gfx {
   1983 
   1984 color::ColorProfileDesc QueryOutputColorProfile();
   1985 
   1986 }  // namespace gfx
   1987 namespace gl {
   1988 
   1989 // -
   1990 
   1991 /* static */
   1992 std::optional<color::ColorProfileDesc> GLBlitHelper::ToColorProfileDesc(
   1993    const gfx::ColorSpace2 cspace) {
   1994  color::ColorspaceDesc cspaceDesc;
   1995  switch (cspace) {
   1996    case gfx::ColorSpace2::Display:
   1997      if (kIsWindows) {
   1998 #ifdef XP_WIN
   1999        return gfx::QueryOutputColorProfile();
   2000 #endif
   2001      }
   2002      return {};
   2003 
   2004    case gfx::ColorSpace2::SRGB:
   2005      cspaceDesc = {.chrom = color::Chromaticities::Srgb(),
   2006                    .tf = color::PiecewiseGammaDesc::Srgb()};
   2007      break;
   2008    case gfx::ColorSpace2::DISPLAY_P3:
   2009      cspaceDesc = {.chrom = color::Chromaticities::DisplayP3(),
   2010                    .tf = color::PiecewiseGammaDesc::DisplayP3()};
   2011      break;
   2012    case gfx::ColorSpace2::BT601_525:  // aka smpte170m NTSC
   2013      cspaceDesc = {.chrom = color::Chromaticities::Rec601_525_Ntsc(),
   2014                    .tf = color::PiecewiseGammaDesc::Rec709()};
   2015      break;
   2016    case gfx::ColorSpace2::BT709:  // Same gamut as SRGB, but different gamma.
   2017      cspaceDesc = {.chrom = color::Chromaticities::Rec709(),
   2018                    .tf = color::PiecewiseGammaDesc::Rec709()};
   2019      break;
   2020    case gfx::ColorSpace2::BT2020:
   2021      cspaceDesc = {.chrom = color::Chromaticities::Rec2020(),
   2022                    .tf = color::PiecewiseGammaDesc::Rec2020_12bit()};
   2023      break;
   2024  }
   2025  const auto profileDesc = color::ColorProfileDesc::From(cspaceDesc);
   2026  return profileDesc;
   2027 }
   2028 
   2029 // -
   2030 
   2031 // For std::visit
   2032 template <class... Ts>
   2033 struct overloaded : Ts... {
   2034  using Ts::operator()...;
   2035 };
   2036 // explicit deduction guide (not needed as of C++20)
   2037 template <class... Ts>
   2038 overloaded(Ts...) -> overloaded<Ts...>;
   2039 
   2040 // -
   2041 
   2042 template <typename C, typename K>
   2043 inline auto MaybeFind(C& container, const K& key)
   2044    -> decltype(&(container.find(key)->second)) {
   2045  const auto itr = container.find(key);
   2046  if (itr == container.end()) return nullptr;
   2047  return &(itr->second);
   2048 }
   2049 
   2050 // -
   2051 
   2052 std::shared_ptr<gl::Texture> GLBlitHelper::GetColorLutTex(
   2053    const ColorLutKey& request) const {
   2054  if (const auto found = gl::MaybeFind(mColorLutTexMap, request)) {
   2055    return *found;  // Might be *Some(nullptr) -> nullptr!
   2056  }
   2057 
   2058  return mColorLutTexMap[request] = [&]() -> std::shared_ptr<gl::Texture> {
   2059    auto& gl = *mGL;
   2060    const auto tex = std::make_shared<gl::Texture>(gl);
   2061 
   2062    // -
   2063 
   2064    const std::optional<color::ColorProfileDesc> srcProfile =
   2065        std::visit(overloaded{
   2066                       [&](const gfx::ColorSpace2& cs)
   2067                           -> std::optional<color::ColorProfileDesc> {
   2068                         MOZ_ASSERT(cs != request.dst);
   2069                         const auto cpd = ToColorProfileDesc(cs);
   2070                         return cpd;
   2071                       },
   2072                       [&](const gfx::YUVRangedColorSpace& cs)
   2073                           -> std::optional<color::ColorProfileDesc> {
   2074                         const auto csd = ToColorspaceDesc(cs);
   2075                         const auto cpd = color::ColorProfileDesc::From(csd);
   2076                         return cpd;
   2077                       },
   2078                   },
   2079                   request.src);
   2080    MOZ_ASSERT(srcProfile);
   2081 
   2082    const auto dstProfile = ToColorProfileDesc(request.dst);
   2083    if (kIsWindows) {
   2084      MOZ_ASSERT(dstProfile);
   2085    }
   2086    if (!srcProfile || !dstProfile) return nullptr;
   2087    const auto conversion = color::ColorProfileConversionDesc::From({
   2088        .src = *srcProfile,
   2089        .dst = *dstProfile,
   2090    });
   2091 
   2092    // -
   2093 
   2094    const auto minLutSize = color::ivec3{2};
   2095    const auto maxLutSize = color::ivec3{256};
   2096    auto lutSize = minLutSize;
   2097    const bool isYcbcr =
   2098        (conversion.srcRgbFromSrcYuv != color::mat4::Identity());
   2099    if (isYcbcr) {
   2100      lutSize.x(int(StaticPrefs::gfx_blithelper_lut_size_ycbcr_y()));
   2101      lutSize.y(int(StaticPrefs::gfx_blithelper_lut_size_ycbcr_cb()));
   2102      lutSize.z(int(StaticPrefs::gfx_blithelper_lut_size_ycbcr_cr()));
   2103    } else {
   2104      lutSize.x(int(StaticPrefs::gfx_blithelper_lut_size_rgb_r()));
   2105      lutSize.y(int(StaticPrefs::gfx_blithelper_lut_size_rgb_g()));
   2106      lutSize.z(int(StaticPrefs::gfx_blithelper_lut_size_rgb_b()));
   2107    }
   2108    lutSize = clamp(lutSize, minLutSize, maxLutSize);
   2109 
   2110    const auto lut = [&]() {
   2111      auto lut = color::Lut3::Create(lutSize);
   2112      lut.SetMap(
   2113          [&](const color::vec3& src) { return conversion.DstFromSrc(src); });
   2114      return lut;
   2115    }();
   2116    const auto& size = lut.size;
   2117 
   2118    // -
   2119 
   2120    constexpr GLenum target = LOCAL_GL_TEXTURE_3D;
   2121    const auto bind = gl::ScopedBindTexture(&gl, tex->name, target);
   2122    gl.fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
   2123    gl.fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
   2124    gl.fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_R, LOCAL_GL_CLAMP_TO_EDGE);
   2125    gl.fTexParameteri(target, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);
   2126    gl.fTexParameteri(target, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
   2127 
   2128    bool useFloat16 = true;
   2129    if (useFloat16) {
   2130      // Use rgba16f, which we can thankfully upload as rgba32f
   2131      static_assert(sizeof(color::vec4) == sizeof(float) * 4);
   2132      std::vector<color::vec4> uploadData;
   2133      uploadData.reserve(lut.data.size());
   2134      for (const auto& src : lut.data) {
   2135        const auto dst = color::vec4{src, 1};
   2136        uploadData.push_back(dst);
   2137      }
   2138 
   2139      gl.fTexStorage3D(target, 1, LOCAL_GL_RGBA16F, size.x(), size.y(),
   2140                       size.z());
   2141      gl.fTexSubImage3D(target, 0, 0, 0, 0, size.x(), size.y(), size.z(),
   2142                        LOCAL_GL_RGBA, LOCAL_GL_FLOAT, uploadData.data());
   2143    } else {
   2144      // Use Rgb10A2
   2145      std::vector<uint32_t> uploadData;
   2146      uploadData.reserve(lut.data.size());
   2147      for (const auto& src : lut.data) {
   2148        const auto dst = toRgb10A2({src, 1});
   2149        uploadData.push_back(dst);
   2150      }
   2151 
   2152      gl.fTexStorage3D(target, 1, LOCAL_GL_RGB10_A2, size.x(), size.y(),
   2153                       size.z());
   2154      gl.fTexSubImage3D(target, 0, 0, 0, 0, size.x(), size.y(), size.z(),
   2155                        LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV,
   2156                        uploadData.data());
   2157    }
   2158    return tex;
   2159  }();
   2160 }
   2161 
   2162 }  // namespace gl
   2163 }  // namespace mozilla