Bug 858914 - New texture classes + OGL backend (preffed off). r=bas, nrc
[gecko.git] / gfx / gl / GLTextureImage.cpp
blob7081b9986cb13e406b4557f3d672c7917b552003
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"
7 #include "GLContext.h"
8 #include "gfxContext.h"
9 #include "gfxPlatform.h"
10 #include "gfxUtils.h"
11 #include "gfx2DGlue.h"
13 namespace mozilla {
14 namespace gl {
16 already_AddRefed<TextureImage>
17 TextureImage::Create(GLContext* gl,
18 const nsIntSize& size,
19 TextureImage::ContentType contentType,
20 GLenum wrapMode,
21 TextureImage::Flags flags)
23 return gl->CreateTextureImage(size, contentType, wrapMode, flags);
26 bool
27 TextureImage::UpdateFromDataSource(gfx::DataSourceSurface *aSurface,
28 const nsIntRegion* aDestRegion,
29 const gfx::IntPoint* aSrcPoint)
31 nsIntRegion destRegion = aDestRegion ? *aDestRegion
32 : nsIntRect(0, 0,
33 aSurface->GetSize().width,
34 aSurface->GetSize().height);
35 nsIntPoint thebesSrcPoint = aSrcPoint ? nsIntPoint(aSrcPoint->x, aSrcPoint->y)
36 : nsIntPoint(0, 0);
37 RefPtr<gfxASurface> thebesSurf
38 = new gfxImageSurface(aSurface->GetData(),
39 ThebesIntSize(aSurface->GetSize()),
40 aSurface->Stride(),
41 SurfaceFormatToImageFormat(aSurface->GetFormat()));
42 return DirectUpdate(thebesSurf, destRegion, thebesSrcPoint);
45 BasicTextureImage::~BasicTextureImage()
47 GLContext *ctx = mGLContext;
48 if (ctx->IsDestroyed() || !ctx->IsOwningThreadCurrent()) {
49 ctx = ctx->GetSharedContext();
52 // If we have a context, then we need to delete the texture;
53 // if we don't have a context (either real or shared),
54 // then they went away when the contex was deleted, because it
55 // was the only one that had access to it.
56 if (ctx && !ctx->IsDestroyed()) {
57 mGLContext->MakeCurrent();
58 mGLContext->fDeleteTextures(1, &mTexture);
62 gfxASurface*
63 BasicTextureImage::BeginUpdate(nsIntRegion& aRegion)
65 NS_ASSERTION(!mUpdateSurface, "BeginUpdate() without EndUpdate()?");
67 // determine the region the client will need to repaint
68 if (mGLContext->CanUploadSubTextures()) {
69 GetUpdateRegion(aRegion);
70 } else {
71 aRegion = nsIntRect(nsIntPoint(0, 0), mSize);
74 mUpdateRegion = aRegion;
76 nsIntRect rgnSize = mUpdateRegion.GetBounds();
77 if (!nsIntRect(nsIntPoint(0, 0), mSize).Contains(rgnSize)) {
78 NS_ERROR("update outside of image");
79 return nullptr;
82 ImageFormat format =
83 (GetContentType() == gfxASurface::CONTENT_COLOR) ?
84 gfxASurface::ImageFormatRGB24 : gfxASurface::ImageFormatARGB32;
85 mUpdateSurface =
86 GetSurfaceForUpdate(gfxIntSize(rgnSize.width, rgnSize.height), format);
88 if (!mUpdateSurface || mUpdateSurface->CairoStatus()) {
89 mUpdateSurface = nullptr;
90 return nullptr;
93 mUpdateSurface->SetDeviceOffset(gfxPoint(-rgnSize.x, -rgnSize.y));
95 return mUpdateSurface;
98 void
99 BasicTextureImage::GetUpdateRegion(nsIntRegion& aForRegion)
101 // if the texture hasn't been initialized yet, or something important
102 // changed, we need to recreate our backing surface and force the
103 // client to paint everything
104 if (mTextureState != Valid)
105 aForRegion = nsIntRect(nsIntPoint(0, 0), mSize);
108 void
109 BasicTextureImage::EndUpdate()
111 NS_ASSERTION(!!mUpdateSurface, "EndUpdate() without BeginUpdate()?");
113 // FIXME: this is the slow boat. Make me fast (with GLXPixmap?).
115 // Undo the device offset that BeginUpdate set; doesn't much matter for us here,
116 // but important if we ever do anything directly with the surface.
117 mUpdateSurface->SetDeviceOffset(gfxPoint(0, 0));
119 bool relative = FinishedSurfaceUpdate();
121 mTextureFormat =
122 mGLContext->UploadSurfaceToTexture(mUpdateSurface,
123 mUpdateRegion,
124 mTexture,
125 mTextureState == Created,
126 mUpdateOffset,
127 relative);
128 FinishedSurfaceUpload();
130 mUpdateSurface = nullptr;
131 mTextureState = Valid;
134 void
135 BasicTextureImage::BindTexture(GLenum aTextureUnit)
137 mGLContext->fActiveTexture(aTextureUnit);
138 mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
139 mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
142 void
143 BasicTextureImage::ApplyFilter()
145 mGLContext->ApplyFilterToBoundTexture(mFilter);
149 already_AddRefed<gfxASurface>
150 BasicTextureImage::GetSurfaceForUpdate(const gfxIntSize& aSize, ImageFormat aFmt)
152 return gfxPlatform::GetPlatform()->
153 CreateOffscreenSurface(aSize, gfxASurface::ContentFromFormat(aFmt));
156 bool
157 BasicTextureImage::FinishedSurfaceUpdate()
159 return false;
162 void
163 BasicTextureImage::FinishedSurfaceUpload()
167 bool
168 BasicTextureImage::DirectUpdate(gfxASurface* aSurf, const nsIntRegion& aRegion, const nsIntPoint& aFrom /* = nsIntPoint(0, 0) */)
170 nsIntRect bounds = aRegion.GetBounds();
171 nsIntRegion region;
172 if (mTextureState != Valid) {
173 bounds = nsIntRect(0, 0, mSize.width, mSize.height);
174 region = nsIntRegion(bounds);
175 } else {
176 region = aRegion;
179 mTextureFormat =
180 mGLContext->UploadSurfaceToTexture(aSurf,
181 region,
182 mTexture,
183 mTextureState == Created,
184 bounds.TopLeft() + aFrom,
185 false);
186 mTextureState = Valid;
187 return true;
190 void
191 BasicTextureImage::Resize(const nsIntSize& aSize)
193 NS_ASSERTION(!mUpdateSurface, "Resize() while in update?");
195 mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
197 mGLContext->fTexImage2D(LOCAL_GL_TEXTURE_2D,
199 LOCAL_GL_RGBA,
200 aSize.width,
201 aSize.height,
203 LOCAL_GL_RGBA,
204 LOCAL_GL_UNSIGNED_BYTE,
205 nullptr);
207 mTextureState = Allocated;
208 mSize = aSize;
211 TiledTextureImage::TiledTextureImage(GLContext* aGL,
212 nsIntSize aSize,
213 TextureImage::ContentType aContentType,
214 TextureImage::Flags aFlags)
215 : TextureImage(aSize, LOCAL_GL_CLAMP_TO_EDGE, aContentType, aFlags)
216 , mCurrentImage(0)
217 , mIterationCallback(nullptr)
218 , mInUpdate(false)
219 , mRows(0)
220 , mColumns(0)
221 , mGL(aGL)
222 , mTextureState(Created)
224 if (!(aFlags & TextureImage::ForceSingleTile) && mGL->WantsSmallTiles()) {
225 mTileSize = 256;
226 } else {
227 mGL->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, (GLint*) &mTileSize);
229 if (aSize.width != 0 && aSize.height != 0) {
230 Resize(aSize);
234 TiledTextureImage::~TiledTextureImage()
238 bool
239 TiledTextureImage::DirectUpdate(gfxASurface* aSurf, const nsIntRegion& aRegion, const nsIntPoint& aFrom /* = nsIntPoint(0, 0) */)
241 if (mSize.width == 0 || mSize.height == 0) {
242 return true;
245 nsIntRegion region;
247 if (mTextureState != Valid) {
248 nsIntRect bounds = nsIntRect(0, 0, mSize.width, mSize.height);
249 region = nsIntRegion(bounds);
250 } else {
251 region = aRegion;
254 bool result = true;
255 int oldCurrentImage = mCurrentImage;
256 BeginTileIteration();
257 do {
258 nsIntRect tileRect = GetSrcTileRect();
259 int xPos = tileRect.x;
260 int yPos = tileRect.y;
262 nsIntRegion tileRegion;
263 tileRegion.And(region, tileRect); // intersect with tile
265 if (tileRegion.IsEmpty())
266 continue;
268 if (mGL->CanUploadSubTextures()) {
269 tileRegion.MoveBy(-xPos, -yPos); // translate into tile local space
270 } else {
271 // If sub-textures are unsupported, expand to tile boundaries
272 tileRect.x = tileRect.y = 0;
273 tileRegion = nsIntRegion(tileRect);
276 result &= mImages[mCurrentImage]->
277 DirectUpdate(aSurf, tileRegion, aFrom + nsIntPoint(xPos, yPos));
279 if (mCurrentImage == mImages.Length() - 1) {
280 // We know we're done, but we still need to ensure that the callback
281 // gets called (e.g. to update the uploaded region).
282 NextTile();
283 break;
285 // Override a callback cancelling iteration if the texture wasn't valid.
286 // We need to force the update in that situation, or we may end up
287 // showing invalid/out-of-date texture data.
288 } while (NextTile() || (mTextureState != Valid));
289 mCurrentImage = oldCurrentImage;
291 mTextureFormat = mImages[0]->GetTextureFormat();
292 mTextureState = Valid;
293 return result;
296 void
297 TiledTextureImage::GetUpdateRegion(nsIntRegion& aForRegion)
299 if (mTextureState != Valid) {
300 // if the texture hasn't been initialized yet, or something important
301 // changed, we need to recreate our backing surface and force the
302 // client to paint everything
303 aForRegion = nsIntRect(nsIntPoint(0, 0), mSize);
304 return;
307 nsIntRegion newRegion;
309 // We need to query each texture with the region it will be drawing and
310 // set aForRegion to be the combination of all of these regions
311 for (unsigned i = 0; i < mImages.Length(); i++) {
312 int xPos = (i % mColumns) * mTileSize;
313 int yPos = (i / mColumns) * mTileSize;
314 nsIntRect imageRect = nsIntRect(nsIntRect(nsIntPoint(xPos,yPos), mImages[i]->GetSize()));
316 if (aForRegion.Intersects(imageRect)) {
317 // Make a copy of the region
318 nsIntRegion subRegion;
319 subRegion.And(aForRegion, imageRect);
320 // Translate it into tile-space
321 subRegion.MoveBy(-xPos, -yPos);
322 // Query region
323 mImages[i]->GetUpdateRegion(subRegion);
324 // Translate back
325 subRegion.MoveBy(xPos, yPos);
326 // Add to the accumulated region
327 newRegion.Or(newRegion, subRegion);
331 aForRegion = newRegion;
334 gfxASurface*
335 TiledTextureImage::BeginUpdate(nsIntRegion& aRegion)
337 NS_ASSERTION(!mInUpdate, "nested update");
338 mInUpdate = true;
340 // Note, we don't call GetUpdateRegion here as if the updated region is
341 // fully contained in a single tile, we get to avoid iterating through
342 // the tiles again (and a little copying).
343 if (mTextureState != Valid)
345 // if the texture hasn't been initialized yet, or something important
346 // changed, we need to recreate our backing surface and force the
347 // client to paint everything
348 aRegion = nsIntRect(nsIntPoint(0, 0), mSize);
351 nsIntRect bounds = aRegion.GetBounds();
353 for (unsigned i = 0; i < mImages.Length(); i++) {
354 int xPos = (i % mColumns) * mTileSize;
355 int yPos = (i / mColumns) * mTileSize;
356 nsIntRegion imageRegion = nsIntRegion(nsIntRect(nsIntPoint(xPos,yPos), mImages[i]->GetSize()));
358 // a single Image can handle this update request
359 if (imageRegion.Contains(aRegion)) {
360 // adjust for tile offset
361 aRegion.MoveBy(-xPos, -yPos);
362 // forward the actual call
363 nsRefPtr<gfxASurface> surface = mImages[i]->BeginUpdate(aRegion);
364 // caller expects container space
365 aRegion.MoveBy(xPos, yPos);
366 // Correct the device offset
367 gfxPoint offset = surface->GetDeviceOffset();
368 surface->SetDeviceOffset(gfxPoint(offset.x - xPos,
369 offset.y - yPos));
370 // we don't have a temp surface
371 mUpdateSurface = nullptr;
372 // remember which image to EndUpdate
373 mCurrentImage = i;
374 return surface.get();
378 // Get the real updated region, taking into account the capabilities of
379 // each TextureImage tile
380 GetUpdateRegion(aRegion);
381 mUpdateRegion = aRegion;
382 bounds = aRegion.GetBounds();
384 // update covers multiple Images - create a temp surface to paint in
385 gfxASurface::gfxImageFormat format =
386 (GetContentType() == gfxASurface::CONTENT_COLOR) ?
387 gfxASurface::ImageFormatRGB24 : gfxASurface::ImageFormatARGB32;
388 mUpdateSurface = gfxPlatform::GetPlatform()->
389 CreateOffscreenSurface(gfxIntSize(bounds.width, bounds.height), gfxASurface::ContentFromFormat(format));
390 mUpdateSurface->SetDeviceOffset(gfxPoint(-bounds.x, -bounds.y));
392 return mUpdateSurface;
395 void
396 TiledTextureImage::EndUpdate()
398 NS_ASSERTION(mInUpdate, "EndUpdate not in update");
399 if (!mUpdateSurface) { // update was to a single TextureImage
400 mImages[mCurrentImage]->EndUpdate();
401 mInUpdate = false;
402 mTextureState = Valid;
403 mTextureFormat = mImages[mCurrentImage]->GetTextureFormat();
404 return;
407 // upload tiles from temp surface
408 for (unsigned i = 0; i < mImages.Length(); i++) {
409 int xPos = (i % mColumns) * mTileSize;
410 int yPos = (i / mColumns) * mTileSize;
411 nsIntRect imageRect = nsIntRect(nsIntPoint(xPos,yPos), mImages[i]->GetSize());
413 nsIntRegion subregion;
414 subregion.And(mUpdateRegion, imageRect);
415 if (subregion.IsEmpty())
416 continue;
417 subregion.MoveBy(-xPos, -yPos); // Tile-local space
418 // copy tile from temp surface
419 gfxASurface* surf = mImages[i]->BeginUpdate(subregion);
420 nsRefPtr<gfxContext> ctx = new gfxContext(surf);
421 gfxUtils::ClipToRegion(ctx, subregion);
422 ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
423 ctx->SetSource(mUpdateSurface, gfxPoint(-xPos, -yPos));
424 ctx->Paint();
425 mImages[i]->EndUpdate();
428 mUpdateSurface = nullptr;
429 mInUpdate = false;
430 mTextureFormat = mImages[0]->GetTextureFormat();
431 mTextureState = Valid;
434 void TiledTextureImage::BeginTileIteration()
436 mCurrentImage = 0;
439 bool TiledTextureImage::NextTile()
441 bool continueIteration = true;
443 if (mIterationCallback)
444 continueIteration = mIterationCallback(this, mCurrentImage,
445 mIterationCallbackData);
447 if (mCurrentImage + 1 < mImages.Length()) {
448 mCurrentImage++;
449 return continueIteration;
451 return false;
454 void TiledTextureImage::SetIterationCallback(TileIterationCallback aCallback,
455 void* aCallbackData)
457 mIterationCallback = aCallback;
458 mIterationCallbackData = aCallbackData;
461 nsIntRect TiledTextureImage::GetTileRect()
463 if (!GetTileCount()) {
464 return nsIntRect();
466 nsIntRect rect = mImages[mCurrentImage]->GetTileRect();
467 unsigned int xPos = (mCurrentImage % mColumns) * mTileSize;
468 unsigned int yPos = (mCurrentImage / mColumns) * mTileSize;
469 rect.MoveBy(xPos, yPos);
470 return rect;
473 nsIntRect TiledTextureImage::GetSrcTileRect()
475 nsIntRect rect = GetTileRect();
476 unsigned int srcY = mFlags & NeedsYFlip
477 ? mSize.height - rect.height - rect.y
478 : rect.y;
479 return nsIntRect(rect.x, srcY, rect.width, rect.height);
482 void
483 TiledTextureImage::BindTexture(GLenum aTextureUnit)
485 if (!GetTileCount()) {
486 return;
488 mImages[mCurrentImage]->BindTexture(aTextureUnit);
491 void
492 TiledTextureImage::ApplyFilter()
494 mGL->ApplyFilterToBoundTexture(mFilter);
498 * Resize, trying to reuse tiles. The reuse strategy is to decide on reuse per
499 * column. A tile on a column is reused if it hasn't changed size, otherwise it
500 * is discarded/replaced. Extra tiles on a column are pruned after iterating
501 * each column, and extra rows are pruned after iteration over the entire image
502 * finishes.
504 void TiledTextureImage::Resize(const nsIntSize& aSize)
506 if (mSize == aSize && mTextureState != Created) {
507 return;
510 // calculate rows and columns, rounding up
511 unsigned int columns = (aSize.width + mTileSize - 1) / mTileSize;
512 unsigned int rows = (aSize.height + mTileSize - 1) / mTileSize;
514 // Iterate over old tile-store and insert/remove tiles as necessary
515 int row;
516 unsigned int i = 0;
517 for (row = 0; row < (int)rows; row++) {
518 // If we've gone beyond how many rows there were before, set mColumns to
519 // zero so that we only create new tiles.
520 if (row >= (int)mRows)
521 mColumns = 0;
523 // Similarly, if we're on the last row of old tiles and the height has
524 // changed, discard all tiles in that row.
525 // This will cause the pruning of columns not to work, but we don't need
526 // to worry about that, as no more tiles will be reused past this point
527 // anyway.
528 if ((row == (int)mRows - 1) && (aSize.height != mSize.height))
529 mColumns = 0;
531 int col;
532 for (col = 0; col < (int)columns; col++) {
533 nsIntSize size( // use tilesize first, then the remainder
534 (col+1) * mTileSize > (unsigned int)aSize.width ? aSize.width % mTileSize : mTileSize,
535 (row+1) * mTileSize > (unsigned int)aSize.height ? aSize.height % mTileSize : mTileSize);
537 bool replace = false;
539 // Check if we can re-use old tiles.
540 if (col < (int)mColumns) {
541 // Reuse an existing tile. If the tile is an end-tile and the
542 // width differs, replace it instead.
543 if (mSize.width != aSize.width) {
544 if (col == (int)mColumns - 1) {
545 // Tile at the end of the old column, replace it with
546 // a new one.
547 replace = true;
548 } else if (col == (int)columns - 1) {
549 // Tile at the end of the new column, create a new one.
550 } else {
551 // Before the last column on both the old and new sizes,
552 // reuse existing tile.
553 i++;
554 continue;
556 } else {
557 // Width hasn't changed, reuse existing tile.
558 i++;
559 continue;
563 // Create a new tile.
564 nsRefPtr<TextureImage> teximg =
565 mGL->TileGenFunc(size, mContentType, mFlags);
566 if (replace)
567 mImages.ReplaceElementAt(i, teximg.forget());
568 else
569 mImages.InsertElementAt(i, teximg.forget());
570 i++;
573 // Prune any unused tiles on the end of the column.
574 if (row < (int)mRows) {
575 for (col = (int)mColumns - col; col > 0; col--) {
576 mImages.RemoveElementAt(i);
581 // Prune any unused tiles at the end of the store.
582 unsigned int length = mImages.Length();
583 for (; i < length; i++)
584 mImages.RemoveElementAt(mImages.Length()-1);
586 // Reset tile-store properties.
587 mRows = rows;
588 mColumns = columns;
589 mSize = aSize;
590 mTextureState = Allocated;
591 mCurrentImage = 0;
594 uint32_t TiledTextureImage::GetTileCount()
596 return mImages.Length();
599 TextureImage::ScopedBindTexture::ScopedBindTexture(TextureImage* aTexture,
600 GLenum aTextureUnit)
601 : mTexture(aTexture)
603 if (mTexture) {
604 MOZ_ASSERT(aTextureUnit >= LOCAL_GL_TEXTURE0);
605 mTexture->BindTexture(aTextureUnit);
609 already_AddRefed<TextureImage>
610 CreateBasicTextureImage(GLContext* aGL,
611 const nsIntSize& aSize,
612 TextureImage::ContentType aContentType,
613 GLenum aWrapMode,
614 TextureImage::Flags aFlags)
616 bool useNearestFilter = aFlags & TextureImage::UseNearestFilter;
617 aGL->MakeCurrent();
619 GLuint texture = 0;
620 aGL->fGenTextures(1, &texture);
622 ScopedBindTexture bind(aGL, texture);
624 GLint texfilter = useNearestFilter ? LOCAL_GL_NEAREST : LOCAL_GL_LINEAR;
625 aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, texfilter);
626 aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, texfilter);
627 aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, aWrapMode);
628 aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, aWrapMode);
630 nsRefPtr<BasicTextureImage> texImage =
631 new BasicTextureImage(texture, aSize, aWrapMode, aContentType, aGL, aFlags);
632 return texImage.forget();
635 } // namespace
636 } // namespace