michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "TiledContentHost.h" michael@0: #include "ThebesLayerComposite.h" // for ThebesLayerComposite michael@0: #include "mozilla/gfx/BaseSize.h" // for BaseSize michael@0: #include "mozilla/gfx/Matrix.h" // for Matrix4x4 michael@0: #include "mozilla/layers/Compositor.h" // for Compositor michael@0: #include "mozilla/layers/Effects.h" // for TexturedEffect, Effect, etc michael@0: #include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL michael@0: #include "nsAString.h" michael@0: #include "nsDebug.h" // for NS_WARNING michael@0: #include "nsPoint.h" // for nsIntPoint michael@0: #include "nsPrintfCString.h" // for nsPrintfCString michael@0: #include "nsRect.h" // for nsIntRect michael@0: #include "nsSize.h" // for nsIntSize michael@0: #include "mozilla/layers/TiledContentClient.h" michael@0: michael@0: class gfxReusableSurfaceWrapper; michael@0: michael@0: namespace mozilla { michael@0: using namespace gfx; michael@0: namespace layers { michael@0: michael@0: class Layer; michael@0: michael@0: TiledLayerBufferComposite::TiledLayerBufferComposite() michael@0: : mFrameResolution(1.0) michael@0: , mHasDoubleBufferedTiles(false) michael@0: , mUninitialized(true) michael@0: {} michael@0: michael@0: /* static */ void michael@0: TiledLayerBufferComposite::RecycleCallback(TextureHost* textureHost, void* aClosure) michael@0: { michael@0: textureHost->CompositorRecycle(); michael@0: } michael@0: michael@0: TiledLayerBufferComposite::TiledLayerBufferComposite(ISurfaceAllocator* aAllocator, michael@0: const SurfaceDescriptorTiles& aDescriptor, michael@0: const nsIntRegion& aOldPaintedRegion) michael@0: { michael@0: mUninitialized = false; michael@0: mHasDoubleBufferedTiles = false; michael@0: mValidRegion = aDescriptor.validRegion(); michael@0: mPaintedRegion = aDescriptor.paintedRegion(); michael@0: mRetainedWidth = aDescriptor.retainedWidth(); michael@0: mRetainedHeight = aDescriptor.retainedHeight(); michael@0: mResolution = aDescriptor.resolution(); michael@0: mFrameResolution = CSSToParentLayerScale(aDescriptor.frameResolution()); michael@0: michael@0: // Combine any valid content that wasn't already uploaded michael@0: nsIntRegion oldPaintedRegion(aOldPaintedRegion); michael@0: oldPaintedRegion.And(oldPaintedRegion, mValidRegion); michael@0: mPaintedRegion.Or(mPaintedRegion, oldPaintedRegion); michael@0: michael@0: const InfallibleTArray& tiles = aDescriptor.tiles(); michael@0: for(size_t i = 0; i < tiles.Length(); i++) { michael@0: RefPtr texture; michael@0: const TileDescriptor& tileDesc = tiles[i]; michael@0: switch (tileDesc.type()) { michael@0: case TileDescriptor::TTexturedTileDescriptor : { michael@0: texture = TextureHost::AsTextureHost(tileDesc.get_TexturedTileDescriptor().textureParent()); michael@0: #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 michael@0: if (!gfxPrefs::LayersUseSimpleTiles()) { michael@0: texture->SetRecycleCallback(RecycleCallback, nullptr); michael@0: } michael@0: #endif michael@0: const TileLock& ipcLock = tileDesc.get_TexturedTileDescriptor().sharedLock(); michael@0: nsRefPtr sharedLock; michael@0: if (ipcLock.type() == TileLock::TShmemSection) { michael@0: sharedLock = gfxShmSharedReadLock::Open(aAllocator, ipcLock.get_ShmemSection()); michael@0: } else { michael@0: sharedLock = reinterpret_cast(ipcLock.get_uintptr_t()); michael@0: if (sharedLock) { michael@0: // The corresponding AddRef is in TiledClient::GetTileDescriptor michael@0: sharedLock->Release(); michael@0: } michael@0: } michael@0: michael@0: mRetainedTiles.AppendElement(TileHost(sharedLock, texture)); michael@0: break; michael@0: } michael@0: default: michael@0: NS_WARNING("Unrecognised tile descriptor type"); michael@0: // Fall through michael@0: case TileDescriptor::TPlaceholderTileDescriptor : michael@0: mRetainedTiles.AppendElement(GetPlaceholderTile()); michael@0: break; michael@0: } michael@0: if (texture && !texture->HasInternalBuffer()) { michael@0: mHasDoubleBufferedTiles = true; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: TiledLayerBufferComposite::ReadUnlock() michael@0: { michael@0: if (!IsValid()) { michael@0: return; michael@0: } michael@0: for (size_t i = 0; i < mRetainedTiles.Length(); i++) { michael@0: mRetainedTiles[i].ReadUnlock(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: TiledLayerBufferComposite::ReleaseTextureHosts() michael@0: { michael@0: if (!IsValid()) { michael@0: return; michael@0: } michael@0: for (size_t i = 0; i < mRetainedTiles.Length(); i++) { michael@0: mRetainedTiles[i].mTextureHost = nullptr; michael@0: } michael@0: } michael@0: michael@0: void michael@0: TiledLayerBufferComposite::Upload() michael@0: { michael@0: if(!IsValid()) { michael@0: return; michael@0: } michael@0: // The TextureClients were created with the TEXTURE_IMMEDIATE_UPLOAD flag, michael@0: // so calling Update on all the texture hosts will perform the texture upload. michael@0: Update(mValidRegion, mPaintedRegion); michael@0: ClearPaintedRegion(); michael@0: } michael@0: michael@0: TileHost michael@0: TiledLayerBufferComposite::ValidateTile(TileHost aTile, michael@0: const nsIntPoint& aTileOrigin, michael@0: const nsIntRegion& aDirtyRect) michael@0: { michael@0: if (aTile.IsPlaceholderTile()) { michael@0: NS_WARNING("Placeholder tile encountered in painted region"); michael@0: return aTile; michael@0: } michael@0: michael@0: #ifdef GFX_TILEDLAYER_PREF_WARNINGS michael@0: printf_stderr("Upload tile %i, %i\n", aTileOrigin.x, aTileOrigin.y); michael@0: long start = PR_IntervalNow(); michael@0: #endif michael@0: michael@0: MOZ_ASSERT(aTile.mTextureHost->GetFlags() & TEXTURE_IMMEDIATE_UPLOAD); michael@0: // We possibly upload the entire texture contents here. This is a purposeful michael@0: // decision, as sub-image upload can often be slow and/or unreliable, but michael@0: // we may want to reevaluate this in the future. michael@0: // For !HasInternalBuffer() textures, this is likely a no-op. michael@0: aTile.mTextureHost->Updated(nullptr); michael@0: michael@0: #ifdef GFX_TILEDLAYER_PREF_WARNINGS michael@0: if (PR_IntervalNow() - start > 1) { michael@0: printf_stderr("Tile Time to upload %i\n", PR_IntervalNow() - start); michael@0: } michael@0: #endif michael@0: return aTile; michael@0: } michael@0: michael@0: void michael@0: TiledLayerBufferComposite::SetCompositor(Compositor* aCompositor) michael@0: { michael@0: if (!IsValid()) { michael@0: return; michael@0: } michael@0: for (size_t i = 0; i < mRetainedTiles.Length(); i++) { michael@0: if (mRetainedTiles[i].IsPlaceholderTile()) continue; michael@0: mRetainedTiles[i].mTextureHost->SetCompositor(aCompositor); michael@0: } michael@0: } michael@0: michael@0: #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 michael@0: void michael@0: TiledLayerBufferComposite::SetReleaseFence(const android::sp& aReleaseFence) michael@0: { michael@0: for (size_t i = 0; i < mRetainedTiles.Length(); i++) { michael@0: if (!mRetainedTiles[i].mTextureHost) { michael@0: continue; michael@0: } michael@0: TextureHostOGL* texture = mRetainedTiles[i].mTextureHost->AsHostOGL(); michael@0: if (!texture) { michael@0: continue; michael@0: } michael@0: texture->SetReleaseFence(new android::Fence(aReleaseFence->dup())); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: TiledContentHost::TiledContentHost(const TextureInfo& aTextureInfo) michael@0: : ContentHost(aTextureInfo) michael@0: , mTiledBuffer(TiledLayerBufferComposite()) michael@0: , mLowPrecisionTiledBuffer(TiledLayerBufferComposite()) michael@0: , mOldTiledBuffer(TiledLayerBufferComposite()) michael@0: , mOldLowPrecisionTiledBuffer(TiledLayerBufferComposite()) michael@0: , mPendingUpload(false) michael@0: , mPendingLowPrecisionUpload(false) michael@0: { michael@0: MOZ_COUNT_CTOR(TiledContentHost); michael@0: } michael@0: michael@0: TiledContentHost::~TiledContentHost() michael@0: { michael@0: MOZ_COUNT_DTOR(TiledContentHost); michael@0: michael@0: // Unlock any buffers that may still be locked. If we have a pending upload, michael@0: // we will need to unlock the buffer that was about to be uploaded. michael@0: // If a buffer that was being composited had double-buffered tiles, we will michael@0: // need to unlock that buffer too. michael@0: if (mPendingUpload) { michael@0: mTiledBuffer.ReadUnlock(); michael@0: if (mOldTiledBuffer.HasDoubleBufferedTiles()) { michael@0: mOldTiledBuffer.ReadUnlock(); michael@0: } michael@0: } else if (mTiledBuffer.HasDoubleBufferedTiles()) { michael@0: mTiledBuffer.ReadUnlock(); michael@0: } michael@0: michael@0: if (mPendingLowPrecisionUpload) { michael@0: mLowPrecisionTiledBuffer.ReadUnlock(); michael@0: if (mOldLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) { michael@0: mOldLowPrecisionTiledBuffer.ReadUnlock(); michael@0: } michael@0: } else if (mLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) { michael@0: mLowPrecisionTiledBuffer.ReadUnlock(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: TiledContentHost::Attach(Layer* aLayer, michael@0: Compositor* aCompositor, michael@0: AttachFlags aFlags /* = NO_FLAGS */) michael@0: { michael@0: CompositableHost::Attach(aLayer, aCompositor, aFlags); michael@0: static_cast(aLayer)->EnsureTiled(); michael@0: } michael@0: michael@0: void michael@0: TiledContentHost::UseTiledLayerBuffer(ISurfaceAllocator* aAllocator, michael@0: const SurfaceDescriptorTiles& aTiledDescriptor) michael@0: { michael@0: if (aTiledDescriptor.resolution() < 1) { michael@0: if (mPendingLowPrecisionUpload) { michael@0: mLowPrecisionTiledBuffer.ReadUnlock(); michael@0: } else { michael@0: mPendingLowPrecisionUpload = true; michael@0: // If the old buffer has double-buffered tiles, hang onto it so we can michael@0: // unlock it after we've composited the new buffer. michael@0: // We only need to hang onto the locks, but not the textures. michael@0: // Releasing the textures here can help prevent a memory spike in the michael@0: // situation that the client starts rendering new content before we get michael@0: // to composite the new buffer. michael@0: if (mLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) { michael@0: mOldLowPrecisionTiledBuffer = mLowPrecisionTiledBuffer; michael@0: mOldLowPrecisionTiledBuffer.ReleaseTextureHosts(); michael@0: } michael@0: } michael@0: mLowPrecisionTiledBuffer = michael@0: TiledLayerBufferComposite(aAllocator, aTiledDescriptor, michael@0: mLowPrecisionTiledBuffer.GetPaintedRegion()); michael@0: } else { michael@0: if (mPendingUpload) { michael@0: mTiledBuffer.ReadUnlock(); michael@0: } else { michael@0: mPendingUpload = true; michael@0: if (mTiledBuffer.HasDoubleBufferedTiles()) { michael@0: mOldTiledBuffer = mTiledBuffer; michael@0: mOldTiledBuffer.ReleaseTextureHosts(); michael@0: } michael@0: } michael@0: mTiledBuffer = TiledLayerBufferComposite(aAllocator, aTiledDescriptor, michael@0: mTiledBuffer.GetPaintedRegion()); michael@0: } michael@0: } michael@0: michael@0: void michael@0: TiledContentHost::Composite(EffectChain& aEffectChain, michael@0: float aOpacity, michael@0: const gfx::Matrix4x4& aTransform, michael@0: const gfx::Filter& aFilter, michael@0: const gfx::Rect& aClipRect, michael@0: const nsIntRegion* aVisibleRegion /* = nullptr */, michael@0: TiledLayerProperties* aLayerProperties /* = nullptr */) michael@0: { michael@0: MOZ_ASSERT(aLayerProperties, "aLayerProperties required for TiledContentHost"); michael@0: michael@0: if (mPendingUpload) { michael@0: mTiledBuffer.SetCompositor(mCompositor); michael@0: mTiledBuffer.Upload(); michael@0: michael@0: // For a single-buffered tiled buffer, Upload will upload the shared memory michael@0: // surface to texture memory and we no longer need to read from them. michael@0: if (!mTiledBuffer.HasDoubleBufferedTiles()) { michael@0: mTiledBuffer.ReadUnlock(); michael@0: } michael@0: } michael@0: if (mPendingLowPrecisionUpload) { michael@0: mLowPrecisionTiledBuffer.SetCompositor(mCompositor); michael@0: mLowPrecisionTiledBuffer.Upload(); michael@0: michael@0: if (!mLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) { michael@0: mLowPrecisionTiledBuffer.ReadUnlock(); michael@0: } michael@0: } michael@0: michael@0: RenderLayerBuffer(mLowPrecisionTiledBuffer, aEffectChain, aOpacity, aFilter, michael@0: aClipRect, aLayerProperties->mVisibleRegion, aTransform); michael@0: RenderLayerBuffer(mTiledBuffer, aEffectChain, aOpacity, aFilter, michael@0: aClipRect, aLayerProperties->mVisibleRegion, aTransform); michael@0: michael@0: // Now release the old buffer if it had double-buffered tiles, as we can michael@0: // guarantee that they're no longer on the screen (and so any locks that may michael@0: // have been held have been released). michael@0: if (mPendingUpload && mOldTiledBuffer.HasDoubleBufferedTiles()) { michael@0: mOldTiledBuffer.ReadUnlock(); michael@0: mOldTiledBuffer = TiledLayerBufferComposite(); michael@0: } michael@0: if (mPendingLowPrecisionUpload && mOldLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) { michael@0: mOldLowPrecisionTiledBuffer.ReadUnlock(); michael@0: mOldLowPrecisionTiledBuffer = TiledLayerBufferComposite(); michael@0: } michael@0: mPendingUpload = mPendingLowPrecisionUpload = false; michael@0: } michael@0: michael@0: michael@0: void michael@0: TiledContentHost::RenderTile(const TileHost& aTile, michael@0: EffectChain& aEffectChain, michael@0: float aOpacity, michael@0: const gfx::Matrix4x4& aTransform, michael@0: const gfx::Filter& aFilter, michael@0: const gfx::Rect& aClipRect, michael@0: const nsIntRegion& aScreenRegion, michael@0: const nsIntPoint& aTextureOffset, michael@0: const nsIntSize& aTextureBounds) michael@0: { michael@0: if (aTile.IsPlaceholderTile()) { michael@0: // This shouldn't ever happen, but let's fail semi-gracefully. No need michael@0: // to warn, the texture update would have already caught this. michael@0: return; michael@0: } michael@0: michael@0: nsIntRect screenBounds = aScreenRegion.GetBounds(); michael@0: Rect quad(screenBounds.x, screenBounds.y, screenBounds.width, screenBounds.height); michael@0: quad = aTransform.TransformBounds(quad); michael@0: michael@0: if (!quad.Intersects(mCompositor->ClipRectInLayersCoordinates(aClipRect))) { michael@0: return; michael@0: } michael@0: michael@0: AutoLockTextureHost autoLock(aTile.mTextureHost); michael@0: if (autoLock.Failed()) { michael@0: NS_WARNING("Failed to lock tile"); michael@0: return; michael@0: } michael@0: RefPtr source = aTile.mTextureHost->GetTextureSources(); michael@0: if (!source) { michael@0: return; michael@0: } michael@0: michael@0: RefPtr effect = michael@0: CreateTexturedEffect(aTile.mTextureHost->GetFormat(), source, aFilter); michael@0: if (!effect) { michael@0: return; michael@0: } michael@0: michael@0: aEffectChain.mPrimaryEffect = effect; michael@0: michael@0: nsIntRegionRectIterator it(aScreenRegion); michael@0: for (const nsIntRect* rect = it.Next(); rect != nullptr; rect = it.Next()) { michael@0: Rect graphicsRect(rect->x, rect->y, rect->width, rect->height); michael@0: Rect textureRect(rect->x - aTextureOffset.x, rect->y - aTextureOffset.y, michael@0: rect->width, rect->height); michael@0: michael@0: effect->mTextureCoords = Rect(textureRect.x / aTextureBounds.width, michael@0: textureRect.y / aTextureBounds.height, michael@0: textureRect.width / aTextureBounds.width, michael@0: textureRect.height / aTextureBounds.height); michael@0: mCompositor->DrawQuad(graphicsRect, aClipRect, aEffectChain, aOpacity, aTransform); michael@0: } michael@0: mCompositor->DrawDiagnostics(DIAGNOSTIC_CONTENT|DIAGNOSTIC_TILE, michael@0: aScreenRegion, aClipRect, aTransform, mFlashCounter); michael@0: } michael@0: michael@0: void michael@0: TiledContentHost::RenderLayerBuffer(TiledLayerBufferComposite& aLayerBuffer, michael@0: EffectChain& aEffectChain, michael@0: float aOpacity, michael@0: const gfx::Filter& aFilter, michael@0: const gfx::Rect& aClipRect, michael@0: nsIntRegion aVisibleRegion, michael@0: gfx::Matrix4x4 aTransform) michael@0: { michael@0: if (!mCompositor) { michael@0: NS_WARNING("Can't render tiled content host - no compositor"); michael@0: return; michael@0: } michael@0: float resolution = aLayerBuffer.GetResolution(); michael@0: gfx::Size layerScale(1, 1); michael@0: michael@0: // We assume that the current frame resolution is the one used in our high michael@0: // precision layer buffer. Compensate for a changing frame resolution when michael@0: // rendering the low precision buffer. michael@0: if (aLayerBuffer.GetFrameResolution() != mTiledBuffer.GetFrameResolution()) { michael@0: const CSSToParentLayerScale& layerResolution = aLayerBuffer.GetFrameResolution(); michael@0: const CSSToParentLayerScale& localResolution = mTiledBuffer.GetFrameResolution(); michael@0: layerScale.width = layerScale.height = layerResolution.scale / localResolution.scale; michael@0: aVisibleRegion.ScaleRoundOut(layerScale.width, layerScale.height); michael@0: } michael@0: michael@0: // If we're drawing the low precision buffer, make sure the high precision michael@0: // buffer is masked out to avoid overdraw and rendering artifacts with michael@0: // non-opaque layers. michael@0: nsIntRegion maskRegion; michael@0: if (resolution != mTiledBuffer.GetResolution()) { michael@0: maskRegion = mTiledBuffer.GetValidRegion(); michael@0: // XXX This should be ScaleRoundIn, but there is no such function on michael@0: // nsIntRegion. michael@0: maskRegion.ScaleRoundOut(layerScale.width, layerScale.height); michael@0: } michael@0: michael@0: // Make sure the resolution and difference in frame resolution are accounted michael@0: // for in the layer transform. michael@0: aTransform.Scale(1/(resolution * layerScale.width), michael@0: 1/(resolution * layerScale.height), 1); michael@0: michael@0: uint32_t rowCount = 0; michael@0: uint32_t tileX = 0; michael@0: nsIntRect visibleRect = aVisibleRegion.GetBounds(); michael@0: gfx::IntSize scaledTileSize = aLayerBuffer.GetScaledTileSize(); michael@0: for (int32_t x = visibleRect.x; x < visibleRect.x + visibleRect.width;) { michael@0: rowCount++; michael@0: int32_t tileStartX = aLayerBuffer.GetTileStart(x, scaledTileSize.width); michael@0: int32_t w = scaledTileSize.width - tileStartX; michael@0: if (x + w > visibleRect.x + visibleRect.width) { michael@0: w = visibleRect.x + visibleRect.width - x; michael@0: } michael@0: int tileY = 0; michael@0: for (int32_t y = visibleRect.y; y < visibleRect.y + visibleRect.height;) { michael@0: int32_t tileStartY = aLayerBuffer.GetTileStart(y, scaledTileSize.height); michael@0: int32_t h = scaledTileSize.height - tileStartY; michael@0: if (y + h > visibleRect.y + visibleRect.height) { michael@0: h = visibleRect.y + visibleRect.height - y; michael@0: } michael@0: michael@0: TileHost tileTexture = aLayerBuffer. michael@0: GetTile(nsIntPoint(aLayerBuffer.RoundDownToTileEdge(x, scaledTileSize.width), michael@0: aLayerBuffer.RoundDownToTileEdge(y, scaledTileSize.height))); michael@0: if (tileTexture != aLayerBuffer.GetPlaceholderTile()) { michael@0: nsIntRegion tileDrawRegion; michael@0: tileDrawRegion.And(nsIntRect(x, y, w, h), aLayerBuffer.GetValidRegion()); michael@0: tileDrawRegion.And(tileDrawRegion, aVisibleRegion); michael@0: tileDrawRegion.Sub(tileDrawRegion, maskRegion); michael@0: michael@0: if (!tileDrawRegion.IsEmpty()) { michael@0: tileDrawRegion.ScaleRoundOut(resolution, resolution); michael@0: nsIntPoint tileOffset((x - tileStartX) * resolution, michael@0: (y - tileStartY) * resolution); michael@0: gfx::IntSize tileSize = aLayerBuffer.GetTileSize(); michael@0: RenderTile(tileTexture, aEffectChain, aOpacity, aTransform, aFilter, aClipRect, tileDrawRegion, michael@0: tileOffset, nsIntSize(tileSize.width, tileSize.height)); michael@0: } michael@0: } michael@0: tileY++; michael@0: y += h; michael@0: } michael@0: tileX++; michael@0: x += w; michael@0: } michael@0: gfx::Rect rect(visibleRect.x, visibleRect.y, michael@0: visibleRect.width, visibleRect.height); michael@0: GetCompositor()->DrawDiagnostics(DIAGNOSTIC_CONTENT, michael@0: rect, aClipRect, aTransform, mFlashCounter); michael@0: } michael@0: michael@0: void michael@0: TiledContentHost::PrintInfo(nsACString& aTo, const char* aPrefix) michael@0: { michael@0: aTo += aPrefix; michael@0: aTo += nsPrintfCString("TiledContentHost (0x%p)", this); michael@0: michael@0: } michael@0: michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: void michael@0: TiledContentHost::Dump(FILE* aFile, michael@0: const char* aPrefix, michael@0: bool aDumpHtml) michael@0: { michael@0: if (!aFile) { michael@0: aFile = stderr; michael@0: } michael@0: michael@0: TiledLayerBufferComposite::Iterator it = mTiledBuffer.TilesBegin(); michael@0: TiledLayerBufferComposite::Iterator stop = mTiledBuffer.TilesEnd(); michael@0: if (aDumpHtml) { michael@0: fprintf_stderr(aFile, ""); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: } // namespace michael@0: } // namespace