tor-browser

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

TexUnpackBlob.cpp (43206B)


      1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "TexUnpackBlob.h"
      7 
      8 #include "GLBlitHelper.h"
      9 #include "GLContext.h"
     10 #include "WebGLBuffer.h"
     11 #include "WebGLContext.h"
     12 #include "WebGLFormats.h"
     13 #include "WebGLTexelConversions.h"
     14 #include "WebGLTexture.h"
     15 #include "mozilla/RefPtr.h"
     16 #include "mozilla/dom/Element.h"
     17 #include "mozilla/dom/HTMLCanvasElement.h"
     18 #include "mozilla/gfx/CanvasManagerParent.h"
     19 #include "mozilla/gfx/Logging.h"
     20 #include "mozilla/layers/ImageDataSerializer.h"
     21 #include "mozilla/layers/SharedSurfacesParent.h"
     22 #include "mozilla/layers/TextureHost.h"
     23 #include "mozilla/layers/VideoBridgeParent.h"
     24 #include "nsLayoutUtils.h"
     25 
     26 namespace mozilla {
     27 
     28 bool webgl::PixelPackingState::AssertCurrentUnpack(gl::GLContext& gl,
     29                                                   const bool isWebgl2) const {
     30  if (!kIsDebug) return true;
     31 
     32  auto actual = PixelPackingState{};
     33  gl.GetInt(LOCAL_GL_UNPACK_ALIGNMENT, &actual.alignmentInTypeElems);
     34  if (isWebgl2) {
     35    gl.GetInt(LOCAL_GL_UNPACK_ROW_LENGTH, &actual.rowLength);
     36    gl.GetInt(LOCAL_GL_UNPACK_IMAGE_HEIGHT, &actual.imageHeight);
     37 
     38    gl.GetInt(LOCAL_GL_UNPACK_SKIP_PIXELS, &actual.skipPixels);
     39    gl.GetInt(LOCAL_GL_UNPACK_SKIP_ROWS, &actual.skipRows);
     40    gl.GetInt(LOCAL_GL_UNPACK_SKIP_IMAGES, &actual.skipImages);
     41  }
     42  if (*this == actual) return true;
     43 
     44  const auto ToStr = [](const PixelPackingState& x) {
     45    const auto text = nsPrintfCString(
     46        "%u,%u,%u;%u,%u,%u", x.alignmentInTypeElems, x.rowLength, x.imageHeight,
     47        x.skipPixels, x.skipRows, x.skipImages);
     48    return mozilla::ToString(text);
     49  };
     50 
     51  const auto was = ToStr(actual);
     52  const auto expected = ToStr(*this);
     53  gfxCriticalError() << "PixelUnpackStateGl was not current. Was " << was
     54                     << ". Expected << " << expected << ".";
     55  return false;
     56 }
     57 
     58 void webgl::PixelPackingState::ApplyUnpack(gl::GLContext& gl,
     59                                           const bool isWebgl2,
     60                                           const uvec3& uploadSize) const {
     61  gl.fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT,
     62                  AssertedCast<GLsizei>(alignmentInTypeElems));
     63  if (!isWebgl2) return;
     64 
     65  // Re-simplify. (ANGLE seems to have an issue with imageHeight ==
     66  // uploadSize.y)
     67  auto rowLengthOrZero = rowLength;
     68  auto imageHeightOrZero = imageHeight;
     69  if (rowLengthOrZero == uploadSize.x) {
     70    rowLengthOrZero = 0;
     71  }
     72  if (imageHeightOrZero == uploadSize.y) {
     73    imageHeightOrZero = 0;
     74  }
     75 
     76  gl.fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH,
     77                  AssertedCast<GLsizei>(rowLengthOrZero));
     78  gl.fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT,
     79                  AssertedCast<GLsizei>(imageHeightOrZero));
     80 
     81  gl.fPixelStorei(LOCAL_GL_UNPACK_SKIP_PIXELS,
     82                  AssertedCast<GLsizei>(skipPixels));
     83  gl.fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS, AssertedCast<GLsizei>(skipRows));
     84  gl.fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES,
     85                  AssertedCast<GLsizei>(skipImages));
     86 }
     87 
     88 namespace webgl {
     89 
     90 static bool IsPIValidForDOM(const webgl::PackingInfo& pi) {
     91  // https://www.khronos.org/registry/webgl/specs/latest/2.0/#TEXTURE_TYPES_FORMATS_FROM_DOM_ELEMENTS_TABLE
     92 
     93  // Just check for invalid individual formats and types, not combinations.
     94  switch (pi.format) {
     95    case LOCAL_GL_RGB:
     96    case LOCAL_GL_RGBA:
     97    case LOCAL_GL_LUMINANCE_ALPHA:
     98    case LOCAL_GL_LUMINANCE:
     99    case LOCAL_GL_ALPHA:
    100    case LOCAL_GL_RED:
    101    case LOCAL_GL_RED_INTEGER:
    102    case LOCAL_GL_RG:
    103    case LOCAL_GL_RG_INTEGER:
    104    case LOCAL_GL_RGB_INTEGER:
    105    case LOCAL_GL_RGBA_INTEGER:
    106      break;
    107 
    108    case LOCAL_GL_SRGB:
    109    case LOCAL_GL_SRGB_ALPHA:
    110      // Allowed in WebGL1+EXT_srgb
    111      break;
    112 
    113    default:
    114      return false;
    115  }
    116 
    117  switch (pi.type) {
    118    case LOCAL_GL_UNSIGNED_BYTE:
    119    case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
    120    case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
    121    case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
    122    case LOCAL_GL_HALF_FLOAT:
    123    case LOCAL_GL_HALF_FLOAT_OES:
    124    case LOCAL_GL_FLOAT:
    125    case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV:
    126      break;
    127 
    128    default:
    129      return false;
    130  }
    131 
    132  return true;
    133 }
    134 
    135 static bool ValidatePIForDOM(const WebGLContext* const webgl,
    136                             const webgl::PackingInfo& pi) {
    137  if (!IsPIValidForDOM(pi)) {
    138    webgl->ErrorInvalidValue("Format or type is invalid for DOM sources.");
    139    return false;
    140  }
    141  return true;
    142 }
    143 
    144 static WebGLTexelFormat FormatForPackingInfo(const PackingInfo& pi) {
    145  switch (pi.type) {
    146    case LOCAL_GL_UNSIGNED_BYTE:
    147      switch (pi.format) {
    148        case LOCAL_GL_RED:
    149        case LOCAL_GL_LUMINANCE:
    150        case LOCAL_GL_RED_INTEGER:
    151          return WebGLTexelFormat::R8;
    152 
    153        case LOCAL_GL_ALPHA:
    154          return WebGLTexelFormat::A8;
    155 
    156        case LOCAL_GL_LUMINANCE_ALPHA:
    157          return WebGLTexelFormat::RA8;
    158 
    159        case LOCAL_GL_RGB:
    160        case LOCAL_GL_RGB_INTEGER:
    161        case LOCAL_GL_SRGB:
    162          return WebGLTexelFormat::RGB8;
    163 
    164        case LOCAL_GL_RGBA:
    165        case LOCAL_GL_RGBA_INTEGER:
    166        case LOCAL_GL_SRGB_ALPHA:
    167          return WebGLTexelFormat::RGBA8;
    168 
    169        case LOCAL_GL_RG:
    170        case LOCAL_GL_RG_INTEGER:
    171          return WebGLTexelFormat::RG8;
    172 
    173        default:
    174          break;
    175      }
    176      break;
    177 
    178    case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
    179      if (pi.format == LOCAL_GL_RGB) return WebGLTexelFormat::RGB565;
    180      break;
    181 
    182    case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
    183      if (pi.format == LOCAL_GL_RGBA) return WebGLTexelFormat::RGBA5551;
    184      break;
    185 
    186    case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
    187      if (pi.format == LOCAL_GL_RGBA) return WebGLTexelFormat::RGBA4444;
    188      break;
    189 
    190    case LOCAL_GL_HALF_FLOAT:
    191    case LOCAL_GL_HALF_FLOAT_OES:
    192      switch (pi.format) {
    193        case LOCAL_GL_RED:
    194        case LOCAL_GL_LUMINANCE:
    195          return WebGLTexelFormat::R16F;
    196 
    197        case LOCAL_GL_ALPHA:
    198          return WebGLTexelFormat::A16F;
    199        case LOCAL_GL_LUMINANCE_ALPHA:
    200          return WebGLTexelFormat::RA16F;
    201        case LOCAL_GL_RG:
    202          return WebGLTexelFormat::RG16F;
    203        case LOCAL_GL_RGB:
    204          return WebGLTexelFormat::RGB16F;
    205        case LOCAL_GL_RGBA:
    206          return WebGLTexelFormat::RGBA16F;
    207 
    208        default:
    209          break;
    210      }
    211      break;
    212 
    213    case LOCAL_GL_FLOAT:
    214      switch (pi.format) {
    215        case LOCAL_GL_RED:
    216        case LOCAL_GL_LUMINANCE:
    217          return WebGLTexelFormat::R32F;
    218 
    219        case LOCAL_GL_ALPHA:
    220          return WebGLTexelFormat::A32F;
    221        case LOCAL_GL_LUMINANCE_ALPHA:
    222          return WebGLTexelFormat::RA32F;
    223        case LOCAL_GL_RG:
    224          return WebGLTexelFormat::RG32F;
    225        case LOCAL_GL_RGB:
    226          return WebGLTexelFormat::RGB32F;
    227        case LOCAL_GL_RGBA:
    228          return WebGLTexelFormat::RGBA32F;
    229 
    230        default:
    231          break;
    232      }
    233      break;
    234 
    235    case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV:
    236      if (pi.format == LOCAL_GL_RGB) return WebGLTexelFormat::RGB11F11F10F;
    237      break;
    238 
    239    default:
    240      break;
    241  }
    242 
    243  return WebGLTexelFormat::FormatNotSupportingAnyConversion;
    244 }
    245 
    246 ////////////////////
    247 
    248 static uint32_t ZeroOn2D(const GLenum target, const uint32_t val) {
    249  const bool is2d = !IsTexTarget3D(target);
    250  if (is2d) return 0;
    251  return val;
    252 }
    253 
    254 static bool ValidateUnpackPixels(const WebGLContext* webgl,
    255                                 const webgl::PackingInfo& pi,
    256                                 const uint32_t availRows,
    257                                 const webgl::TexUnpackBlob& blob) {
    258  const auto& unpackingRes = blob.mDesc.ExplicitUnpacking(pi, {});
    259  if (!unpackingRes.isOk()) {
    260    webgl->ErrorInvalidOperation("%s", unpackingRes.inspectErr().c_str());
    261    return false;
    262  }
    263  const auto& unpacking = unpackingRes.inspect();
    264 
    265  if (availRows < unpacking.metrics.totalRows) {
    266    webgl->ErrorInvalidOperation(
    267        "Desired upload requires more rows (%zu) than is"
    268        " available (%zu).",
    269        unpacking.metrics.totalRows, availRows);
    270    return false;
    271  }
    272 
    273  return true;
    274 }
    275 
    276 static bool ValidateUnpackBytes(const WebGLContext* const webgl,
    277                                const webgl::PackingInfo& pi,
    278                                const size_t availByteCount,
    279                                const webgl::TexUnpackBlob& blob) {
    280  const auto& unpackingRes = blob.mDesc.ExplicitUnpacking(pi, {});
    281  if (!unpackingRes.isOk()) {
    282    webgl->ErrorInvalidOperation("%s", unpackingRes.inspectErr().c_str());
    283    return false;
    284  }
    285  const auto& unpacking = unpackingRes.inspect();
    286 
    287  if (availByteCount < unpacking.metrics.totalBytesUsed) {
    288    webgl->ErrorInvalidOperation(
    289        "Desired upload requires more bytes (%zu) than are"
    290        " available (%zu).",
    291        unpacking.metrics.totalBytesUsed, availByteCount);
    292    return false;
    293  }
    294 
    295  return true;
    296 }
    297 
    298 ////////////////////
    299 
    300 // Check if the surface descriptor describes a memory which contains a single
    301 // RGBA data source.
    302 static bool SDIsRGBBuffer(const layers::SurfaceDescriptor& sd) {
    303  return sd.type() == layers::SurfaceDescriptor::TSurfaceDescriptorBuffer &&
    304         sd.get_SurfaceDescriptorBuffer().desc().type() ==
    305             layers::BufferDescriptor::TRGBDescriptor;
    306 }
    307 
    308 // Check if the surface descriptor describes a GPUVideo texture for which we
    309 // only have an opaque source/handle from SurfaceDescriptorRemoteDecoder to
    310 // derive the actual texture from.
    311 static bool SDIsNullRemoteDecoder(const layers::SurfaceDescriptor& sd) {
    312  return sd.type() == layers::SurfaceDescriptor::TSurfaceDescriptorGPUVideo &&
    313         sd.get_SurfaceDescriptorGPUVideo()
    314                 .get_SurfaceDescriptorRemoteDecoder()
    315                 .subdesc()
    316                 .type() == layers::RemoteDecoderVideoSubDescriptor::Tnull_t;
    317 }
    318 
    319 // Check if the surface descriptor describes an ExternalImage surface for which
    320 // we only have an opaque source/handle to derive the actual surface from.
    321 static bool SDIsExternalImage(const layers::SurfaceDescriptor& sd) {
    322  return sd.type() ==
    323             layers::SurfaceDescriptor::TSurfaceDescriptorExternalImage &&
    324         sd.get_SurfaceDescriptorExternalImage().source() ==
    325             wr::ExternalImageSource::SharedSurfaces;
    326 }
    327 
    328 static bool SDIsCanvasSurface(const layers::SurfaceDescriptor& sd) {
    329  return sd.type() ==
    330         layers::SurfaceDescriptor::TSurfaceDescriptorCanvasSurface;
    331 }
    332 
    333 // static
    334 std::unique_ptr<TexUnpackBlob> TexUnpackBlob::Create(
    335    const TexUnpackBlobDesc& desc) {
    336  return std::unique_ptr<TexUnpackBlob>{[&]() -> TexUnpackBlob* {
    337    if (!IsTarget3D(desc.imageTarget) && desc.size.z != 1) {
    338      MOZ_ASSERT(false);
    339      return nullptr;
    340    }
    341 
    342    switch (desc.unpacking.alignmentInTypeElems) {
    343      case 1:
    344      case 2:
    345      case 4:
    346      case 8:
    347        break;
    348      default:
    349        MOZ_ASSERT(false);
    350        return nullptr;
    351    }
    352 
    353    if (desc.sd || desc.sourceSurf) {
    354      return new TexUnpackSurface(desc);
    355    }
    356 
    357    if (desc.srcAlphaType != gfxAlphaType::NonPremult) {
    358      MOZ_ASSERT(false);
    359      return nullptr;
    360    }
    361    return new TexUnpackBytes(desc);
    362  }()};
    363 }
    364 
    365 static bool HasColorAndAlpha(const WebGLTexelFormat format) {
    366  switch (format) {
    367    case WebGLTexelFormat::RA8:
    368    case WebGLTexelFormat::RA16F:
    369    case WebGLTexelFormat::RA32F:
    370    case WebGLTexelFormat::RGBA8:
    371    case WebGLTexelFormat::RGBA5551:
    372    case WebGLTexelFormat::RGBA4444:
    373    case WebGLTexelFormat::RGBA16F:
    374    case WebGLTexelFormat::RGBA32F:
    375    case WebGLTexelFormat::BGRA8:
    376      return true;
    377    default:
    378      return false;
    379  }
    380 }
    381 
    382 bool TexUnpackBlob::ConvertIfNeeded(
    383    const WebGLContext* const webgl, const uint32_t rowLength,
    384    const uint32_t rowCount, WebGLTexelFormat srcFormat,
    385    const uint8_t* const srcBegin, const ptrdiff_t srcStride,
    386    WebGLTexelFormat dstFormat, const ptrdiff_t dstStride,
    387    const uint8_t** const out_begin,
    388    UniqueBuffer* const out_anchoredBuffer) const {
    389  MOZ_ASSERT(srcFormat != WebGLTexelFormat::FormatNotSupportingAnyConversion);
    390  MOZ_ASSERT(dstFormat != WebGLTexelFormat::FormatNotSupportingAnyConversion);
    391 
    392  *out_begin = srcBegin;
    393 
    394  const auto& unpacking = mDesc.unpacking;
    395 
    396  if (!rowLength || !rowCount) return true;
    397 
    398  auto minSrcStride =
    399      CheckedInt<size_t>(
    400          WebGLTexelConversions::TexelBytesForFormat(srcFormat)) *
    401      rowLength;
    402  auto minDstStride =
    403      CheckedInt<size_t>(
    404          WebGLTexelConversions::TexelBytesForFormat(dstFormat)) *
    405      rowLength;
    406  if (srcStride <= 0 || dstStride <= 0 || !minSrcStride.isValid() ||
    407      !minDstStride.isValid() || size_t(srcStride) < minSrcStride.value() ||
    408      size_t(dstStride) < minDstStride.value()) {
    409    webgl->ErrorInvalidOperation("Invalid stride.");
    410    return false;
    411  }
    412 
    413  const auto srcIsPremult = (mDesc.srcAlphaType == gfxAlphaType::Premult);
    414  auto dstIsPremult = unpacking.premultiplyAlpha;
    415  const auto fnHasPremultMismatch = [&]() {
    416    if (mDesc.srcAlphaType == gfxAlphaType::Opaque) return false;
    417 
    418    if (!HasColorAndAlpha(srcFormat)) return false;
    419 
    420    return srcIsPremult != dstIsPremult;
    421  };
    422 
    423  const auto srcOrigin =
    424      (unpacking.flipY ? gl::OriginPos::TopLeft : gl::OriginPos::BottomLeft);
    425  auto dstOrigin = gl::OriginPos::BottomLeft;
    426 
    427  if (!mDesc.applyUnpackTransforms) {
    428    dstIsPremult = srcIsPremult;
    429    dstOrigin = srcOrigin;
    430  }
    431 
    432  // TODO (Bug 754256): Figure out the source colorSpace.
    433  dom::PredefinedColorSpace srcColorSpace = dom::PredefinedColorSpace::Srgb;
    434  dom::PredefinedColorSpace dstColorSpace =
    435      webgl->mUnpackColorSpace ? *webgl->mUnpackColorSpace
    436                               : dom::PredefinedColorSpace::Srgb;
    437 
    438  if (srcFormat != dstFormat) {
    439    webgl->GeneratePerfWarning(
    440        "Conversion requires pixel reformatting. (%u->%u)", uint32_t(srcFormat),
    441        uint32_t(dstFormat));
    442  } else if (fnHasPremultMismatch()) {
    443    webgl->GeneratePerfWarning(
    444        "Conversion requires change in"
    445        " alpha-premultiplication.");
    446  } else if (srcOrigin != dstOrigin) {
    447    webgl->GeneratePerfWarning("Conversion requires y-flip.");
    448  } else if (srcStride != dstStride) {
    449    webgl->GeneratePerfWarning("Conversion requires change in stride. (%u->%u)",
    450                               uint32_t(srcStride), uint32_t(dstStride));
    451  } else if (srcColorSpace != dstColorSpace) {
    452    webgl->GeneratePerfWarning(
    453        "Conversion requires colorSpace conversion. (%u->%u)",
    454        uint32_t(srcColorSpace), uint32_t(dstColorSpace));
    455  } else {
    456    return true;
    457  }
    458 
    459  ////
    460 
    461  const auto dstTotalBytes = CheckedUint32(rowCount) * dstStride;
    462  if (!dstTotalBytes.isValid()) {
    463    webgl->ErrorOutOfMemory("Calculation failed.");
    464    return false;
    465  }
    466 
    467  auto dstBuffer = UniqueBuffer::Take(calloc(1u, dstTotalBytes.value()));
    468  if (!dstBuffer.get()) {
    469    webgl->ErrorOutOfMemory("Failed to allocate dest buffer.");
    470    return false;
    471  }
    472  const auto dstBegin = static_cast<uint8_t*>(dstBuffer.get());
    473 
    474  ////
    475 
    476  // And go!:
    477  bool wasTrivial;
    478  if (!ConvertImage(rowLength, rowCount, srcBegin, srcStride, srcOrigin,
    479                    srcFormat, srcIsPremult, dstBegin, dstStride, dstOrigin,
    480                    dstFormat, dstIsPremult, srcColorSpace, dstColorSpace,
    481                    &wasTrivial)) {
    482    webgl->ErrorImplementationBug("ConvertImage failed.");
    483    return false;
    484  }
    485 
    486  *out_begin = dstBegin;
    487  *out_anchoredBuffer = std::move(dstBuffer);
    488  return true;
    489 }
    490 
    491 static GLenum DoTexOrSubImage(bool isSubImage, gl::GLContext* gl,
    492                              TexImageTarget target, GLint level,
    493                              const DriverUnpackInfo* dui, GLint xOffset,
    494                              GLint yOffset, GLint zOffset, GLsizei width,
    495                              GLsizei height, GLsizei depth, const void* data) {
    496  if (isSubImage) {
    497    return DoTexSubImage(gl, target, level, xOffset, yOffset, zOffset, width,
    498                         height, depth, dui->ToPacking(), data);
    499  } else {
    500    return DoTexImage(gl, target, level, dui, width, height, depth, data);
    501  }
    502 }
    503 
    504 //////////////////////////////////////////////////////////////////////////////////////////
    505 // TexUnpackBytes
    506 
    507 bool TexUnpackBytes::Validate(const WebGLContext* const webgl,
    508                              const webgl::PackingInfo& pi) {
    509  if (!HasData()) return true;
    510 
    511  CheckedInt<size_t> availBytes = 0;
    512  if (mDesc.cpuData) {
    513    availBytes = mDesc.cpuData->size();
    514  } else if (mDesc.pboOffset) {
    515    const auto& pboOffset = *mDesc.pboOffset;
    516 
    517    const auto& pbo =
    518        webgl->ValidateBufferSelection(LOCAL_GL_PIXEL_UNPACK_BUFFER);
    519    if (!pbo) return false;  // Might be invalid e.g. due to in-use by TF.
    520    availBytes = pbo->ByteLength();
    521    availBytes -= pboOffset;
    522  } else {
    523    MOZ_ASSERT(false, "Must be one of the above");
    524  }
    525  if (!availBytes.isValid()) {
    526    webgl->ErrorInvalidOperation("Offset is passed end of buffer.");
    527    return false;
    528  }
    529 
    530  return ValidateUnpackBytes(webgl, pi, availBytes.value(), *this);
    531 }
    532 
    533 bool TexUnpackBytes::TexOrSubImage(bool isSubImage, bool needsRespec,
    534                                   WebGLTexture* tex, GLint level,
    535                                   const webgl::DriverUnpackInfo* dui,
    536                                   GLint xOffset, GLint yOffset, GLint zOffset,
    537                                   const webgl::PackingInfo& pi,
    538                                   GLenum* const out_error) const {
    539  const auto& webgl = tex->mContext;
    540  const auto& target = mDesc.imageTarget;
    541  const auto& size = mDesc.size;
    542  const auto& webglUnpackState = mDesc.unpacking;
    543 
    544  const auto unpackingRes = mDesc.ExplicitUnpacking(pi, {});
    545 
    546  const auto format = FormatForPackingInfo(pi);
    547 
    548  const uint8_t* uploadPtr = nullptr;
    549  if (mDesc.cpuData) {
    550    uploadPtr = mDesc.cpuData->data();
    551  } else if (mDesc.pboOffset) {
    552    uploadPtr = reinterpret_cast<const uint8_t*>(*mDesc.pboOffset);
    553  }
    554 
    555  UniqueBuffer tempBuffer;
    556 
    557  do {
    558    if (mDesc.pboOffset || !uploadPtr) break;
    559 
    560    if (!webglUnpackState.flipY && !webglUnpackState.premultiplyAlpha) {
    561      break;
    562    }
    563 
    564    webgl->GenerateWarning(
    565        "Alpha-premult and y-flip are deprecated for"
    566        " non-DOM-Element uploads.");
    567 
    568    MOZ_RELEASE_ASSERT(unpackingRes.isOk());
    569    const auto& unpacking = unpackingRes.inspect();
    570    const auto stride = unpacking.metrics.bytesPerRowStride;
    571    // clang-format off
    572    if (!ConvertIfNeeded(webgl, unpacking.state.rowLength,
    573                         unpacking.metrics.totalRows,
    574                         format, uploadPtr, AutoAssertCast(stride),
    575                         format, AutoAssertCast(stride), &uploadPtr, &tempBuffer)) {
    576      return false;
    577    }
    578    // clang-format on
    579  } while (false);
    580 
    581  //////
    582 
    583  const auto& gl = webgl->gl;
    584 
    585  bool useParanoidHandling = false;
    586  if (mNeedsExactUpload && webgl->mBoundPixelUnpackBuffer) {
    587    webgl->GenerateWarning(
    588        "Uploads from a buffer with a final row with a byte"
    589        " count smaller than the row stride can incur extra"
    590        " overhead.");
    591 
    592    if (gl->WorkAroundDriverBugs()) {
    593      useParanoidHandling |= (gl->Vendor() == gl::GLVendor::NVIDIA);
    594    }
    595  }
    596 
    597  if (!useParanoidHandling) {
    598    const ScopedLazyBind bindPBO(gl, LOCAL_GL_PIXEL_UNPACK_BUFFER,
    599                                 webgl->mBoundPixelUnpackBuffer);
    600 
    601    *out_error =
    602        DoTexOrSubImage(isSubImage, gl, target, level, dui, xOffset, yOffset,
    603                        zOffset, size.x, size.y, size.z, uploadPtr);
    604    return true;
    605  }
    606 
    607  //////
    608 
    609  MOZ_ASSERT(webgl->mBoundPixelUnpackBuffer);
    610 
    611  if (!isSubImage) {
    612    // Alloc first to catch OOMs.
    613    AssertUintParamCorrect(gl, LOCAL_GL_PIXEL_UNPACK_BUFFER_BINDING, 0);
    614    *out_error =
    615        DoTexOrSubImage(false, gl, target, level, dui, xOffset, yOffset,
    616                        zOffset, size.x, size.y, size.z, nullptr);
    617    if (*out_error) return true;
    618  }
    619  if (!size.x || !size.y || !size.z) {
    620    // Nothing to do.
    621    return true;
    622  }
    623 
    624  MOZ_RELEASE_ASSERT(unpackingRes.isOk());
    625  const auto& unpacking = unpackingRes.inspect();
    626 
    627  const ScopedLazyBind bindPBO(gl, LOCAL_GL_PIXEL_UNPACK_BUFFER,
    628                               webgl->mBoundPixelUnpackBuffer);
    629 
    630  //////
    631 
    632  // Make our sometimes-implicit values explicit. Also this keeps them constant
    633  // when we ask for height=mHeight-1 and such.
    634  gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH,
    635                   AutoAssertCast(unpacking.state.rowLength));
    636  gl->fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT,
    637                   AutoAssertCast(unpacking.state.imageHeight));
    638 
    639  if (size.z > 1) {
    640    *out_error =
    641        DoTexOrSubImage(true, gl, target, level, dui, xOffset, yOffset, zOffset,
    642                        size.x, size.y, size.z - 1, uploadPtr);
    643  }
    644 
    645  // Skip the images we uploaded.
    646  const auto skipImages = ZeroOn2D(target, unpacking.state.skipImages);
    647  gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES, skipImages + size.z - 1);
    648 
    649  if (size.y > 1) {
    650    *out_error =
    651        DoTexOrSubImage(true, gl, target, level, dui, xOffset, yOffset,
    652                        zOffset + size.z - 1, size.x, size.y - 1, 1, uploadPtr);
    653  }
    654 
    655  // -
    656 
    657  const auto lastRowOffset =
    658      unpacking.metrics.totalBytesStrided - unpacking.metrics.bytesPerRowStride;
    659  const auto lastRowPtr =
    660      mDesc.pboOffset
    661          ? reinterpret_cast<const uint8_t*>(*mDesc.pboOffset + lastRowOffset)
    662          : (uploadPtr ? uploadPtr + lastRowOffset : nullptr);
    663 
    664  gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1);    // No stride padding.
    665  gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0);   // No padding in general.
    666  gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES, 0);  // Don't skip images,
    667  gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS,
    668                   0);  // or rows.
    669                        // Keep skipping pixels though!
    670  *out_error = DoTexOrSubImage(true, gl, target, level, dui, xOffset,
    671                               yOffset + size.y - 1, zOffset + size.z - 1,
    672                               AutoAssertCast(size.x), 1, 1, lastRowPtr);
    673 
    674  // Caller will reset all our modified PixelStorei state.
    675 
    676  return true;
    677 }
    678 
    679 ////////////////////////////////////////////////////////////////////////////////
    680 ////////////////////////////////////////////////////////////////////////////////
    681 // TexUnpackSurface
    682 
    683 TexUnpackSurface::~TexUnpackSurface() = default;
    684 
    685 bool TexUnpackSurface::Validate(const WebGLContext* const webgl,
    686                                const webgl::PackingInfo& pi) {
    687  if (!ValidatePIForDOM(webgl, pi)) return false;
    688 
    689  if (!mDesc.structuredSrcSize) {
    690    gfxCriticalError() << "TexUnpackSurface missing structuredSrcSize.";
    691    return false;
    692  }
    693  const auto& elemSize = *mDesc.structuredSrcSize;
    694  if (mDesc.sourceSurf) {
    695    const auto& surfSize = mDesc.sourceSurf->GetSize();
    696    const auto surfSize2 = ivec2::FromSize(surfSize)->StaticCast<uvec2>();
    697    if (uvec2{elemSize.x, elemSize.y} != surfSize2) {
    698      gfxCriticalError()
    699          << "TexUnpackSurface mismatched structuredSrcSize for sourceSurf.";
    700      return false;
    701    }
    702  }
    703 
    704  const auto fullRows = elemSize.y;
    705  return ValidateUnpackPixels(webgl, pi, fullRows, *this);
    706 }
    707 
    708 const char* BlitPreventReason(
    709    GLenum target, int32_t level, const ivec3& offset, GLenum internalFormat,
    710    const webgl::PackingInfo& pi, const TexUnpackBlobDesc& desc,
    711    OptionalRenderableFormatBits optionalRenderableFormatBits,
    712    bool sameColorSpace, bool allowConversion, bool allowSRGB, bool allow3D) {
    713  const auto& size = desc.size;
    714  const auto& unpacking = desc.unpacking;
    715 
    716  if (size.z != 1) {
    717    return "depth is not 1";
    718  }
    719  if (offset.z != 0) {
    720    return "zOffset is not 0";
    721  }
    722 
    723  if (unpacking.skipPixels || unpacking.skipRows || unpacking.skipImages) {
    724    return "non-zero UNPACK_SKIP_* not yet supported";
    725  }
    726 
    727  if (desc.srcAlphaType != gfxAlphaType::Opaque) {
    728    const bool srcIsPremult = (desc.srcAlphaType == gfxAlphaType::Premult);
    729    const auto& dstIsPremult = unpacking.premultiplyAlpha;
    730    if (srcIsPremult != dstIsPremult && !allowConversion) {
    731      return dstIsPremult ? "UNPACK_PREMULTIPLY_ALPHA_WEBGL is not true"
    732                          : "UNPACK_PREMULTIPLY_ALPHA_WEBGL is not false";
    733    }
    734  }
    735 
    736  if (!sameColorSpace) {
    737    return "not same colorSpace";
    738  }
    739 
    740  const auto formatReason = [&]() -> const char* {
    741    if (pi.type != LOCAL_GL_UNSIGNED_BYTE) {
    742      return "`unpackType` must be `UNSIGNED_BYTE`";
    743    }
    744 
    745    // GL_FRAMEBUFFER_SRGB is not available in some GLES implementations,
    746    // which is required for blitting to emulate Tex(Sub)Image, which does no
    747    // encoding when uploading to an sRGB texture. In GLES (but not desktop
    748    // GL) blitting to an sRGB framebuffer encodes by default unless
    749    // explicitly disabled. To workaround the availability of this extension,
    750    // just disallow it for now. Note, this is also an optional boolean as
    751    // some internal data transfers intentionally rely on this conversion.
    752    switch (internalFormat) {
    753      case LOCAL_GL_SRGB:
    754      case LOCAL_GL_SRGB8:
    755      case LOCAL_GL_SRGB_ALPHA:
    756      case LOCAL_GL_SRGB8_ALPHA8:
    757        if (!allowSRGB) {
    758          return "sRGB-encoded internal formats are not supported";
    759        }
    760        break;
    761    }
    762 
    763    switch (pi.format) {
    764      case LOCAL_GL_RGBA:
    765        return nullptr;  // All internalFormats for unpackFormat=RGBA are
    766                         // renderable.
    767 
    768      case LOCAL_GL_RGB:
    769        break;
    770 
    771      default:
    772        return "`unpackFormat` must be `RGBA` or maybe `RGB`";
    773    }
    774 
    775    // -
    776 
    777    struct {
    778      OptionalRenderableFormatBits bits;
    779      const char* errorMsg;
    780    } required;
    781 
    782    switch (internalFormat) {
    783      case LOCAL_GL_RGB565:
    784        return nullptr;
    785      case LOCAL_GL_RGB:
    786      case LOCAL_GL_RGB8:
    787        required = {
    788            OptionalRenderableFormatBits::RGB8,
    789            "Unavailable, as blitting internalFormats RGB or RGB8 requires "
    790            "that RGB8 must be a renderable format.",
    791        };
    792        break;
    793      case LOCAL_GL_SRGB:
    794      case LOCAL_GL_SRGB8:
    795        required = {
    796            OptionalRenderableFormatBits::SRGB8,
    797            "Unavailable, as blitting internalFormats SRGB or SRGB8 requires "
    798            "that SRGB8 must be a renderable format.",
    799        };
    800        break;
    801      case 0:
    802        // texSubImage, so internalFormat is unknown, and could be anything!
    803        required = {
    804            OptionalRenderableFormatBits::RGB8 |
    805                OptionalRenderableFormatBits::SRGB8,
    806            "Unavailable, as blitting texSubImage with unpackFormat=RGB "
    807            "requires that RGB8 and SRGB8 must be renderable formats.",
    808        };
    809        break;
    810      default:
    811        gfxCriticalError()
    812            << "Unexpected internalFormat for unpackFormat=RGB: 0x"
    813            << gfx::hexa(internalFormat);
    814        return "Unexpected internalFormat for unpackFormat=RGB";
    815    }
    816 
    817    const auto availableBits = optionalRenderableFormatBits;
    818    if ((required.bits | availableBits) != availableBits) {
    819      return required.errorMsg;
    820    }
    821 
    822    // -
    823 
    824    return nullptr;
    825  }();
    826  if (formatReason) return formatReason;
    827 
    828  // If the texture has multiple faces or layers that are not allocated,
    829  // then the framebuffer attachment required to blit the descriptor may
    830  // be determined incomplete. Avoid this for now by restricting to 2D
    831  // targets.
    832  if (!allow3D) {
    833    bool is2D = ImageToTexTarget(target) == target && !IsTexTarget3D(target);
    834    if (!is2D) {
    835      return "texture is not 2D";
    836    }
    837  }
    838 
    839  return nullptr;
    840 }
    841 
    842 bool TexUnpackSurface::AllowBlitSd(WebGLContext* const webgl,
    843                                   const GLenum target, const int32_t level,
    844                                   const ivec3& offset,
    845                                   const GLenum internalFormat,
    846                                   const webgl::PackingInfo& pi,
    847                                   bool allowConversion, bool allowSRGB,
    848                                   bool allow3D, bool warn) const {
    849  dom::PredefinedColorSpace srcColorSpace = dom::PredefinedColorSpace::Srgb;
    850  dom::PredefinedColorSpace dstColorSpace =
    851      webgl->mUnpackColorSpace ? *webgl->mUnpackColorSpace
    852                               : dom::PredefinedColorSpace::Srgb;
    853  bool sameColorSpace = srcColorSpace == dstColorSpace;
    854  if (const char* reason = BlitPreventReason(
    855          target, level, offset, internalFormat, pi, mDesc,
    856          webgl->mOptionalRenderableFormatBits, sameColorSpace, allowConversion,
    857          allowSRGB, allow3D)) {
    858    if (warn) {
    859      webgl->GeneratePerfWarning(
    860          "Failed to hit GPU-copy fast-path."
    861          " (%s) Falling back to CPU upload.",
    862          reason);
    863    }
    864    return false;
    865  }
    866  return true;
    867 }
    868 
    869 // The texture may be mipmap incomplete which will prevent the framebuffer
    870 // from being complete while drawing to it. To avoid this scenario, override
    871 // the texture base and max level temporarily to ignore incomplete mipmaps
    872 // while blitting to it. Depending on GL implementation (desktop vs ES), the
    873 // min filter may contribute to mipmap completeness.
    874 struct AutoRestoreMipmapState {
    875  AutoRestoreMipmapState(gl::GLContext* gl, GLenum target, GLint level)
    876      : mGL(gl), mTarget(target), mLevel(level) {
    877    mGL->fGetTexParameteriv(mTarget, LOCAL_GL_TEXTURE_MIN_FILTER, &mMinFilter);
    878    if (IsTexMipmapFilter(mMinFilter)) {
    879      mGL->fTexParameteri(mTarget, LOCAL_GL_TEXTURE_MIN_FILTER,
    880                          LOCAL_GL_NEAREST);
    881    }
    882    if (mGL->HasTexParamMipmapLevel()) {
    883      mGL->fGetTexParameteriv(mTarget, LOCAL_GL_TEXTURE_BASE_LEVEL,
    884                              &mLevelBase);
    885      mGL->fGetTexParameteriv(mTarget, LOCAL_GL_TEXTURE_MAX_LEVEL, &mLevelMax);
    886      if (mLevelBase != mLevel) {
    887        mGL->fTexParameteri(mTarget, LOCAL_GL_TEXTURE_BASE_LEVEL, mLevel);
    888      }
    889      if (mLevelMax != mLevel) {
    890        mGL->fTexParameteri(mTarget, LOCAL_GL_TEXTURE_MAX_LEVEL, mLevel);
    891      }
    892    }
    893  }
    894 
    895  ~AutoRestoreMipmapState() {
    896    if (IsTexMipmapFilter(mMinFilter)) {
    897      mGL->fTexParameteri(mTarget, LOCAL_GL_TEXTURE_MIN_FILTER, mMinFilter);
    898    }
    899    if (mGL->HasTexParamMipmapLevel()) {
    900      if (mLevelBase != mLevel) {
    901        mGL->fTexParameteri(mTarget, LOCAL_GL_TEXTURE_BASE_LEVEL, mLevelBase);
    902      }
    903      if (mLevelMax != mLevel) {
    904        mGL->fTexParameteri(mTarget, LOCAL_GL_TEXTURE_MAX_LEVEL, mLevelMax);
    905      }
    906    }
    907  }
    908 
    909  gl::GLContext* mGL = nullptr;
    910  GLenum mTarget = 0;
    911  GLint mLevel = 0;
    912  GLint mMinFilter = 0;
    913  GLint mLevelBase = 0;
    914  GLint mLevelMax = 0;
    915 };
    916 
    917 bool TexUnpackSurface::BlitSd(
    918    const layers::SurfaceDescriptor& sd, bool isSubImage, bool needsRespec,
    919    WebGLTexture* tex, GLint level, const webgl::DriverUnpackInfo* dui,
    920    GLint xOffset, GLint yOffset, GLint zOffset, const webgl::PackingInfo& pi,
    921    GLenum* const out_error, bool allowFallback) const {
    922  MOZ_ASSERT_IF(needsRespec, !isSubImage);
    923 
    924  const auto& webgl = tex->mContext;
    925  const auto& target = mDesc.imageTarget;
    926  const auto& size = mDesc.size;
    927  const auto& unpacking = mDesc.unpacking;
    928 
    929  const auto& gl = webgl->GL();
    930 
    931  // -
    932 
    933  if (needsRespec) {
    934    *out_error =
    935        DoTexOrSubImage(isSubImage, gl, target, level, dui, xOffset, yOffset,
    936                        zOffset, size.x, size.y, size.z, nullptr);
    937    if (*out_error) return true;
    938  }
    939 
    940  {
    941    gl::ScopedBindTexture scopedTex(gl, tex->mGLName, target);
    942    AutoRestoreMipmapState restoreMipmapState(gl, target, level);
    943 
    944    gl::ScopedFramebuffer scopedFB(gl);
    945    gl::ScopedBindFramebuffer bindFB(gl, scopedFB.FB());
    946 
    947    {
    948      gl::GLContext::LocalErrorScope errorScope(*gl);
    949 
    950      gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
    951                                LOCAL_GL_COLOR_ATTACHMENT0, target,
    952                                tex->mGLName, level);
    953 
    954      const auto err = errorScope.GetError();
    955      if (err) {
    956        if (allowFallback) {
    957          return false;
    958        }
    959        MOZ_DIAGNOSTIC_CRASH("BlitSd failed attaching texture to framebuffer");
    960      }
    961    }
    962 
    963    const GLenum status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
    964    if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
    965      if (allowFallback) {
    966        return false;
    967      }
    968      MOZ_DIAGNOSTIC_CRASH("BlitSd framebuffer is not complete");
    969    }
    970 
    971    const auto dstOrigin =
    972        (unpacking.flipY ? gl::OriginPos::TopLeft : gl::OriginPos::BottomLeft);
    973    gfx::IntSize fbSize(size.x, size.y);
    974    if (isSubImage) {
    975      const auto& imageInfo = tex->ImageInfoAt(target, level);
    976      fbSize = gfx::IntSize(imageInfo.mWidth, imageInfo.mHeight);
    977    }
    978    Maybe<gfxAlphaType> convertAlpha;
    979    if (mDesc.srcAlphaType != gfxAlphaType::Opaque) {
    980      const bool srcIsPremult = mDesc.srcAlphaType == gfxAlphaType::Premult;
    981      const bool dstIsPremult = unpacking.premultiplyAlpha;
    982      if (srcIsPremult != dstIsPremult) {
    983        convertAlpha = Some(dstIsPremult ? gfxAlphaType::Premult
    984                                         : gfxAlphaType::NonPremult);
    985      }
    986    }
    987    if (!gl->BlitHelper()->BlitSdToFramebuffer(
    988            sd, {xOffset, yOffset, size.x, size.y}, dstOrigin, fbSize,
    989            convertAlpha)) {
    990      gfxCriticalNote << "BlitSdToFramebuffer failed for type "
    991                      << int(sd.type());
    992      if (allowFallback) {
    993        return false;
    994      }
    995      // Maybe the resource isn't valid anymore?
    996      gl->fClearColor(0.2, 0.0, 0.2, 1.0);
    997      gl->fClear(LOCAL_GL_COLOR_BUFFER_BIT);
    998      const auto& cur = webgl->mColorClearValue;
    999      gl->fClearColor(cur[0], cur[1], cur[2], cur[3]);
   1000      webgl->GenerateWarning(
   1001          "Fast Tex(Sub)Image upload failed without recourse, clearing to "
   1002          "[0.2, 0.0, 0.2, 1.0]. Please file a bug!");
   1003    }
   1004  }
   1005 
   1006  return true;
   1007 }
   1008 
   1009 static bool GetFormatForSurf(const gfx::SourceSurface* surf,
   1010                             WebGLTexelFormat* const out_texelFormat,
   1011                             uint8_t* const out_bpp) {
   1012  const auto surfFormat = surf->GetFormat();
   1013  switch (surfFormat) {
   1014    case gfx::SurfaceFormat::B8G8R8A8:
   1015      *out_texelFormat = WebGLTexelFormat::BGRA8;
   1016      *out_bpp = 4;
   1017      return true;
   1018 
   1019    case gfx::SurfaceFormat::B8G8R8X8:
   1020      *out_texelFormat = WebGLTexelFormat::BGRX8;
   1021      *out_bpp = 4;
   1022      return true;
   1023 
   1024    case gfx::SurfaceFormat::R8G8B8A8:
   1025      *out_texelFormat = WebGLTexelFormat::RGBA8;
   1026      *out_bpp = 4;
   1027      return true;
   1028 
   1029    case gfx::SurfaceFormat::R8G8B8X8:
   1030      *out_texelFormat = WebGLTexelFormat::RGBX8;
   1031      *out_bpp = 4;
   1032      return true;
   1033 
   1034    case gfx::SurfaceFormat::R5G6B5_UINT16:
   1035      *out_texelFormat = WebGLTexelFormat::RGB565;
   1036      *out_bpp = 2;
   1037      return true;
   1038 
   1039    case gfx::SurfaceFormat::A8:
   1040      *out_texelFormat = WebGLTexelFormat::A8;
   1041      *out_bpp = 1;
   1042      return true;
   1043 
   1044    case gfx::SurfaceFormat::YUV420:
   1045      // Ugh...
   1046      NS_ERROR("We don't handle uploads from YUV sources yet.");
   1047      // When we want to, check out gfx/ycbcr/YCbCrUtils.h. (specifically
   1048      // GetYCbCrToRGBDestFormatAndSize and ConvertYCbCrToRGB)
   1049      return false;
   1050 
   1051    default:
   1052      return false;
   1053  }
   1054 }
   1055 
   1056 bool TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec,
   1057                                     WebGLTexture* tex, GLint level,
   1058                                     const webgl::DriverUnpackInfo* dui,
   1059                                     GLint xOffset, GLint yOffset,
   1060                                     GLint zOffset,
   1061                                     const webgl::PackingInfo& dstPI,
   1062                                     GLenum* const out_error) const {
   1063  const auto& webgl = tex->mContext;
   1064  const auto& size = mDesc.size;
   1065  RefPtr<gfx::DataSourceSurface> surf;
   1066  if (mDesc.sd) {
   1067    // First check if the SD describes an RGBA Shmem.
   1068    const auto& sd = *(mDesc.sd);
   1069    if (SDIsCanvasSurface(sd)) {
   1070      // The canvas surface resides on a 2D canvas within the same content
   1071      // process as the WebGL canvas. Query it for the surface.
   1072      const auto& sdc = sd.get_SurfaceDescriptorCanvasSurface();
   1073      uint32_t managerId = sdc.managerId();
   1074      mozilla::ipc::ActorId canvasId = sdc.canvasId();
   1075      uintptr_t surfaceId = sdc.surfaceId();
   1076      Maybe<layers::SurfaceDescriptor> exportSd;
   1077      // If the texture has multiple faces or layers that are not allocated,
   1078      // then the framebuffer attachment required to blit the descriptor may
   1079      // be determined incomplete. Avoid this for now by restricting to 2D
   1080      // targets.
   1081      bool allowBlit = AllowBlitSd(
   1082          webgl, mDesc.imageTarget, level, {xOffset, yOffset, zOffset},
   1083          dui->internalFormat, dstPI, true, false, false, false);
   1084      if (RefPtr<gfx::SourceSurface> data =
   1085              gfx::CanvasManagerParent::GetCanvasSurface(
   1086                  webgl->GetContentId(), managerId, canvasId, surfaceId,
   1087                  allowBlit ? &exportSd : nullptr)) {
   1088        if (exportSd && !SDIsRGBBuffer(*exportSd) &&
   1089            BlitSd(*exportSd, isSubImage, needsRespec, tex, level, dui, xOffset,
   1090                   yOffset, zOffset, dstPI, out_error, true)) {
   1091          return true;
   1092        }
   1093        surf = data->GetDataSurface();
   1094      }
   1095      if (!surf) {
   1096        gfxCriticalError() << "TexUnpackSurface failed to get CanvasSurface";
   1097        return false;
   1098      }
   1099    } else if (SDIsRGBBuffer(sd)) {
   1100      const auto& sdb = sd.get_SurfaceDescriptorBuffer();
   1101      const auto& rgb = sdb.desc().get_RGBDescriptor();
   1102      const auto& data = sdb.data();
   1103      MOZ_ASSERT(data.type() == layers::MemoryOrShmem::TShmem);
   1104      const auto& shmem = data.get_Shmem();
   1105      size_t shmemSize = shmem.Size<uint8_t>();
   1106      int32_t stride = layers::ImageDataSerializer::GetRGBStride(rgb);
   1107      if (stride <= 0) {
   1108        gfxCriticalError() << "TexUnpackSurface failed to get rgb stride";
   1109        return false;
   1110      }
   1111      size_t bufSize = layers::ImageDataSerializer::ComputeRGBBufferSize(
   1112          rgb.size(), rgb.format());
   1113      if (!bufSize || bufSize > shmemSize) {
   1114        gfxCriticalError() << "TexUnpackSurface failed to get rgb buffer size";
   1115        return false;
   1116      }
   1117      surf = gfx::Factory::CreateWrappingDataSourceSurface(
   1118          shmem.get<uint8_t>(), stride, rgb.size(), rgb.format());
   1119    } else if (SDIsNullRemoteDecoder(sd)) {
   1120      const auto& sdrd = sd.get_SurfaceDescriptorGPUVideo()
   1121                             .get_SurfaceDescriptorRemoteDecoder();
   1122      RefPtr<layers::VideoBridgeParent> parent =
   1123          layers::VideoBridgeParent::GetSingleton(sdrd.source());
   1124      if (!parent) {
   1125        gfxCriticalNote << "TexUnpackSurface failed to get VideoBridgeParent";
   1126        return false;
   1127      }
   1128      RefPtr<layers::TextureHost> texture =
   1129          parent->LookupTexture(webgl->GetContentId(), sdrd.handle());
   1130      if (!texture) {
   1131        gfxCriticalNote << "TexUnpackSurface failed to get TextureHost";
   1132        return false;
   1133      }
   1134      surf = texture->GetAsSurface();
   1135    } else if (SDIsExternalImage(sd)) {
   1136      const auto& sdei = sd.get_SurfaceDescriptorExternalImage();
   1137      if (auto* sharedSurfacesHolder = webgl->GetSharedSurfacesHolder()) {
   1138        surf = sharedSurfacesHolder->Get(sdei.id());
   1139      }
   1140      if (!surf) {
   1141        // Most likely the content process crashed before it was able to finish
   1142        // sharing the surface with the compositor process.
   1143        gfxCriticalNote << "TexUnpackSurface failed to get ExternalImage";
   1144        return false;
   1145      }
   1146    } else if (AllowBlitSd(webgl, mDesc.imageTarget, level,
   1147                           {xOffset, yOffset, zOffset}, dui->internalFormat,
   1148                           dstPI, false, true, true, true) &&
   1149               BlitSd(sd, isSubImage, needsRespec, tex, level, dui, xOffset,
   1150                      yOffset, zOffset, dstPI, out_error)) {
   1151      // The SD wasn't an RGBA shmem, but were able to blit the SD directly to
   1152      // the texture.
   1153      return true;
   1154    } else if (mDesc.sourceSurf) {
   1155      // In case neither a wrapper was created nor a blit succeeded, check for a
   1156      // backup source surface.
   1157      surf = mDesc.sourceSurf->GetDataSurface();
   1158    }
   1159    if (!surf) {
   1160      gfxCriticalError() << "TexUnpackSurface failed to create wrapping "
   1161                            "DataSourceSurface for Shmem.";
   1162      return false;
   1163    }
   1164  } else if (mDesc.sourceSurf) {
   1165    surf = mDesc.sourceSurf->GetDataSurface();
   1166    if (!surf) {
   1167      gfxCriticalError() << "TexUnpackSurface failed to get data for "
   1168                            "sourceSurf.";
   1169      return false;
   1170    }
   1171  }
   1172 
   1173  ////
   1174 
   1175  WebGLTexelFormat srcFormat;
   1176  uint8_t srcBPP;
   1177  if (!GetFormatForSurf(surf, &srcFormat, &srcBPP)) {
   1178    webgl->ErrorImplementationBug(
   1179        "GetFormatForSurf failed for"
   1180        " WebGLTexelFormat::%u.",
   1181        uint32_t(surf->GetFormat()));
   1182    return false;
   1183  }
   1184 
   1185  gfx::DataSourceSurface::ScopedMap map(surf,
   1186                                        gfx::DataSourceSurface::MapType::READ);
   1187  if (!map.IsMapped()) {
   1188    webgl->ErrorOutOfMemory("Failed to map source surface for upload.");
   1189    return false;
   1190  }
   1191 
   1192  const auto& srcBegin = map.GetData();
   1193  const auto srcStride = static_cast<size_t>(map.GetStride());
   1194 
   1195  // -
   1196 
   1197  const auto dstFormat = FormatForPackingInfo(dstPI);
   1198  const size_t dstBpp = BytesPerPixel(dstPI);
   1199  const size_t dstUsedBytesPerRow = dstBpp * surf->GetSize().width;
   1200  size_t dstStride = dstFormat == srcFormat ? srcStride  // Try To match
   1201                                            : dstUsedBytesPerRow;
   1202 
   1203  // -
   1204 
   1205  auto dstUnpackingRes = mDesc.ExplicitUnpacking(dstPI, Some(dstStride));
   1206  if (dstUnpackingRes.isOk()) {
   1207    const auto& dstUnpacking = dstUnpackingRes.inspect();
   1208    if (!webgl->IsWebGL2() && dstUnpacking.state.rowLength != size.x) {
   1209      dstUnpackingRes = Err("WebGL1 can't handle rowLength != size.x");
   1210    }
   1211  }
   1212  if (!dstUnpackingRes.isOk()) {
   1213    dstStride = dstUsedBytesPerRow;
   1214    dstUnpackingRes = mDesc.ExplicitUnpacking(dstPI, Some(dstStride));
   1215  }
   1216  if (!dstUnpackingRes.isOk()) {
   1217    gfxCriticalError() << dstUnpackingRes.inspectErr();
   1218    webgl->ErrorImplementationBug("ExplicitUnpacking failed: %s",
   1219                                  dstUnpackingRes.inspectErr().c_str());
   1220    return false;
   1221  }
   1222  const auto& dstUnpacking = dstUnpackingRes.inspect();
   1223  MOZ_ASSERT(dstUnpacking.metrics.bytesPerRowStride == dstStride);
   1224 
   1225  // -
   1226 
   1227  const uint8_t* dstBegin = srcBegin;
   1228  UniqueBuffer tempBuffer;
   1229  // clang-format off
   1230  if (!ConvertIfNeeded(webgl, surf->GetSize().width, surf->GetSize().height,
   1231                       srcFormat, srcBegin, AutoAssertCast(srcStride),
   1232                       dstFormat, AutoAssertCast(dstUnpacking.metrics.bytesPerRowStride), &dstBegin,
   1233                       &tempBuffer)) {
   1234    return false;
   1235  }
   1236  // clang-format on
   1237 
   1238  ////
   1239 
   1240  const auto& gl = webgl->gl;
   1241  if (!gl->MakeCurrent()) {
   1242    *out_error = LOCAL_GL_CONTEXT_LOST;
   1243    return true;
   1244  }
   1245 
   1246  dstUnpacking.state.ApplyUnpack(*gl, webgl->IsWebGL2(), size);
   1247 
   1248  *out_error =
   1249      DoTexOrSubImage(isSubImage, gl, mDesc.imageTarget, level, dui, xOffset,
   1250                      yOffset, zOffset, size.x, size.y, size.z, dstBegin);
   1251 
   1252  // Caller will reset all our modified PixelStorei state.
   1253 
   1254  return true;
   1255 }
   1256 
   1257 }  // namespace webgl
   1258 }  // namespace mozilla