tor-browser

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

GLUploadHelpers.cpp (20463B)


      1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 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 "GLUploadHelpers.h"
      7 
      8 #include "GLContext.h"
      9 #include "mozilla/gfx/2D.h"
     10 #include "gfxUtils.h"
     11 #include "mozilla/gfx/Tools.h"  // For BytesPerPixel
     12 #include "nsRegion.h"
     13 #include "GfxTexturesReporter.h"
     14 #include "mozilla/gfx/Logging.h"
     15 
     16 namespace mozilla {
     17 
     18 using namespace gfx;
     19 
     20 namespace gl {
     21 
     22 static unsigned int DataOffset(const IntPoint& aPoint, int32_t aStride,
     23                               SurfaceFormat aFormat) {
     24  unsigned int data = aPoint.y * aStride;
     25  data += aPoint.x * BytesPerPixel(aFormat);
     26  return data;
     27 }
     28 
     29 static bool CheckUploadBounds(const IntSize& aDst, const IntSize& aSrc,
     30                              const IntPoint& aOffset) {
     31  if (aOffset.x < 0 || aOffset.y < 0 || aOffset.x >= aSrc.width ||
     32      aOffset.y >= aSrc.height) {
     33    MOZ_ASSERT_UNREACHABLE("Offset outside source bounds");
     34    return false;
     35  }
     36  if (aDst.width > (aSrc.width - aOffset.x) ||
     37      aDst.height > (aSrc.height - aOffset.y)) {
     38    MOZ_ASSERT_UNREACHABLE("Source has insufficient data");
     39    return false;
     40  }
     41  return true;
     42 }
     43 
     44 static GLint GetAddressAlignment(ptrdiff_t aAddress) {
     45  if (!(aAddress & 0x7)) {
     46    return 8;
     47  } else if (!(aAddress & 0x3)) {
     48    return 4;
     49  } else if (!(aAddress & 0x1)) {
     50    return 2;
     51  } else {
     52    return 1;
     53  }
     54 }
     55 
     56 // Take texture data in a given buffer and copy it into a larger buffer,
     57 // padding out the edge pixels for filtering if necessary
     58 static void CopyAndPadTextureData(const GLvoid* srcBuffer, GLvoid* dstBuffer,
     59                                  GLsizei srcWidth, GLsizei srcHeight,
     60                                  GLsizei dstWidth, GLsizei dstHeight,
     61                                  GLsizei stride, GLint pixelsize) {
     62  unsigned char* rowDest = static_cast<unsigned char*>(dstBuffer);
     63  const unsigned char* source = static_cast<const unsigned char*>(srcBuffer);
     64 
     65  for (GLsizei h = 0; h < srcHeight; ++h) {
     66    memcpy(rowDest, source, srcWidth * pixelsize);
     67    rowDest += dstWidth * pixelsize;
     68    source += stride;
     69  }
     70 
     71  GLsizei padHeight = srcHeight;
     72 
     73  // Pad out an extra row of pixels so that edge filtering doesn't use garbage
     74  // data
     75  if (dstHeight > srcHeight) {
     76    memcpy(rowDest, source - stride, srcWidth * pixelsize);
     77    padHeight++;
     78  }
     79 
     80  // Pad out an extra column of pixels
     81  if (dstWidth > srcWidth) {
     82    rowDest = static_cast<unsigned char*>(dstBuffer) + srcWidth * pixelsize;
     83    for (GLsizei h = 0; h < padHeight; ++h) {
     84      memcpy(rowDest, rowDest - pixelsize, pixelsize);
     85      rowDest += dstWidth * pixelsize;
     86    }
     87  }
     88 }
     89 
     90 // In both of these cases (for the Adreno at least) it is impossible
     91 // to determine good or bad driver versions for POT texture uploads,
     92 // so blacklist them all. Newer drivers use a different rendering
     93 // string in the form "Adreno (TM) 200" and the drivers we've seen so
     94 // far work fine with NPOT textures, so don't blacklist those until we
     95 // have evidence of any problems with them.
     96 bool ShouldUploadSubTextures(GLContext* gl) {
     97  if (!gl->WorkAroundDriverBugs()) return true;
     98 
     99  // There are certain GPUs that we don't want to use glTexSubImage2D on
    100  // because that function can be very slow and/or buggy
    101  if (gl->Renderer() == GLRenderer::Adreno200 ||
    102      gl->Renderer() == GLRenderer::Adreno205) {
    103    return false;
    104  }
    105 
    106  // On PowerVR glTexSubImage does a readback, so it will be slower
    107  // than just doing a glTexImage2D() directly. i.e. 26ms vs 10ms
    108  if (gl->Renderer() == GLRenderer::SGX540 ||
    109      gl->Renderer() == GLRenderer::SGX530) {
    110    return false;
    111  }
    112 
    113  return true;
    114 }
    115 
    116 static void TexSubImage2DWithUnpackSubimageGLES(
    117    GLContext* gl, GLenum target, GLint level, GLint xoffset, GLint yoffset,
    118    GLsizei width, GLsizei height, GLsizei stride, GLint pixelsize,
    119    GLenum format, GLenum type, const GLvoid* pixels) {
    120  gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT,
    121                   std::min(GetAddressAlignment((ptrdiff_t)pixels),
    122                            GetAddressAlignment((ptrdiff_t)stride)));
    123  // When using GL_UNPACK_ROW_LENGTH, we need to work around a Tegra
    124  // driver crash where the driver apparently tries to read
    125  // (stride - width * pixelsize) bytes past the end of the last input
    126  // row. We only upload the first height-1 rows using GL_UNPACK_ROW_LENGTH,
    127  // and then we upload the final row separately. See bug 697990.
    128  int rowLength = stride / pixelsize;
    129  if (gl->HasPBOState()) {
    130    gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, rowLength);
    131    gl->fTexSubImage2D(target, level, xoffset, yoffset, width, height, format,
    132                       type, pixels);
    133    gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0);
    134  } else {
    135    gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, rowLength);
    136    gl->fTexSubImage2D(target, level, xoffset, yoffset, width, height - 1,
    137                       format, type, pixels);
    138    gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0);
    139    gl->fTexSubImage2D(target, level, xoffset, yoffset + height - 1, width, 1,
    140                       format, type,
    141                       (const unsigned char*)pixels + (height - 1) * stride);
    142  }
    143  gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
    144 }
    145 
    146 static void TexSubImage2DWithoutUnpackSubimage(
    147    GLContext* gl, GLenum target, GLint level, GLint xoffset, GLint yoffset,
    148    GLsizei width, GLsizei height, GLsizei stride, GLint pixelsize,
    149    GLenum format, GLenum type, const GLvoid* pixels) {
    150  // Not using the whole row of texture data and GL_UNPACK_ROW_LENGTH
    151  // isn't supported. We make a copy of the texture data we're using,
    152  // such that we're using the whole row of data in the copy. This turns
    153  // out to be more efficient than uploading row-by-row; see bug 698197.
    154 
    155  // Width and height are never more than 16384. At 16Ki*16Ki, 4bpp is 1GiB, but
    156  // if we allow 8bpp (or higher) here, that's 2GiB, which would overflow on
    157  // 32-bit.
    158  MOZ_ASSERT(width <= 16384);
    159  MOZ_ASSERT(height <= 16384);
    160  MOZ_ASSERT(pixelsize < 8);
    161 
    162  const auto size = CheckedInt<size_t>(width) * height * pixelsize;
    163  if (!size.isValid()) {
    164    // This should never happen, but we use a defensive check.
    165    MOZ_ASSERT_UNREACHABLE("Unacceptable size calculated.!");
    166    return;
    167  }
    168 
    169  unsigned char* newPixels = new (fallible) unsigned char[size.value()];
    170 
    171  if (newPixels) {
    172    unsigned char* rowDest = newPixels;
    173    const unsigned char* rowSource = (const unsigned char*)pixels;
    174    for (int h = 0; h < height; h++) {
    175      memcpy(rowDest, rowSource, width * pixelsize);
    176      rowDest += width * pixelsize;
    177      rowSource += stride;
    178    }
    179 
    180    stride = width * pixelsize;
    181    gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT,
    182                     std::min(GetAddressAlignment((ptrdiff_t)newPixels),
    183                              GetAddressAlignment((ptrdiff_t)stride)));
    184    gl->fTexSubImage2D(target, level, xoffset, yoffset, width, height, format,
    185                       type, newPixels);
    186    delete[] newPixels;
    187    gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
    188 
    189  } else {
    190    // If we did not have sufficient memory for the required
    191    // temporary buffer, then fall back to uploading row-by-row.
    192    const unsigned char* rowSource = (const unsigned char*)pixels;
    193 
    194    gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT,
    195                     std::min(GetAddressAlignment((ptrdiff_t)pixels),
    196                              GetAddressAlignment((ptrdiff_t)stride)));
    197 
    198    for (int i = 0; i < height; i++) {
    199      gl->fTexSubImage2D(target, level, xoffset, yoffset + i, width, 1, format,
    200                         type, rowSource);
    201      rowSource += stride;
    202    }
    203 
    204    gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
    205  }
    206 }
    207 static void TexSubImage2DHelper(GLContext* gl, GLenum target, GLint level,
    208                                GLint xoffset, GLint yoffset, GLsizei width,
    209                                GLsizei height, GLsizei stride, GLint pixelsize,
    210                                GLenum format, GLenum type,
    211                                const GLvoid* pixels) {
    212  if (gl->IsGLES()) {
    213    if (stride == width * pixelsize) {
    214      gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT,
    215                       std::min(GetAddressAlignment((ptrdiff_t)pixels),
    216                                GetAddressAlignment((ptrdiff_t)stride)));
    217      gl->fTexSubImage2D(target, level, xoffset, yoffset, width, height, format,
    218                         type, pixels);
    219      gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
    220    } else if (gl->IsExtensionSupported(GLContext::EXT_unpack_subimage) ||
    221               gl->HasPBOState()) {
    222      TexSubImage2DWithUnpackSubimageGLES(gl, target, level, xoffset, yoffset,
    223                                          width, height, stride, pixelsize,
    224                                          format, type, pixels);
    225 
    226    } else {
    227      TexSubImage2DWithoutUnpackSubimage(gl, target, level, xoffset, yoffset,
    228                                         width, height, stride, pixelsize,
    229                                         format, type, pixels);
    230    }
    231  } else {
    232    // desktop GL (non-ES) path
    233    gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT,
    234                     std::min(GetAddressAlignment((ptrdiff_t)pixels),
    235                              GetAddressAlignment((ptrdiff_t)stride)));
    236    int rowLength = stride / pixelsize;
    237    gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, rowLength);
    238    gl->fTexSubImage2D(target, level, xoffset, yoffset, width, height, format,
    239                       type, pixels);
    240    gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0);
    241    gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
    242  }
    243 }
    244 
    245 static void TexImage2DHelper(GLContext* gl, GLenum target, GLint level,
    246                             GLint internalformat, GLsizei width,
    247                             GLsizei height, GLsizei stride, GLint pixelsize,
    248                             GLint border, GLenum format, GLenum type,
    249                             const GLvoid* pixels) {
    250  if (gl->IsGLES()) {
    251    NS_ASSERTION(
    252        format == (GLenum)internalformat,
    253        "format and internalformat not the same for glTexImage2D on GLES2");
    254 
    255    MOZ_ASSERT(width >= 0 && height >= 0);
    256    if (!CanUploadNonPowerOfTwo(gl) &&
    257        (stride != width * pixelsize || !IsPowerOfTwo((uint32_t)width) ||
    258         !IsPowerOfTwo((uint32_t)height))) {
    259      // Pad out texture width and height to the next power of two
    260      // as we don't support/want non power of two texture uploads
    261      GLsizei paddedWidth = RoundUpPow2((uint32_t)width);
    262      GLsizei paddedHeight = RoundUpPow2((uint32_t)height);
    263 
    264      // Width and height are never more than 16384. At 16Ki*16Ki, 4bpp is 1GiB,
    265      // but if we allow 8bpp (or higher) here, that's 2GiB, which would
    266      // overflow on 32-bit.
    267      MOZ_ASSERT(width <= 16384);
    268      MOZ_ASSERT(height <= 16384);
    269      MOZ_ASSERT(pixelsize < 8);
    270 
    271      const auto size =
    272          CheckedInt<size_t>(paddedWidth) * paddedHeight * pixelsize;
    273      if (!size.isValid()) {
    274        // This should never happen, but we use a defensive check.
    275        MOZ_ASSERT_UNREACHABLE("Unacceptable size calculated.!");
    276        return;
    277      }
    278 
    279      GLvoid* paddedPixels = new unsigned char[size.value()];
    280 
    281      // Pad out texture data to be in a POT sized buffer for uploading to
    282      // a POT sized texture
    283      CopyAndPadTextureData(pixels, paddedPixels, width, height, paddedWidth,
    284                            paddedHeight, stride, pixelsize);
    285 
    286      gl->fPixelStorei(
    287          LOCAL_GL_UNPACK_ALIGNMENT,
    288          std::min(GetAddressAlignment((ptrdiff_t)paddedPixels),
    289                   GetAddressAlignment((ptrdiff_t)paddedWidth * pixelsize)));
    290      gl->fTexImage2D(target, border, internalformat, paddedWidth, paddedHeight,
    291                      border, format, type, paddedPixels);
    292      gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
    293 
    294      delete[] static_cast<unsigned char*>(paddedPixels);
    295      return;
    296    }
    297 
    298    if (stride == width * pixelsize) {
    299      gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT,
    300                       std::min(GetAddressAlignment((ptrdiff_t)pixels),
    301                                GetAddressAlignment((ptrdiff_t)stride)));
    302      gl->fTexImage2D(target, border, internalformat, width, height, border,
    303                      format, type, pixels);
    304      gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
    305    } else {
    306      // Use GLES-specific workarounds for GL_UNPACK_ROW_LENGTH; these are
    307      // implemented in TexSubImage2D.
    308      gl->fTexImage2D(target, border, internalformat, width, height, border,
    309                      format, type, nullptr);
    310      TexSubImage2DHelper(gl, target, level, 0, 0, width, height, stride,
    311                          pixelsize, format, type, pixels);
    312    }
    313  } else {
    314    // desktop GL (non-ES) path
    315 
    316    gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT,
    317                     std::min(GetAddressAlignment((ptrdiff_t)pixels),
    318                              GetAddressAlignment((ptrdiff_t)stride)));
    319    int rowLength = stride / pixelsize;
    320    gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, rowLength);
    321    gl->fTexImage2D(target, level, internalformat, width, height, border,
    322                    format, type, pixels);
    323    gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0);
    324    gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
    325  }
    326 }
    327 
    328 SurfaceFormat UploadImageDataToTexture(
    329    GLContext* gl, unsigned char* aData, const gfx::IntSize& aDataSize,
    330    const IntPoint& aDstOffset, int32_t aStride, SurfaceFormat aFormat,
    331    const nsIntRegion& aDstRegion, GLuint aTexture, const gfx::IntSize& aSize,
    332    size_t* aOutUploadSize, bool aNeedInit, GLenum aTextureUnit,
    333    GLenum aTextureTarget) {
    334  gl->MakeCurrent();
    335  gl->fActiveTexture(aTextureUnit);
    336  gl->fBindTexture(aTextureTarget, aTexture);
    337 
    338  GLenum format = 0;
    339  GLenum internalFormat = 0;
    340  GLenum type = 0;
    341  int32_t pixelSize = BytesPerPixel(aFormat);
    342  SurfaceFormat surfaceFormat = gfx::SurfaceFormat::UNKNOWN;
    343 
    344  MOZ_ASSERT(gl->GetPreferredARGB32Format() == LOCAL_GL_BGRA ||
    345             gl->GetPreferredARGB32Format() == LOCAL_GL_RGBA);
    346 
    347  switch (aFormat) {
    348    case SurfaceFormat::B8G8R8A8:
    349      if (gl->GetPreferredARGB32Format() == LOCAL_GL_BGRA) {
    350        format = LOCAL_GL_BGRA;
    351        surfaceFormat = SurfaceFormat::R8G8B8A8;
    352        type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV;
    353      } else {
    354        format = LOCAL_GL_RGBA;
    355        surfaceFormat = SurfaceFormat::B8G8R8A8;
    356        type = LOCAL_GL_UNSIGNED_BYTE;
    357      }
    358      internalFormat = LOCAL_GL_RGBA;
    359      break;
    360    case SurfaceFormat::B8G8R8X8:
    361      // Treat BGRX surfaces as BGRA except for the surface
    362      // format used.
    363      if (gl->GetPreferredARGB32Format() == LOCAL_GL_BGRA) {
    364        format = LOCAL_GL_BGRA;
    365        surfaceFormat = SurfaceFormat::R8G8B8X8;
    366        type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV;
    367      } else {
    368        format = LOCAL_GL_RGBA;
    369        surfaceFormat = SurfaceFormat::B8G8R8X8;
    370        type = LOCAL_GL_UNSIGNED_BYTE;
    371      }
    372      internalFormat = LOCAL_GL_RGBA;
    373      break;
    374    case SurfaceFormat::R8G8B8A8:
    375      if (gl->GetPreferredARGB32Format() == LOCAL_GL_BGRA) {
    376        // Upload our RGBA as BGRA, but store that the uploaded format is
    377        // BGRA. (sample from R to get B)
    378        format = LOCAL_GL_BGRA;
    379        type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV;
    380        surfaceFormat = SurfaceFormat::B8G8R8A8;
    381      } else {
    382        format = LOCAL_GL_RGBA;
    383        type = LOCAL_GL_UNSIGNED_BYTE;
    384        surfaceFormat = SurfaceFormat::R8G8B8A8;
    385      }
    386      internalFormat = LOCAL_GL_RGBA;
    387      break;
    388    case SurfaceFormat::R8G8B8X8:
    389      // Treat RGBX surfaces as RGBA except for the surface
    390      // format used.
    391      if (gl->GetPreferredARGB32Format() == LOCAL_GL_BGRA) {
    392        format = LOCAL_GL_BGRA;
    393        type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV;
    394        surfaceFormat = SurfaceFormat::B8G8R8X8;
    395      } else {
    396        format = LOCAL_GL_RGBA;
    397        type = LOCAL_GL_UNSIGNED_BYTE;
    398        surfaceFormat = SurfaceFormat::R8G8B8X8;
    399      }
    400      internalFormat = LOCAL_GL_RGBA;
    401      break;
    402    case SurfaceFormat::R5G6B5_UINT16:
    403      internalFormat = format = LOCAL_GL_RGB;
    404      type = LOCAL_GL_UNSIGNED_SHORT_5_6_5;
    405      surfaceFormat = SurfaceFormat::R5G6B5_UINT16;
    406      break;
    407    case SurfaceFormat::A8:
    408      if (gl->IsGLES()) {
    409        format = LOCAL_GL_LUMINANCE;
    410        internalFormat = LOCAL_GL_LUMINANCE;
    411      } else {
    412        format = LOCAL_GL_RED;
    413        internalFormat = LOCAL_GL_R8;
    414      }
    415      type = LOCAL_GL_UNSIGNED_BYTE;
    416      // We don't have a specific luminance shader
    417      surfaceFormat = SurfaceFormat::A8;
    418      break;
    419    case SurfaceFormat::A16:
    420      if (gl->IsGLES()) {
    421        format = LOCAL_GL_LUMINANCE;
    422        internalFormat = LOCAL_GL_LUMINANCE16;
    423      } else {
    424        format = LOCAL_GL_RED;
    425        internalFormat = LOCAL_GL_R16;
    426      }
    427      type = LOCAL_GL_UNSIGNED_SHORT;
    428      // We don't have a specific luminance shader
    429      surfaceFormat = SurfaceFormat::A8;
    430      pixelSize = 2;
    431      break;
    432    default:
    433      MOZ_ASSERT_UNREACHABLE("Unhandled image surface format!");
    434  }
    435 
    436  if (aOutUploadSize) {
    437    *aOutUploadSize = 0;
    438  }
    439 
    440  if (surfaceFormat == gfx::SurfaceFormat::UNKNOWN) {
    441    return gfx::SurfaceFormat::UNKNOWN;
    442  }
    443 
    444  // We can only skip SubTextures if aOffset = 0 because we need the whole
    445  // buffer
    446  if (aNeedInit || (!ShouldUploadSubTextures(gl) && aDstOffset == IntPoint())) {
    447    if (!CheckUploadBounds(aSize, aDataSize, IntPoint())) {
    448      return SurfaceFormat::UNKNOWN;
    449    }
    450    // If the texture needs initialized, or we are unable to
    451    // upload sub textures, then initialize and upload the entire
    452    // texture.
    453    TexImage2DHelper(gl, aTextureTarget, 0, internalFormat, aSize.width,
    454                     aSize.height, aStride, pixelSize, 0, format, type, aData);
    455 
    456    if (aOutUploadSize && aNeedInit) {
    457      uint32_t texelSize = GetBytesPerTexel(internalFormat, type);
    458      size_t numTexels = size_t(aSize.width) * size_t(aSize.height);
    459      *aOutUploadSize += texelSize * numTexels;
    460    }
    461  } else {
    462    // Upload each rect in the region to the texture
    463    for (auto iter = aDstRegion.RectIter(); !iter.Done(); iter.Next()) {
    464      IntRect rect = iter.Get();
    465      if (!CheckUploadBounds(rect.Size(), aDataSize, rect.TopLeft())) {
    466        return SurfaceFormat::UNKNOWN;
    467      }
    468 
    469      const unsigned char* rectData =
    470          aData + DataOffset(rect.TopLeft(), aStride, aFormat);
    471 
    472      rect += aDstOffset;
    473      TexSubImage2DHelper(gl, aTextureTarget, 0, rect.X(), rect.Y(),
    474                          rect.Width(), rect.Height(), aStride, pixelSize,
    475                          format, type, rectData);
    476    }
    477  }
    478 
    479  return surfaceFormat;
    480 }
    481 
    482 SurfaceFormat UploadSurfaceToTexture(GLContext* gl, DataSourceSurface* aSurface,
    483                                     const nsIntRegion& aDstRegion,
    484                                     GLuint aTexture, const gfx::IntSize& aSize,
    485                                     size_t* aOutUploadSize, bool aNeedInit,
    486                                     const gfx::IntPoint& aSrcOffset,
    487                                     const gfx::IntPoint& aDstOffset,
    488                                     GLenum aTextureUnit,
    489                                     GLenum aTextureTarget) {
    490  DataSourceSurface::ScopedMap map(aSurface, DataSourceSurface::READ);
    491  int32_t stride = map.GetStride();
    492  SurfaceFormat format = aSurface->GetFormat();
    493  gfx::IntSize size = aSurface->GetSize();
    494 
    495  // only fail if we'll need the entire surface for initialization
    496  if (aNeedInit && !CheckUploadBounds(aSize, size, aSrcOffset)) {
    497    return SurfaceFormat::UNKNOWN;
    498  }
    499 
    500  unsigned char* data = map.GetData() + DataOffset(aSrcOffset, stride, format);
    501  size.width -= aSrcOffset.x;
    502  size.height -= aSrcOffset.y;
    503 
    504  return UploadImageDataToTexture(gl, data, size, aDstOffset, stride, format,
    505                                  aDstRegion, aTexture, aSize, aOutUploadSize,
    506                                  aNeedInit, aTextureUnit, aTextureTarget);
    507 }
    508 
    509 bool CanUploadNonPowerOfTwo(GLContext* gl) {
    510  if (!gl->WorkAroundDriverBugs()) return true;
    511 
    512  // Some GPUs driver crash when uploading non power of two 565 textures.
    513  return gl->Renderer() != GLRenderer::Adreno200 &&
    514         gl->Renderer() != GLRenderer::Adreno205;
    515 }
    516 
    517 }  // namespace gl
    518 }  // namespace mozilla