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/. */
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 using namespace mozilla::gfx
;
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
) {
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
,
34 return CreateBasicTextureImage(gl
, aSize
, aContentType
, aWrapMode
, aFlags
);
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
);
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
);
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
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
);
65 gfx::IntRect
TextureImage::GetTileRect() {
66 return gfx::IntRect(gfx::IntPoint(0, 0), mSize
);
69 gfx::IntRect
TextureImage::GetSrcTileRect() { return GetTileRect(); }
71 void TextureImage::UpdateUploadSize(size_t amount
) {
72 if (mUploadSize
> 0) {
73 GfxTexturesReporter::UpdateAmount(GfxTexturesReporter::MemoryFreed
,
77 GfxTexturesReporter::UpdateAmount(GfxTexturesReporter::MemoryAllocated
,
81 BasicTextureImage::~BasicTextureImage() {
82 GLContext
* ctx
= mGLContext
;
83 if (ctx
->IsDestroyed() || !ctx
->IsValidOwningThread()) {
84 ctx
= ctx
->GetSharedContext();
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
);
96 void BasicTextureImage::BindTexture(GLenum aTextureUnit
) {
97 mGLContext
->fActiveTexture(aTextureUnit
);
98 mGLContext
->fBindTexture(LOCAL_GL_TEXTURE_2D
, mTexture
);
99 mGLContext
->fActiveTexture(LOCAL_GL_TEXTURE0
);
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) */) {
107 if (mTextureState
== Valid
) {
110 region
= nsIntRegion(IntRect(0, 0, mSize
.width
, mSize
.height
));
112 bool needInit
= mTextureState
== Created
;
116 UploadSurfaceToTexture(mGLContext
, aSurf
, region
, mTexture
, mSize
,
117 &uploadSize
, needInit
, aSrcOffset
, aDstOffset
);
118 if (mTextureFormat
== SurfaceFormat::UNKNOWN
) {
122 if (uploadSize
> 0) {
123 UpdateUploadSize(uploadSize
);
125 mTextureState
= Valid
;
129 void BasicTextureImage::Resize(const gfx::IntSize
& aSize
) {
130 mGLContext
->fBindTexture(LOCAL_GL_TEXTURE_2D
, mTexture
);
132 // This matches the logic in UploadImageDataToTexture so that
133 // we avoid mixing formats.
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
;
141 format
= LOCAL_GL_RGBA
;
142 type
= LOCAL_GL_UNSIGNED_BYTE
;
145 mGLContext
->fTexImage2D(LOCAL_GL_TEXTURE_2D
, 0, LOCAL_GL_RGBA
, aSize
.width
,
146 aSize
.height
, 0, format
, type
, nullptr);
148 mTextureState
= Allocated
;
152 gfx::IntSize
TextureImage::GetSize() const { return mSize
; }
154 TextureImage::TextureImage(const gfx::IntSize
& aSize
, GLenum aWrapMode
,
155 ContentType aContentType
, Flags aFlags
)
157 mWrapMode(aWrapMode
),
158 mContentType(aContentType
),
159 mTextureFormat(gfx::SurfaceFormat::UNKNOWN
),
160 mSamplingFilter(SamplingFilter::GOOD
),
164 BasicTextureImage::BasicTextureImage(GLuint aTexture
, const gfx::IntSize
& aSize
,
165 GLenum aWrapMode
, ContentType aContentType
,
167 TextureImage::Flags aFlags
)
168 : TextureImage(aSize
, aWrapMode
, aContentType
, aFlags
),
170 mTextureState(Created
),
171 mGLContext(aContext
) {}
173 static bool WantsSmallTiles(GLContext
* gl
) {
174 // We can't use small tiles on the SGX 540, because of races in texture
176 if (gl
->WorkAroundDriverBugs() && gl
->Renderer() == GLRenderer::SGX540
)
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;
183 // Don't use small tiles otherwise. (If we implement incremental texture
184 // upload, then we will want to revisit this.)
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
),
194 mIterationCallback(nullptr),
195 mIterationCallbackData(nullptr),
200 mTextureState(Created
),
201 mImageFormat(aImageFormat
) {
202 if (!(aFlags
& TextureImage::DisallowBigImage
) && WantsSmallTiles(mGL
)) {
205 mGL
->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE
, (GLint
*)&mTileSize
);
207 if (aSize
.width
!= 0 && aSize
.height
!= 0) {
212 TiledTextureImage::~TiledTextureImage() = default;
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());
221 if (mSize
.width
== 0 || mSize
.height
== 0) {
227 if (mTextureState
!= Valid
) {
228 IntRect bounds
= IntRect(0, 0, mSize
.width
, mSize
.height
);
229 region
= nsIntRegion(bounds
);
235 int oldCurrentImage
= mCurrentImage
;
236 BeginBigImageIteration();
238 IntRect tileRect
= GetSrcTileRect();
239 int xPos
= tileRect
.X();
240 int yPos
= tileRect
.Y();
242 nsIntRegion tileRegion
;
243 tileRegion
.And(region
, tileRect
); // intersect with tile
245 if (tileRegion
.IsEmpty()) continue;
247 tileRegion
.MoveBy(-xPos
, -yPos
); // translate into tile local space
249 result
&= mImages
[mCurrentImage
]->DirectUpdate(
250 aSurf
, tileRegion
, aSrcOffset
+ gfx::IntPoint(xPos
, yPos
));
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).
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
;
264 mTextureFormat
= mImages
[0]->GetTextureFormat();
265 mTextureState
= Valid
;
269 void TiledTextureImage::BeginBigImageIteration() { mCurrentImage
= 0; }
271 bool TiledTextureImage::NextTile() {
272 bool continueIteration
= true;
274 if (mIterationCallback
)
276 mIterationCallback(this, mCurrentImage
, mIterationCallbackData
);
278 if (mCurrentImage
+ 1 < mImages
.Length()) {
280 return continueIteration
;
285 void TiledTextureImage::SetIterationCallback(
286 BigImageIterationCallback aCallback
, void* aCallbackData
) {
287 mIterationCallback
= aCallback
;
288 mIterationCallbackData
= aCallbackData
;
291 gfx::IntRect
TiledTextureImage::GetTileRect() {
292 if (!GetTileCount()) {
293 return gfx::IntRect();
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
);
302 gfx::IntRect
TiledTextureImage::GetSrcTileRect() {
303 gfx::IntRect rect
= GetTileRect();
304 const bool needsYFlip
= mFlags
& OriginBottomLeft
;
306 needsYFlip
? mSize
.height
- rect
.Height() - rect
.Y() : rect
.Y();
307 return gfx::IntRect(rect
.X(), srcY
, rect
.Width(), rect
.Height());
310 void TiledTextureImage::BindTexture(GLenum aTextureUnit
) {
311 if (!GetTileCount()) {
314 mImages
[mCurrentImage
]->BindTexture(aTextureUnit
);
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
324 void TiledTextureImage::Resize(const gfx::IntSize
& aSize
) {
325 if (mSize
== aSize
&& mTextureState
!= Created
) {
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
;
333 // Iterate over old tile-store and insert/remove tiles as necessary
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;
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
346 if ((row
== (int)mRows
- 1) && (aSize
.height
!= mSize
.height
)) mColumns
= 0;
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
354 (row
+ 1) * mTileSize
> (unsigned int)aSize
.height
355 ? aSize
.height
% mTileSize
358 bool replace
= false;
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
369 } else if (col
== (int)columns
- 1) {
370 // Tile at the end of the new column, create a new one.
372 // Before the last column on both the old and new sizes,
373 // reuse existing tile.
378 // Width hasn't changed, reuse existing tile.
384 // Create a new tile.
385 RefPtr
<TextureImage
> teximg
=
386 TileGenFunc(mGL
, size
, mContentType
, mFlags
, mImageFormat
);
388 mImages
.ReplaceElementAt(i
, teximg
);
390 mImages
.InsertElementAt(i
, teximg
);
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
);
402 // Prune any unused tiles at the end of the store.
403 mImages
.RemoveLastElements(mImages
.Length() - i
);
405 // Reset tile-store properties.
409 mTextureState
= Allocated
;
413 uint32_t TiledTextureImage::GetTileCount() { return mImages
.Length(); }
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();
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()) {
435 aGL
->fGenTextures(1, &texture
);
437 ScopedBindTexture
bind(aGL
, texture
);
439 GLint texfilter
= useNearestFilter
? LOCAL_GL_NEAREST
: LOCAL_GL_LINEAR
;
440 aGL
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_MIN_FILTER
,
442 aGL
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_MAG_FILTER
,
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
);
447 RefPtr
<BasicTextureImage
> texImage
= new BasicTextureImage(
448 texture
, aSize
, aWrapMode
, aContentType
, aGL
, aFlags
);
449 return texImage
.forget();
453 } // namespace mozilla