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