LCOV - code coverage report
Current view: top level - dom/canvas - WebGLTextureUpload.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 933 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 47 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       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 "WebGLTexture.h"
       7             : 
       8             : #include <algorithm>
       9             : 
      10             : #include "CanvasUtils.h"
      11             : #include "gfxPrefs.h"
      12             : #include "GLBlitHelper.h"
      13             : #include "GLContext.h"
      14             : #include "mozilla/gfx/2D.h"
      15             : #include "mozilla/dom/HTMLVideoElement.h"
      16             : #include "mozilla/dom/ImageBitmap.h"
      17             : #include "mozilla/dom/ImageData.h"
      18             : #include "mozilla/MathAlgorithms.h"
      19             : #include "mozilla/Scoped.h"
      20             : #include "mozilla/SizePrintfMacros.h"
      21             : #include "mozilla/Unused.h"
      22             : #include "ScopedGLHelpers.h"
      23             : #include "TexUnpackBlob.h"
      24             : #include "WebGLBuffer.h"
      25             : #include "WebGLContext.h"
      26             : #include "WebGLContextUtils.h"
      27             : #include "WebGLFramebuffer.h"
      28             : #include "WebGLTexelConversions.h"
      29             : 
      30             : namespace mozilla {
      31             : 
      32             : /* This file handles:
      33             :  * TexStorage2D(texTarget, levels, internalFormat, width, height)
      34             :  * TexStorage3D(texTarget, levels, intenralFormat, width, height, depth)
      35             :  *
      36             :  * TexImage2D(texImageTarget, level, internalFormat, width, height, border, unpackFormat,
      37             :  *            unpackType, data)
      38             :  * TexImage3D(texImageTarget, level, internalFormat, width, height, depth, border,
      39             :  *            unpackFormat, unpackType, data)
      40             :  * TexSubImage2D(texImageTarget, level, xOffset, yOffset, width, height, unpackFormat,
      41             :  *               unpackType, data)
      42             :  * TexSubImage3D(texImageTarget, level, xOffset, yOffset, zOffset, width, height, depth,
      43             :  *               unpackFormat, unpackType, data)
      44             :  *
      45             :  * CompressedTexImage2D(texImageTarget, level, internalFormat, width, height, border,
      46             :  *                      imageSize, data)
      47             :  * CompressedTexImage3D(texImageTarget, level, internalFormat, width, height, depth,
      48             :  *                      border, imageSize, data)
      49             :  * CompressedTexSubImage2D(texImageTarget, level, xOffset, yOffset, width, height,
      50             :  *                         sizedUnpackFormat, imageSize, data)
      51             :  * CompressedTexSubImage3D(texImageTarget, level, xOffset, yOffset, zOffset, width,
      52             :  *                         height, depth, sizedUnpackFormat, imageSize, data)
      53             :  *
      54             :  * CopyTexImage2D(texImageTarget, level, internalFormat, x, y, width, height, border)
      55             :  * CopyTexImage3D - "Because the framebuffer is inhererntly two-dimensional, there is no
      56             :  *                   CopyTexImage3D command."
      57             :  * CopyTexSubImage2D(texImageTarget, level, xOffset, yOffset, x, y, width, height)
      58             :  * CopyTexSubImage3D(texImageTarget, level, xOffset, yOffset, zOffset, x, y, width,
      59             :  *                   height)
      60             :  */
      61             : 
      62             : static bool
      63           0 : ValidateExtents(WebGLContext* webgl, const char* funcName, GLsizei width, GLsizei height,
      64             :                 GLsizei depth, GLint border, uint32_t* const out_width,
      65             :                 uint32_t* const out_height, uint32_t* const out_depth)
      66             : {
      67             :     // Check border
      68           0 :     if (border != 0) {
      69           0 :         webgl->ErrorInvalidValue("%s: `border` must be 0.", funcName);
      70           0 :         return false;
      71             :     }
      72             : 
      73           0 :     if (width < 0 || height < 0 || depth < 0) {
      74             :         /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification
      75             :          *   "If wt and ht are the specified image width and height,
      76             :          *   and if either wt or ht are less than zero, then the error
      77             :          *   INVALID_VALUE is generated."
      78             :          */
      79           0 :         webgl->ErrorInvalidValue("%s: `width`/`height`/`depth` must be >= 0.", funcName);
      80           0 :         return false;
      81             :     }
      82             : 
      83           0 :     *out_width = width;
      84           0 :     *out_height = height;
      85           0 :     *out_depth = depth;
      86           0 :     return true;
      87             : }
      88             : 
      89             : ////////////////////////////////////////
      90             : // ArrayBufferView?
      91             : 
      92             : static inline bool
      93           0 : DoesJSTypeMatchUnpackType(GLenum unpackType, js::Scalar::Type jsType)
      94             : {
      95           0 :     switch (unpackType) {
      96             :     case LOCAL_GL_BYTE:
      97           0 :         return jsType == js::Scalar::Type::Int8;
      98             : 
      99             :     case LOCAL_GL_UNSIGNED_BYTE:
     100           0 :         return jsType == js::Scalar::Type::Uint8 ||
     101           0 :                jsType == js::Scalar::Type::Uint8Clamped;
     102             : 
     103             :     case LOCAL_GL_SHORT:
     104           0 :         return jsType == js::Scalar::Type::Int16;
     105             : 
     106             :     case LOCAL_GL_UNSIGNED_SHORT:
     107             :     case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
     108             :     case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
     109             :     case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
     110             :     case LOCAL_GL_HALF_FLOAT:
     111             :     case LOCAL_GL_HALF_FLOAT_OES:
     112           0 :         return jsType == js::Scalar::Type::Uint16;
     113             : 
     114             :     case LOCAL_GL_INT:
     115           0 :         return jsType == js::Scalar::Type::Int32;
     116             : 
     117             :     case LOCAL_GL_UNSIGNED_INT:
     118             :     case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV:
     119             :     case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV:
     120             :     case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV:
     121             :     case LOCAL_GL_UNSIGNED_INT_24_8:
     122           0 :         return jsType == js::Scalar::Type::Uint32;
     123             : 
     124             :     case LOCAL_GL_FLOAT:
     125           0 :         return jsType == js::Scalar::Type::Float32;
     126             : 
     127             :     default:
     128           0 :         return false;
     129             :     }
     130             : }
     131             : 
     132             : static bool
     133           0 : ValidateViewType(WebGLContext* webgl, const char* funcName, GLenum unpackType,
     134             :                  const TexImageSource& src)
     135             : {
     136           0 :     if (!src.mView)
     137           0 :         return true;
     138           0 :     const auto& view = *(src.mView);
     139             : 
     140           0 :     const auto& jsType = view.Type();
     141           0 :     if (!DoesJSTypeMatchUnpackType(unpackType, jsType)) {
     142             :         webgl->ErrorInvalidOperation("%s: ArrayBufferView type not compatible with"
     143             :                                      " `type`.",
     144           0 :                                      funcName);
     145           0 :         return false;
     146             :     }
     147             : 
     148           0 :     return true;
     149             : }
     150             : 
     151             : static bool
     152           0 : ValidateUnpackInfo(WebGLContext* webgl, const char* funcName,
     153             :                    const webgl::PackingInfo& pi)
     154             : {
     155           0 :     if (!webgl->mFormatUsage->AreUnpackEnumsValid(pi.format, pi.type)) {
     156             :         webgl->ErrorInvalidEnum("%s: Invalid unpack format/type: 0x%04x/0x%04x", funcName,
     157           0 :                                 pi.format, pi.type);
     158           0 :         return false;
     159             :     }
     160             : 
     161           0 :     return true;
     162             : }
     163             : 
     164             : ////////////////////////////////////////////////////////////////////////////////
     165             : 
     166             : static UniquePtr<webgl::TexUnpackBytes>
     167           0 : FromView(WebGLContext* webgl, const char* funcName, TexImageTarget target,
     168             :          uint32_t width, uint32_t height, uint32_t depth,
     169             :          const dom::ArrayBufferView* view, GLuint viewElemOffset,
     170             :          GLuint viewElemLengthOverride)
     171             : {
     172           0 :     const bool isClientData = true;
     173           0 :     const uint8_t* bytes = nullptr;
     174           0 :     size_t availByteCount = 0;
     175           0 :     if (view) {
     176           0 :         if (!webgl->ValidateArrayBufferView(funcName, *view, viewElemOffset,
     177             :                                             viewElemLengthOverride,
     178             :                                             const_cast<uint8_t**>(&bytes),
     179             :                                             &availByteCount))
     180             :         {
     181           0 :             return nullptr;
     182             :         }
     183             :     }
     184             :     return MakeUnique<webgl::TexUnpackBytes>(webgl, target, width, height, depth,
     185           0 :                                              isClientData, bytes, availByteCount);
     186             : }
     187             : 
     188             : static UniquePtr<webgl::TexUnpackBytes>
     189           0 : FromPboOffset(WebGLContext* webgl, const char* funcName, TexImageTarget target,
     190             :               uint32_t width, uint32_t height, uint32_t depth, WebGLsizeiptr pboOffset)
     191             : {
     192           0 :     if (pboOffset < 0) {
     193           0 :         webgl->ErrorInvalidValue("%s: offset cannot be negative.", funcName);
     194           0 :         return nullptr;
     195             :     }
     196             : 
     197           0 :     const auto& buffer = webgl->ValidateBufferSelection(funcName,
     198           0 :                                                         LOCAL_GL_PIXEL_UNPACK_BUFFER);
     199           0 :     if (!buffer)
     200           0 :         return nullptr;
     201             : 
     202           0 :     size_t availBufferBytes = buffer->ByteLength();
     203           0 :     if (size_t(pboOffset) > availBufferBytes) {
     204           0 :         webgl->ErrorInvalidOperation("%s: Offset is passed end of buffer.", funcName);
     205           0 :         return nullptr;
     206             :     }
     207           0 :     availBufferBytes -= pboOffset;
     208             : 
     209           0 :     const bool isClientData = false;
     210           0 :     const auto ptr = (const uint8_t*)pboOffset;
     211             :     return MakeUnique<webgl::TexUnpackBytes>(webgl, target, width, height, depth,
     212           0 :                                              isClientData, ptr, availBufferBytes);
     213             : }
     214             : 
     215             : static UniquePtr<webgl::TexUnpackBlob>
     216           0 : FromImageBitmap(WebGLContext* webgl, const char* funcName, TexImageTarget target,
     217             :               uint32_t width, uint32_t height, uint32_t depth,
     218             :               const dom::ImageBitmap& imageBitmap)
     219             : {
     220           0 :     UniquePtr<dom::ImageBitmapCloneData> cloneData = Move(imageBitmap.ToCloneData());
     221           0 :     const RefPtr<gfx::DataSourceSurface> surf = cloneData->mSurface;
     222             : 
     223           0 :     if (!width) {
     224           0 :         width = surf->GetSize().width;
     225             :     }
     226             : 
     227           0 :     if (!height) {
     228           0 :         height = surf->GetSize().height;
     229             :     }
     230             : 
     231             :     // WhatWG "HTML Living Standard" (30 October 2015):
     232             :     // "The getImageData(sx, sy, sw, sh) method [...] Pixels must be returned as
     233             :     //  non-premultiplied alpha values."
     234           0 :     return MakeUnique<webgl::TexUnpackSurface>(webgl, target, width, height, depth, surf,
     235           0 :                                                cloneData->mAlphaType);
     236             : }
     237             : 
     238             : static UniquePtr<webgl::TexUnpackBlob>
     239           0 : FromImageData(WebGLContext* webgl, const char* funcName, TexImageTarget target,
     240             :               uint32_t width, uint32_t height, uint32_t depth,
     241             :               const dom::ImageData& imageData, dom::Uint8ClampedArray* scopedArr)
     242             : {
     243           0 :     DebugOnly<bool> inited = scopedArr->Init(imageData.GetDataObject());
     244           0 :     MOZ_ASSERT(inited);
     245             : 
     246           0 :     scopedArr->ComputeLengthAndData();
     247           0 :     const DebugOnly<size_t> dataSize = scopedArr->Length();
     248           0 :     const void* const data = scopedArr->Data();
     249             : 
     250           0 :     const gfx::IntSize size(imageData.Width(), imageData.Height());
     251           0 :     const size_t stride = size.width * 4;
     252           0 :     const gfx::SurfaceFormat surfFormat = gfx::SurfaceFormat::R8G8B8A8;
     253             : 
     254             :     // WhatWG "HTML Living Standard" (30 October 2015):
     255             :     // "The getImageData(sx, sy, sw, sh) method [...] Pixels must be returned as
     256             :     //  non-premultiplied alpha values."
     257           0 :     const auto alphaType = gfxAlphaType::NonPremult;
     258             : 
     259           0 :     MOZ_ASSERT(dataSize == stride * size.height);
     260             : 
     261           0 :     uint8_t* wrappableData = (uint8_t*)data;
     262             : 
     263             :     const RefPtr<gfx::DataSourceSurface> surf =
     264           0 :         gfx::Factory::CreateWrappingDataSourceSurface(wrappableData, stride, size,
     265           0 :                                                       surfFormat);
     266           0 :     if (!surf) {
     267           0 :         webgl->ErrorOutOfMemory("%s: OOM in FromImageData.", funcName);
     268           0 :         return nullptr;
     269             :     }
     270             : 
     271             :     ////
     272             : 
     273           0 :     if (!width) {
     274           0 :         width = imageData.Width();
     275             :     }
     276             : 
     277           0 :     if (!height) {
     278           0 :         height = imageData.Height();
     279             :     }
     280             : 
     281             :     ////
     282             : 
     283           0 :     return MakeUnique<webgl::TexUnpackSurface>(webgl, target, width, height, depth, surf,
     284           0 :                                                alphaType);
     285             : }
     286             : 
     287             : UniquePtr<webgl::TexUnpackBlob>
     288           0 : WebGLContext::FromDomElem(const char* funcName, TexImageTarget target, uint32_t width,
     289             :                           uint32_t height, uint32_t depth, const dom::Element& elem,
     290             :                           ErrorResult* const out_error)
     291             : {
     292             :     // The canvas spec says that drawImage should draw the first frame of
     293             :     // animated images. The webgl spec doesn't mention the issue, so we do the
     294             :     // same as drawImage.
     295             :     uint32_t flags = nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE |
     296             :                      nsLayoutUtils::SFE_WANT_IMAGE_SURFACE |
     297           0 :                      nsLayoutUtils::SFE_USE_ELEMENT_SIZE_IF_VECTOR;
     298             : 
     299           0 :     if (mPixelStore_ColorspaceConversion == LOCAL_GL_NONE)
     300           0 :         flags |= nsLayoutUtils::SFE_NO_COLORSPACE_CONVERSION;
     301             : 
     302           0 :     if (!mPixelStore_PremultiplyAlpha)
     303           0 :         flags |= nsLayoutUtils::SFE_PREFER_NO_PREMULTIPLY_ALPHA;
     304             : 
     305           0 :     RefPtr<gfx::DrawTarget> idealDrawTarget = nullptr; // Don't care for now.
     306             :     auto sfer = nsLayoutUtils::SurfaceFromElement(const_cast<dom::Element*>(&elem), flags,
     307           0 :                                                   idealDrawTarget);
     308             : 
     309             :     //////
     310             : 
     311           0 :     uint32_t elemWidth = 0;
     312           0 :     uint32_t elemHeight = 0;
     313           0 :     layers::Image* layersImage = nullptr;
     314           0 :     if (!gfxPrefs::WebGLDisableDOMBlitUploads() && sfer.mLayersImage) {
     315           0 :         layersImage = sfer.mLayersImage;
     316           0 :         elemWidth = layersImage->GetSize().width;
     317           0 :         elemHeight = layersImage->GetSize().height;
     318             :     }
     319             : 
     320           0 :     RefPtr<gfx::DataSourceSurface> dataSurf;
     321           0 :     if (!layersImage && sfer.GetSourceSurface()) {
     322           0 :         const auto surf = sfer.GetSourceSurface();
     323           0 :         elemWidth = surf->GetSize().width;
     324           0 :         elemHeight = surf->GetSize().height;
     325             : 
     326             :         // WARNING: OSX can lose our MakeCurrent here.
     327           0 :         dataSurf = surf->GetDataSurface();
     328             :     }
     329             : 
     330             :     //////
     331             : 
     332           0 :     if (!width) {
     333           0 :         width = elemWidth;
     334             :     }
     335             : 
     336           0 :     if (!height) {
     337           0 :         height = elemHeight;
     338             :     }
     339             : 
     340             :     ////
     341             : 
     342           0 :     if (!layersImage && !dataSurf) {
     343           0 :         const bool isClientData = true;
     344           0 :         return MakeUnique<webgl::TexUnpackBytes>(this, target, width, height, depth,
     345           0 :                                                  isClientData, nullptr, 0);
     346             :     }
     347             : 
     348             :     //////
     349             : 
     350             :     // While it's counter-intuitive, the shape of the SFEResult API means that we should
     351             :     // try to pull out a surface first, and then, if we do pull out a surface, check
     352             :     // CORS/write-only/etc..
     353             : 
     354           0 :     if (!sfer.mCORSUsed) {
     355           0 :         auto& srcPrincipal = sfer.mPrincipal;
     356           0 :         nsIPrincipal* dstPrincipal = GetCanvas()->NodePrincipal();
     357             : 
     358           0 :         if (!dstPrincipal->Subsumes(srcPrincipal)) {
     359           0 :             GenerateWarning("%s: Cross-origin elements require CORS.", funcName);
     360           0 :             out_error->Throw(NS_ERROR_DOM_SECURITY_ERR);
     361           0 :             return nullptr;
     362             :         }
     363             :     }
     364             : 
     365           0 :     if (sfer.mIsWriteOnly) {
     366             :         // mIsWriteOnly defaults to true, and so will be true even if SFE merely
     367             :         // failed. Thus we must test mIsWriteOnly after successfully retrieving an
     368             :         // Image or SourceSurface.
     369           0 :         GenerateWarning("%s: Element is write-only, thus cannot be uploaded.", funcName);
     370           0 :         out_error->Throw(NS_ERROR_DOM_SECURITY_ERR);
     371           0 :         return nullptr;
     372             :     }
     373             : 
     374             :     //////
     375             :     // Ok, we're good!
     376             : 
     377           0 :     if (layersImage) {
     378           0 :         return MakeUnique<webgl::TexUnpackImage>(this, target, width, height, depth,
     379           0 :                                                  layersImage, sfer.mAlphaType);
     380             :     }
     381             : 
     382           0 :     MOZ_ASSERT(dataSurf);
     383           0 :     return MakeUnique<webgl::TexUnpackSurface>(this, target, width, height, depth,
     384           0 :                                                dataSurf, sfer.mAlphaType);
     385             : }
     386             : 
     387             : ////////////////////////////////////////
     388             : 
     389             : UniquePtr<webgl::TexUnpackBlob>
     390           0 : WebGLContext::From(const char* funcName, TexImageTarget target, GLsizei rawWidth,
     391             :                    GLsizei rawHeight, GLsizei rawDepth, GLint border,
     392             :                    const TexImageSource& src, dom::Uint8ClampedArray* const scopedArr)
     393             : {
     394             :     uint32_t width, height, depth;
     395           0 :     if (!ValidateExtents(this, funcName, rawWidth, rawHeight, rawDepth, border, &width,
     396             :                          &height, &depth))
     397             :     {
     398           0 :         return nullptr;
     399             :     }
     400             : 
     401           0 :     if (src.mPboOffset) {
     402           0 :         return FromPboOffset(this, funcName, target, width, height, depth,
     403           0 :                              *(src.mPboOffset));
     404             :     }
     405             : 
     406           0 :     if (mBoundPixelUnpackBuffer) {
     407           0 :         ErrorInvalidOperation("%s: PIXEL_UNPACK_BUFFER must be null.", funcName);
     408           0 :         return nullptr;
     409             :     }
     410             : 
     411           0 :     if (src.mImageBitmap) {
     412             :         return FromImageBitmap(this, funcName, target, width, height, depth,
     413           0 :                                *(src.mImageBitmap));
     414             :     }
     415             : 
     416           0 :     if (src.mImageData) {
     417             :         return FromImageData(this, funcName, target, width, height, depth,
     418           0 :                              *(src.mImageData), scopedArr);
     419             :     }
     420             : 
     421           0 :     if (src.mDomElem) {
     422           0 :         return FromDomElem(funcName, target, width, height, depth, *(src.mDomElem),
     423           0 :                            src.mOut_error);
     424             :     }
     425             : 
     426           0 :     return FromView(this, funcName, target, width, height, depth, src.mView,
     427           0 :                     src.mViewElemOffset, src.mViewElemLengthOverride);
     428             : }
     429             : 
     430             : ////////////////////////////////////////////////////////////////////////////////
     431             : 
     432             : static UniquePtr<webgl::TexUnpackBlob>
     433           0 : ValidateTexOrSubImage(WebGLContext* webgl, const char* funcName, TexImageTarget target,
     434             :                       GLsizei rawWidth, GLsizei rawHeight, GLsizei rawDepth,
     435             :                       GLint border, const webgl::PackingInfo& pi,
     436             :                       const TexImageSource& src, dom::Uint8ClampedArray* const scopedArr)
     437             : {
     438           0 :     if (!ValidateUnpackInfo(webgl, funcName, pi))
     439           0 :         return nullptr;
     440             : 
     441           0 :     if (!ValidateViewType(webgl, funcName, pi.type, src))
     442           0 :         return nullptr;
     443             : 
     444             :     auto blob = webgl->From(funcName, target, rawWidth, rawHeight, rawDepth, border, src,
     445           0 :                             scopedArr);
     446           0 :     if (!blob || !blob->Validate(webgl, funcName, pi))
     447           0 :         return nullptr;
     448             : 
     449           0 :     return Move(blob);
     450             : }
     451             : 
     452             : void
     453           0 : WebGLTexture::TexImage(const char* funcName, TexImageTarget target, GLint level,
     454             :                        GLenum internalFormat, GLsizei width, GLsizei height,
     455             :                        GLsizei depth, GLint border, const webgl::PackingInfo& pi,
     456             :                        const TexImageSource& src)
     457             : {
     458           0 :     dom::RootedTypedArray<dom::Uint8ClampedArray> scopedArr(dom::RootingCx());
     459           0 :     const auto blob = ValidateTexOrSubImage(mContext, funcName, target, width, height,
     460           0 :                                             depth, border, pi, src, &scopedArr);
     461           0 :     if (!blob)
     462           0 :         return;
     463             : 
     464           0 :     TexImage(funcName, target, level, internalFormat, pi, blob.get());
     465             : }
     466             : 
     467             : void
     468           0 : WebGLTexture::TexSubImage(const char* funcName, TexImageTarget target, GLint level,
     469             :                           GLint xOffset, GLint yOffset, GLint zOffset, GLsizei width,
     470             :                           GLsizei height, GLsizei depth,
     471             :                           const webgl::PackingInfo& pi, const TexImageSource& src)
     472             : {
     473           0 :     const GLint border = 0;
     474           0 :     dom::RootedTypedArray<dom::Uint8ClampedArray> scopedArr(dom::RootingCx());
     475           0 :     const auto blob = ValidateTexOrSubImage(mContext, funcName, target, width, height,
     476           0 :                                             depth, border, pi, src, &scopedArr);
     477           0 :     if (!blob)
     478           0 :         return;
     479             : 
     480           0 :     if (!blob->HasData()) {
     481           0 :         mContext->ErrorInvalidValue("%s: Source must not be null.", funcName);
     482           0 :         return;
     483             :     }
     484             : 
     485           0 :     TexSubImage(funcName, target, level, xOffset, yOffset, zOffset, pi, blob.get());
     486             : }
     487             : 
     488             : //////////////////////////////////////////////////////////////////////////////////////////
     489             : //////////////////////////////////////////////////////////////////////////////////////////
     490             : 
     491             : static bool
     492           0 : ValidateTexImage(WebGLContext* webgl, WebGLTexture* texture, const char* funcName,
     493             :                  TexImageTarget target, GLint level,
     494             :                  WebGLTexture::ImageInfo** const out_imageInfo)
     495             : {
     496             :     // Check level
     497           0 :     if (level < 0) {
     498           0 :         webgl->ErrorInvalidValue("%s: `level` must be >= 0.", funcName);
     499           0 :         return false;
     500             :     }
     501             : 
     502           0 :     if (level >= WebGLTexture::kMaxLevelCount) {
     503           0 :         webgl->ErrorInvalidValue("%s: `level` is too large.", funcName);
     504           0 :         return false;
     505             :     }
     506             : 
     507           0 :     WebGLTexture::ImageInfo& imageInfo = texture->ImageInfoAt(target, level);
     508             : 
     509           0 :     *out_imageInfo = &imageInfo;
     510           0 :     return true;
     511             : }
     512             : 
     513             : // For *TexImage*
     514             : bool
     515           0 : WebGLTexture::ValidateTexImageSpecification(const char* funcName, TexImageTarget target,
     516             :                                             GLint rawLevel, uint32_t width,
     517             :                                             uint32_t height, uint32_t depth,
     518             :                                             WebGLTexture::ImageInfo** const out_imageInfo)
     519             : {
     520           0 :     if (mImmutable) {
     521           0 :         mContext->ErrorInvalidOperation("%s: Specified texture is immutable.", funcName);
     522           0 :         return false;
     523             :     }
     524             : 
     525             :     // Do this early to validate `level`.
     526             :     WebGLTexture::ImageInfo* imageInfo;
     527           0 :     if (!ValidateTexImage(mContext, this, funcName, target, rawLevel, &imageInfo))
     528           0 :         return false;
     529           0 :     const uint32_t level(rawLevel);
     530             : 
     531           0 :     if (mTarget == LOCAL_GL_TEXTURE_CUBE_MAP &&
     532             :         width != height)
     533             :     {
     534           0 :         mContext->ErrorInvalidValue("%s: Cube map images must be square.", funcName);
     535           0 :         return false;
     536             :     }
     537             : 
     538             :     /* GLES 3.0.4, p133-134:
     539             :      * GL_MAX_TEXTURE_SIZE is *not* the max allowed texture size. Rather, it is the
     540             :      * max (width/height) size guaranteed not to generate an INVALID_VALUE for too-large
     541             :      * dimensions. Sizes larger than GL_MAX_TEXTURE_SIZE *may or may not* result in an
     542             :      * INVALID_VALUE, or possibly GL_OOM.
     543             :      *
     544             :      * However, we have needed to set our maximums lower in the past to prevent resource
     545             :      * corruption. Therefore we have mImplMaxTextureSize, which is neither necessarily
     546             :      * lower nor higher than MAX_TEXTURE_SIZE.
     547             :      *
     548             :      * Note that mImplMaxTextureSize must be >= than the advertized MAX_TEXTURE_SIZE.
     549             :      * For simplicity, we advertize MAX_TEXTURE_SIZE as mImplMaxTextureSize.
     550             :      */
     551             : 
     552           0 :     uint32_t maxWidthHeight = 0;
     553           0 :     uint32_t maxDepth = 0;
     554           0 :     uint32_t maxLevel = 0;
     555             : 
     556           0 :     MOZ_ASSERT(level <= 31);
     557           0 :     switch (target.get()) {
     558             :     case LOCAL_GL_TEXTURE_2D:
     559           0 :         maxWidthHeight = mContext->mImplMaxTextureSize >> level;
     560           0 :         maxDepth = 1;
     561           0 :         maxLevel = CeilingLog2(mContext->mImplMaxTextureSize);
     562           0 :         break;
     563             : 
     564             :     case LOCAL_GL_TEXTURE_3D:
     565           0 :         maxWidthHeight = mContext->mImplMax3DTextureSize >> level;
     566           0 :         maxDepth = maxWidthHeight;
     567           0 :         maxLevel = CeilingLog2(mContext->mImplMax3DTextureSize);
     568           0 :         break;
     569             : 
     570             :     case LOCAL_GL_TEXTURE_2D_ARRAY:
     571           0 :         maxWidthHeight = mContext->mImplMaxTextureSize >> level;
     572             :         // "The maximum number of layers for two-dimensional array textures (depth)
     573             :         //  must be at least MAX_ARRAY_TEXTURE_LAYERS for all levels."
     574           0 :         maxDepth = mContext->mImplMaxArrayTextureLayers;
     575           0 :         maxLevel = CeilingLog2(mContext->mImplMaxTextureSize);
     576           0 :         break;
     577             : 
     578             :     default: // cube maps
     579           0 :         MOZ_ASSERT(IsCubeMap());
     580           0 :         maxWidthHeight = mContext->mImplMaxCubeMapTextureSize >> level;
     581           0 :         maxDepth = 1;
     582           0 :         maxLevel = CeilingLog2(mContext->mImplMaxCubeMapTextureSize);
     583           0 :         break;
     584             :     }
     585             : 
     586           0 :     if (level > maxLevel) {
     587           0 :         mContext->ErrorInvalidValue("%s: Requested level is not supported for target.",
     588           0 :                                     funcName);
     589           0 :         return false;
     590             :     }
     591             : 
     592           0 :     if (width > maxWidthHeight ||
     593           0 :         height > maxWidthHeight ||
     594             :         depth > maxDepth)
     595             :     {
     596           0 :         mContext->ErrorInvalidValue("%s: Requested size at this level is unsupported.",
     597           0 :                                     funcName);
     598           0 :         return false;
     599             :     }
     600             : 
     601             :     {
     602             :         /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification
     603             :          *   "If level is greater than zero, and either width or
     604             :          *   height is not a power-of-two, the error INVALID_VALUE is
     605             :          *   generated."
     606             :          *
     607             :          * This restriction does not apply to GL ES Version 3.0+.
     608             :          */
     609           0 :         bool requirePOT = (!mContext->IsWebGL2() && level != 0);
     610             : 
     611           0 :         if (requirePOT) {
     612           0 :             if (!IsPowerOfTwo(width) || !IsPowerOfTwo(height)) {
     613           0 :                 mContext->ErrorInvalidValue("%s: For level > 0, width and height must be"
     614             :                                             " powers of two.",
     615           0 :                                             funcName);
     616           0 :                 return false;
     617             :             }
     618             :         }
     619             :     }
     620             : 
     621           0 :     *out_imageInfo = imageInfo;
     622           0 :     return true;
     623             : }
     624             : 
     625             : // For *TexSubImage*
     626             : bool
     627           0 : WebGLTexture::ValidateTexImageSelection(const char* funcName, TexImageTarget target,
     628             :                                         GLint level, GLint xOffset, GLint yOffset,
     629             :                                         GLint zOffset, uint32_t width, uint32_t height,
     630             :                                         uint32_t depth,
     631             :                                         WebGLTexture::ImageInfo** const out_imageInfo)
     632             : {
     633             :     // The conformance test wants bad arg checks before imageInfo checks.
     634           0 :     if (xOffset < 0 || yOffset < 0 || zOffset < 0) {
     635           0 :         mContext->ErrorInvalidValue("%s: Offsets must be >=0.", funcName);
     636           0 :         return false;
     637             :     }
     638             : 
     639             :     WebGLTexture::ImageInfo* imageInfo;
     640           0 :     if (!ValidateTexImage(mContext, this, funcName, target, level, &imageInfo))
     641           0 :         return false;
     642             : 
     643           0 :     if (!imageInfo->IsDefined()) {
     644           0 :         mContext->ErrorInvalidOperation("%s: The specified TexImage has not yet been"
     645             :                                         " specified.",
     646           0 :                                         funcName);
     647           0 :         return false;
     648             :     }
     649             : 
     650           0 :     const auto totalX = CheckedUint32(xOffset) + width;
     651           0 :     const auto totalY = CheckedUint32(yOffset) + height;
     652           0 :     const auto totalZ = CheckedUint32(zOffset) + depth;
     653             : 
     654           0 :     if (!totalX.isValid() || totalX.value() > imageInfo->mWidth ||
     655           0 :         !totalY.isValid() || totalY.value() > imageInfo->mHeight ||
     656           0 :         !totalZ.isValid() || totalZ.value() > imageInfo->mDepth)
     657             :     {
     658           0 :         mContext->ErrorInvalidValue("%s: Offset+size must be <= the size of the existing"
     659             :                                     " specified image.",
     660           0 :                                     funcName);
     661           0 :         return false;
     662             :     }
     663             : 
     664           0 :     *out_imageInfo = imageInfo;
     665           0 :     return true;
     666             : }
     667             : 
     668             : static bool
     669           0 : ValidateCompressedTexUnpack(WebGLContext* webgl, const char* funcName, GLsizei width,
     670             :                             GLsizei height, GLsizei depth,
     671             :                             const webgl::FormatInfo* format, size_t dataSize)
     672             : {
     673           0 :     auto compression = format->compression;
     674             : 
     675           0 :     auto bytesPerBlock = compression->bytesPerBlock;
     676           0 :     auto blockWidth = compression->blockWidth;
     677           0 :     auto blockHeight = compression->blockHeight;
     678             : 
     679           0 :     auto widthInBlocks = CheckedUint32(width) / blockWidth;
     680           0 :     auto heightInBlocks = CheckedUint32(height) / blockHeight;
     681           0 :     if (width % blockWidth) widthInBlocks += 1;
     682           0 :     if (height % blockHeight) heightInBlocks += 1;
     683             : 
     684           0 :     const CheckedUint32 blocksPerImage = widthInBlocks * heightInBlocks;
     685           0 :     const CheckedUint32 bytesPerImage = bytesPerBlock * blocksPerImage;
     686           0 :     const CheckedUint32 bytesNeeded = bytesPerImage * depth;
     687             : 
     688           0 :     if (!bytesNeeded.isValid()) {
     689             :         webgl->ErrorOutOfMemory("%s: Overflow while computing the needed buffer size.",
     690           0 :                                 funcName);
     691           0 :         return false;
     692             :     }
     693             : 
     694           0 :     if (dataSize != bytesNeeded.value()) {
     695           0 :         webgl->ErrorInvalidValue("%s: Provided buffer's size must match expected size."
     696             :                                  " (needs %u, has %" PRIuSIZE ")",
     697           0 :                                  funcName, bytesNeeded.value(), dataSize);
     698           0 :         return false;
     699             :     }
     700             : 
     701           0 :     return true;
     702             : }
     703             : 
     704             : static bool
     705           0 : DoChannelsMatchForCopyTexImage(const webgl::FormatInfo* srcFormat,
     706             :                                const webgl::FormatInfo* dstFormat)
     707             : {
     708             :     // GLES 3.0.4 p140 Table 3.16 "Valid CopyTexImage source framebuffer/destination
     709             :     // texture base internal format combinations."
     710             : 
     711           0 :     switch (srcFormat->unsizedFormat) {
     712             :     case webgl::UnsizedFormat::RGBA:
     713           0 :         switch (dstFormat->unsizedFormat) {
     714             :         case webgl::UnsizedFormat::A:
     715             :         case webgl::UnsizedFormat::L:
     716             :         case webgl::UnsizedFormat::LA:
     717             :         case webgl::UnsizedFormat::R:
     718             :         case webgl::UnsizedFormat::RG:
     719             :         case webgl::UnsizedFormat::RGB:
     720             :         case webgl::UnsizedFormat::RGBA:
     721           0 :             return true;
     722             :         default:
     723           0 :             return false;
     724             :         }
     725             : 
     726             :     case webgl::UnsizedFormat::RGB:
     727           0 :         switch (dstFormat->unsizedFormat) {
     728             :         case webgl::UnsizedFormat::L:
     729             :         case webgl::UnsizedFormat::R:
     730             :         case webgl::UnsizedFormat::RG:
     731             :         case webgl::UnsizedFormat::RGB:
     732           0 :             return true;
     733             :         default:
     734           0 :             return false;
     735             :         }
     736             : 
     737             :     case webgl::UnsizedFormat::RG:
     738           0 :         switch (dstFormat->unsizedFormat) {
     739             :         case webgl::UnsizedFormat::L:
     740             :         case webgl::UnsizedFormat::R:
     741             :         case webgl::UnsizedFormat::RG:
     742           0 :             return true;
     743             :         default:
     744           0 :             return false;
     745             :         }
     746             : 
     747             :     case webgl::UnsizedFormat::R:
     748           0 :         switch (dstFormat->unsizedFormat) {
     749             :         case webgl::UnsizedFormat::L:
     750             :         case webgl::UnsizedFormat::R:
     751           0 :             return true;
     752             :         default:
     753           0 :             return false;
     754             :         }
     755             : 
     756             :     default:
     757           0 :         return false;
     758             :     }
     759             : }
     760             : 
     761             : static bool
     762           0 : EnsureImageDataInitializedForUpload(WebGLTexture* tex, const char* funcName,
     763             :                                     TexImageTarget target, GLint level, GLint xOffset,
     764             :                                     GLint yOffset, GLint zOffset, uint32_t width,
     765             :                                     uint32_t height, uint32_t depth,
     766             :                                     WebGLTexture::ImageInfo* imageInfo,
     767             :                                     bool* const out_uploadWillInitialize)
     768             : {
     769           0 :     *out_uploadWillInitialize = false;
     770             : 
     771           0 :     if (!imageInfo->IsDataInitialized()) {
     772           0 :         const bool isFullUpload = (!xOffset && !yOffset && !zOffset &&
     773           0 :                                    width == imageInfo->mWidth &&
     774           0 :                                    height == imageInfo->mHeight &&
     775           0 :                                    depth == imageInfo->mDepth);
     776           0 :         if (isFullUpload) {
     777           0 :             *out_uploadWillInitialize = true;
     778             :         } else {
     779           0 :             WebGLContext* webgl = tex->mContext;
     780             :             webgl->GenerateWarning("%s: Texture has not been initialized prior to a"
     781             :                                    " partial upload, forcing the browser to clear it."
     782             :                                    " This may be slow.",
     783           0 :                                    funcName);
     784           0 :             if (!tex->InitializeImageData(funcName, target, level)) {
     785           0 :                 MOZ_ASSERT(false, "Unexpected failure to init image data.");
     786             :                 return false;
     787             :             }
     788             :         }
     789             :     }
     790             : 
     791           0 :     return true;
     792             : }
     793             : 
     794             : //////////////////////////////////////////////////////////////////////////////////////////
     795             : //////////////////////////////////////////////////////////////////////////////////////////
     796             : // Actual calls
     797             : 
     798             : static inline GLenum
     799           0 : DoTexStorage(gl::GLContext* gl, TexTarget target, GLsizei levels, GLenum sizedFormat,
     800             :              GLsizei width, GLsizei height, GLsizei depth)
     801             : {
     802           0 :     gl::GLContext::LocalErrorScope errorScope(*gl);
     803             : 
     804           0 :     switch (target.get()) {
     805             :     case LOCAL_GL_TEXTURE_2D:
     806             :     case LOCAL_GL_TEXTURE_CUBE_MAP:
     807           0 :         MOZ_ASSERT(depth == 1);
     808           0 :         gl->fTexStorage2D(target.get(), levels, sizedFormat, width, height);
     809           0 :         break;
     810             : 
     811             :     case LOCAL_GL_TEXTURE_3D:
     812             :     case LOCAL_GL_TEXTURE_2D_ARRAY:
     813           0 :         gl->fTexStorage3D(target.get(), levels, sizedFormat, width, height, depth);
     814           0 :         break;
     815             : 
     816             :     default:
     817           0 :         MOZ_CRASH("GFX: bad target");
     818             :     }
     819             : 
     820           0 :     return errorScope.GetError();
     821             : }
     822             : 
     823             : bool
     824           0 : IsTarget3D(TexImageTarget target)
     825             : {
     826           0 :     switch (target.get()) {
     827             :     case LOCAL_GL_TEXTURE_2D:
     828             :     case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
     829             :     case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
     830             :     case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
     831             :     case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
     832             :     case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
     833             :     case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
     834           0 :         return false;
     835             : 
     836             :     case LOCAL_GL_TEXTURE_3D:
     837             :     case LOCAL_GL_TEXTURE_2D_ARRAY:
     838           0 :         return true;
     839             : 
     840             :     default:
     841           0 :         MOZ_CRASH("GFX: bad target");
     842             :     }
     843             : }
     844             : 
     845             : GLenum
     846           0 : DoTexImage(gl::GLContext* gl, TexImageTarget target, GLint level,
     847             :            const webgl::DriverUnpackInfo* dui, GLsizei width, GLsizei height,
     848             :            GLsizei depth, const void* data)
     849             : {
     850           0 :     const GLint border = 0;
     851             : 
     852           0 :     gl::GLContext::LocalErrorScope errorScope(*gl);
     853             : 
     854           0 :     if (IsTarget3D(target)) {
     855           0 :         gl->fTexImage3D(target.get(), level, dui->internalFormat, width, height, depth,
     856           0 :                         border, dui->unpackFormat, dui->unpackType, data);
     857             :     } else {
     858           0 :         MOZ_ASSERT(depth == 1);
     859           0 :         gl->fTexImage2D(target.get(), level, dui->internalFormat, width, height, border,
     860           0 :                         dui->unpackFormat, dui->unpackType, data);
     861             :     }
     862             : 
     863           0 :     return errorScope.GetError();
     864             : }
     865             : 
     866             : GLenum
     867           0 : DoTexSubImage(gl::GLContext* gl, TexImageTarget target, GLint level, GLint xOffset,
     868             :               GLint yOffset, GLint zOffset, GLsizei width, GLsizei height, GLsizei depth,
     869             :               const webgl::PackingInfo& pi, const void* data)
     870             : {
     871           0 :     gl::GLContext::LocalErrorScope errorScope(*gl);
     872             : 
     873           0 :     if (IsTarget3D(target)) {
     874           0 :         gl->fTexSubImage3D(target.get(), level, xOffset, yOffset, zOffset, width, height,
     875           0 :                            depth, pi.format, pi.type, data);
     876             :     } else {
     877           0 :         MOZ_ASSERT(zOffset == 0);
     878           0 :         MOZ_ASSERT(depth == 1);
     879           0 :         gl->fTexSubImage2D(target.get(), level, xOffset, yOffset, width, height,
     880           0 :                            pi.format, pi.type, data);
     881             :     }
     882             : 
     883           0 :     return errorScope.GetError();
     884             : }
     885             : 
     886             : static inline GLenum
     887           0 : DoCompressedTexImage(gl::GLContext* gl, TexImageTarget target, GLint level,
     888             :                      GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth,
     889             :                      GLsizei dataSize, const void* data)
     890             : {
     891           0 :     const GLint border = 0;
     892             : 
     893           0 :     gl::GLContext::LocalErrorScope errorScope(*gl);
     894             : 
     895           0 :     if (IsTarget3D(target)) {
     896           0 :         gl->fCompressedTexImage3D(target.get(), level, internalFormat, width, height,
     897           0 :                                   depth, border, dataSize, data);
     898             :     } else {
     899           0 :         MOZ_ASSERT(depth == 1);
     900           0 :         gl->fCompressedTexImage2D(target.get(), level, internalFormat, width, height,
     901           0 :                                   border, dataSize, data);
     902             :     }
     903             : 
     904           0 :     return errorScope.GetError();
     905             : }
     906             : 
     907             : GLenum
     908           0 : DoCompressedTexSubImage(gl::GLContext* gl, TexImageTarget target, GLint level,
     909             :                         GLint xOffset, GLint yOffset, GLint zOffset, GLsizei width,
     910             :                         GLsizei height, GLsizei depth, GLenum sizedUnpackFormat,
     911             :                         GLsizei dataSize, const void* data)
     912             : {
     913           0 :     gl::GLContext::LocalErrorScope errorScope(*gl);
     914             : 
     915           0 :     if (IsTarget3D(target)) {
     916           0 :         gl->fCompressedTexSubImage3D(target.get(), level, xOffset, yOffset, zOffset,
     917             :                                      width, height, depth, sizedUnpackFormat, dataSize,
     918           0 :                                      data);
     919             :     } else {
     920           0 :         MOZ_ASSERT(zOffset == 0);
     921           0 :         MOZ_ASSERT(depth == 1);
     922           0 :         gl->fCompressedTexSubImage2D(target.get(), level, xOffset, yOffset, width,
     923           0 :                                      height, sizedUnpackFormat, dataSize, data);
     924             :     }
     925             : 
     926           0 :     return errorScope.GetError();
     927             : }
     928             : 
     929             : static inline GLenum
     930             : DoCopyTexImage2D(gl::GLContext* gl, TexImageTarget target, GLint level,
     931             :                  GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height)
     932             : {
     933             :     const GLint border = 0;
     934             : 
     935             :     gl::GLContext::LocalErrorScope errorScope(*gl);
     936             : 
     937             :     MOZ_ASSERT(!IsTarget3D(target));
     938             :     gl->fCopyTexImage2D(target.get(), level, internalFormat, x, y, width, height,
     939             :                         border);
     940             : 
     941             :     return errorScope.GetError();
     942             : }
     943             : 
     944             : static inline GLenum
     945           0 : DoCopyTexSubImage(gl::GLContext* gl, TexImageTarget target, GLint level, GLint xOffset,
     946             :                   GLint yOffset, GLint zOffset, GLint x, GLint y, GLsizei width,
     947             :                   GLsizei height)
     948             : {
     949           0 :     gl::GLContext::LocalErrorScope errorScope(*gl);
     950             : 
     951           0 :     if (IsTarget3D(target)) {
     952           0 :         gl->fCopyTexSubImage3D(target.get(), level, xOffset, yOffset, zOffset, x, y,
     953           0 :                                width, height);
     954             :     } else {
     955           0 :         MOZ_ASSERT(zOffset == 0);
     956           0 :         gl->fCopyTexSubImage2D(target.get(), level, xOffset, yOffset, x, y, width,
     957           0 :                                height);
     958             :     }
     959             : 
     960           0 :     return errorScope.GetError();
     961             : }
     962             : 
     963             : //////////////////////////////////////////////////////////////////////////////////////////
     964             : //////////////////////////////////////////////////////////////////////////////////////////
     965             : // Actual (mostly generic) function implementations
     966             : 
     967             : static bool
     968           0 : ValidateCompressedTexImageRestrictions(const char* funcName, WebGLContext* webgl,
     969             :                                        TexImageTarget target, uint32_t level,
     970             :                                        const webgl::FormatInfo* format, uint32_t width,
     971             :                                        uint32_t height, uint32_t depth)
     972             : {
     973           0 :     const auto fnIsDimValid_S3TC = [level](uint32_t size, uint32_t blockSize) {
     974           0 :         if (size % blockSize == 0)
     975           0 :             return true;
     976             : 
     977           0 :         if (level == 0)
     978           0 :             return false;
     979             : 
     980           0 :         return (size == 0 || size == 1 || size == 2);
     981           0 :     };
     982             : 
     983           0 :     switch (format->compression->family) {
     984             :     case webgl::CompressionFamily::ASTC:
     985           0 :         if (target == LOCAL_GL_TEXTURE_3D &&
     986           0 :             !webgl->gl->IsExtensionSupported(gl::GLContext::KHR_texture_compression_astc_hdr))
     987             :         {
     988             :             webgl->ErrorInvalidOperation("%s: TEXTURE_3D requires ASTC's hdr profile.",
     989           0 :                                          funcName);
     990           0 :             return false;
     991             :         }
     992           0 :         break;
     993             : 
     994             :     case webgl::CompressionFamily::PVRTC:
     995           0 :         if (!IsPowerOfTwo(width) || !IsPowerOfTwo(height)) {
     996             :             webgl->ErrorInvalidValue("%s: %s requires power-of-two width and height.",
     997           0 :                                      funcName, format->name);
     998           0 :             return false;
     999             :         }
    1000             : 
    1001           0 :         break;
    1002             : 
    1003             :     case webgl::CompressionFamily::S3TC:
    1004           0 :         if (!fnIsDimValid_S3TC(width, format->compression->blockWidth) ||
    1005           0 :             !fnIsDimValid_S3TC(height, format->compression->blockHeight))
    1006             :         {
    1007             :             webgl->ErrorInvalidOperation("%s: %s requires that width and height are"
    1008             :                                          " block-aligned, or, if level>0, equal to 0, 1,"
    1009             :                                          " or 2.",
    1010           0 :                                          funcName, format->name);
    1011           0 :             return false;
    1012             :         }
    1013             : 
    1014           0 :         break;
    1015             : 
    1016             :     // Default: There are no restrictions on CompressedTexImage.
    1017             :     default: // ATC, ETC1, ES3
    1018           0 :         break;
    1019             :     }
    1020             : 
    1021           0 :     return true;
    1022             : }
    1023             : 
    1024             : static bool
    1025           0 : ValidateTargetForFormat(const char* funcName, WebGLContext* webgl, TexImageTarget target,
    1026             :                         const webgl::FormatInfo* format)
    1027             : {
    1028             :     // GLES 3.0.4 p127:
    1029             :     // "Textures with a base internal format of DEPTH_COMPONENT or DEPTH_STENCIL are
    1030             :     //  supported by texture image specification commands only if `target` is TEXTURE_2D,
    1031             :     //  TEXTURE_2D_ARRAY, or TEXTURE_CUBE_MAP. Using these formats in conjunction with any
    1032             :     //  other `target` will result in an INVALID_OPERATION error."
    1033             : 
    1034           0 :     switch (format->effectiveFormat) {
    1035             :     // TEXTURE_2D_ARRAY but not TEXTURE_3D:
    1036             :     // D and DS formats
    1037             :     case webgl::EffectiveFormat::DEPTH_COMPONENT16:
    1038             :     case webgl::EffectiveFormat::DEPTH_COMPONENT24:
    1039             :     case webgl::EffectiveFormat::DEPTH_COMPONENT32F:
    1040             :     case webgl::EffectiveFormat::DEPTH24_STENCIL8:
    1041             :     case webgl::EffectiveFormat::DEPTH32F_STENCIL8:
    1042             :     // CompressionFamily::ES3
    1043             :     case webgl::EffectiveFormat::COMPRESSED_R11_EAC:
    1044             :     case webgl::EffectiveFormat::COMPRESSED_SIGNED_R11_EAC:
    1045             :     case webgl::EffectiveFormat::COMPRESSED_RG11_EAC:
    1046             :     case webgl::EffectiveFormat::COMPRESSED_SIGNED_RG11_EAC:
    1047             :     case webgl::EffectiveFormat::COMPRESSED_RGB8_ETC2:
    1048             :     case webgl::EffectiveFormat::COMPRESSED_SRGB8_ETC2:
    1049             :     case webgl::EffectiveFormat::COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
    1050             :     case webgl::EffectiveFormat::COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
    1051             :     case webgl::EffectiveFormat::COMPRESSED_RGBA8_ETC2_EAC:
    1052             :     case webgl::EffectiveFormat::COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
    1053             :     // CompressionFamily::S3TC
    1054             :     case webgl::EffectiveFormat::COMPRESSED_RGB_S3TC_DXT1_EXT:
    1055             :     case webgl::EffectiveFormat::COMPRESSED_RGBA_S3TC_DXT1_EXT:
    1056             :     case webgl::EffectiveFormat::COMPRESSED_RGBA_S3TC_DXT3_EXT:
    1057             :     case webgl::EffectiveFormat::COMPRESSED_RGBA_S3TC_DXT5_EXT:
    1058           0 :         if (target == LOCAL_GL_TEXTURE_3D) {
    1059             :             webgl->ErrorInvalidOperation("%s: Format %s cannot be used with TEXTURE_3D.",
    1060           0 :                                          funcName, format->name);
    1061           0 :             return false;
    1062             :         }
    1063           0 :         break;
    1064             : 
    1065             :     // No 3D targets:
    1066             :     // CompressionFamily::ATC
    1067             :     case webgl::EffectiveFormat::ATC_RGB_AMD:
    1068             :     case webgl::EffectiveFormat::ATC_RGBA_EXPLICIT_ALPHA_AMD:
    1069             :     case webgl::EffectiveFormat::ATC_RGBA_INTERPOLATED_ALPHA_AMD:
    1070             :     // CompressionFamily::PVRTC
    1071             :     case webgl::EffectiveFormat::COMPRESSED_RGB_PVRTC_4BPPV1:
    1072             :     case webgl::EffectiveFormat::COMPRESSED_RGBA_PVRTC_4BPPV1:
    1073             :     case webgl::EffectiveFormat::COMPRESSED_RGB_PVRTC_2BPPV1:
    1074             :     case webgl::EffectiveFormat::COMPRESSED_RGBA_PVRTC_2BPPV1:
    1075             :     // CompressionFamily::ETC1
    1076             :     case webgl::EffectiveFormat::ETC1_RGB8_OES:
    1077           0 :         if (target == LOCAL_GL_TEXTURE_3D ||
    1078           0 :             target == LOCAL_GL_TEXTURE_2D_ARRAY)
    1079             :         {
    1080             :             webgl->ErrorInvalidOperation("%s: Format %s cannot be used with TEXTURE_3D or"
    1081             :                                          " TEXTURE_2D_ARRAY.",
    1082           0 :                                          funcName, format->name);
    1083           0 :             return false;
    1084             :         }
    1085           0 :         break;
    1086             : 
    1087             :     default:
    1088           0 :         break;
    1089             :     }
    1090             : 
    1091           0 :     return true;
    1092             : }
    1093             : 
    1094             : void
    1095           0 : WebGLTexture::TexStorage(const char* funcName, TexTarget target, GLsizei levels,
    1096             :                          GLenum sizedFormat, GLsizei width, GLsizei height, GLsizei depth)
    1097             : {
    1098             :     // Check levels
    1099           0 :     if (levels < 1) {
    1100           0 :         mContext->ErrorInvalidValue("%s: `levels` must be >= 1.", funcName);
    1101           0 :         return;
    1102             :     }
    1103             : 
    1104           0 :     if (!width || !height || !depth) {
    1105           0 :         mContext->ErrorInvalidValue("%s: Dimensions must be non-zero.", funcName);
    1106           0 :         return;
    1107             :     }
    1108             : 
    1109           0 :     const TexImageTarget testTarget = IsCubeMap() ? LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X
    1110           0 :                                                   : target.get();
    1111           0 :     const GLint testLevel = 0;
    1112             : 
    1113             :     WebGLTexture::ImageInfo* testImageInfo;
    1114           0 :     if (!ValidateTexImageSpecification(funcName, testTarget, testLevel, width, height,
    1115             :                                        depth, &testImageInfo))
    1116             :     {
    1117           0 :         return;
    1118             :     }
    1119           0 :     MOZ_ASSERT(testImageInfo);
    1120             :     mozilla::Unused << testImageInfo;
    1121             : 
    1122           0 :     auto dstUsage = mContext->mFormatUsage->GetSizedTexUsage(sizedFormat);
    1123           0 :     if (!dstUsage) {
    1124           0 :         mContext->ErrorInvalidEnum("%s: Invalid internalformat: 0x%04x", funcName,
    1125           0 :                                    sizedFormat);
    1126           0 :         return;
    1127             :     }
    1128           0 :     auto dstFormat = dstUsage->format;
    1129             : 
    1130           0 :     if (!ValidateTargetForFormat(funcName, mContext, testTarget, dstFormat))
    1131           0 :         return;
    1132             : 
    1133           0 :     if (dstFormat->compression) {
    1134           0 :         if (!ValidateCompressedTexImageRestrictions(funcName, mContext, testTarget,
    1135             :                                                     testLevel, dstFormat, width, height,
    1136             :                                                     depth))
    1137             :         {
    1138           0 :             return;
    1139             :         }
    1140             :     }
    1141             : 
    1142             :     ////////////////////////////////////
    1143             : 
    1144           0 :     const auto lastLevel = levels - 1;
    1145           0 :     MOZ_ASSERT(lastLevel <= 31, "Right-shift is only defined for bits-1.");
    1146             : 
    1147           0 :     const uint32_t lastLevelWidth = uint32_t(width) >> lastLevel;
    1148           0 :     const uint32_t lastLevelHeight = uint32_t(height) >> lastLevel;
    1149           0 :     const uint32_t lastLevelDepth = uint32_t(depth) >> lastLevel;
    1150             : 
    1151             :     // If these are all zero, then some earlier level was the final 1x1x1 level.
    1152           0 :     if (!lastLevelWidth && !lastLevelHeight && !lastLevelDepth) {
    1153           0 :         mContext->ErrorInvalidOperation("%s: Too many levels requested for the given"
    1154             :                                         " dimensions. (levels: %u, width: %u, height: %u,"
    1155             :                                         " depth: %u)",
    1156           0 :                                         funcName, levels, width, height, depth);
    1157           0 :         return;
    1158             :     }
    1159             : 
    1160             :     ////////////////////////////////////
    1161             :     // Do the thing!
    1162             : 
    1163           0 :     mContext->gl->MakeCurrent();
    1164             : 
    1165           0 :     GLenum error = DoTexStorage(mContext->gl, target.get(), levels, sizedFormat, width,
    1166           0 :                                 height, depth);
    1167             : 
    1168           0 :     if (error == LOCAL_GL_OUT_OF_MEMORY) {
    1169           0 :         mContext->ErrorOutOfMemory("%s: Ran out of memory during texture allocation.",
    1170           0 :                                    funcName);
    1171           0 :         return;
    1172             :     }
    1173           0 :     if (error) {
    1174           0 :         MOZ_RELEASE_ASSERT(false, "GFX: We should have caught all other errors.");
    1175             :         mContext->ErrorInvalidOperation("%s: Unexpected error during texture allocation.",
    1176             :                                         funcName);
    1177             :         return;
    1178             :     }
    1179             : 
    1180             :     ////////////////////////////////////
    1181             :     // Update our specification data.
    1182             : 
    1183           0 :     const bool isDataInitialized = false;
    1184             :     const WebGLTexture::ImageInfo newInfo(dstUsage, width, height, depth,
    1185           0 :                                           isDataInitialized);
    1186           0 :     SetImageInfosAtLevel(funcName, 0, newInfo);
    1187             : 
    1188           0 :     PopulateMipChain(funcName, 0, levels-1);
    1189             : 
    1190           0 :     mImmutable = true;
    1191           0 :     mImmutableLevelCount = levels;
    1192             : }
    1193             : 
    1194             : ////////////////////////////////////////
    1195             : // Tex(Sub)Image
    1196             : 
    1197             : void
    1198           0 : WebGLTexture::TexImage(const char* funcName, TexImageTarget target, GLint level,
    1199             :                        GLenum internalFormat, const webgl::PackingInfo& pi,
    1200             :                        const webgl::TexUnpackBlob* blob)
    1201             : {
    1202             :     ////////////////////////////////////
    1203             :     // Get dest info
    1204             : 
    1205             :     WebGLTexture::ImageInfo* imageInfo;
    1206           0 :     if (!ValidateTexImageSpecification(funcName, target, level, blob->mWidth,
    1207           0 :                                        blob->mHeight, blob->mDepth, &imageInfo))
    1208             :     {
    1209           0 :         return;
    1210             :     }
    1211           0 :     MOZ_ASSERT(imageInfo);
    1212             : 
    1213           0 :     const auto& fua = mContext->mFormatUsage;
    1214           0 :     if (!fua->IsInternalFormatEnumValid(internalFormat)) {
    1215           0 :         mContext->ErrorInvalidValue("%s: Invalid internalformat: 0x%04x",
    1216           0 :                                     funcName, internalFormat);
    1217           0 :         return;
    1218             :     }
    1219             : 
    1220           0 :     auto dstUsage = fua->GetSizedTexUsage(internalFormat);
    1221           0 :     if (!dstUsage) {
    1222           0 :         if (internalFormat != pi.format) {
    1223             :             /* GL ES Version 3.0.4 - 3.8.3 Texture Image Specification
    1224             :              *   "Specifying a combination of values for format, type, and
    1225             :              *   internalformat that is not listed as a valid combination
    1226             :              *   in tables 3.2 or 3.3 generates the error INVALID_OPERATION."
    1227             :              */
    1228           0 :             mContext->ErrorInvalidOperation("%s: Unsized internalFormat must match"
    1229             :                                             " unpack format.",
    1230           0 :                                             funcName);
    1231           0 :             return;
    1232             :         }
    1233             : 
    1234           0 :         dstUsage = fua->GetUnsizedTexUsage(pi);
    1235             :     }
    1236             : 
    1237           0 :     if (!dstUsage) {
    1238           0 :         mContext->ErrorInvalidOperation("%s: Invalid internalformat/format/type:"
    1239             :                                         " 0x%04x/0x%04x/0x%04x",
    1240           0 :                                         funcName, internalFormat, pi.format, pi.type);
    1241           0 :         return;
    1242             :     }
    1243             : 
    1244             :     const webgl::DriverUnpackInfo* driverUnpackInfo;
    1245           0 :     if (!dstUsage->IsUnpackValid(pi, &driverUnpackInfo)) {
    1246           0 :         mContext->ErrorInvalidOperation("%s: Mismatched internalFormat and format/type:"
    1247             :                                         " 0x%04x and 0x%04x/0x%04x",
    1248           0 :                                         funcName, internalFormat, pi.format, pi.type);
    1249           0 :         return;
    1250             :     }
    1251             : 
    1252             :     ////////////////////////////////////
    1253             :     // Check that source and dest info are compatible
    1254           0 :     auto dstFormat = dstUsage->format;
    1255             : 
    1256           0 :     if (!ValidateTargetForFormat(funcName, mContext, target, dstFormat))
    1257           0 :         return;
    1258             : 
    1259           0 :     if (!mContext->IsWebGL2() && dstFormat->d) {
    1260           0 :         if (target != LOCAL_GL_TEXTURE_2D ||
    1261           0 :             blob->HasData() ||
    1262             :             level != 0)
    1263             :         {
    1264           0 :             mContext->ErrorInvalidOperation("%s: With format %s, this function may only"
    1265             :                                             " be called with target=TEXTURE_2D,"
    1266             :                                             " data=null, and level=0.",
    1267           0 :                                             funcName, dstFormat->name);
    1268           0 :             return;
    1269             :         }
    1270             :     }
    1271             : 
    1272             :     ////////////////////////////////////
    1273             :     // Do the thing!
    1274             : 
    1275           0 :     MOZ_ALWAYS_TRUE( mContext->gl->MakeCurrent() );
    1276           0 :     MOZ_ASSERT(mContext->gl->IsCurrent());
    1277             : 
    1278             :     // It's tempting to do allocation first, and TexSubImage second, but this is generally
    1279             :     // slower.
    1280             : 
    1281           0 :     const ImageInfo newImageInfo(dstUsage, blob->mWidth, blob->mHeight, blob->mDepth,
    1282           0 :                                  blob->HasData());
    1283             : 
    1284           0 :     const bool isSubImage = false;
    1285           0 :     const bool needsRespec = (imageInfo->mWidth  != newImageInfo.mWidth ||
    1286           0 :                               imageInfo->mHeight != newImageInfo.mHeight ||
    1287           0 :                               imageInfo->mDepth  != newImageInfo.mDepth ||
    1288           0 :                               imageInfo->mFormat != newImageInfo.mFormat);
    1289           0 :     const GLint xOffset = 0;
    1290           0 :     const GLint yOffset = 0;
    1291           0 :     const GLint zOffset = 0;
    1292             : 
    1293             :     GLenum glError;
    1294           0 :     if (!blob->TexOrSubImage(isSubImage, needsRespec, funcName, this, target, level,
    1295           0 :                              driverUnpackInfo, xOffset, yOffset, zOffset, pi, &glError))
    1296             :     {
    1297           0 :         return;
    1298             :     }
    1299             : 
    1300           0 :     if (glError == LOCAL_GL_OUT_OF_MEMORY) {
    1301           0 :         mContext->ErrorOutOfMemory("%s: Driver ran out of memory during upload.",
    1302           0 :                                    funcName);
    1303           0 :         return;
    1304             :     }
    1305             : 
    1306           0 :     if (glError) {
    1307           0 :         mContext->ErrorInvalidOperation("%s: Unexpected error during upload: 0x%04x",
    1308           0 :                                         funcName, glError);
    1309           0 :         printf_stderr("%s: dui: %x/%x/%x\n", funcName, driverUnpackInfo->internalFormat,
    1310           0 :                       driverUnpackInfo->unpackFormat, driverUnpackInfo->unpackType);
    1311           0 :         MOZ_ASSERT(false, "Unexpected GL error.");
    1312             :         return;
    1313             :     }
    1314             : 
    1315             :     ////////////////////////////////////
    1316             :     // Update our specification data.
    1317             : 
    1318           0 :     SetImageInfo(funcName, imageInfo, newImageInfo);
    1319             : }
    1320             : 
    1321             : void
    1322           0 : WebGLTexture::TexSubImage(const char* funcName, TexImageTarget target, GLint level,
    1323             :                           GLint xOffset, GLint yOffset, GLint zOffset,
    1324             :                           const webgl::PackingInfo& pi, const webgl::TexUnpackBlob* blob)
    1325             : {
    1326             :     ////////////////////////////////////
    1327             :     // Get dest info
    1328             : 
    1329             :     WebGLTexture::ImageInfo* imageInfo;
    1330           0 :     if (!ValidateTexImageSelection(funcName, target, level, xOffset, yOffset, zOffset,
    1331           0 :                                    blob->mWidth, blob->mHeight, blob->mDepth, &imageInfo))
    1332             :     {
    1333           0 :         return;
    1334             :     }
    1335           0 :     MOZ_ASSERT(imageInfo);
    1336             : 
    1337           0 :     auto dstUsage = imageInfo->mFormat;
    1338           0 :     auto dstFormat = dstUsage->format;
    1339             : 
    1340           0 :     if (dstFormat->compression) {
    1341           0 :         mContext->ErrorInvalidEnum("%s: Specified TexImage must not be compressed.",
    1342           0 :                                    funcName);
    1343           0 :         return;
    1344             :     }
    1345             : 
    1346           0 :     if (!mContext->IsWebGL2() && dstFormat->d) {
    1347           0 :         mContext->ErrorInvalidOperation("%s: Function may not be called on a texture of"
    1348             :                                         " format %s.",
    1349           0 :                                         funcName, dstFormat->name);
    1350           0 :         return;
    1351             :     }
    1352             : 
    1353             :     ////////////////////////////////////
    1354             :     // Get source info
    1355             : 
    1356             :     const webgl::DriverUnpackInfo* driverUnpackInfo;
    1357           0 :     if (!dstUsage->IsUnpackValid(pi, &driverUnpackInfo)) {
    1358           0 :         mContext->ErrorInvalidOperation("%s: Mismatched internalFormat and format/type:"
    1359             :                                         " %s and 0x%04x/0x%04x",
    1360           0 :                                         funcName, dstFormat->name, pi.format, pi.type);
    1361           0 :         return;
    1362             :     }
    1363             : 
    1364             :     ////////////////////////////////////
    1365             :     // Do the thing!
    1366             : 
    1367           0 :     mContext->gl->MakeCurrent();
    1368             : 
    1369             :     bool uploadWillInitialize;
    1370           0 :     if (!EnsureImageDataInitializedForUpload(this, funcName, target, level, xOffset,
    1371           0 :                                              yOffset, zOffset, blob->mWidth,
    1372           0 :                                              blob->mHeight, blob->mDepth, imageInfo,
    1373             :                                              &uploadWillInitialize))
    1374             :     {
    1375           0 :         return;
    1376             :     }
    1377             : 
    1378           0 :     const bool isSubImage = true;
    1379           0 :     const bool needsRespec = false;
    1380             : 
    1381             :     GLenum glError;
    1382           0 :     if (!blob->TexOrSubImage(isSubImage, needsRespec, funcName, this, target, level,
    1383           0 :                              driverUnpackInfo, xOffset, yOffset, zOffset, pi, &glError))
    1384             :     {
    1385           0 :         return;
    1386             :     }
    1387             : 
    1388           0 :     if (glError == LOCAL_GL_OUT_OF_MEMORY) {
    1389           0 :         mContext->ErrorOutOfMemory("%s: Driver ran out of memory during upload.",
    1390           0 :                                    funcName);
    1391           0 :         return;
    1392             :     }
    1393             : 
    1394           0 :     if (glError) {
    1395           0 :         mContext->ErrorInvalidOperation("%s: Unexpected error during upload: 0x%04x",
    1396           0 :                                         funcName, glError);
    1397           0 :         MOZ_ASSERT(false, "Unexpected GL error.");
    1398             :         return;
    1399             :     }
    1400             : 
    1401             :     ////////////////////////////////////
    1402             :     // Update our specification data?
    1403             : 
    1404           0 :     if (uploadWillInitialize) {
    1405           0 :         imageInfo->SetIsDataInitialized(true, this);
    1406             :     }
    1407             : }
    1408             : 
    1409             : ////////////////////////////////////////
    1410             : // CompressedTex(Sub)Image
    1411             : 
    1412             : UniquePtr<webgl::TexUnpackBytes>
    1413           0 : WebGLContext::FromCompressed(const char* funcName, TexImageTarget target,
    1414             :                              GLsizei rawWidth, GLsizei rawHeight, GLsizei rawDepth,
    1415             :                              GLint border, const TexImageSource& src)
    1416             : {
    1417             :     uint32_t width, height, depth;
    1418           0 :     if (!ValidateExtents(this, funcName, rawWidth, rawHeight, rawDepth, border, &width,
    1419             :                          &height, &depth))
    1420             :     {
    1421           0 :         return nullptr;
    1422             :     }
    1423             : 
    1424           0 :     if (src.mPboOffset) {
    1425             :         return FromPboOffset(this, funcName, target, width, height, depth,
    1426           0 :                              *(src.mPboOffset));
    1427             :     }
    1428             : 
    1429           0 :     if (mBoundPixelUnpackBuffer) {
    1430           0 :         ErrorInvalidOperation("%s: PIXEL_UNPACK_BUFFER must be null.", funcName);
    1431           0 :         return nullptr;
    1432             :     }
    1433             : 
    1434           0 :     return FromView(this, funcName, target, width, height, depth, src.mView,
    1435           0 :                     src.mViewElemOffset, src.mViewElemLengthOverride);
    1436             : }
    1437             : 
    1438             : void
    1439           0 : WebGLTexture::CompressedTexImage(const char* funcName, TexImageTarget target, GLint level,
    1440             :                                  GLenum internalFormat, GLsizei rawWidth,
    1441             :                                  GLsizei rawHeight, GLsizei rawDepth, GLint border,
    1442             :                                  const TexImageSource& src)
    1443             : {
    1444           0 :     const auto blob = mContext->FromCompressed(funcName, target, rawWidth, rawHeight,
    1445           0 :                                                rawDepth, border, src);
    1446           0 :     if (!blob)
    1447           0 :         return;
    1448             : 
    1449             :     ////////////////////////////////////
    1450             :     // Get dest info
    1451             : 
    1452             :     WebGLTexture::ImageInfo* imageInfo;
    1453           0 :     if (!ValidateTexImageSpecification(funcName, target, level, blob->mWidth,
    1454           0 :                                        blob->mHeight, blob->mDepth, &imageInfo))
    1455             :     {
    1456           0 :         return;
    1457             :     }
    1458           0 :     MOZ_ASSERT(imageInfo);
    1459             : 
    1460           0 :     auto usage = mContext->mFormatUsage->GetSizedTexUsage(internalFormat);
    1461           0 :     if (!usage) {
    1462           0 :         mContext->ErrorInvalidEnum("%s: Invalid internalFormat: 0x%04x", funcName,
    1463           0 :                                    internalFormat);
    1464           0 :         return;
    1465             :     }
    1466             : 
    1467           0 :     auto format = usage->format;
    1468           0 :     if (!format->compression) {
    1469           0 :         mContext->ErrorInvalidEnum("%s: Specified internalFormat must be compressed.",
    1470           0 :                                    funcName);
    1471           0 :         return;
    1472             :     }
    1473             : 
    1474           0 :     if (!ValidateTargetForFormat(funcName, mContext, target, format))
    1475           0 :         return;
    1476             : 
    1477             :     ////////////////////////////////////
    1478             :     // Get source info
    1479             : 
    1480           0 :     if (!ValidateCompressedTexUnpack(mContext, funcName, blob->mWidth, blob->mHeight,
    1481           0 :                                      blob->mDepth, format, blob->mAvailBytes))
    1482             :     {
    1483           0 :         return;
    1484             :     }
    1485             : 
    1486             :     ////////////////////////////////////
    1487             :     // Check that source is compatible with dest
    1488             : 
    1489           0 :     if (!ValidateCompressedTexImageRestrictions(funcName, mContext, target, level, format,
    1490           0 :                                                 blob->mWidth, blob->mHeight,
    1491           0 :                                                 blob->mDepth))
    1492             :     {
    1493           0 :         return;
    1494             :     }
    1495             : 
    1496             :     ////////////////////////////////////
    1497             :     // Do the thing!
    1498             : 
    1499           0 :     mContext->gl->MakeCurrent();
    1500             : 
    1501             :     // Warning: Possibly shared memory.  See bug 1225033.
    1502           0 :     GLenum error = DoCompressedTexImage(mContext->gl, target, level, internalFormat,
    1503           0 :                                         blob->mWidth, blob->mHeight, blob->mDepth,
    1504           0 :                                         blob->mAvailBytes, blob->mPtr);
    1505           0 :     if (error == LOCAL_GL_OUT_OF_MEMORY) {
    1506           0 :         mContext->ErrorOutOfMemory("%s: Ran out of memory during upload.", funcName);
    1507           0 :         return;
    1508             :     }
    1509           0 :     if (error) {
    1510           0 :         MOZ_RELEASE_ASSERT(false, "GFX: We should have caught all other errors.");
    1511             :         mContext->GenerateWarning("%s: Unexpected error during texture upload. Context"
    1512             :                                   " lost.",
    1513             :                                   funcName);
    1514             :         mContext->ForceLoseContext();
    1515             :         return;
    1516             :     }
    1517             : 
    1518             :     ////////////////////////////////////
    1519             :     // Update our specification data.
    1520             : 
    1521           0 :     const bool isDataInitialized = true;
    1522           0 :     const ImageInfo newImageInfo(usage, blob->mWidth, blob->mHeight, blob->mDepth,
    1523           0 :                                  isDataInitialized);
    1524           0 :     SetImageInfo(funcName, imageInfo, newImageInfo);
    1525             : }
    1526             : 
    1527             : static inline bool
    1528           0 : IsSubImageBlockAligned(const webgl::CompressedFormatInfo* compression,
    1529             :                        const WebGLTexture::ImageInfo* imageInfo, GLint xOffset,
    1530             :                        GLint yOffset, uint32_t width, uint32_t height)
    1531             : {
    1532           0 :     if (xOffset % compression->blockWidth != 0 ||
    1533           0 :         yOffset % compression->blockHeight != 0)
    1534             :     {
    1535           0 :         return false;
    1536             :     }
    1537             : 
    1538           0 :     if (width % compression->blockWidth != 0 && xOffset + width != imageInfo->mWidth)
    1539           0 :         return false;
    1540             : 
    1541           0 :     if (height % compression->blockHeight != 0 && yOffset + height != imageInfo->mHeight)
    1542           0 :         return false;
    1543             : 
    1544           0 :     return true;
    1545             : }
    1546             : 
    1547             : void
    1548           0 : WebGLTexture::CompressedTexSubImage(const char* funcName, TexImageTarget target,
    1549             :                                     GLint level, GLint xOffset, GLint yOffset,
    1550             :                                     GLint zOffset, GLsizei rawWidth, GLsizei rawHeight,
    1551             :                                     GLsizei rawDepth, GLenum sizedUnpackFormat,
    1552             :                                     const TexImageSource& src)
    1553             : {
    1554           0 :     const GLint border = 0;
    1555           0 :     const auto blob = mContext->FromCompressed(funcName, target, rawWidth, rawHeight,
    1556           0 :                                                rawDepth, border, src);
    1557           0 :     if (!blob)
    1558           0 :         return;
    1559             : 
    1560             :     ////////////////////////////////////
    1561             :     // Get dest info
    1562             : 
    1563             :     WebGLTexture::ImageInfo* imageInfo;
    1564           0 :     if (!ValidateTexImageSelection(funcName, target, level, xOffset, yOffset, zOffset,
    1565           0 :                                    blob->mWidth, blob->mHeight, blob->mDepth, &imageInfo))
    1566             :     {
    1567           0 :         return;
    1568             :     }
    1569           0 :     MOZ_ASSERT(imageInfo);
    1570             : 
    1571           0 :     auto dstUsage = imageInfo->mFormat;
    1572           0 :     auto dstFormat = dstUsage->format;
    1573             : 
    1574             :     ////////////////////////////////////
    1575             :     // Get source info
    1576             : 
    1577           0 :     auto srcUsage = mContext->mFormatUsage->GetSizedTexUsage(sizedUnpackFormat);
    1578           0 :     if (!srcUsage->format->compression) {
    1579           0 :         mContext->ErrorInvalidEnum("%s: Specified format must be compressed.", funcName);
    1580           0 :         return;
    1581             :     }
    1582             : 
    1583           0 :     if (srcUsage != dstUsage) {
    1584           0 :         mContext->ErrorInvalidOperation("%s: `format` must match the format of the"
    1585             :                                         " existing texture image.",
    1586           0 :                                         funcName);
    1587           0 :         return;
    1588             :     }
    1589             : 
    1590           0 :     auto format = srcUsage->format;
    1591           0 :     MOZ_ASSERT(format == dstFormat);
    1592           0 :     if (!ValidateCompressedTexUnpack(mContext, funcName, blob->mWidth, blob->mHeight,
    1593           0 :                                      blob->mDepth, format, blob->mAvailBytes))
    1594             :     {
    1595           0 :         return;
    1596             :     }
    1597             : 
    1598             :     ////////////////////////////////////
    1599             :     // Check that source is compatible with dest
    1600             : 
    1601           0 :     switch (format->compression->family) {
    1602             :     // Forbidden:
    1603             :     case webgl::CompressionFamily::ETC1:
    1604             :     case webgl::CompressionFamily::ATC:
    1605           0 :         mContext->ErrorInvalidOperation("%s: Format does not allow sub-image"
    1606           0 :                                         " updates.", funcName);
    1607           0 :         return;
    1608             : 
    1609             :     // Block-aligned:
    1610             :     case webgl::CompressionFamily::ES3:  // Yes, the ES3 formats don't match the ES3
    1611             :     case webgl::CompressionFamily::S3TC: // default behavior.
    1612           0 :         if (!IsSubImageBlockAligned(dstFormat->compression, imageInfo, xOffset, yOffset,
    1613           0 :                                     blob->mWidth, blob->mHeight))
    1614             :         {
    1615           0 :             mContext->ErrorInvalidOperation("%s: Format requires block-aligned sub-image"
    1616             :                                             " updates.",
    1617           0 :                                             funcName);
    1618           0 :             return;
    1619             :         }
    1620           0 :         break;
    1621             : 
    1622             :     // Full-only: (The ES3 default)
    1623             :     default: // PVRTC
    1624           0 :         if (xOffset || yOffset ||
    1625           0 :             blob->mWidth != imageInfo->mWidth ||
    1626           0 :             blob->mHeight != imageInfo->mHeight)
    1627             :         {
    1628           0 :             mContext->ErrorInvalidOperation("%s: Format does not allow partial sub-image"
    1629             :                                             " updates.",
    1630           0 :                                             funcName);
    1631           0 :             return;
    1632             :         }
    1633           0 :         break;
    1634             :     }
    1635             : 
    1636             :     ////////////////////////////////////
    1637             :     // Do the thing!
    1638             : 
    1639           0 :     mContext->gl->MakeCurrent();
    1640             : 
    1641             :     bool uploadWillInitialize;
    1642           0 :     if (!EnsureImageDataInitializedForUpload(this, funcName, target, level, xOffset,
    1643           0 :                                              yOffset, zOffset, blob->mWidth,
    1644           0 :                                              blob->mHeight, blob->mDepth, imageInfo,
    1645             :                                              &uploadWillInitialize))
    1646             :     {
    1647           0 :         return;
    1648             :     }
    1649             : 
    1650             :     // Warning: Possibly shared memory.  See bug 1225033.
    1651           0 :     GLenum error = DoCompressedTexSubImage(mContext->gl, target, level, xOffset, yOffset,
    1652           0 :                                            zOffset, blob->mWidth, blob->mHeight,
    1653           0 :                                            blob->mDepth, sizedUnpackFormat,
    1654           0 :                                            blob->mAvailBytes, blob->mPtr);
    1655           0 :     if (error == LOCAL_GL_OUT_OF_MEMORY) {
    1656           0 :         mContext->ErrorOutOfMemory("%s: Ran out of memory during upload.", funcName);
    1657           0 :         return;
    1658             :     }
    1659           0 :     if (error) {
    1660           0 :         MOZ_RELEASE_ASSERT(false, "GFX: We should have caught all other errors.");
    1661             :         mContext->GenerateWarning("%s: Unexpected error during texture upload. Context"
    1662             :                                   " lost.",
    1663             :                                   funcName);
    1664             :         mContext->ForceLoseContext();
    1665             :         return;
    1666             :     }
    1667             : 
    1668             :     ////////////////////////////////////
    1669             :     // Update our specification data?
    1670             : 
    1671           0 :     if (uploadWillInitialize) {
    1672           0 :         imageInfo->SetIsDataInitialized(true, this);
    1673             :     }
    1674             : }
    1675             : 
    1676             : ////////////////////////////////////////
    1677             : // CopyTex(Sub)Image
    1678             : 
    1679             : static bool
    1680           0 : ValidateCopyTexImageFormats(WebGLContext* webgl, const char* funcName,
    1681             :                             const webgl::FormatInfo* srcFormat,
    1682             :                             const webgl::FormatInfo* dstFormat)
    1683             : {
    1684           0 :     MOZ_ASSERT(!srcFormat->compression);
    1685           0 :     if (dstFormat->compression) {
    1686             :         webgl->ErrorInvalidEnum("%s: Specified destination must not have a compressed"
    1687             :                                 " format.",
    1688           0 :                                 funcName);
    1689           0 :         return false;
    1690             :     }
    1691             : 
    1692           0 :     if (dstFormat->effectiveFormat == webgl::EffectiveFormat::RGB9_E5) {
    1693             :         webgl->ErrorInvalidOperation("%s: RGB9_E5 is an invalid destination for"
    1694             :                                      " CopyTex(Sub)Image. (GLES 3.0.4 p145)",
    1695           0 :                                      funcName);
    1696           0 :         return false;
    1697             :     }
    1698             : 
    1699           0 :     if (!DoChannelsMatchForCopyTexImage(srcFormat, dstFormat)) {
    1700             :         webgl->ErrorInvalidOperation("%s: Destination channels must be compatible with"
    1701             :                                      " source channels. (GLES 3.0.4 p140 Table 3.16)",
    1702           0 :                                      funcName);
    1703           0 :         return false;
    1704             :     }
    1705             : 
    1706           0 :     return true;
    1707             : }
    1708             : 
    1709             : ////////////////////////////////////////////////////////////////////////////////
    1710             : 
    1711             : class ScopedCopyTexImageSource
    1712             : {
    1713             :     WebGLContext* const mWebGL;
    1714             :     GLuint mRB;
    1715             :     GLuint mFB;
    1716             : 
    1717             : public:
    1718             :     ScopedCopyTexImageSource(WebGLContext* webgl, const char* funcName, uint32_t srcWidth,
    1719             :                              uint32_t srcHeight, const webgl::FormatInfo* srcFormat,
    1720             :                              const webgl::FormatUsageInfo* dstUsage);
    1721             :     ~ScopedCopyTexImageSource();
    1722             : };
    1723             : 
    1724           0 : ScopedCopyTexImageSource::ScopedCopyTexImageSource(WebGLContext* webgl,
    1725             :                                                    const char* funcName,
    1726             :                                                    uint32_t srcWidth, uint32_t srcHeight,
    1727             :                                                    const webgl::FormatInfo* srcFormat,
    1728           0 :                                                    const webgl::FormatUsageInfo* dstUsage)
    1729             :     : mWebGL(webgl)
    1730             :     , mRB(0)
    1731           0 :     , mFB(0)
    1732             : {
    1733           0 :     switch (dstUsage->format->unsizedFormat) {
    1734             :     case webgl::UnsizedFormat::L:
    1735             :     case webgl::UnsizedFormat::A:
    1736             :     case webgl::UnsizedFormat::LA:
    1737             :         webgl->GenerateWarning("%s: Copying to a LUMINANCE, ALPHA, or LUMINANCE_ALPHA"
    1738             :                                " is deprecated, and has severely reduced performance"
    1739             :                                " on some platforms.",
    1740           0 :                                funcName);
    1741           0 :         break;
    1742             : 
    1743             :     default:
    1744           0 :         MOZ_ASSERT(!dstUsage->textureSwizzleRGBA);
    1745           0 :         return;
    1746             :     }
    1747             : 
    1748           0 :     if (!dstUsage->textureSwizzleRGBA)
    1749           0 :         return;
    1750             : 
    1751           0 :     gl::GLContext* gl = webgl->gl;
    1752             : 
    1753             :     GLenum sizedFormat;
    1754             : 
    1755           0 :     switch (srcFormat->componentType) {
    1756             :     case webgl::ComponentType::NormUInt:
    1757           0 :         sizedFormat = LOCAL_GL_RGBA8;
    1758           0 :         break;
    1759             : 
    1760             :     case webgl::ComponentType::Float:
    1761           0 :         if (webgl->IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float)) {
    1762           0 :             sizedFormat = LOCAL_GL_RGBA32F;
    1763           0 :             break;
    1764             :         }
    1765             : 
    1766           0 :         if (webgl->IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float)) {
    1767           0 :             sizedFormat = LOCAL_GL_RGBA16F;
    1768           0 :             break;
    1769             :         }
    1770           0 :         MOZ_CRASH("GFX: Should be able to request CopyTexImage from Float.");
    1771             : 
    1772             :     default:
    1773           0 :         MOZ_CRASH("GFX: Should be able to request CopyTexImage from this type.");
    1774             :     }
    1775             : 
    1776           0 :     gl::ScopedTexture scopedTex(gl);
    1777           0 :     gl::ScopedBindTexture scopedBindTex(gl, scopedTex.Texture(), LOCAL_GL_TEXTURE_2D);
    1778             : 
    1779           0 :     gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST);
    1780           0 :     gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_NEAREST);
    1781             : 
    1782           0 :     GLint blitSwizzle[4] = {LOCAL_GL_ZERO};
    1783           0 :     switch (dstUsage->format->unsizedFormat) {
    1784             :     case webgl::UnsizedFormat::L:
    1785           0 :         blitSwizzle[0] = LOCAL_GL_RED;
    1786           0 :         break;
    1787             : 
    1788             :     case webgl::UnsizedFormat::A:
    1789           0 :         blitSwizzle[0] = LOCAL_GL_ALPHA;
    1790           0 :         break;
    1791             : 
    1792             :     case webgl::UnsizedFormat::LA:
    1793           0 :         blitSwizzle[0] = LOCAL_GL_RED;
    1794           0 :         blitSwizzle[1] = LOCAL_GL_ALPHA;
    1795           0 :         break;
    1796             : 
    1797             :     default:
    1798           0 :         MOZ_CRASH("GFX: Unhandled unsizedFormat.");
    1799             :     }
    1800             : 
    1801           0 :     gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_R, blitSwizzle[0]);
    1802           0 :     gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_G, blitSwizzle[1]);
    1803           0 :     gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_B, blitSwizzle[2]);
    1804           0 :     gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_A, blitSwizzle[3]);
    1805             : 
    1806           0 :     gl->fCopyTexImage2D(LOCAL_GL_TEXTURE_2D, 0, sizedFormat, 0, 0, srcWidth,
    1807           0 :                         srcHeight, 0);
    1808             : 
    1809             :     // Now create the swizzled FB we'll be exposing.
    1810             : 
    1811           0 :     GLuint rgbaRB = 0;
    1812           0 :     gl->fGenRenderbuffers(1, &rgbaRB);
    1813           0 :     gl::ScopedBindRenderbuffer scopedRB(gl, rgbaRB);
    1814           0 :     gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, sizedFormat, srcWidth, srcHeight);
    1815             : 
    1816           0 :     GLuint rgbaFB = 0;
    1817           0 :     gl->fGenFramebuffers(1, &rgbaFB);
    1818           0 :     gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, rgbaFB);
    1819             :     gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
    1820           0 :                                  LOCAL_GL_RENDERBUFFER, rgbaRB);
    1821             : 
    1822           0 :     const GLenum status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
    1823           0 :     if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
    1824           0 :         MOZ_CRASH("GFX: Temp framebuffer is not complete.");
    1825             :     }
    1826             : 
    1827             :     // Restore RB binding.
    1828           0 :     scopedRB.Unwrap(); // This function should really have a better name.
    1829             : 
    1830             :     // Draw-blit rgbaTex into rgbaFB.
    1831           0 :     const gfx::IntSize srcSize(srcWidth, srcHeight);
    1832           0 :     gl->BlitHelper()->DrawBlitTextureToFramebuffer(scopedTex.Texture(), rgbaFB,
    1833           0 :                                                    srcSize, srcSize);
    1834             : 
    1835             :     // Restore Tex2D binding and destroy the temp tex.
    1836           0 :     scopedBindTex.Unwrap();
    1837           0 :     scopedTex.Unwrap();
    1838             : 
    1839             :     // Leave RB and FB alive, and FB bound.
    1840           0 :     mRB = rgbaRB;
    1841           0 :     mFB = rgbaFB;
    1842             : }
    1843             : 
    1844             : template<typename T>
    1845             : static inline GLenum
    1846           0 : ToGLHandle(const T& obj)
    1847             : {
    1848           0 :     return (obj ? obj->mGLName : 0);
    1849             : }
    1850             : 
    1851           0 : ScopedCopyTexImageSource::~ScopedCopyTexImageSource()
    1852             : {
    1853           0 :     if (!mFB) {
    1854           0 :         MOZ_ASSERT(!mRB);
    1855           0 :         return;
    1856             :     }
    1857           0 :     MOZ_ASSERT(mRB);
    1858             : 
    1859           0 :     gl::GLContext* gl = mWebGL->gl;
    1860             : 
    1861             :     // If we're swizzling, it's because we're on a GL core (3.2+) profile, which has
    1862             :     // split framebuffer support.
    1863           0 :     gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER,
    1864           0 :                          ToGLHandle(mWebGL->mBoundDrawFramebuffer));
    1865           0 :     gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER,
    1866           0 :                          ToGLHandle(mWebGL->mBoundReadFramebuffer));
    1867             : 
    1868           0 :     gl->fDeleteFramebuffers(1, &mFB);
    1869           0 :     gl->fDeleteRenderbuffers(1, &mRB);
    1870           0 : }
    1871             : 
    1872             : ////////////////////////////////////////////////////////////////////////////////
    1873             : 
    1874             : static bool
    1875           0 : GetUnsizedFormatForCopy(GLenum internalFormat, webgl::UnsizedFormat* const out)
    1876             : {
    1877           0 :     switch (internalFormat) {
    1878           0 :     case LOCAL_GL_RED:             *out = webgl::UnsizedFormat::R;    break;
    1879           0 :     case LOCAL_GL_RG:              *out = webgl::UnsizedFormat::RG;   break;
    1880           0 :     case LOCAL_GL_RGB:             *out = webgl::UnsizedFormat::RGB;  break;
    1881           0 :     case LOCAL_GL_RGBA:            *out = webgl::UnsizedFormat::RGBA; break;
    1882           0 :     case LOCAL_GL_LUMINANCE:       *out = webgl::UnsizedFormat::L;    break;
    1883           0 :     case LOCAL_GL_ALPHA:           *out = webgl::UnsizedFormat::A;    break;
    1884           0 :     case LOCAL_GL_LUMINANCE_ALPHA: *out = webgl::UnsizedFormat::LA;   break;
    1885             : 
    1886             :     default:
    1887           0 :         return false;
    1888             :     }
    1889             : 
    1890           0 :     return true;
    1891             : }
    1892             : 
    1893             : static const webgl::FormatUsageInfo*
    1894           0 : ValidateCopyDestUsage(const char* funcName, WebGLContext* webgl,
    1895             :                       const webgl::FormatInfo* srcFormat, GLenum internalFormat)
    1896             : {
    1897           0 :     const auto& fua = webgl->mFormatUsage;
    1898             : 
    1899           0 :     auto dstUsage = fua->GetSizedTexUsage(internalFormat);
    1900           0 :     if (!dstUsage) {
    1901             :         // Ok, maybe it's unsized.
    1902             :         webgl::UnsizedFormat unsizedFormat;
    1903           0 :         if (!GetUnsizedFormatForCopy(internalFormat, &unsizedFormat)) {
    1904             :             webgl->ErrorInvalidEnum("%s: Unrecongnized internalFormat 0x%04x.", funcName,
    1905           0 :                                     internalFormat);
    1906           0 :             return nullptr;
    1907             :         }
    1908             : 
    1909           0 :         const auto dstFormat = srcFormat->GetCopyDecayFormat(unsizedFormat);
    1910           0 :         if (dstFormat) {
    1911           0 :             dstUsage = fua->GetUsage(dstFormat->effectiveFormat);
    1912             :         }
    1913           0 :         if (!dstUsage) {
    1914             :             webgl->ErrorInvalidOperation("%s: 0x%04x is not a valid unsized format for"
    1915             :                                          " source format %s.",
    1916           0 :                                          funcName, internalFormat, srcFormat->name);
    1917           0 :             return nullptr;
    1918             :         }
    1919             : 
    1920           0 :         return dstUsage;
    1921             :     }
    1922             :     // Alright, it's sized.
    1923             : 
    1924           0 :     const auto dstFormat = dstUsage->format;
    1925             : 
    1926           0 :     const auto fnNarrowType = [&](webgl::ComponentType type) {
    1927           0 :         switch (type) {
    1928             :         case webgl::ComponentType::NormInt:
    1929             :         case webgl::ComponentType::NormUInt:
    1930             :             // These both count as "fixed-point".
    1931           0 :             return webgl::ComponentType::NormInt;
    1932             : 
    1933             :         default:
    1934           0 :             return type;
    1935             :         }
    1936             :     };
    1937             : 
    1938           0 :     const auto srcType = fnNarrowType(srcFormat->componentType);
    1939           0 :     const auto dstType = fnNarrowType(dstFormat->componentType);
    1940           0 :     if (dstType != srcType) {
    1941             :         webgl->ErrorInvalidOperation("%s: For sized internalFormats, source and dest"
    1942             :                                      " component types must match. (source: %s, dest:"
    1943             :                                      " %s)",
    1944           0 :                                      funcName, srcFormat->name, dstFormat->name);
    1945           0 :         return nullptr;
    1946             :     }
    1947             : 
    1948           0 :     bool componentSizesMatch = true;
    1949           0 :     if (dstFormat->r) {
    1950           0 :         componentSizesMatch &= (dstFormat->r == srcFormat->r);
    1951             :     }
    1952           0 :     if (dstFormat->g) {
    1953           0 :         componentSizesMatch &= (dstFormat->g == srcFormat->g);
    1954             :     }
    1955           0 :     if (dstFormat->b) {
    1956           0 :         componentSizesMatch &= (dstFormat->b == srcFormat->b);
    1957             :     }
    1958           0 :     if (dstFormat->a) {
    1959           0 :         componentSizesMatch &= (dstFormat->a == srcFormat->a);
    1960             :     }
    1961             : 
    1962           0 :     if (!componentSizesMatch) {
    1963             :         webgl->ErrorInvalidOperation("%s: For sized internalFormats, source and dest"
    1964             :                                      " component sizes must match exactly. (source: %s,"
    1965             :                                      " dest: %s)",
    1966           0 :                                      funcName, srcFormat->name, dstFormat->name);
    1967           0 :         return nullptr;
    1968             :     }
    1969             : 
    1970           0 :     return dstUsage;
    1971             : }
    1972             : 
    1973             : bool
    1974           0 : WebGLTexture::ValidateCopyTexImageForFeedback(const char* funcName, uint32_t level, GLint layer) const
    1975             : {
    1976           0 :     const auto& fb = mContext->mBoundReadFramebuffer;
    1977           0 :     if (fb) {
    1978           0 :         const auto& attach = fb->ColorReadBuffer();
    1979           0 :         MOZ_ASSERT(attach);
    1980             : 
    1981           0 :         if (attach->Texture() == this &&
    1982           0 :             attach->Layer() == layer &&
    1983           0 :             uint32_t(attach->MipLevel()) == level)
    1984             :         {
    1985             :             // Note that the TexImageTargets *don't* have to match for this to be
    1986             :             // undefined per GLES 3.0.4 p211, thus an INVALID_OP in WebGL.
    1987           0 :             mContext->ErrorInvalidOperation("%s: Feedback loop detected, as this texture"
    1988             :                                             " is already attached to READ_FRAMEBUFFER's"
    1989             :                                             " READ_BUFFER-selected COLOR_ATTACHMENT%u.",
    1990           0 :                                             funcName, attach->mAttachmentPoint);
    1991           0 :             return false;
    1992             :         }
    1993             :     }
    1994           0 :     return true;
    1995             : }
    1996             : 
    1997             : static bool
    1998           0 : DoCopyTexOrSubImage(WebGLContext* webgl, const char* funcName, bool isSubImage,
    1999             :                     const WebGLTexture* tex, TexImageTarget target, GLint level,
    2000             :                     GLint xWithinSrc, GLint yWithinSrc,
    2001             :                     uint32_t srcTotalWidth, uint32_t srcTotalHeight,
    2002             :                     const webgl::FormatUsageInfo* srcUsage,
    2003             :                     GLint xOffset, GLint yOffset, GLint zOffset,
    2004             :                     uint32_t dstWidth, uint32_t dstHeight,
    2005             :                     const webgl::FormatUsageInfo* dstUsage)
    2006             : {
    2007           0 :     const auto& gl = webgl->gl;
    2008             : 
    2009             :     ////
    2010             : 
    2011             :     int32_t readX, readY;
    2012             :     int32_t writeX, writeY;
    2013             :     int32_t rwWidth, rwHeight;
    2014           0 :     if (!Intersect(srcTotalWidth, xWithinSrc, dstWidth, &readX, &writeX, &rwWidth) ||
    2015           0 :         !Intersect(srcTotalHeight, yWithinSrc, dstHeight, &readY, &writeY, &rwHeight))
    2016             :     {
    2017           0 :         webgl->ErrorOutOfMemory("%s: Bad subrect selection.", funcName);
    2018           0 :         return false;
    2019             :     }
    2020             : 
    2021           0 :     writeX += xOffset;
    2022           0 :     writeY += yOffset;
    2023             : 
    2024             :     ////
    2025             : 
    2026           0 :     GLenum error = 0;
    2027             :     do {
    2028           0 :         const auto& idealUnpack = dstUsage->idealUnpack;
    2029           0 :         if (!isSubImage) {
    2030           0 :             UniqueBuffer buffer;
    2031             : 
    2032           0 :             if (uint32_t(rwWidth) != dstWidth || uint32_t(rwHeight) != dstHeight) {
    2033           0 :                 const auto& pi = idealUnpack->ToPacking();
    2034           0 :                 CheckedUint32 byteCount = BytesPerPixel(pi);
    2035           0 :                 byteCount *= dstWidth;
    2036           0 :                 byteCount *= dstHeight;
    2037             : 
    2038           0 :                 if (byteCount.isValid()) {
    2039           0 :                     buffer = calloc(1, byteCount.value());
    2040             :                 }
    2041             : 
    2042           0 :                 if (!buffer.get()) {
    2043             :                     webgl->ErrorOutOfMemory("%s: Ran out of memory allocating zeros.",
    2044           0 :                                             funcName);
    2045           0 :                     return false;
    2046             :                 }
    2047             :             }
    2048             : 
    2049           0 :             const ScopedUnpackReset unpackReset(webgl);
    2050           0 :             gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1);
    2051           0 :             error = DoTexImage(gl, target, level, idealUnpack, dstWidth, dstHeight, 1,
    2052           0 :                                buffer.get());
    2053           0 :             if (error)
    2054           0 :                 break;
    2055             :         }
    2056             : 
    2057           0 :         if (!rwWidth || !rwHeight) {
    2058             :             // There aren't any pixels to copy, so we're 'done'.
    2059           0 :             return true;
    2060             :         }
    2061             : 
    2062           0 :         const auto& srcFormat = srcUsage->format;
    2063             :         ScopedCopyTexImageSource maybeSwizzle(webgl, funcName, srcTotalWidth,
    2064           0 :                                               srcTotalHeight, srcFormat, dstUsage);
    2065             : 
    2066           0 :         error = DoCopyTexSubImage(gl, target, level, writeX, writeY, zOffset, readX,
    2067           0 :                                   readY, rwWidth, rwHeight);
    2068           0 :         if (error)
    2069           0 :             break;
    2070             : 
    2071           0 :         return true;
    2072             :     } while (false);
    2073             : 
    2074           0 :     if (error == LOCAL_GL_OUT_OF_MEMORY) {
    2075           0 :         webgl->ErrorOutOfMemory("%s: Ran out of memory during texture copy.", funcName);
    2076           0 :         return false;
    2077             :     }
    2078             : 
    2079           0 :     if (gl->IsANGLE() && error == LOCAL_GL_INVALID_OPERATION) {
    2080             :         webgl->ErrorImplementationBug("%s: ANGLE is particular about CopyTexSubImage"
    2081             :                                       " formats matching exactly.",
    2082           0 :                                       funcName);
    2083           0 :         return false;
    2084             :     }
    2085             : 
    2086           0 :     MOZ_RELEASE_ASSERT(false, "GFX: We should have caught all other errors.");
    2087             :     webgl->GenerateWarning("%s: Unexpected error during texture copy. Context lost.",
    2088             :                            funcName);
    2089             :     webgl->ForceLoseContext();
    2090             :     return false;
    2091             : }
    2092             : 
    2093             : // There is no CopyTexImage3D.
    2094             : void
    2095           0 : WebGLTexture::CopyTexImage2D(TexImageTarget target, GLint level, GLenum internalFormat,
    2096             :                              GLint x, GLint y, GLsizei rawWidth, GLsizei rawHeight,
    2097             :                              GLint border)
    2098             : {
    2099           0 :     const char funcName[] = "copyTexImage2D";
    2100             : 
    2101             :     ////////////////////////////////////
    2102             :     // Get dest info
    2103             : 
    2104             :     uint32_t width, height, depth;
    2105           0 :     if (!ValidateExtents(mContext, funcName, rawWidth, rawHeight, 1, border, &width,
    2106             :                          &height, &depth))
    2107             :     {
    2108           0 :         return;
    2109             :     }
    2110             : 
    2111             :     WebGLTexture::ImageInfo* imageInfo;
    2112           0 :     if (!ValidateTexImageSpecification(funcName, target, level, width, height, depth,
    2113             :                                        &imageInfo))
    2114             :     {
    2115           0 :         return;
    2116             :     }
    2117           0 :     MOZ_ASSERT(imageInfo);
    2118             : 
    2119             :     ////////////////////////////////////
    2120             :     // Get source info
    2121             : 
    2122             :     const webgl::FormatUsageInfo* srcUsage;
    2123             :     uint32_t srcTotalWidth;
    2124             :     uint32_t srcTotalHeight;
    2125           0 :     if (!mContext->ValidateCurFBForRead(funcName, &srcUsage, &srcTotalWidth,
    2126             :                                         &srcTotalHeight))
    2127             :     {
    2128           0 :         return;
    2129             :     }
    2130             : 
    2131           0 :     if (!ValidateCopyTexImageForFeedback(funcName, level))
    2132           0 :         return;
    2133             : 
    2134             :     ////////////////////////////////////
    2135             :     // Check that source and dest info are compatible
    2136             : 
    2137           0 :     const auto& srcFormat = srcUsage->format;
    2138           0 :     const auto dstUsage = ValidateCopyDestUsage(funcName, mContext, srcFormat,
    2139           0 :                                                 internalFormat);
    2140           0 :     if (!dstUsage)
    2141           0 :         return;
    2142             : 
    2143           0 :     const auto& dstFormat = dstUsage->format;
    2144           0 :     if (!ValidateTargetForFormat(funcName, mContext, target, dstFormat))
    2145           0 :         return;
    2146             : 
    2147           0 :     if (!mContext->IsWebGL2() && dstFormat->d) {
    2148           0 :         mContext->ErrorInvalidOperation("%s: Function may not be called with format %s.",
    2149           0 :                                         funcName, dstFormat->name);
    2150           0 :         return;
    2151             :     }
    2152             : 
    2153           0 :     if (!ValidateCopyTexImageFormats(mContext, funcName, srcFormat, dstFormat))
    2154           0 :         return;
    2155             : 
    2156             :     ////////////////////////////////////
    2157             :     // Do the thing!
    2158             : 
    2159           0 :     mContext->gl->MakeCurrent();
    2160           0 :     mContext->OnBeforeReadCall();
    2161             : 
    2162           0 :     const bool isSubImage = false;
    2163           0 :     if (!DoCopyTexOrSubImage(mContext, funcName, isSubImage, this, target, level, x, y,
    2164             :                              srcTotalWidth, srcTotalHeight, srcUsage, 0, 0, 0, width,
    2165             :                              height, dstUsage))
    2166             :     {
    2167           0 :         return;
    2168             :     }
    2169             : 
    2170             :     ////////////////////////////////////
    2171             :     // Update our specification data.
    2172             : 
    2173           0 :     const bool isDataInitialized = true;
    2174           0 :     const ImageInfo newImageInfo(dstUsage, width, height, depth, isDataInitialized);
    2175           0 :     SetImageInfo(funcName, imageInfo, newImageInfo);
    2176             : }
    2177             : 
    2178             : void
    2179           0 : WebGLTexture::CopyTexSubImage(const char* funcName, TexImageTarget target, GLint level,
    2180             :                               GLint xOffset, GLint yOffset, GLint zOffset, GLint x,
    2181             :                               GLint y, GLsizei rawWidth, GLsizei rawHeight)
    2182             : {
    2183             :     uint32_t width, height, depth;
    2184           0 :     if (!ValidateExtents(mContext, funcName, rawWidth, rawHeight, 1, 0, &width,
    2185             :                          &height, &depth))
    2186             :     {
    2187           0 :         return;
    2188             :     }
    2189             : 
    2190             :     ////////////////////////////////////
    2191             :     // Get dest info
    2192             : 
    2193             :     WebGLTexture::ImageInfo* imageInfo;
    2194           0 :     if (!ValidateTexImageSelection(funcName, target, level, xOffset, yOffset, zOffset,
    2195             :                                    width, height, depth, &imageInfo))
    2196             :     {
    2197           0 :         return;
    2198             :     }
    2199           0 :     MOZ_ASSERT(imageInfo);
    2200             : 
    2201           0 :     auto dstUsage = imageInfo->mFormat;
    2202           0 :     MOZ_ASSERT(dstUsage);
    2203             : 
    2204           0 :     auto dstFormat = dstUsage->format;
    2205           0 :     if (!mContext->IsWebGL2() && dstFormat->d) {
    2206           0 :         mContext->ErrorInvalidOperation("%s: Function may not be called on a texture of"
    2207             :                                         " format %s.",
    2208           0 :                                         funcName, dstFormat->name);
    2209           0 :         return;
    2210             :     }
    2211             : 
    2212             :     ////////////////////////////////////
    2213             :     // Get source info
    2214             : 
    2215             :     const webgl::FormatUsageInfo* srcUsage;
    2216             :     uint32_t srcTotalWidth;
    2217             :     uint32_t srcTotalHeight;
    2218           0 :     if (!mContext->ValidateCurFBForRead(funcName, &srcUsage, &srcTotalWidth,
    2219             :                                         &srcTotalHeight))
    2220             :     {
    2221           0 :         return;
    2222             :     }
    2223             : 
    2224           0 :     if (!ValidateCopyTexImageForFeedback(funcName, level, zOffset))
    2225           0 :         return;
    2226             : 
    2227             :     ////////////////////////////////////
    2228             :     // Check that source and dest info are compatible
    2229             : 
    2230           0 :     auto srcFormat = srcUsage->format;
    2231           0 :     if (!ValidateCopyTexImageFormats(mContext, funcName, srcFormat, dstFormat))
    2232           0 :         return;
    2233             : 
    2234             :     ////////////////////////////////////
    2235             :     // Do the thing!
    2236             : 
    2237           0 :     mContext->gl->MakeCurrent();
    2238           0 :     mContext->OnBeforeReadCall();
    2239             : 
    2240             :     bool uploadWillInitialize;
    2241           0 :     if (!EnsureImageDataInitializedForUpload(this, funcName, target, level, xOffset,
    2242             :                                              yOffset, zOffset, width, height, depth,
    2243             :                                              imageInfo, &uploadWillInitialize))
    2244             :     {
    2245           0 :         return;
    2246             :     }
    2247             : 
    2248           0 :     const bool isSubImage = true;
    2249           0 :     if (!DoCopyTexOrSubImage(mContext, funcName, isSubImage, this, target, level, x, y,
    2250             :                              srcTotalWidth, srcTotalHeight, srcUsage, xOffset, yOffset,
    2251             :                              zOffset, width, height, dstUsage))
    2252             :     {
    2253           0 :         return;
    2254             :     }
    2255             : 
    2256             :     ////////////////////////////////////
    2257             :     // Update our specification data?
    2258             : 
    2259           0 :     if (uploadWillInitialize) {
    2260           0 :         imageInfo->SetIsDataInitialized(true, this);
    2261             :     }
    2262             : }
    2263             : 
    2264             : } // namespace mozilla

Generated by: LCOV version 1.13