Bug 1876335 - use GRADLE_MAVEN_REPOSITORIES in more places. r=owlish,geckoview-review...
[gecko.git] / gfx / gl / GLTextureImage.cpp
blobe2295753fc918be4850fe3c94f2e1f22ef8953d6
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"
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"
17 using namespace mozilla::gfx;
19 namespace mozilla {
20 namespace gl {
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);
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
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);
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,
74 mUploadSize);
76 mUploadSize = amount;
77 GfxTexturesReporter::UpdateAmount(GfxTexturesReporter::MemoryAllocated,
78 mUploadSize);
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) */) {
106 nsIntRegion region;
107 if (mTextureState == Valid) {
108 region = aRegion;
109 } else {
110 region = nsIntRegion(IntRect(0, 0, mSize.width, mSize.height));
112 bool needInit = mTextureState == Created;
113 size_t uploadSize;
115 mTextureFormat =
116 UploadSurfaceToTexture(mGLContext, aSurf, region, mTexture, mSize,
117 &uploadSize, needInit, aSrcOffset, aDstOffset);
118 if (mTextureFormat == SurfaceFormat::UNKNOWN) {
119 return false;
122 if (uploadSize > 0) {
123 UpdateUploadSize(uploadSize);
125 mTextureState = Valid;
126 return true;
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.
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;
145 mGLContext->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA, aSize.width,
146 aSize.height, 0, format, type, nullptr);
148 mTextureState = Allocated;
149 mSize = aSize;
152 gfx::IntSize TextureImage::GetSize() const { return mSize; }
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) {}
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) {}
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;
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.)
185 return false;
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);
207 if (aSize.width != 0 && aSize.height != 0) {
208 Resize(aSize);
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) {
222 return true;
225 nsIntRegion region;
227 if (mTextureState != Valid) {
228 IntRect bounds = IntRect(0, 0, mSize.width, mSize.height);
229 region = nsIntRegion(bounds);
230 } else {
231 region = aRegion;
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();
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).
255 NextTile();
256 break;
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;
266 return result;
269 void TiledTextureImage::BeginBigImageIteration() { mCurrentImage = 0; }
271 bool TiledTextureImage::NextTile() {
272 bool continueIteration = true;
274 if (mIterationCallback)
275 continueIteration =
276 mIterationCallback(this, mCurrentImage, mIterationCallbackData);
278 if (mCurrentImage + 1 < mImages.Length()) {
279 mCurrentImage++;
280 return continueIteration;
282 return false;
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);
299 return rect;
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());
310 void TiledTextureImage::BindTexture(GLenum aTextureUnit) {
311 if (!GetTileCount()) {
312 return;
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
322 * finishes.
324 void TiledTextureImage::Resize(const gfx::IntSize& aSize) {
325 if (mSize == aSize && mTextureState != Created) {
326 return;
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
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;
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;
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);
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
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;
377 } else {
378 // Width hasn't changed, reuse existing tile.
379 i++;
380 continue;
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++;
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.
406 mRows = rows;
407 mColumns = columns;
408 mSize = aSize;
409 mTextureState = Allocated;
410 mCurrentImage = 0;
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()) {
431 return nullptr;
434 GLuint texture = 0;
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,
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);
447 RefPtr<BasicTextureImage> texImage = new BasicTextureImage(
448 texture, aSize, aWrapMode, aContentType, aGL, aFlags);
449 return texImage.forget();
452 } // namespace gl
453 } // namespace mozilla