LCOV - code coverage report
Current view: top level - dom/canvas - TexUnpackBlob.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 414 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 25 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 "TexUnpackBlob.h"
       7             : 
       8             : #include "GLBlitHelper.h"
       9             : #include "GLContext.h"
      10             : #include "mozilla/dom/Element.h"
      11             : #include "mozilla/dom/HTMLCanvasElement.h"
      12             : #include "mozilla/RefPtr.h"
      13             : #include "nsLayoutUtils.h"
      14             : #include "WebGLBuffer.h"
      15             : #include "WebGLContext.h"
      16             : #include "WebGLTexelConversions.h"
      17             : #include "WebGLTexture.h"
      18             : 
      19             : namespace mozilla {
      20             : namespace webgl {
      21             : 
      22             : static bool
      23           0 : IsPIValidForDOM(const webgl::PackingInfo& pi)
      24             : {
      25             :     // https://www.khronos.org/registry/webgl/specs/latest/2.0/#TEXTURE_TYPES_FORMATS_FROM_DOM_ELEMENTS_TABLE
      26             : 
      27             :     // Just check for invalid individual formats and types, not combinations.
      28           0 :     switch (pi.format) {
      29             :     case LOCAL_GL_RGB:
      30             :     case LOCAL_GL_RGBA:
      31             :     case LOCAL_GL_LUMINANCE_ALPHA:
      32             :     case LOCAL_GL_LUMINANCE:
      33             :     case LOCAL_GL_ALPHA:
      34             :     case LOCAL_GL_RED:
      35             :     case LOCAL_GL_RED_INTEGER:
      36             :     case LOCAL_GL_RG:
      37             :     case LOCAL_GL_RG_INTEGER:
      38             :     case LOCAL_GL_RGB_INTEGER:
      39             :     case LOCAL_GL_RGBA_INTEGER:
      40           0 :         break;
      41             : 
      42             :     case LOCAL_GL_SRGB:
      43             :     case LOCAL_GL_SRGB_ALPHA:
      44             :         // Allowed in WebGL1+EXT_srgb
      45           0 :         break;
      46             : 
      47             :     default:
      48           0 :         return false;
      49             :     }
      50             : 
      51           0 :     switch (pi.type) {
      52             :     case LOCAL_GL_UNSIGNED_BYTE:
      53             :     case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
      54             :     case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
      55             :     case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
      56             :     case LOCAL_GL_HALF_FLOAT:
      57             :     case LOCAL_GL_HALF_FLOAT_OES:
      58             :     case LOCAL_GL_FLOAT:
      59             :     case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV:
      60           0 :         break;
      61             : 
      62             :     default:
      63           0 :         return false;
      64             :     }
      65             : 
      66           0 :     return true;
      67             : }
      68             : 
      69             : static bool
      70           0 : ValidatePIForDOM(WebGLContext* webgl, const char* funcName,
      71             :                  const webgl::PackingInfo& pi)
      72             : {
      73           0 :     if (!IsPIValidForDOM(pi)) {
      74             :         webgl->ErrorInvalidOperation("%s: Format or type is invalid for DOM sources.",
      75           0 :                                      funcName);
      76           0 :         return false;
      77             :     }
      78           0 :     return true;
      79             : }
      80             : 
      81             : static WebGLTexelFormat
      82           0 : FormatForPackingInfo(const PackingInfo& pi)
      83             : {
      84           0 :     switch (pi.type) {
      85             :     case LOCAL_GL_UNSIGNED_BYTE:
      86           0 :         switch (pi.format) {
      87             :         case LOCAL_GL_RED:
      88             :         case LOCAL_GL_LUMINANCE:
      89             :         case LOCAL_GL_RED_INTEGER:
      90           0 :             return WebGLTexelFormat::R8;
      91             : 
      92             :         case LOCAL_GL_ALPHA:
      93           0 :             return WebGLTexelFormat::A8;
      94             : 
      95             :         case LOCAL_GL_LUMINANCE_ALPHA:
      96           0 :             return WebGLTexelFormat::RA8;
      97             : 
      98             :         case LOCAL_GL_RGB:
      99             :         case LOCAL_GL_RGB_INTEGER:
     100             :         case LOCAL_GL_SRGB:
     101           0 :             return WebGLTexelFormat::RGB8;
     102             : 
     103             :         case LOCAL_GL_RGBA:
     104             :         case LOCAL_GL_RGBA_INTEGER:
     105             :         case LOCAL_GL_SRGB_ALPHA:
     106           0 :             return WebGLTexelFormat::RGBA8;
     107             : 
     108             :         case LOCAL_GL_RG:
     109             :         case LOCAL_GL_RG_INTEGER:
     110           0 :             return WebGLTexelFormat::RG8;
     111             : 
     112             :         default:
     113           0 :             break;
     114             :         }
     115           0 :         break;
     116             : 
     117             :     case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
     118           0 :         if (pi.format == LOCAL_GL_RGB)
     119           0 :             return WebGLTexelFormat::RGB565;
     120           0 :         break;
     121             : 
     122             :     case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
     123           0 :         if (pi.format == LOCAL_GL_RGBA)
     124           0 :             return WebGLTexelFormat::RGBA5551;
     125           0 :         break;
     126             : 
     127             :     case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
     128           0 :         if (pi.format == LOCAL_GL_RGBA)
     129           0 :             return WebGLTexelFormat::RGBA4444;
     130           0 :         break;
     131             : 
     132             :     case LOCAL_GL_HALF_FLOAT:
     133             :     case LOCAL_GL_HALF_FLOAT_OES:
     134           0 :         switch (pi.format) {
     135             :         case LOCAL_GL_RED:
     136             :         case LOCAL_GL_LUMINANCE:
     137           0 :             return WebGLTexelFormat::R16F;
     138             : 
     139           0 :         case LOCAL_GL_ALPHA:           return WebGLTexelFormat::A16F;
     140           0 :         case LOCAL_GL_LUMINANCE_ALPHA: return WebGLTexelFormat::RA16F;
     141           0 :         case LOCAL_GL_RG:              return WebGLTexelFormat::RG16F;
     142           0 :         case LOCAL_GL_RGB:             return WebGLTexelFormat::RGB16F;
     143           0 :         case LOCAL_GL_RGBA:            return WebGLTexelFormat::RGBA16F;
     144             : 
     145             :         default:
     146           0 :             break;
     147             :         }
     148           0 :         break;
     149             : 
     150             :     case LOCAL_GL_FLOAT:
     151           0 :         switch (pi.format) {
     152             :         case LOCAL_GL_RED:
     153             :         case LOCAL_GL_LUMINANCE:
     154           0 :             return WebGLTexelFormat::R32F;
     155             : 
     156           0 :         case LOCAL_GL_ALPHA:           return WebGLTexelFormat::A32F;
     157           0 :         case LOCAL_GL_LUMINANCE_ALPHA: return WebGLTexelFormat::RA32F;
     158           0 :         case LOCAL_GL_RG:              return WebGLTexelFormat::RG32F;
     159           0 :         case LOCAL_GL_RGB:             return WebGLTexelFormat::RGB32F;
     160           0 :         case LOCAL_GL_RGBA:            return WebGLTexelFormat::RGBA32F;
     161             : 
     162             :         default:
     163           0 :             break;
     164             :         }
     165           0 :         break;
     166             : 
     167             :     case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV:
     168           0 :         if (pi.format == LOCAL_GL_RGB)
     169           0 :             return WebGLTexelFormat::RGB11F11F10F;
     170           0 :         break;
     171             : 
     172             :     default:
     173           0 :         break;
     174             :     }
     175             : 
     176           0 :     return WebGLTexelFormat::FormatNotSupportingAnyConversion;
     177             : }
     178             : 
     179             : ////////////////////
     180             : 
     181             : static bool
     182           0 : ValidateUnpackPixels(WebGLContext* webgl, const char* funcName, uint32_t fullRows,
     183             :                      uint32_t tailPixels, webgl::TexUnpackBlob* blob)
     184             : {
     185           0 :     if (!blob->mWidth || !blob->mHeight || !blob->mDepth)
     186           0 :         return true;
     187             : 
     188           0 :     const auto usedPixelsPerRow = CheckedUint32(blob->mSkipPixels) + blob->mWidth;
     189           0 :     if (!usedPixelsPerRow.isValid() || usedPixelsPerRow.value() > blob->mRowLength) {
     190             :         webgl->ErrorInvalidOperation("%s: UNPACK_SKIP_PIXELS + width >"
     191             :                                      " UNPACK_ROW_LENGTH.",
     192           0 :                                      funcName);
     193           0 :         return false;
     194             :     }
     195             : 
     196           0 :     if (blob->mHeight > blob->mImageHeight) {
     197           0 :         webgl->ErrorInvalidOperation("%s: height > UNPACK_IMAGE_HEIGHT.", funcName);
     198           0 :         return false;
     199             :     }
     200             : 
     201             :     //////
     202             : 
     203             :     // The spec doesn't bound SKIP_ROWS + height <= IMAGE_HEIGHT, unfortunately.
     204           0 :     auto skipFullRows = CheckedUint32(blob->mSkipImages) * blob->mImageHeight;
     205           0 :     skipFullRows += blob->mSkipRows;
     206             : 
     207           0 :     MOZ_ASSERT(blob->mDepth >= 1);
     208           0 :     MOZ_ASSERT(blob->mHeight >= 1);
     209           0 :     auto usedFullRows = CheckedUint32(blob->mDepth - 1) * blob->mImageHeight;
     210           0 :     usedFullRows += blob->mHeight - 1; // Full rows in the final image, excluding the tail.
     211             : 
     212           0 :     const auto fullRowsNeeded = skipFullRows + usedFullRows;
     213           0 :     if (!fullRowsNeeded.isValid()) {
     214             :         webgl->ErrorOutOfMemory("%s: Invalid calculation for required row count.",
     215           0 :                                 funcName);
     216           0 :         return false;
     217             :     }
     218             : 
     219           0 :     if (fullRows > fullRowsNeeded.value())
     220           0 :         return true;
     221             : 
     222           0 :     if (fullRows == fullRowsNeeded.value() && tailPixels >= usedPixelsPerRow.value()) {
     223           0 :         blob->mNeedsExactUpload = true;
     224           0 :         return true;
     225             :     }
     226             : 
     227           0 :     webgl->ErrorInvalidOperation("%s: Desired upload requires more data than is"
     228             :                                  " available: (%u rows plus %u pixels needed, %u rows"
     229             :                                  " plus %u pixels available)",
     230             :                                  funcName, fullRowsNeeded.value(),
     231           0 :                                  usedPixelsPerRow.value(), fullRows, tailPixels);
     232           0 :     return false;
     233             : }
     234             : 
     235             : static bool
     236           0 : ValidateUnpackBytes(WebGLContext* webgl, const char* funcName,
     237             :                     const webgl::PackingInfo& pi, size_t availByteCount,
     238             :                     webgl::TexUnpackBlob* blob)
     239             : {
     240           0 :     if (!blob->mWidth || !blob->mHeight || !blob->mDepth)
     241           0 :         return true;
     242             : 
     243           0 :     const auto bytesPerPixel = webgl::BytesPerPixel(pi);
     244           0 :     const auto bytesPerRow = CheckedUint32(blob->mRowLength) * bytesPerPixel;
     245           0 :     const auto rowStride = RoundUpToMultipleOf(bytesPerRow, blob->mAlignment);
     246             : 
     247           0 :     const auto fullRows = availByteCount / rowStride;
     248           0 :     if (!fullRows.isValid()) {
     249           0 :         webgl->ErrorOutOfMemory("%s: Unacceptable upload size calculated.", funcName);
     250           0 :         return false;
     251             :     }
     252             : 
     253           0 :     const auto bodyBytes = fullRows.value() * rowStride.value();
     254           0 :     const auto tailPixels = (availByteCount - bodyBytes) / bytesPerPixel;
     255             : 
     256           0 :     return ValidateUnpackPixels(webgl, funcName, fullRows.value(), tailPixels, blob);
     257             : }
     258             : 
     259             : ////////////////////
     260             : 
     261             : static uint32_t
     262           0 : ZeroOn2D(TexImageTarget target, uint32_t val)
     263             : {
     264           0 :     return (IsTarget3D(target) ? val : 0);
     265             : }
     266             : 
     267             : static uint32_t
     268           0 : FallbackOnZero(uint32_t val, uint32_t fallback)
     269             : {
     270           0 :     return (val ? val : fallback);
     271             : }
     272             : 
     273           0 : TexUnpackBlob::TexUnpackBlob(const WebGLContext* webgl, TexImageTarget target,
     274             :                              uint32_t rowLength, uint32_t width, uint32_t height,
     275           0 :                              uint32_t depth, gfxAlphaType srcAlphaType)
     276           0 :     : mAlignment(webgl->mPixelStore_UnpackAlignment)
     277             :     , mRowLength(rowLength)
     278           0 :     , mImageHeight(FallbackOnZero(ZeroOn2D(target, webgl->mPixelStore_UnpackImageHeight),
     279             :                                   height))
     280             : 
     281           0 :     , mSkipPixels(webgl->mPixelStore_UnpackSkipPixels)
     282           0 :     , mSkipRows(webgl->mPixelStore_UnpackSkipRows)
     283           0 :     , mSkipImages(ZeroOn2D(target, webgl->mPixelStore_UnpackSkipImages))
     284             : 
     285             :     , mWidth(width)
     286             :     , mHeight(height)
     287             :     , mDepth(depth)
     288             : 
     289             :     , mSrcAlphaType(srcAlphaType)
     290             : 
     291           0 :     , mNeedsExactUpload(false)
     292             : {
     293           0 :     MOZ_ASSERT_IF(!IsTarget3D(target), mDepth == 1);
     294           0 : }
     295             : 
     296             : static bool
     297           0 : HasColorAndAlpha(const WebGLTexelFormat format)
     298             : {
     299           0 :     switch (format) {
     300             :     case WebGLTexelFormat::RA8:
     301             :     case WebGLTexelFormat::RA16F:
     302             :     case WebGLTexelFormat::RA32F:
     303             :     case WebGLTexelFormat::RGBA8:
     304             :     case WebGLTexelFormat::RGBA5551:
     305             :     case WebGLTexelFormat::RGBA4444:
     306             :     case WebGLTexelFormat::RGBA16F:
     307             :     case WebGLTexelFormat::RGBA32F:
     308             :     case WebGLTexelFormat::BGRA8:
     309           0 :         return true;
     310             :     default:
     311           0 :         return false;
     312             :     }
     313             : }
     314             : 
     315             : bool
     316           0 : TexUnpackBlob::ConvertIfNeeded(WebGLContext* webgl, const char* funcName,
     317             :                                const uint32_t rowLength, const uint32_t rowCount,
     318             :                                WebGLTexelFormat srcFormat,
     319             :                                const uint8_t* const srcBegin, const ptrdiff_t srcStride,
     320             :                                WebGLTexelFormat dstFormat, const ptrdiff_t dstStride,
     321             :                                const uint8_t** const out_begin,
     322             :                                UniqueBuffer* const out_anchoredBuffer) const
     323             : {
     324           0 :     MOZ_ASSERT(srcFormat != WebGLTexelFormat::FormatNotSupportingAnyConversion);
     325           0 :     MOZ_ASSERT(dstFormat != WebGLTexelFormat::FormatNotSupportingAnyConversion);
     326             : 
     327           0 :     *out_begin = srcBegin;
     328             : 
     329           0 :     if (!rowLength || !rowCount)
     330           0 :         return true;
     331             : 
     332           0 :     const auto srcIsPremult = (mSrcAlphaType == gfxAlphaType::Premult);
     333           0 :     const auto& dstIsPremult = webgl->mPixelStore_PremultiplyAlpha;
     334           0 :     const auto fnHasPremultMismatch = [&]() {
     335           0 :         if (mSrcAlphaType == gfxAlphaType::Opaque)
     336           0 :             return false;
     337             : 
     338           0 :         if (!HasColorAndAlpha(srcFormat))
     339           0 :             return false;
     340             : 
     341           0 :         return srcIsPremult != dstIsPremult;
     342           0 :     };
     343             : 
     344           0 :     const auto srcOrigin = (webgl->mPixelStore_FlipY ? gl::OriginPos::TopLeft
     345           0 :                                                      : gl::OriginPos::BottomLeft);
     346           0 :     const auto dstOrigin = gl::OriginPos::BottomLeft;
     347             : 
     348           0 :     if (srcFormat != dstFormat) {
     349           0 :         webgl->GeneratePerfWarning("%s: Conversion requires pixel reformatting. (%u->%u)",
     350             :                                    funcName, uint32_t(srcFormat),
     351           0 :                                    uint32_t(dstFormat));
     352           0 :     } else if (fnHasPremultMismatch()) {
     353             :         webgl->GeneratePerfWarning("%s: Conversion requires change in"
     354             :                                    " alpha-premultiplication.",
     355           0 :                                    funcName);
     356           0 :     } else if (srcOrigin != dstOrigin) {
     357           0 :         webgl->GeneratePerfWarning("%s: Conversion requires y-flip.", funcName);
     358           0 :     } else if (srcStride != dstStride) {
     359           0 :         webgl->GeneratePerfWarning("%s: Conversion requires change in stride. (%u->%u)",
     360           0 :                                    funcName, uint32_t(srcStride), uint32_t(dstStride));
     361             :     } else {
     362           0 :         return true;
     363             :     }
     364             : 
     365             :     ////
     366             : 
     367           0 :     const auto dstTotalBytes = CheckedUint32(rowCount) * dstStride;
     368           0 :     if (!dstTotalBytes.isValid()) {
     369           0 :         webgl->ErrorOutOfMemory("%s: Calculation failed.", funcName);
     370           0 :         return false;
     371             :     }
     372             : 
     373           0 :     UniqueBuffer dstBuffer = calloc(1, dstTotalBytes.value());
     374           0 :     if (!dstBuffer.get()) {
     375           0 :         webgl->ErrorOutOfMemory("%s: Failed to allocate dest buffer.", funcName);
     376           0 :         return false;
     377             :     }
     378           0 :     const auto dstBegin = static_cast<uint8_t*>(dstBuffer.get());
     379             : 
     380             :     ////
     381             : 
     382             :     // And go!:
     383             :     bool wasTrivial;
     384           0 :     if (!ConvertImage(rowLength, rowCount,
     385             :                       srcBegin, srcStride, srcOrigin, srcFormat, srcIsPremult,
     386           0 :                       dstBegin, dstStride, dstOrigin, dstFormat, dstIsPremult,
     387             :                       &wasTrivial))
     388             :     {
     389           0 :         webgl->ErrorImplementationBug("%s: ConvertImage failed.", funcName);
     390           0 :         return false;
     391             :     }
     392             : 
     393           0 :     *out_begin = dstBegin;
     394           0 :     *out_anchoredBuffer = Move(dstBuffer);
     395           0 :     return true;
     396             : }
     397             : 
     398             : static GLenum
     399           0 : DoTexOrSubImage(bool isSubImage, gl::GLContext* gl, TexImageTarget target, GLint level,
     400             :                 const DriverUnpackInfo* dui, GLint xOffset, GLint yOffset, GLint zOffset,
     401             :                 GLsizei width, GLsizei height, GLsizei depth, const void* data)
     402             : {
     403           0 :     if (isSubImage) {
     404             :         return DoTexSubImage(gl, target, level, xOffset, yOffset, zOffset, width, height,
     405           0 :                              depth, dui->ToPacking(), data);
     406             :     } else {
     407           0 :         return DoTexImage(gl, target, level, dui, width, height, depth, data);
     408             :     }
     409             : }
     410             : 
     411             : //////////////////////////////////////////////////////////////////////////////////////////
     412             : // TexUnpackBytes
     413             : 
     414           0 : TexUnpackBytes::TexUnpackBytes(const WebGLContext* webgl, TexImageTarget target,
     415             :                                uint32_t width, uint32_t height, uint32_t depth,
     416           0 :                                bool isClientData, const uint8_t* ptr, size_t availBytes)
     417             :     : TexUnpackBlob(webgl, target,
     418           0 :                     FallbackOnZero(webgl->mPixelStore_UnpackRowLength, width),
     419             :                     width, height, depth, gfxAlphaType::NonPremult)
     420             :     , mIsClientData(isClientData)
     421             :     , mPtr(ptr)
     422           0 :     , mAvailBytes(availBytes)
     423           0 : { }
     424             : 
     425             : bool
     426           0 : TexUnpackBytes::Validate(WebGLContext* webgl, const char* funcName,
     427             :                          const webgl::PackingInfo& pi)
     428             : {
     429           0 :     if (mIsClientData && !mPtr)
     430           0 :         return true;
     431             : 
     432           0 :     return ValidateUnpackBytes(webgl, funcName, pi, mAvailBytes, this);
     433             : }
     434             : 
     435             : bool
     436           0 : TexUnpackBytes::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
     437             :                               WebGLTexture* tex, TexImageTarget target, GLint level,
     438             :                               const webgl::DriverUnpackInfo* dui, GLint xOffset,
     439             :                               GLint yOffset, GLint zOffset, const webgl::PackingInfo& pi,
     440             :                               GLenum* const out_error) const
     441             : {
     442           0 :     WebGLContext* webgl = tex->mContext;
     443             : 
     444           0 :     const auto format = FormatForPackingInfo(pi);
     445           0 :     const auto bytesPerPixel = webgl::BytesPerPixel(pi);
     446             : 
     447           0 :     const uint8_t* uploadPtr = mPtr;
     448           0 :     UniqueBuffer tempBuffer;
     449             : 
     450             :     do {
     451           0 :         if (!mIsClientData || !mPtr)
     452             :             break;
     453             : 
     454           0 :         if (!webgl->mPixelStore_FlipY &&
     455           0 :             !webgl->mPixelStore_PremultiplyAlpha)
     456             :         {
     457           0 :             break;
     458             :         }
     459             : 
     460           0 :         if (webgl->mPixelStore_UnpackImageHeight ||
     461           0 :             webgl->mPixelStore_UnpackSkipImages ||
     462           0 :             webgl->mPixelStore_UnpackRowLength ||
     463           0 :             webgl->mPixelStore_UnpackSkipRows ||
     464           0 :             webgl->mPixelStore_UnpackSkipPixels)
     465             :         {
     466             :             webgl->ErrorInvalidOperation("%s: Non-DOM-Element uploads with alpha-premult"
     467             :                                          " or y-flip do not support subrect selection.",
     468           0 :                                          funcName);
     469           0 :             return false;
     470             :         }
     471             : 
     472             :         webgl->GenerateWarning("%s: Alpha-premult and y-flip are deprecated for"
     473             :                                " non-DOM-Element uploads.",
     474           0 :                                funcName);
     475             : 
     476           0 :         const uint32_t rowLength = mWidth;
     477           0 :         const uint32_t rowCount = mHeight * mDepth;
     478           0 :         const auto stride = RoundUpToMultipleOf(rowLength * bytesPerPixel, mAlignment);
     479           0 :         if (!ConvertIfNeeded(webgl, funcName, rowLength, rowCount, format, mPtr, stride,
     480             :                              format, stride, &uploadPtr, &tempBuffer))
     481             :         {
     482           0 :             return false;
     483             :         }
     484             :     } while (false);
     485             : 
     486             :     //////
     487             : 
     488           0 :     const auto& gl = webgl->gl;
     489             : 
     490           0 :     bool useParanoidHandling = false;
     491           0 :     if (mNeedsExactUpload && webgl->mBoundPixelUnpackBuffer) {
     492             :         webgl->GenerateWarning("%s: Uploads from a buffer with a final row with a byte"
     493             :                                " count smaller than the row stride can incur extra"
     494             :                                " overhead.",
     495           0 :                                funcName);
     496             : 
     497           0 :         if (gl->WorkAroundDriverBugs()) {
     498           0 :             useParanoidHandling |= (gl->Vendor() == gl::GLVendor::NVIDIA);
     499             :         }
     500             :     }
     501             : 
     502           0 :     if (!useParanoidHandling) {
     503           0 :         if (webgl->mBoundPixelUnpackBuffer) {
     504           0 :             gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER,
     505           0 :                             webgl->mBoundPixelUnpackBuffer->mGLName);
     506             :         }
     507             : 
     508           0 :         *out_error = DoTexOrSubImage(isSubImage, gl, target, level, dui, xOffset, yOffset,
     509           0 :                                      zOffset, mWidth, mHeight, mDepth, uploadPtr);
     510             : 
     511           0 :         if (webgl->mBoundPixelUnpackBuffer) {
     512           0 :             gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0);
     513             :         }
     514           0 :         return true;
     515             :     }
     516             : 
     517             :     //////
     518             : 
     519           0 :     MOZ_ASSERT(webgl->mBoundPixelUnpackBuffer);
     520             : 
     521           0 :     if (!isSubImage) {
     522             :         // Alloc first to catch OOMs.
     523           0 :         AssertUintParamCorrect(gl, LOCAL_GL_PIXEL_UNPACK_BUFFER, 0);
     524           0 :         *out_error = DoTexOrSubImage(false, gl, target, level, dui, xOffset, yOffset,
     525           0 :                                      zOffset, mWidth, mHeight, mDepth, nullptr);
     526           0 :         if (*out_error)
     527           0 :             return true;
     528             :     }
     529             : 
     530             :     const ScopedLazyBind bindPBO(gl, LOCAL_GL_PIXEL_UNPACK_BUFFER,
     531           0 :                                  webgl->mBoundPixelUnpackBuffer);
     532             : 
     533             :     //////
     534             : 
     535             :     // Make our sometimes-implicit values explicit. Also this keeps them constant when we
     536             :     // ask for height=mHeight-1 and such.
     537           0 :     gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, mRowLength);
     538           0 :     gl->fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT, mImageHeight);
     539             : 
     540           0 :     if (mDepth > 1) {
     541           0 :         *out_error = DoTexOrSubImage(true, gl, target, level, dui, xOffset, yOffset,
     542           0 :                                      zOffset, mWidth, mHeight, mDepth-1, uploadPtr);
     543             :     }
     544             : 
     545             :     // Skip the images we uploaded.
     546           0 :     gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES, mSkipImages + mDepth - 1);
     547             : 
     548           0 :     if (mHeight > 1) {
     549           0 :         *out_error = DoTexOrSubImage(true, gl, target, level, dui, xOffset, yOffset,
     550           0 :                                      zOffset+mDepth-1, mWidth, mHeight-1, 1, uploadPtr);
     551             :     }
     552             : 
     553           0 :     const auto totalSkipRows = CheckedUint32(mSkipImages) * mImageHeight + mSkipRows;
     554           0 :     const auto totalFullRows = CheckedUint32(mDepth - 1) * mImageHeight + mHeight - 1;
     555           0 :     const auto tailOffsetRows = totalSkipRows + totalFullRows;
     556             : 
     557           0 :     const auto bytesPerRow = CheckedUint32(mRowLength) * bytesPerPixel;
     558           0 :     const auto rowStride = RoundUpToMultipleOf(bytesPerRow, mAlignment);
     559           0 :     if (!rowStride.isValid()) {
     560           0 :         MOZ_CRASH("Should be checked earlier.");
     561             :     }
     562           0 :     const auto tailOffsetBytes = tailOffsetRows * rowStride;
     563             : 
     564           0 :     uploadPtr += tailOffsetBytes.value();
     565             : 
     566             :     //////
     567             : 
     568           0 :     gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1);   // No stride padding.
     569           0 :     gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0);  // No padding in general.
     570           0 :     gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES, 0); // Don't skip images,
     571           0 :     gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS, 0);   // or rows.
     572             :                                                       // Keep skipping pixels though!
     573             : 
     574           0 :     *out_error = DoTexOrSubImage(true, gl, target, level, dui, xOffset,
     575           0 :                                  yOffset+mHeight-1, zOffset+mDepth-1, mWidth, 1, 1,
     576             :                                  uploadPtr);
     577             : 
     578             :     // Reset all our modified state.
     579           0 :     gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, webgl->mPixelStore_UnpackAlignment);
     580           0 :     gl->fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT, webgl->mPixelStore_UnpackImageHeight);
     581           0 :     gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, webgl->mPixelStore_UnpackRowLength);
     582           0 :     gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES, webgl->mPixelStore_UnpackSkipImages);
     583           0 :     gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS, webgl->mPixelStore_UnpackSkipRows);
     584             : 
     585           0 :     return true;
     586             : }
     587             : 
     588             : ////////////////////////////////////////////////////////////////////////////////
     589             : ////////////////////////////////////////////////////////////////////////////////
     590             : // TexUnpackImage
     591             : 
     592           0 : TexUnpackImage::TexUnpackImage(const WebGLContext* webgl, TexImageTarget target,
     593             :                                uint32_t width, uint32_t height, uint32_t depth,
     594           0 :                                layers::Image* image, gfxAlphaType srcAlphaType)
     595           0 :     : TexUnpackBlob(webgl, target, image->GetSize().width, width, height, depth,
     596             :                     srcAlphaType)
     597           0 :     , mImage(image)
     598           0 : { }
     599             : 
     600           0 : TexUnpackImage::~TexUnpackImage()
     601           0 : { }
     602             : 
     603             : bool
     604           0 : TexUnpackImage::Validate(WebGLContext* webgl, const char* funcName,
     605             :                          const webgl::PackingInfo& pi)
     606             : {
     607           0 :     if (!ValidatePIForDOM(webgl, funcName, pi))
     608           0 :         return false;
     609             : 
     610           0 :     const auto fullRows = mImage->GetSize().height;
     611           0 :     return ValidateUnpackPixels(webgl, funcName, fullRows, 0, this);
     612             : }
     613             : 
     614             : bool
     615           0 : TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
     616             :                               WebGLTexture* tex, TexImageTarget target, GLint level,
     617             :                               const webgl::DriverUnpackInfo* dui, GLint xOffset,
     618             :                               GLint yOffset, GLint zOffset, const webgl::PackingInfo& pi,
     619             :                               GLenum* const out_error) const
     620             : {
     621           0 :     MOZ_ASSERT_IF(needsRespec, !isSubImage);
     622             : 
     623           0 :     WebGLContext* webgl = tex->mContext;
     624             : 
     625           0 :     gl::GLContext* gl = webgl->GL();
     626           0 :     gl->MakeCurrent();
     627             : 
     628           0 :     if (needsRespec) {
     629           0 :         *out_error = DoTexOrSubImage(isSubImage, gl, target.get(), level, dui, xOffset,
     630           0 :                                      yOffset, zOffset, mWidth, mHeight, mDepth,
     631             :                                      nullptr);
     632           0 :         if (*out_error)
     633           0 :             return true;
     634             :     }
     635             : 
     636             :     const char* fallbackReason;
     637             :     do {
     638           0 :         if (mDepth != 1) {
     639           0 :             fallbackReason = "depth is not 1";
     640           0 :             break;
     641             :         }
     642             : 
     643           0 :         if (webgl->mPixelStore_UnpackSkipPixels ||
     644           0 :             webgl->mPixelStore_UnpackSkipRows ||
     645           0 :             webgl->mPixelStore_UnpackSkipImages)
     646             :         {
     647           0 :             fallbackReason = "non-zero UNPACK_SKIP_* not yet supported";
     648           0 :             break;
     649             :         }
     650             : 
     651           0 :         const auto fnHasPremultMismatch = [&]() {
     652           0 :             if (mSrcAlphaType == gfxAlphaType::Opaque)
     653           0 :                 return false;
     654             : 
     655           0 :             const bool srcIsPremult = (mSrcAlphaType == gfxAlphaType::Premult);
     656           0 :             const auto& dstIsPremult = webgl->mPixelStore_PremultiplyAlpha;
     657           0 :             if (srcIsPremult == dstIsPremult)
     658           0 :                 return false;
     659             : 
     660           0 :             if (dstIsPremult) {
     661           0 :                 fallbackReason = "UNPACK_PREMULTIPLY_ALPHA_WEBGL is not true";
     662             :             } else {
     663           0 :                 fallbackReason = "UNPACK_PREMULTIPLY_ALPHA_WEBGL is not false";
     664             :             }
     665           0 :             return true;
     666           0 :         };
     667           0 :         if (fnHasPremultMismatch())
     668           0 :             break;
     669             : 
     670           0 :         if (dui->unpackFormat != LOCAL_GL_RGB && dui->unpackFormat != LOCAL_GL_RGBA) {
     671           0 :             fallbackReason = "`format` is not RGB or RGBA";
     672           0 :             break;
     673             :         }
     674             : 
     675           0 :         if (dui->unpackType != LOCAL_GL_UNSIGNED_BYTE) {
     676           0 :             fallbackReason = "`type` is not UNSIGNED_BYTE";
     677           0 :             break;
     678             :         }
     679             : 
     680           0 :         gl::ScopedFramebuffer scopedFB(gl);
     681           0 :         gl::ScopedBindFramebuffer bindFB(gl, scopedFB.FB());
     682             : 
     683             :         {
     684           0 :             gl::GLContext::LocalErrorScope errorScope(*gl);
     685             : 
     686           0 :             gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
     687           0 :                                       target.get(), tex->mGLName, level);
     688             : 
     689           0 :             if (errorScope.GetError()) {
     690           0 :                 fallbackReason = "bug: failed to attach to FB for blit";
     691           0 :                 break;
     692             :             }
     693             :         }
     694             : 
     695           0 :         const GLenum status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
     696           0 :         if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
     697           0 :             fallbackReason = "bug: failed to confirm FB for blit";
     698           0 :             break;
     699             :         }
     700             : 
     701           0 :         const gfx::IntSize destSize(mWidth, mHeight);
     702           0 :         const auto dstOrigin = (webgl->mPixelStore_FlipY ? gl::OriginPos::TopLeft
     703           0 :                                                          : gl::OriginPos::BottomLeft);
     704           0 :         if (!gl->BlitHelper()->BlitImageToFramebuffer(mImage, destSize, scopedFB.FB(),
     705             :                                                       dstOrigin))
     706             :         {
     707           0 :             fallbackReason = "likely bug: failed to blit";
     708           0 :             break;
     709             :         }
     710             : 
     711             :         // Blitting was successful, so we're done!
     712           0 :         *out_error = 0;
     713           0 :         return true;
     714             :     } while (false);
     715             : 
     716             :     const nsPrintfCString perfMsg("%s: Failed to hit GPU-copy fast-path: %s (src type %u)",
     717           0 :                                   funcName, fallbackReason, uint32_t(mImage->GetFormat()));
     718             : 
     719           0 :     if (webgl->mPixelStore_RequireFastPath) {
     720           0 :         webgl->ErrorInvalidOperation("%s", perfMsg.BeginReading());
     721           0 :         return false;
     722             :     }
     723             : 
     724           0 :     webgl->GeneratePerfWarning("%s Falling back to CPU upload.",
     725           0 :                                perfMsg.BeginReading());
     726             : 
     727           0 :     const RefPtr<gfx::SourceSurface> surf = mImage->GetAsSourceSurface();
     728             : 
     729           0 :     RefPtr<gfx::DataSourceSurface> dataSurf;
     730           0 :     if (surf) {
     731             :         // WARNING: OSX can lose our MakeCurrent here.
     732           0 :         dataSurf = surf->GetDataSurface();
     733             :     }
     734           0 :     if (!dataSurf) {
     735           0 :         webgl->ErrorOutOfMemory("%s: GetAsSourceSurface or GetDataSurface failed after"
     736             :                                 " blit failed for TexUnpackImage.",
     737           0 :                                 funcName);
     738           0 :         return false;
     739             :     }
     740             : 
     741           0 :     const TexUnpackSurface surfBlob(webgl, target, mWidth, mHeight, mDepth, dataSurf,
     742           0 :                                     mSrcAlphaType);
     743             : 
     744           0 :     return surfBlob.TexOrSubImage(isSubImage, needsRespec, funcName, tex, target, level,
     745           0 :                                   dui, xOffset, yOffset, zOffset, pi, out_error);
     746             : }
     747             : 
     748             : ////////////////////////////////////////////////////////////////////////////////
     749             : ////////////////////////////////////////////////////////////////////////////////
     750             : // TexUnpackSurface
     751             : 
     752           0 : TexUnpackSurface::TexUnpackSurface(const WebGLContext* webgl, TexImageTarget target,
     753             :                                    uint32_t width, uint32_t height, uint32_t depth,
     754             :                                    gfx::DataSourceSurface* surf,
     755           0 :                                    gfxAlphaType srcAlphaType)
     756           0 :     : TexUnpackBlob(webgl, target, surf->GetSize().width, width, height, depth,
     757             :                     srcAlphaType)
     758           0 :     , mSurf(surf)
     759           0 : { }
     760             : 
     761             : //////////
     762             : 
     763             : static bool
     764           0 : GetFormatForSurf(gfx::SourceSurface* surf, WebGLTexelFormat* const out_texelFormat,
     765             :                  uint8_t* const out_bpp)
     766             : {
     767           0 :     const auto surfFormat = surf->GetFormat();
     768           0 :     switch (surfFormat) {
     769             :     case gfx::SurfaceFormat::B8G8R8A8:
     770           0 :         *out_texelFormat = WebGLTexelFormat::BGRA8;
     771           0 :         *out_bpp = 4;
     772           0 :         return true;
     773             : 
     774             :     case gfx::SurfaceFormat::B8G8R8X8:
     775           0 :         *out_texelFormat = WebGLTexelFormat::BGRX8;
     776           0 :         *out_bpp = 4;
     777           0 :         return true;
     778             : 
     779             :     case gfx::SurfaceFormat::R8G8B8A8:
     780           0 :         *out_texelFormat = WebGLTexelFormat::RGBA8;
     781           0 :         *out_bpp = 4;
     782           0 :         return true;
     783             : 
     784             :     case gfx::SurfaceFormat::R8G8B8X8:
     785           0 :         *out_texelFormat = WebGLTexelFormat::RGBX8;
     786           0 :         *out_bpp = 4;
     787           0 :         return true;
     788             : 
     789             :     case gfx::SurfaceFormat::R5G6B5_UINT16:
     790           0 :         *out_texelFormat = WebGLTexelFormat::RGB565;
     791           0 :         *out_bpp = 2;
     792           0 :         return true;
     793             : 
     794             :     case gfx::SurfaceFormat::A8:
     795           0 :         *out_texelFormat = WebGLTexelFormat::A8;
     796           0 :         *out_bpp = 1;
     797           0 :         return true;
     798             : 
     799             :     case gfx::SurfaceFormat::YUV:
     800             :         // Ugh...
     801           0 :         NS_ERROR("We don't handle uploads from YUV sources yet.");
     802             :         // When we want to, check out gfx/ycbcr/YCbCrUtils.h. (specifically
     803             :         // GetYCbCrToRGBDestFormatAndSize and ConvertYCbCrToRGB)
     804           0 :         return false;
     805             : 
     806             :     default:
     807           0 :         return false;
     808             :     }
     809             : }
     810             : 
     811             : //////////
     812             : 
     813             : bool
     814           0 : TexUnpackSurface::Validate(WebGLContext* webgl, const char* funcName,
     815             :                            const webgl::PackingInfo& pi)
     816             : {
     817           0 :     if (!ValidatePIForDOM(webgl, funcName, pi))
     818           0 :         return false;
     819             : 
     820           0 :     const auto fullRows = mSurf->GetSize().height;
     821           0 :     return ValidateUnpackPixels(webgl, funcName, fullRows, 0, this);
     822             : }
     823             : 
     824             : bool
     825           0 : TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
     826             :                                 WebGLTexture* tex, TexImageTarget target, GLint level,
     827             :                                 const webgl::DriverUnpackInfo* dui, GLint xOffset,
     828             :                                 GLint yOffset, GLint zOffset, const webgl::PackingInfo& dstPI,
     829             :                                 GLenum* const out_error) const
     830             : {
     831           0 :     const auto& webgl = tex->mContext;
     832             : 
     833             :     ////
     834             : 
     835           0 :     const auto rowLength = mSurf->GetSize().width;
     836           0 :     const auto rowCount = mSurf->GetSize().height;
     837             : 
     838           0 :     const auto& dstBPP = webgl::BytesPerPixel(dstPI);
     839           0 :     const auto dstFormat = FormatForPackingInfo(dstPI);
     840             : 
     841             :     ////
     842             : 
     843             :     WebGLTexelFormat srcFormat;
     844             :     uint8_t srcBPP;
     845           0 :     if (!GetFormatForSurf(mSurf, &srcFormat, &srcBPP)) {
     846           0 :         webgl->ErrorImplementationBug("%s: GetFormatForSurf failed for"
     847             :                                       " WebGLTexelFormat::%u.",
     848           0 :                                       funcName, uint32_t(mSurf->GetFormat()));
     849           0 :         return false;
     850             :     }
     851             : 
     852           0 :     gfx::DataSourceSurface::ScopedMap map(mSurf, gfx::DataSourceSurface::MapType::READ);
     853           0 :     if (!map.IsMapped()) {
     854           0 :         webgl->ErrorOutOfMemory("%s: Failed to map source surface for upload.", funcName);
     855           0 :         return false;
     856             :     }
     857             : 
     858           0 :     const auto& srcBegin = map.GetData();
     859           0 :     const auto& srcStride = map.GetStride();
     860             : 
     861             :     ////
     862             : 
     863           0 :     const auto srcRowLengthBytes = rowLength * srcBPP;
     864             : 
     865           0 :     const uint8_t maxGLAlignment = 8;
     866           0 :     uint8_t srcAlignment = 1;
     867           0 :     for (; srcAlignment <= maxGLAlignment; srcAlignment *= 2) {
     868           0 :         const auto strideGuess = RoundUpToMultipleOf(srcRowLengthBytes, srcAlignment);
     869           0 :         if (strideGuess == srcStride)
     870           0 :             break;
     871             :     }
     872           0 :     const uint32_t dstAlignment = (srcAlignment > maxGLAlignment) ? 1 : srcAlignment;
     873             : 
     874           0 :     const auto dstRowLengthBytes = rowLength * dstBPP;
     875           0 :     const auto dstStride = RoundUpToMultipleOf(dstRowLengthBytes, dstAlignment);
     876             : 
     877             :     ////
     878             : 
     879           0 :     const uint8_t* dstBegin = srcBegin;
     880           0 :     UniqueBuffer tempBuffer;
     881           0 :     if (!ConvertIfNeeded(webgl, funcName, rowLength, rowCount, srcFormat, srcBegin,
     882           0 :                          srcStride, dstFormat, dstStride, &dstBegin, &tempBuffer))
     883             :     {
     884           0 :         return false;
     885             :     }
     886             : 
     887             :     ////
     888             : 
     889           0 :     const auto& gl = webgl->gl;
     890           0 :     MOZ_ALWAYS_TRUE( gl->MakeCurrent() );
     891             : 
     892           0 :     gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, dstAlignment);
     893           0 :     if (webgl->IsWebGL2()) {
     894           0 :         gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, rowLength);
     895             :     }
     896             : 
     897           0 :     *out_error = DoTexOrSubImage(isSubImage, gl, target.get(), level, dui, xOffset,
     898           0 :                                  yOffset, zOffset, mWidth, mHeight, mDepth, dstBegin);
     899             : 
     900           0 :     gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, webgl->mPixelStore_UnpackAlignment);
     901           0 :     if (webgl->IsWebGL2()) {
     902           0 :         gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, webgl->mPixelStore_UnpackRowLength);
     903             :     }
     904             : 
     905           0 :     return true;
     906             : }
     907             : 
     908             : } // namespace webgl
     909             : } // namespace mozilla

Generated by: LCOV version 1.13