Coverage Report

Created: 2018-09-25 14:53

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