gfx/gl/GLUploadHelpers.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
     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/. */
     6 #include "GLUploadHelpers.h"
     8 #include "GLContext.h"
     9 #include "mozilla/gfx/2D.h"
    10 #include "mozilla/gfx/Tools.h"  // For BytesPerPixel
    11 #include "nsRegion.h"
    13 namespace mozilla {
    15 using namespace gfx;
    17 namespace gl {
    19 /* These two techniques are suggested by "Bit Twiddling Hacks"
    20  */
    22 /**
    23  * Returns true if |aNumber| is a power of two
    24  * 0 is incorreclty considered a power of two
    25  */
    26 static bool
    27 IsPowerOfTwo(int aNumber)
    28 {
    29     return (aNumber & (aNumber - 1)) == 0;
    30 }
    32 /**
    33  * Returns the first integer greater than |aNumber| which is a power of two
    34  * Undefined for |aNumber| < 0
    35  */
    36 static int
    37 NextPowerOfTwo(int aNumber)
    38 {
    39 #if defined(__arm__)
    40     return 1 << (32 - __builtin_clz(aNumber - 1));
    41 #else
    42     --aNumber;
    43     aNumber |= aNumber >> 1;
    44     aNumber |= aNumber >> 2;
    45     aNumber |= aNumber >> 4;
    46     aNumber |= aNumber >> 8;
    47     aNumber |= aNumber >> 16;
    48     return ++aNumber;
    49 #endif
    50 }
    52 static unsigned int
    53 DataOffset(const nsIntPoint &aPoint, int32_t aStride, SurfaceFormat aFormat)
    54 {
    55   unsigned int data = aPoint.y * aStride;
    56   data += aPoint.x * BytesPerPixel(aFormat);
    57   return data;
    58 }
    60 static GLint GetAddressAlignment(ptrdiff_t aAddress)
    61 {
    62     if (!(aAddress & 0x7)) {
    63        return 8;
    64     } else if (!(aAddress & 0x3)) {
    65         return 4;
    66     } else if (!(aAddress & 0x1)) {
    67         return 2;
    68     } else {
    69         return 1;
    70     }
    71 }
    73 // Take texture data in a given buffer and copy it into a larger buffer,
    74 // padding out the edge pixels for filtering if necessary
    75 static void
    76 CopyAndPadTextureData(const GLvoid* srcBuffer,
    77                       GLvoid* dstBuffer,
    78                       GLsizei srcWidth, GLsizei srcHeight,
    79                       GLsizei dstWidth, GLsizei dstHeight,
    80                       GLsizei stride, GLint pixelsize)
    81 {
    82     unsigned char *rowDest = static_cast<unsigned char*>(dstBuffer);
    83     const unsigned char *source = static_cast<const unsigned char*>(srcBuffer);
    85     for (GLsizei h = 0; h < srcHeight; ++h) {
    86         memcpy(rowDest, source, srcWidth * pixelsize);
    87         rowDest += dstWidth * pixelsize;
    88         source += stride;
    89     }
    91     GLsizei padHeight = srcHeight;
    93     // Pad out an extra row of pixels so that edge filtering doesn't use garbage data
    94     if (dstHeight > srcHeight) {
    95         memcpy(rowDest, source - stride, srcWidth * pixelsize);
    96         padHeight++;
    97     }
    99     // Pad out an extra column of pixels
   100     if (dstWidth > srcWidth) {
   101         rowDest = static_cast<unsigned char*>(dstBuffer) + srcWidth * pixelsize;
   102         for (GLsizei h = 0; h < padHeight; ++h) {
   103             memcpy(rowDest, rowDest - pixelsize, pixelsize);
   104             rowDest += dstWidth * pixelsize;
   105         }
   106     }
   107 }
   109 // In both of these cases (for the Adreno at least) it is impossible
   110 // to determine good or bad driver versions for POT texture uploads,
   111 // so blacklist them all. Newer drivers use a different rendering
   112 // string in the form "Adreno (TM) 200" and the drivers we've seen so
   113 // far work fine with NPOT textures, so don't blacklist those until we
   114 // have evidence of any problems with them.
   115 bool
   116 CanUploadSubTextures(GLContext* gl)
   117 {
   118     if (!gl->WorkAroundDriverBugs())
   119         return true;
   121     // There are certain GPUs that we don't want to use glTexSubImage2D on
   122     // because that function can be very slow and/or buggy
   123     if (gl->Renderer() == GLRenderer::Adreno200 ||
   124         gl->Renderer() == GLRenderer::Adreno205)
   125     {
   126         return false;
   127     }
   129     // On PowerVR glTexSubImage does a readback, so it will be slower
   130     // than just doing a glTexImage2D() directly. i.e. 26ms vs 10ms
   131     if (gl->Renderer() == GLRenderer::SGX540 ||
   132         gl->Renderer() == GLRenderer::SGX530)
   133     {
   134         return false;
   135     }
   137     return true;
   138 }
   140 static void
   141 TexSubImage2DWithUnpackSubimageGLES(GLContext* gl,
   142                                     GLenum target, GLint level,
   143                                     GLint xoffset, GLint yoffset,
   144                                     GLsizei width, GLsizei height,
   145                                     GLsizei stride, GLint pixelsize,
   146                                     GLenum format, GLenum type,
   147                                     const GLvoid* pixels)
   148 {
   149     gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT,
   150                      std::min(GetAddressAlignment((ptrdiff_t)pixels),
   151                               GetAddressAlignment((ptrdiff_t)stride)));
   152     // When using GL_UNPACK_ROW_LENGTH, we need to work around a Tegra
   153     // driver crash where the driver apparently tries to read
   154     // (stride - width * pixelsize) bytes past the end of the last input
   155     // row. We only upload the first height-1 rows using GL_UNPACK_ROW_LENGTH,
   156     // and then we upload the final row separately. See bug 697990.
   157     int rowLength = stride/pixelsize;
   158     gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, rowLength);
   159     gl->fTexSubImage2D(target,
   160                        level,
   161                        xoffset,
   162                        yoffset,
   163                        width,
   164                        height-1,
   165                        format,
   166                        type,
   167                        pixels);
   168     gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0);
   169     gl->fTexSubImage2D(target,
   170                        level,
   171                        xoffset,
   172                        yoffset+height-1,
   173                        width,
   174                        1,
   175                        format,
   176                        type,
   177                        (const unsigned char *)pixels+(height-1)*stride);
   178     gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
   179 }
   181 static void
   182 TexSubImage2DWithoutUnpackSubimage(GLContext* gl,
   183                                    GLenum target, GLint level,
   184                                    GLint xoffset, GLint yoffset,
   185                                    GLsizei width, GLsizei height,
   186                                    GLsizei stride, GLint pixelsize,
   187                                    GLenum format, GLenum type,
   188                                    const GLvoid* pixels)
   189 {
   190     // Not using the whole row of texture data and GL_UNPACK_ROW_LENGTH
   191     // isn't supported. We make a copy of the texture data we're using,
   192     // such that we're using the whole row of data in the copy. This turns
   193     // out to be more efficient than uploading row-by-row; see bug 698197.
   194     unsigned char *newPixels = new unsigned char[width*height*pixelsize];
   195     unsigned char *rowDest = newPixels;
   196     const unsigned char *rowSource = (const unsigned char *)pixels;
   197     for (int h = 0; h < height; h++) {
   198             memcpy(rowDest, rowSource, width*pixelsize);
   199             rowDest += width*pixelsize;
   200             rowSource += stride;
   201     }
   203     stride = width*pixelsize;
   204     gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT,
   205                      std::min(GetAddressAlignment((ptrdiff_t)newPixels),
   206                               GetAddressAlignment((ptrdiff_t)stride)));
   207     gl->fTexSubImage2D(target,
   208                        level,
   209                        xoffset,
   210                        yoffset,
   211                        width,
   212                        height,
   213                        format,
   214                        type,
   215                        newPixels);
   216     delete [] newPixels;
   217     gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
   218 }
   220 static void
   221 TexSubImage2DHelper(GLContext *gl,
   222                     GLenum target, GLint level,
   223                     GLint xoffset, GLint yoffset,
   224                     GLsizei width, GLsizei height, GLsizei stride,
   225                     GLint pixelsize, GLenum format,
   226                     GLenum type, const GLvoid* pixels)
   227 {
   228     if (gl->IsGLES()) {
   229         if (stride == width * pixelsize) {
   230             gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT,
   231                              std::min(GetAddressAlignment((ptrdiff_t)pixels),
   232                                       GetAddressAlignment((ptrdiff_t)stride)));
   233             gl->fTexSubImage2D(target,
   234                                level,
   235                                xoffset,
   236                                yoffset,
   237                                width,
   238                                height,
   239                                format,
   240                                type,
   241                                pixels);
   242             gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
   243         } else if (gl->IsExtensionSupported(GLContext::EXT_unpack_subimage)) {
   244             TexSubImage2DWithUnpackSubimageGLES(gl, target, level, xoffset, yoffset,
   245                                                 width, height, stride,
   246                                                 pixelsize, format, type, pixels);
   248         } else {
   249             TexSubImage2DWithoutUnpackSubimage(gl, target, level, xoffset, yoffset,
   250                                               width, height, stride,
   251                                               pixelsize, format, type, pixels);
   252         }
   253     } else {
   254         // desktop GL (non-ES) path
   255         gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT,
   256                          std::min(GetAddressAlignment((ptrdiff_t)pixels),
   257                                   GetAddressAlignment((ptrdiff_t)stride)));
   258         int rowLength = stride/pixelsize;
   259         gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, rowLength);
   260         gl->fTexSubImage2D(target,
   261                            level,
   262                            xoffset,
   263                            yoffset,
   264                            width,
   265                            height,
   266                            format,
   267                            type,
   268                            pixels);
   269         gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0);
   270         gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
   271     }
   272 }
   274 static void
   275 TexImage2DHelper(GLContext *gl,
   276                  GLenum target, GLint level, GLint internalformat,
   277                  GLsizei width, GLsizei height, GLsizei stride,
   278                  GLint pixelsize, GLint border, GLenum format,
   279                  GLenum type, const GLvoid *pixels)
   280 {
   281     if (gl->IsGLES()) {
   283         NS_ASSERTION(format == (GLenum)internalformat,
   284                     "format and internalformat not the same for glTexImage2D on GLES2");
   286         if (!CanUploadNonPowerOfTwo(gl)
   287             && (stride != width * pixelsize
   288             || !IsPowerOfTwo(width)
   289             || !IsPowerOfTwo(height))) {
   291             // Pad out texture width and height to the next power of two
   292             // as we don't support/want non power of two texture uploads
   293             GLsizei paddedWidth = NextPowerOfTwo(width);
   294             GLsizei paddedHeight = NextPowerOfTwo(height);
   296             GLvoid* paddedPixels = new unsigned char[paddedWidth * paddedHeight * pixelsize];
   298             // Pad out texture data to be in a POT sized buffer for uploading to
   299             // a POT sized texture
   300             CopyAndPadTextureData(pixels, paddedPixels, width, height,
   301                                   paddedWidth, paddedHeight, stride, pixelsize);
   303             gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT,
   304                              std::min(GetAddressAlignment((ptrdiff_t)paddedPixels),
   305                                       GetAddressAlignment((ptrdiff_t)paddedWidth * pixelsize)));
   306             gl->fTexImage2D(target,
   307                             border,
   308                             internalformat,
   309                             paddedWidth,
   310                             paddedHeight,
   311                             border,
   312                             format,
   313                             type,
   314                             paddedPixels);
   315             gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
   317             delete[] static_cast<unsigned char*>(paddedPixels);
   318             return;
   319         }
   321         if (stride == width * pixelsize) {
   322             gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT,
   323                              std::min(GetAddressAlignment((ptrdiff_t)pixels),
   324                                       GetAddressAlignment((ptrdiff_t)stride)));
   325             gl->fTexImage2D(target,
   326                             border,
   327                             internalformat,
   328                             width,
   329                             height,
   330                             border,
   331                             format,
   332                             type,
   333                             pixels);
   334             gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
   335         } else {
   336             // Use GLES-specific workarounds for GL_UNPACK_ROW_LENGTH; these are
   337             // implemented in TexSubImage2D.
   338             gl->fTexImage2D(target,
   339                             border,
   340                             internalformat,
   341                             width,
   342                             height,
   343                             border,
   344                             format,
   345                             type,
   346                             nullptr);
   347             TexSubImage2DHelper(gl,
   348                                 target,
   349                                 level,
   350                                 0,
   351                                 0,
   352                                 width,
   353                                 height,
   354                                 stride,
   355                                 pixelsize,
   356                                 format,
   357                                 type,
   358                                 pixels);
   359         }
   360     } else {
   361         // desktop GL (non-ES) path
   363         gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT,
   364                          std::min(GetAddressAlignment((ptrdiff_t)pixels),
   365                                   GetAddressAlignment((ptrdiff_t)stride)));
   366         int rowLength = stride/pixelsize;
   367         gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, rowLength);
   368         gl->fTexImage2D(target,
   369                         level,
   370                         internalformat,
   371                         width,
   372                         height,
   373                         border,
   374                         format,
   375                         type,
   376                         pixels);
   377         gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0);
   378         gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
   379     }
   380 }
   382 SurfaceFormat
   383 UploadImageDataToTexture(GLContext* gl,
   384                          unsigned char* aData,
   385                          int32_t aStride,
   386                          SurfaceFormat aFormat,
   387                          const nsIntRegion& aDstRegion,
   388                          GLuint& aTexture,
   389                          bool aOverwrite,
   390                          bool aPixelBuffer,
   391                          GLenum aTextureUnit,
   392                          GLenum aTextureTarget)
   393 {
   394     bool textureInited = aOverwrite ? false : true;
   395     gl->MakeCurrent();
   396     gl->fActiveTexture(aTextureUnit);
   398     if (!aTexture) {
   399         gl->fGenTextures(1, &aTexture);
   400         gl->fBindTexture(aTextureTarget, aTexture);
   401         gl->fTexParameteri(aTextureTarget,
   402                            LOCAL_GL_TEXTURE_MIN_FILTER,
   403                            LOCAL_GL_LINEAR);
   404         gl->fTexParameteri(aTextureTarget,
   405                            LOCAL_GL_TEXTURE_MAG_FILTER,
   406                            LOCAL_GL_LINEAR);
   407         gl->fTexParameteri(aTextureTarget,
   408                            LOCAL_GL_TEXTURE_WRAP_S,
   409                            LOCAL_GL_CLAMP_TO_EDGE);
   410         gl->fTexParameteri(aTextureTarget,
   411                            LOCAL_GL_TEXTURE_WRAP_T,
   412                            LOCAL_GL_CLAMP_TO_EDGE);
   413         textureInited = false;
   414     } else {
   415         gl->fBindTexture(aTextureTarget, aTexture);
   416     }
   418     nsIntRegion paintRegion;
   419     if (!textureInited) {
   420         paintRegion = nsIntRegion(aDstRegion.GetBounds());
   421     } else {
   422         paintRegion = aDstRegion;
   423     }
   425     GLenum format = 0;
   426     GLenum internalFormat = 0;
   427     GLenum type = 0;
   428     int32_t pixelSize = BytesPerPixel(aFormat);
   429     SurfaceFormat surfaceFormat = gfx::SurfaceFormat::UNKNOWN;
   431     MOZ_ASSERT(gl->GetPreferredARGB32Format() == LOCAL_GL_BGRA ||
   432                gl->GetPreferredARGB32Format() == LOCAL_GL_RGBA);
   433     switch (aFormat) {
   434         case SurfaceFormat::B8G8R8A8:
   435             if (gl->GetPreferredARGB32Format() == LOCAL_GL_BGRA) {
   436               format = LOCAL_GL_BGRA;
   437               surfaceFormat = SurfaceFormat::R8G8B8A8;
   438               type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV;
   439             } else {
   440               format = LOCAL_GL_RGBA;
   441               surfaceFormat = SurfaceFormat::B8G8R8A8;
   442               type = LOCAL_GL_UNSIGNED_BYTE;
   443             }
   444             internalFormat = LOCAL_GL_RGBA;
   445             break;
   446         case SurfaceFormat::B8G8R8X8:
   447             // Treat BGRX surfaces as BGRA except for the surface
   448             // format used.
   449             if (gl->GetPreferredARGB32Format() == LOCAL_GL_BGRA) {
   450               format = LOCAL_GL_BGRA;
   451               surfaceFormat = SurfaceFormat::R8G8B8X8;
   452               type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV;
   453             } else {
   454               format = LOCAL_GL_RGBA;
   455               surfaceFormat = SurfaceFormat::B8G8R8X8;
   456               type = LOCAL_GL_UNSIGNED_BYTE;
   457             }
   458             internalFormat = LOCAL_GL_RGBA;
   459             break;
   460         case SurfaceFormat::R5G6B5:
   461             internalFormat = format = LOCAL_GL_RGB;
   462             type = LOCAL_GL_UNSIGNED_SHORT_5_6_5;
   463             surfaceFormat = SurfaceFormat::R5G6B5;
   464             break;
   465         case SurfaceFormat::A8:
   466             internalFormat = format = LOCAL_GL_LUMINANCE;
   467             type = LOCAL_GL_UNSIGNED_BYTE;
   468             // We don't have a specific luminance shader
   469             surfaceFormat = SurfaceFormat::A8;
   470             break;
   471         default:
   472             NS_ASSERTION(false, "Unhandled image surface format!");
   473     }
   475     nsIntRegionRectIterator iter(paintRegion);
   476     const nsIntRect *iterRect;
   478     // Top left point of the region's bounding rectangle.
   479     nsIntPoint topLeft = paintRegion.GetBounds().TopLeft();
   481     while ((iterRect = iter.Next())) {
   482         // The inital data pointer is at the top left point of the region's
   483         // bounding rectangle. We need to find the offset of this rect
   484         // within the region and adjust the data pointer accordingly.
   485         unsigned char *rectData =
   486             aData + DataOffset(iterRect->TopLeft() - topLeft, aStride, aFormat);
   488         NS_ASSERTION(textureInited || (iterRect->x == 0 && iterRect->y == 0),
   489                      "Must be uploading to the origin when we don't have an existing texture");
   491         if (textureInited && CanUploadSubTextures(gl)) {
   492             TexSubImage2DHelper(gl,
   493                                 aTextureTarget,
   494                                 0,
   495                                 iterRect->x,
   496                                 iterRect->y,
   497                                 iterRect->width,
   498                                 iterRect->height,
   499                                 aStride,
   500                                 pixelSize,
   501                                 format,
   502                                 type,
   503                                 rectData);
   504         } else {
   505             TexImage2DHelper(gl,
   506                              aTextureTarget,
   507                              0,
   508                              internalFormat,
   509                              iterRect->width,
   510                              iterRect->height,
   511                              aStride,
   512                              pixelSize,
   513                              0,
   514                              format,
   515                              type,
   516                              rectData);
   517         }
   519     }
   521     return surfaceFormat;
   522 }
   524 SurfaceFormat
   525 UploadSurfaceToTexture(GLContext* gl,
   526                        DataSourceSurface *aSurface,
   527                        const nsIntRegion& aDstRegion,
   528                        GLuint& aTexture,
   529                        bool aOverwrite,
   530                        const nsIntPoint& aSrcPoint,
   531                        bool aPixelBuffer,
   532                        GLenum aTextureUnit,
   533                        GLenum aTextureTarget)
   534 {
   535     unsigned char* data = aPixelBuffer ? nullptr : aSurface->GetData();
   536     int32_t stride = aSurface->Stride();
   537     SurfaceFormat format = aSurface->GetFormat();
   538     data += DataOffset(aSrcPoint, stride, format);
   539     return UploadImageDataToTexture(gl, data, stride, format,
   540                                     aDstRegion, aTexture, aOverwrite,
   541                                     aPixelBuffer, aTextureUnit,
   542                                     aTextureTarget);
   543 }
   545 bool
   546 CanUploadNonPowerOfTwo(GLContext* gl)
   547 {
   548     if (!gl->WorkAroundDriverBugs())
   549         return true;
   551     // Some GPUs driver crash when uploading non power of two 565 textures.
   552     return gl->Renderer() != GLRenderer::Adreno200 &&
   553            gl->Renderer() != GLRenderer::Adreno205;
   554 }
   556 }
   557 }

mercurial