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 "GLTextureImage.h"
8 #include "gfxContext.h"
9 #include "gfxPlatform.h"
11 #include "gfx2DGlue.h"
12 #include "mozilla/gfx/2D.h"
13 #include "ScopedGLHelpers.h"
14 #include "GLUploadHelpers.h"
15 #include "GfxTexturesReporter.h"
17 #include "TextureImageEGL.h"
19 using namespace mozilla::gfx
;
24 already_AddRefed
<TextureImage
>
25 CreateTextureImage(GLContext
* gl
,
26 const gfx::IntSize
& aSize
,
27 TextureImage::ContentType aContentType
,
29 TextureImage::Flags aFlags
,
30 TextureImage::ImageFormat aImageFormat
)
32 switch (gl
->GetContextType()) {
33 case GLContextType::EGL
:
34 return CreateTextureImageEGL(gl
, aSize
, aContentType
, aWrapMode
, aFlags
, aImageFormat
);
37 gl
->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE
, &maxTextureSize
);
38 if (aSize
.width
> maxTextureSize
|| aSize
.height
> maxTextureSize
) {
39 NS_ASSERTION(aWrapMode
== LOCAL_GL_CLAMP_TO_EDGE
, "Can't support wrapping with tiles!");
40 return CreateTiledTextureImage(gl
, aSize
, aContentType
, aFlags
, aImageFormat
);
42 return CreateBasicTextureImage(gl
, aSize
, aContentType
, aWrapMode
, aFlags
);
49 static already_AddRefed
<TextureImage
>
50 TileGenFunc(GLContext
* gl
,
52 TextureImage::ContentType aContentType
,
53 TextureImage::Flags aFlags
,
54 TextureImage::ImageFormat aImageFormat
)
56 switch (gl
->GetContextType()) {
57 case GLContextType::EGL
:
58 return TileGenFuncEGL(gl
, aSize
, aContentType
, aFlags
, aImageFormat
);
60 return CreateBasicTextureImage(gl
, aSize
, aContentType
,
61 LOCAL_GL_CLAMP_TO_EDGE
, aFlags
);
65 already_AddRefed
<TextureImage
>
66 TextureImage::Create(GLContext
* gl
,
67 const gfx::IntSize
& size
,
68 TextureImage::ContentType contentType
,
70 TextureImage::Flags flags
)
72 return CreateTextureImage(gl
, size
, contentType
, wrapMode
, flags
);
76 TextureImage::UpdateFromDataSource(gfx::DataSourceSurface
* aSurface
,
77 const nsIntRegion
* aDestRegion
,
78 const gfx::IntPoint
* aSrcPoint
)
80 nsIntRegion destRegion
= aDestRegion
? *aDestRegion
82 aSurface
->GetSize().width
,
83 aSurface
->GetSize().height
);
84 gfx::IntPoint srcPoint
= aSrcPoint
? *aSrcPoint
85 : gfx::IntPoint(0, 0);
86 return DirectUpdate(aSurface
, destRegion
, srcPoint
);
89 gfx::IntRect
TextureImage::GetTileRect() {
90 return gfx::IntRect(gfx::IntPoint(0,0), mSize
);
93 gfx::IntRect
TextureImage::GetSrcTileRect() {
98 TextureImage::UpdateUploadSize(size_t amount
)
100 if (mUploadSize
> 0) {
101 GfxTexturesReporter::UpdateAmount(GfxTexturesReporter::MemoryFreed
, mUploadSize
);
103 mUploadSize
= amount
;
104 GfxTexturesReporter::UpdateAmount(GfxTexturesReporter::MemoryAllocated
, mUploadSize
);
107 BasicTextureImage::~BasicTextureImage()
109 GLContext
* ctx
= mGLContext
;
110 if (ctx
->IsDestroyed() || !ctx
->IsOwningThreadCurrent()) {
111 ctx
= ctx
->GetSharedContext();
114 // If we have a context, then we need to delete the texture;
115 // if we don't have a context (either real or shared),
116 // then they went away when the contex was deleted, because it
117 // was the only one that had access to it.
118 if (ctx
&& ctx
->MakeCurrent()) {
119 ctx
->fDeleteTextures(1, &mTexture
);
124 BasicTextureImage::BindTexture(GLenum aTextureUnit
)
126 mGLContext
->fActiveTexture(aTextureUnit
);
127 mGLContext
->fBindTexture(LOCAL_GL_TEXTURE_2D
, mTexture
);
128 mGLContext
->fActiveTexture(LOCAL_GL_TEXTURE0
);
132 BasicTextureImage::DirectUpdate(gfx::DataSourceSurface
* aSurf
, const nsIntRegion
& aRegion
, const gfx::IntPoint
& aFrom
/* = gfx::IntPoint(0, 0) */)
135 if (mTextureState
== Valid
) {
138 region
= nsIntRegion(IntRect(0, 0, mSize
.width
, mSize
.height
));
140 bool needInit
= mTextureState
== Created
;
144 UploadSurfaceToTexture(mGLContext
,
152 if (mTextureFormat
== SurfaceFormat::UNKNOWN
) {
156 if (uploadSize
> 0) {
157 UpdateUploadSize(uploadSize
);
159 mTextureState
= Valid
;
164 BasicTextureImage::Resize(const gfx::IntSize
& aSize
)
166 mGLContext
->fBindTexture(LOCAL_GL_TEXTURE_2D
, mTexture
);
168 // This matches the logic in UploadImageDataToTexture so that
169 // we avoid mixing formats.
172 if (mGLContext
->GetPreferredARGB32Format() == LOCAL_GL_BGRA
) {
173 MOZ_ASSERT(!mGLContext
->IsGLES());
174 format
= LOCAL_GL_BGRA
;
175 type
= LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV
;
177 format
= LOCAL_GL_RGBA
;
178 type
= LOCAL_GL_UNSIGNED_BYTE
;
181 mGLContext
->fTexImage2D(LOCAL_GL_TEXTURE_2D
,
191 mTextureState
= Allocated
;
195 gfx::IntSize
TextureImage::GetSize() const {
199 TextureImage::TextureImage(const gfx::IntSize
& aSize
,
200 GLenum aWrapMode
, ContentType aContentType
,
203 , mWrapMode(aWrapMode
)
204 , mContentType(aContentType
)
205 , mTextureFormat(gfx::SurfaceFormat::UNKNOWN
)
206 , mSamplingFilter(SamplingFilter::GOOD
)
211 BasicTextureImage::BasicTextureImage(GLuint aTexture
,
212 const gfx::IntSize
& aSize
,
214 ContentType aContentType
,
216 TextureImage::Flags aFlags
)
217 : TextureImage(aSize
, aWrapMode
, aContentType
, aFlags
)
219 , mTextureState(Created
)
220 , mGLContext(aContext
)
224 WantsSmallTiles(GLContext
* gl
)
226 // We can't use small tiles on the SGX 540, because of races in texture upload.
227 if (gl
->WorkAroundDriverBugs() &&
228 gl
->Renderer() == GLRenderer::SGX540
)
231 // We should use small tiles for good performance if we can't use
232 // glTexSubImage2D() for some reason.
233 if (!CanUploadSubTextures(gl
))
236 // Don't use small tiles otherwise. (If we implement incremental texture upload,
237 // then we will want to revisit this.)
241 TiledTextureImage::TiledTextureImage(GLContext
* aGL
,
243 TextureImage::ContentType aContentType
,
244 TextureImage::Flags aFlags
,
245 TextureImage::ImageFormat aImageFormat
)
246 : TextureImage(aSize
, LOCAL_GL_CLAMP_TO_EDGE
, aContentType
, aFlags
)
248 , mIterationCallback(nullptr)
249 , mIterationCallbackData(nullptr)
254 , mTextureState(Created
)
255 , mImageFormat(aImageFormat
)
257 if (!(aFlags
& TextureImage::DisallowBigImage
) && WantsSmallTiles(mGL
)) {
260 mGL
->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE
, (GLint
*) &mTileSize
);
262 if (aSize
.width
!= 0 && aSize
.height
!= 0) {
267 TiledTextureImage::~TiledTextureImage()
272 TiledTextureImage::DirectUpdate(gfx::DataSourceSurface
* aSurf
, const nsIntRegion
& aRegion
, const gfx::IntPoint
& aFrom
/* = gfx::IntPoint(0, 0) */)
274 if (mSize
.width
== 0 || mSize
.height
== 0) {
280 if (mTextureState
!= Valid
) {
281 IntRect bounds
= IntRect(0, 0, mSize
.width
, mSize
.height
);
282 region
= nsIntRegion(bounds
);
288 int oldCurrentImage
= mCurrentImage
;
289 BeginBigImageIteration();
291 IntRect tileRect
= GetSrcTileRect();
292 int xPos
= tileRect
.X();
293 int yPos
= tileRect
.Y();
295 nsIntRegion tileRegion
;
296 tileRegion
.And(region
, tileRect
); // intersect with tile
298 if (tileRegion
.IsEmpty())
301 tileRegion
.MoveBy(-xPos
, -yPos
); // translate into tile local space
303 result
&= mImages
[mCurrentImage
]->
304 DirectUpdate(aSurf
, tileRegion
, aFrom
+ gfx::IntPoint(xPos
, yPos
));
306 if (mCurrentImage
== mImages
.Length() - 1) {
307 // We know we're done, but we still need to ensure that the callback
308 // gets called (e.g. to update the uploaded region).
312 // Override a callback cancelling iteration if the texture wasn't valid.
313 // We need to force the update in that situation, or we may end up
314 // showing invalid/out-of-date texture data.
315 } while (NextTile() || (mTextureState
!= Valid
));
316 mCurrentImage
= oldCurrentImage
;
318 mTextureFormat
= mImages
[0]->GetTextureFormat();
319 mTextureState
= Valid
;
323 void TiledTextureImage::BeginBigImageIteration()
328 bool TiledTextureImage::NextTile()
330 bool continueIteration
= true;
332 if (mIterationCallback
)
333 continueIteration
= mIterationCallback(this, mCurrentImage
,
334 mIterationCallbackData
);
336 if (mCurrentImage
+ 1 < mImages
.Length()) {
338 return continueIteration
;
343 void TiledTextureImage::SetIterationCallback(BigImageIterationCallback aCallback
,
346 mIterationCallback
= aCallback
;
347 mIterationCallbackData
= aCallbackData
;
350 gfx::IntRect
TiledTextureImage::GetTileRect()
352 if (!GetTileCount()) {
353 return gfx::IntRect();
355 gfx::IntRect rect
= mImages
[mCurrentImage
]->GetTileRect();
356 unsigned int xPos
= (mCurrentImage
% mColumns
) * mTileSize
;
357 unsigned int yPos
= (mCurrentImage
/ mColumns
) * mTileSize
;
358 rect
.MoveBy(xPos
, yPos
);
362 gfx::IntRect
TiledTextureImage::GetSrcTileRect()
364 gfx::IntRect rect
= GetTileRect();
365 const bool needsYFlip
= mFlags
& OriginBottomLeft
;
366 unsigned int srcY
= needsYFlip
? mSize
.height
- rect
.Height() - rect
.Y()
368 return gfx::IntRect(rect
.X(), srcY
, rect
.Width(), rect
.Height());
372 TiledTextureImage::BindTexture(GLenum aTextureUnit
)
374 if (!GetTileCount()) {
377 mImages
[mCurrentImage
]->BindTexture(aTextureUnit
);
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
387 void TiledTextureImage::Resize(const gfx::IntSize
& aSize
)
389 if (mSize
== aSize
&& mTextureState
!= Created
) {
393 // calculate rows and columns, rounding up
394 unsigned int columns
= (aSize
.width
+ mTileSize
- 1) / mTileSize
;
395 unsigned int rows
= (aSize
.height
+ mTileSize
- 1) / mTileSize
;
397 // Iterate over old tile-store and insert/remove tiles as necessary
400 for (row
= 0; row
< (int)rows
; row
++) {
401 // If we've gone beyond how many rows there were before, set mColumns to
402 // zero so that we only create new tiles.
403 if (row
>= (int)mRows
)
406 // Similarly, if we're on the last row of old tiles and the height has
407 // changed, discard all tiles in that row.
408 // This will cause the pruning of columns not to work, but we don't need
409 // to worry about that, as no more tiles will be reused past this point
411 if ((row
== (int)mRows
- 1) && (aSize
.height
!= mSize
.height
))
415 for (col
= 0; col
< (int)columns
; col
++) {
416 IntSize
size( // use tilesize first, then the remainder
417 (col
+1) * mTileSize
> (unsigned int)aSize
.width
? aSize
.width
% mTileSize
: mTileSize
,
418 (row
+1) * mTileSize
> (unsigned int)aSize
.height
? aSize
.height
% mTileSize
: mTileSize
);
420 bool replace
= false;
422 // Check if we can re-use old tiles.
423 if (col
< (int)mColumns
) {
424 // Reuse an existing tile. If the tile is an end-tile and the
425 // width differs, replace it instead.
426 if (mSize
.width
!= aSize
.width
) {
427 if (col
== (int)mColumns
- 1) {
428 // Tile at the end of the old column, replace it with
431 } else if (col
== (int)columns
- 1) {
432 // Tile at the end of the new column, create a new one.
434 // Before the last column on both the old and new sizes,
435 // reuse existing tile.
440 // Width hasn't changed, reuse existing tile.
446 // Create a new tile.
447 RefPtr
<TextureImage
> teximg
=
448 TileGenFunc(mGL
, size
, mContentType
, mFlags
, mImageFormat
);
450 mImages
.ReplaceElementAt(i
, teximg
);
452 mImages
.InsertElementAt(i
, teximg
);
456 // Prune any unused tiles on the end of the column.
457 if (row
< (int)mRows
) {
458 for (col
= (int)mColumns
- col
; col
> 0; col
--) {
459 mImages
.RemoveElementAt(i
);
464 // Prune any unused tiles at the end of the store.
465 unsigned int length
= mImages
.Length();
466 for (; i
< length
; i
++)
467 mImages
.RemoveLastElement();
469 // Reset tile-store properties.
473 mTextureState
= Allocated
;
477 uint32_t TiledTextureImage::GetTileCount()
479 return mImages
.Length();
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
)
489 RefPtr
<TextureImage
> texImage
= static_cast<TextureImage
*>(
490 new gl::TiledTextureImage(aGL
, aSize
, aContentType
, aFlags
, aImageFormat
));
491 return texImage
.forget();
495 already_AddRefed
<TextureImage
>
496 CreateBasicTextureImage(GLContext
* aGL
,
497 const gfx::IntSize
& aSize
,
498 TextureImage::ContentType aContentType
,
500 TextureImage::Flags aFlags
)
502 bool useNearestFilter
= aFlags
& TextureImage::UseNearestFilter
;
503 if (!aGL
->MakeCurrent()) {
508 aGL
->fGenTextures(1, &texture
);
510 ScopedBindTexture
bind(aGL
, texture
);
512 GLint texfilter
= useNearestFilter
? LOCAL_GL_NEAREST
: LOCAL_GL_LINEAR
;
513 aGL
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_MIN_FILTER
, texfilter
);
514 aGL
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_MAG_FILTER
, texfilter
);
515 aGL
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_WRAP_S
, aWrapMode
);
516 aGL
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_WRAP_T
, aWrapMode
);
518 RefPtr
<BasicTextureImage
> texImage
=
519 new BasicTextureImage(texture
, aSize
, aWrapMode
, aContentType
,
521 return texImage
.forget();
525 } // namespace mozilla