tor-browser

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

GLTextureImage.cpp (15586B)


      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 "GLTextureImage.h"
      7 #include "GLContext.h"
      8 #include "gfxContext.h"
      9 #include "gfxPlatform.h"
     10 #include "gfxUtils.h"
     11 #include "gfx2DGlue.h"
     12 #include "mozilla/gfx/2D.h"
     13 #include "ScopedGLHelpers.h"
     14 #include "GLUploadHelpers.h"
     15 #include "GfxTexturesReporter.h"
     16 
     17 using namespace mozilla::gfx;
     18 
     19 namespace mozilla {
     20 namespace gl {
     21 
     22 already_AddRefed<TextureImage> CreateTextureImage(
     23    GLContext* gl, const gfx::IntSize& aSize,
     24    TextureImage::ContentType aContentType, GLenum aWrapMode,
     25    TextureImage::Flags aFlags, TextureImage::ImageFormat aImageFormat) {
     26  GLint maxTextureSize;
     27  gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, &maxTextureSize);
     28  if (aSize.width > maxTextureSize || aSize.height > maxTextureSize) {
     29    NS_ASSERTION(aWrapMode == LOCAL_GL_CLAMP_TO_EDGE,
     30                 "Can't support wrapping with tiles!");
     31    return CreateTiledTextureImage(gl, aSize, aContentType, aFlags,
     32                                   aImageFormat);
     33  } else {
     34    return CreateBasicTextureImage(gl, aSize, aContentType, aWrapMode, aFlags);
     35  }
     36 }
     37 
     38 static already_AddRefed<TextureImage> TileGenFunc(
     39    GLContext* gl, const IntSize& aSize, TextureImage::ContentType aContentType,
     40    TextureImage::Flags aFlags, TextureImage::ImageFormat aImageFormat) {
     41  return CreateBasicTextureImage(gl, aSize, aContentType,
     42                                 LOCAL_GL_CLAMP_TO_EDGE, aFlags);
     43 }
     44 
     45 already_AddRefed<TextureImage> TextureImage::Create(
     46    GLContext* gl, const gfx::IntSize& size,
     47    TextureImage::ContentType contentType, GLenum wrapMode,
     48    TextureImage::Flags flags) {
     49  return CreateTextureImage(gl, size, contentType, wrapMode, flags);
     50 }
     51 
     52 bool TextureImage::UpdateFromDataSource(gfx::DataSourceSurface* aSurface,
     53                                        const nsIntRegion* aDestRegion,
     54                                        const gfx::IntPoint* aSrcOffset,
     55                                        const gfx::IntPoint* aDstOffset) {
     56  nsIntRegion destRegion = aDestRegion
     57                               ? *aDestRegion
     58                               : IntRect(0, 0, aSurface->GetSize().width,
     59                                         aSurface->GetSize().height);
     60  gfx::IntPoint srcPoint = aSrcOffset ? *aSrcOffset : gfx::IntPoint(0, 0);
     61  gfx::IntPoint srcPointOut = aDstOffset ? *aDstOffset : gfx::IntPoint(0, 0);
     62  return DirectUpdate(aSurface, destRegion, srcPoint, srcPointOut);
     63 }
     64 
     65 gfx::IntRect TextureImage::GetTileRect() {
     66  return gfx::IntRect(gfx::IntPoint(0, 0), mSize);
     67 }
     68 
     69 gfx::IntRect TextureImage::GetSrcTileRect() { return GetTileRect(); }
     70 
     71 void TextureImage::UpdateUploadSize(size_t amount) {
     72  if (mUploadSize > 0) {
     73    GfxTexturesReporter::UpdateAmount(GfxTexturesReporter::MemoryFreed,
     74                                      mUploadSize);
     75  }
     76  mUploadSize = amount;
     77  GfxTexturesReporter::UpdateAmount(GfxTexturesReporter::MemoryAllocated,
     78                                    mUploadSize);
     79 }
     80 
     81 BasicTextureImage::~BasicTextureImage() {
     82  GLContext* ctx = mGLContext;
     83  if (ctx->IsDestroyed() || !ctx->IsValidOwningThread()) {
     84    ctx = ctx->GetSharedContext();
     85  }
     86 
     87  // If we have a context, then we need to delete the texture;
     88  // if we don't have a context (either real or shared),
     89  // then they went away when the contex was deleted, because it
     90  // was the only one that had access to it.
     91  if (ctx && ctx->MakeCurrent()) {
     92    ctx->fDeleteTextures(1, &mTexture);
     93  }
     94 }
     95 
     96 void BasicTextureImage::BindTexture(GLenum aTextureUnit) {
     97  mGLContext->fActiveTexture(aTextureUnit);
     98  mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
     99  mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
    100 }
    101 
    102 bool BasicTextureImage::DirectUpdate(
    103    gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion,
    104    const gfx::IntPoint& aSrcOffset /* = gfx::IntPoint(0, 0) */,
    105    const gfx::IntPoint& aDstOffset /* = gfx::IntPoint(0, 0) */) {
    106  nsIntRegion region;
    107  if (mTextureState == Valid) {
    108    region = aRegion;
    109  } else {
    110    region = nsIntRegion(IntRect(0, 0, mSize.width, mSize.height));
    111  }
    112  bool needInit = mTextureState == Created;
    113  size_t uploadSize;
    114 
    115  mTextureFormat =
    116      UploadSurfaceToTexture(mGLContext, aSurf, region, mTexture, mSize,
    117                             &uploadSize, needInit, aSrcOffset, aDstOffset);
    118  if (mTextureFormat == SurfaceFormat::UNKNOWN) {
    119    return false;
    120  }
    121 
    122  if (uploadSize > 0) {
    123    UpdateUploadSize(uploadSize);
    124  }
    125  mTextureState = Valid;
    126  return true;
    127 }
    128 
    129 void BasicTextureImage::Resize(const gfx::IntSize& aSize) {
    130  mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
    131 
    132  // This matches the logic in UploadImageDataToTexture so that
    133  // we avoid mixing formats.
    134  GLenum format;
    135  GLenum type;
    136  if (mGLContext->GetPreferredARGB32Format() == LOCAL_GL_BGRA) {
    137    MOZ_ASSERT(!mGLContext->IsGLES());
    138    format = LOCAL_GL_BGRA;
    139    type = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV;
    140  } else {
    141    format = LOCAL_GL_RGBA;
    142    type = LOCAL_GL_UNSIGNED_BYTE;
    143  }
    144 
    145  mGLContext->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA, aSize.width,
    146                          aSize.height, 0, format, type, nullptr);
    147 
    148  mTextureState = Allocated;
    149  mSize = aSize;
    150 }
    151 
    152 gfx::IntSize TextureImage::GetSize() const { return mSize; }
    153 
    154 TextureImage::TextureImage(const gfx::IntSize& aSize, GLenum aWrapMode,
    155                           ContentType aContentType, Flags aFlags)
    156    : mSize(aSize),
    157      mWrapMode(aWrapMode),
    158      mContentType(aContentType),
    159      mTextureFormat(gfx::SurfaceFormat::UNKNOWN),
    160      mSamplingFilter(SamplingFilter::GOOD),
    161      mFlags(aFlags),
    162      mUploadSize(0) {}
    163 
    164 BasicTextureImage::BasicTextureImage(GLuint aTexture, const gfx::IntSize& aSize,
    165                                     GLenum aWrapMode, ContentType aContentType,
    166                                     GLContext* aContext,
    167                                     TextureImage::Flags aFlags)
    168    : TextureImage(aSize, aWrapMode, aContentType, aFlags),
    169      mTexture(aTexture),
    170      mTextureState(Created),
    171      mGLContext(aContext) {}
    172 
    173 static bool WantsSmallTiles(GLContext* gl) {
    174  // We can't use small tiles on the SGX 540, because of races in texture
    175  // upload.
    176  if (gl->WorkAroundDriverBugs() && gl->Renderer() == GLRenderer::SGX540)
    177    return false;
    178 
    179  // We should use small tiles for good performance if we can't use
    180  // glTexSubImage2D() for some reason.
    181  if (!ShouldUploadSubTextures(gl)) return true;
    182 
    183  // Don't use small tiles otherwise. (If we implement incremental texture
    184  // upload, then we will want to revisit this.)
    185  return false;
    186 }
    187 
    188 TiledTextureImage::TiledTextureImage(GLContext* aGL, gfx::IntSize aSize,
    189                                     TextureImage::ContentType aContentType,
    190                                     TextureImage::Flags aFlags,
    191                                     TextureImage::ImageFormat aImageFormat)
    192    : TextureImage(aSize, LOCAL_GL_CLAMP_TO_EDGE, aContentType, aFlags),
    193      mCurrentImage(0),
    194      mIterationCallback(nullptr),
    195      mIterationCallbackData(nullptr),
    196      mTileSize(0),
    197      mRows(0),
    198      mColumns(0),
    199      mGL(aGL),
    200      mTextureState(Created),
    201      mImageFormat(aImageFormat) {
    202  if (!(aFlags & TextureImage::DisallowBigImage) && WantsSmallTiles(mGL)) {
    203    mTileSize = 256;
    204  } else {
    205    mGL->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, (GLint*)&mTileSize);
    206  }
    207  if (aSize.width != 0 && aSize.height != 0) {
    208    Resize(aSize);
    209  }
    210 }
    211 
    212 TiledTextureImage::~TiledTextureImage() = default;
    213 
    214 bool TiledTextureImage::DirectUpdate(
    215    gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion,
    216    const gfx::IntPoint& aSrcOffset /* = gfx::IntPoint(0, 0) */,
    217    const gfx::IntPoint& aDstOffset /* = gfx::IntPoint(0, 0) */) {
    218  // We don't handle non-zero aDstOffset
    219  MOZ_RELEASE_ASSERT(aDstOffset == gfx::IntPoint());
    220 
    221  if (mSize.width == 0 || mSize.height == 0) {
    222    return true;
    223  }
    224 
    225  nsIntRegion region;
    226 
    227  if (mTextureState != Valid) {
    228    IntRect bounds = IntRect(0, 0, mSize.width, mSize.height);
    229    region = nsIntRegion(bounds);
    230  } else {
    231    region = aRegion;
    232  }
    233 
    234  bool result = true;
    235  int oldCurrentImage = mCurrentImage;
    236  BeginBigImageIteration();
    237  do {
    238    IntRect tileRect = GetSrcTileRect();
    239    int xPos = tileRect.X();
    240    int yPos = tileRect.Y();
    241 
    242    nsIntRegion tileRegion;
    243    tileRegion.And(region, tileRect);  // intersect with tile
    244 
    245    if (tileRegion.IsEmpty()) continue;
    246 
    247    tileRegion.MoveBy(-xPos, -yPos);  // translate into tile local space
    248 
    249    result &= mImages[mCurrentImage]->DirectUpdate(
    250        aSurf, tileRegion, aSrcOffset + gfx::IntPoint(xPos, yPos));
    251 
    252    if (mCurrentImage == mImages.Length() - 1) {
    253      // We know we're done, but we still need to ensure that the callback
    254      // gets called (e.g. to update the uploaded region).
    255      NextTile();
    256      break;
    257    }
    258    // Override a callback cancelling iteration if the texture wasn't valid.
    259    // We need to force the update in that situation, or we may end up
    260    // showing invalid/out-of-date texture data.
    261  } while (NextTile() || (mTextureState != Valid));
    262  mCurrentImage = oldCurrentImage;
    263 
    264  mTextureFormat = mImages[0]->GetTextureFormat();
    265  mTextureState = Valid;
    266  return result;
    267 }
    268 
    269 void TiledTextureImage::BeginBigImageIteration() { mCurrentImage = 0; }
    270 
    271 bool TiledTextureImage::NextTile() {
    272  bool continueIteration = true;
    273 
    274  if (mIterationCallback)
    275    continueIteration =
    276        mIterationCallback(this, mCurrentImage, mIterationCallbackData);
    277 
    278  if (mCurrentImage + 1 < mImages.Length()) {
    279    mCurrentImage++;
    280    return continueIteration;
    281  }
    282  return false;
    283 }
    284 
    285 void TiledTextureImage::SetIterationCallback(
    286    BigImageIterationCallback aCallback, void* aCallbackData) {
    287  mIterationCallback = aCallback;
    288  mIterationCallbackData = aCallbackData;
    289 }
    290 
    291 gfx::IntRect TiledTextureImage::GetTileRect() {
    292  if (!GetTileCount()) {
    293    return gfx::IntRect();
    294  }
    295  gfx::IntRect rect = mImages[mCurrentImage]->GetTileRect();
    296  unsigned int xPos = (mCurrentImage % mColumns) * mTileSize;
    297  unsigned int yPos = (mCurrentImage / mColumns) * mTileSize;
    298  rect.MoveBy(xPos, yPos);
    299  return rect;
    300 }
    301 
    302 gfx::IntRect TiledTextureImage::GetSrcTileRect() {
    303  gfx::IntRect rect = GetTileRect();
    304  const bool needsYFlip = mFlags & OriginBottomLeft;
    305  unsigned int srcY =
    306      needsYFlip ? mSize.height - rect.Height() - rect.Y() : rect.Y();
    307  return gfx::IntRect(rect.X(), srcY, rect.Width(), rect.Height());
    308 }
    309 
    310 void TiledTextureImage::BindTexture(GLenum aTextureUnit) {
    311  if (!GetTileCount()) {
    312    return;
    313  }
    314  mImages[mCurrentImage]->BindTexture(aTextureUnit);
    315 }
    316 
    317 /*
    318 * Resize, trying to reuse tiles. The reuse strategy is to decide on reuse per
    319 * column. A tile on a column is reused if it hasn't changed size, otherwise it
    320 * is discarded/replaced. Extra tiles on a column are pruned after iterating
    321 * each column, and extra rows are pruned after iteration over the entire image
    322 * finishes.
    323 */
    324 void TiledTextureImage::Resize(const gfx::IntSize& aSize) {
    325  if (mSize == aSize && mTextureState != Created) {
    326    return;
    327  }
    328 
    329  // calculate rows and columns, rounding up
    330  unsigned int columns = (aSize.width + mTileSize - 1) / mTileSize;
    331  unsigned int rows = (aSize.height + mTileSize - 1) / mTileSize;
    332 
    333  // Iterate over old tile-store and insert/remove tiles as necessary
    334  int row;
    335  unsigned int i = 0;
    336  for (row = 0; row < (int)rows; row++) {
    337    // If we've gone beyond how many rows there were before, set mColumns to
    338    // zero so that we only create new tiles.
    339    if (row >= (int)mRows) mColumns = 0;
    340 
    341    // Similarly, if we're on the last row of old tiles and the height has
    342    // changed, discard all tiles in that row.
    343    // This will cause the pruning of columns not to work, but we don't need
    344    // to worry about that, as no more tiles will be reused past this point
    345    // anyway.
    346    if ((row == (int)mRows - 1) && (aSize.height != mSize.height)) mColumns = 0;
    347 
    348    int col;
    349    for (col = 0; col < (int)columns; col++) {
    350      IntSize size(  // use tilesize first, then the remainder
    351          (col + 1) * mTileSize > (unsigned int)aSize.width
    352              ? aSize.width % mTileSize
    353              : mTileSize,
    354          (row + 1) * mTileSize > (unsigned int)aSize.height
    355              ? aSize.height % mTileSize
    356              : mTileSize);
    357 
    358      bool replace = false;
    359 
    360      // Check if we can re-use old tiles.
    361      if (col < (int)mColumns) {
    362        // Reuse an existing tile. If the tile is an end-tile and the
    363        // width differs, replace it instead.
    364        if (mSize.width != aSize.width) {
    365          if (col == (int)mColumns - 1) {
    366            // Tile at the end of the old column, replace it with
    367            // a new one.
    368            replace = true;
    369          } else if (col == (int)columns - 1) {
    370            // Tile at the end of the new column, create a new one.
    371          } else {
    372            // Before the last column on both the old and new sizes,
    373            // reuse existing tile.
    374            i++;
    375            continue;
    376          }
    377        } else {
    378          // Width hasn't changed, reuse existing tile.
    379          i++;
    380          continue;
    381        }
    382      }
    383 
    384      // Create a new tile.
    385      RefPtr<TextureImage> teximg =
    386          TileGenFunc(mGL, size, mContentType, mFlags, mImageFormat);
    387      if (replace)
    388        mImages.ReplaceElementAt(i, teximg);
    389      else
    390        mImages.InsertElementAt(i, teximg);
    391      i++;
    392    }
    393 
    394    // Prune any unused tiles on the end of the column.
    395    if (row < (int)mRows) {
    396      for (col = (int)mColumns - col; col > 0; col--) {
    397        mImages.RemoveElementAt(i);
    398      }
    399    }
    400  }
    401 
    402  // Prune any unused tiles at the end of the store.
    403  mImages.RemoveLastElements(mImages.Length() - i);
    404 
    405  // Reset tile-store properties.
    406  mRows = rows;
    407  mColumns = columns;
    408  mSize = aSize;
    409  mTextureState = Allocated;
    410  mCurrentImage = 0;
    411 }
    412 
    413 uint32_t TiledTextureImage::GetTileCount() { return mImages.Length(); }
    414 
    415 already_AddRefed<TextureImage> CreateTiledTextureImage(
    416    GLContext* aGL, const gfx::IntSize& aSize,
    417    TextureImage::ContentType aContentType, TextureImage::Flags aFlags,
    418    TextureImage::ImageFormat aImageFormat) {
    419  RefPtr<TextureImage> texImage =
    420      static_cast<TextureImage*>(new gl::TiledTextureImage(
    421          aGL, aSize, aContentType, aFlags, aImageFormat));
    422  return texImage.forget();
    423 }
    424 
    425 already_AddRefed<TextureImage> CreateBasicTextureImage(
    426    GLContext* aGL, const gfx::IntSize& aSize,
    427    TextureImage::ContentType aContentType, GLenum aWrapMode,
    428    TextureImage::Flags aFlags) {
    429  bool useNearestFilter = aFlags & TextureImage::UseNearestFilter;
    430  if (!aGL->MakeCurrent()) {
    431    return nullptr;
    432  }
    433 
    434  GLuint texture = 0;
    435  aGL->fGenTextures(1, &texture);
    436 
    437  ScopedBindTexture bind(aGL, texture);
    438 
    439  GLint texfilter = useNearestFilter ? LOCAL_GL_NEAREST : LOCAL_GL_LINEAR;
    440  aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER,
    441                      texfilter);
    442  aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER,
    443                      texfilter);
    444  aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, aWrapMode);
    445  aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, aWrapMode);
    446 
    447  RefPtr<BasicTextureImage> texImage = new BasicTextureImage(
    448      texture, aSize, aWrapMode, aContentType, aGL, aFlags);
    449  return texImage.forget();
    450 }
    451 
    452 }  // namespace gl
    453 }  // namespace mozilla