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 "mozilla/layers/SimpleTiledContentClient.h" michael@0: michael@0: #include // for ceil, ceilf, floor michael@0: #include "ClientTiledThebesLayer.h" // for ClientTiledThebesLayer michael@0: #include "GeckoProfiler.h" // for PROFILER_LABEL michael@0: #include "Units.h" // for ScreenIntRect, CSSPoint, etc michael@0: #include "UnitTransforms.h" // for TransformTo michael@0: #include "ClientLayerManager.h" // for ClientLayerManager michael@0: #include "CompositorChild.h" // for CompositorChild michael@0: #include "gfxContext.h" // for gfxContext, etc michael@0: #include "gfxPlatform.h" // for gfxPlatform michael@0: #include "gfxPrefs.h" // for gfxPrefs::LayersTileWidth/Height michael@0: #include "gfxRect.h" // for gfxRect michael@0: #include "mozilla/Attributes.h" // for MOZ_THIS_IN_INITIALIZER_LIST michael@0: #include "mozilla/MathAlgorithms.h" // for Abs michael@0: #include "mozilla/gfx/Point.h" // for IntSize michael@0: #include "mozilla/gfx/Rect.h" // for Rect michael@0: #include "mozilla/layers/CompositableForwarder.h" michael@0: #include "mozilla/layers/ShadowLayers.h" // for ShadowLayerForwarder michael@0: #include "SimpleTextureClientPool.h" michael@0: #include "nsDebug.h" // for NS_ASSERTION michael@0: #include "nsISupportsImpl.h" // for gfxContext::AddRef, etc michael@0: #include "nsSize.h" // for nsIntSize michael@0: #include "gfxReusableSharedImageSurfaceWrapper.h" michael@0: #include "nsMathUtils.h" // for NS_roundf michael@0: #include "gfx2DGlue.h" michael@0: michael@0: #define ALOG(...) __android_log_print(ANDROID_LOG_INFO, "SimpleTiles", __VA_ARGS__) michael@0: michael@0: using namespace mozilla::gfx; michael@0: michael@0: namespace mozilla { michael@0: namespace layers { michael@0: michael@0: void michael@0: SimpleTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion, michael@0: const nsIntRegion& aPaintRegion, michael@0: LayerManager::DrawThebesLayerCallback aCallback, michael@0: void* aCallbackData) michael@0: { michael@0: mCallback = aCallback; michael@0: mCallbackData = aCallbackData; michael@0: michael@0: #ifdef GFX_TILEDLAYER_PREF_WARNINGS michael@0: long start = PR_IntervalNow(); michael@0: #endif michael@0: michael@0: // If this region is empty XMost() - 1 will give us a negative value. michael@0: NS_ASSERTION(!aPaintRegion.GetBounds().IsEmpty(), "Empty paint region\n"); michael@0: michael@0: PROFILER_LABEL("SimpleTiledLayerBuffer", "PaintThebesUpdate"); michael@0: michael@0: Update(aNewValidRegion, aPaintRegion); michael@0: michael@0: #ifdef GFX_TILEDLAYER_PREF_WARNINGS michael@0: if (PR_IntervalNow() - start > 10) { michael@0: const nsIntRect bounds = aPaintRegion.GetBounds(); michael@0: printf_stderr("Time to tile [%i, %i, %i, %i] -> %i\n", bounds.x, bounds.y, bounds.width, bounds.height, PR_IntervalNow() - start); michael@0: } michael@0: #endif michael@0: michael@0: mLastPaintOpaque = mThebesLayer->CanUseOpaqueSurface(); michael@0: mCallback = nullptr; michael@0: mCallbackData = nullptr; michael@0: } michael@0: michael@0: SimpleTiledLayerTile michael@0: SimpleTiledLayerBuffer::ValidateTile(SimpleTiledLayerTile aTile, michael@0: const nsIntPoint& aTileOrigin, michael@0: const nsIntRegion& aDirtyRegion) michael@0: { michael@0: PROFILER_LABEL("SimpleTiledLayerBuffer", "ValidateTile"); michael@0: static gfx::IntSize kTileSize(gfxPrefs::LayersTileWidth(), gfxPrefs::LayersTileHeight()); michael@0: michael@0: gfx::SurfaceFormat tileFormat = gfxPlatform::GetPlatform()->Optimal2DFormatForContent(GetContentType()); michael@0: michael@0: // if this is true, we're using a separate buffer to do our drawing first michael@0: bool doBufferedDrawing = true; michael@0: bool fullPaint = false; michael@0: michael@0: RefPtr textureClient = mManager->GetSimpleTileTexturePool(tileFormat)->GetTextureClientWithAutoRecycle(); michael@0: michael@0: if (!textureClient) { michael@0: NS_WARNING("TextureClient allocation failed"); michael@0: return SimpleTiledLayerTile(); michael@0: } michael@0: michael@0: if (!textureClient->Lock(OPEN_READ_WRITE)) { michael@0: NS_WARNING("TextureClient lock failed"); michael@0: return SimpleTiledLayerTile(); michael@0: } michael@0: michael@0: if (!textureClient->CanExposeDrawTarget()) { michael@0: doBufferedDrawing = false; michael@0: } michael@0: michael@0: RefPtr drawTarget; michael@0: michael@0: unsigned char *bufferData = nullptr; michael@0: michael@0: // these are set/updated differently based on doBufferedDrawing michael@0: nsIntRect drawBounds; michael@0: nsIntRegion drawRegion; michael@0: nsIntRegion invalidateRegion; michael@0: michael@0: RefPtr srcDT; michael@0: uint8_t* srcData = nullptr; michael@0: int32_t srcStride = 0; michael@0: gfx::IntSize srcSize; michael@0: gfx::SurfaceFormat srcFormat = gfx::SurfaceFormat::UNKNOWN; michael@0: michael@0: if (doBufferedDrawing) { michael@0: // try to directly access the pixels of the TextureClient michael@0: srcDT = textureClient->GetAsDrawTarget(); michael@0: if (srcDT->LockBits(&srcData, &srcSize, &srcStride, &srcFormat)) { michael@0: if (!aTile.mCachedBuffer) { michael@0: aTile.mCachedBuffer = SharedBuffer::Create(srcStride * srcSize.height); michael@0: fullPaint = true; michael@0: } michael@0: bufferData = (unsigned char*) aTile.mCachedBuffer->Data(); michael@0: michael@0: drawTarget = gfxPlatform::GetPlatform()->CreateDrawTargetForData(bufferData, michael@0: kTileSize, michael@0: srcStride, michael@0: tileFormat); michael@0: michael@0: if (fullPaint) { michael@0: drawBounds = nsIntRect(aTileOrigin.x, aTileOrigin.y, GetScaledTileSize().width, GetScaledTileSize().height); michael@0: drawRegion = nsIntRegion(drawBounds); michael@0: } else { michael@0: drawBounds = aDirtyRegion.GetBounds(); michael@0: drawRegion = nsIntRegion(drawBounds); michael@0: if (GetContentType() == gfxContentType::COLOR_ALPHA) michael@0: drawTarget->ClearRect(Rect(drawBounds.x - aTileOrigin.x, drawBounds.y - aTileOrigin.y, michael@0: drawBounds.width, drawBounds.height)); michael@0: } michael@0: } else { michael@0: // failed to obtain the client as an ImageSurface michael@0: doBufferedDrawing = false; michael@0: } michael@0: } michael@0: michael@0: // this might get set above if we couldn't extract out a buffer michael@0: if (!doBufferedDrawing) { michael@0: drawTarget = textureClient->GetAsDrawTarget(); michael@0: michael@0: fullPaint = true; michael@0: drawBounds = nsIntRect(aTileOrigin.x, aTileOrigin.y, GetScaledTileSize().width, GetScaledTileSize().height); michael@0: drawRegion = nsIntRegion(drawBounds); michael@0: michael@0: if (GetContentType() == gfxContentType::COLOR_ALPHA) michael@0: drawTarget->ClearRect(Rect(0, 0, drawBounds.width, drawBounds.height)); michael@0: } michael@0: michael@0: // do the drawing michael@0: RefPtr ctxt = new gfxContext(drawTarget); michael@0: michael@0: ctxt->Scale(mResolution, mResolution); michael@0: ctxt->Translate(gfxPoint(-aTileOrigin.x, -aTileOrigin.y)); michael@0: michael@0: mCallback(mThebesLayer, ctxt, michael@0: drawRegion, michael@0: fullPaint ? DrawRegionClip::CLIP_NONE : DrawRegionClip::DRAW_SNAPPED, // XXX DRAW or DRAW_SNAPPED? michael@0: invalidateRegion, michael@0: mCallbackData); michael@0: michael@0: ctxt = nullptr; michael@0: michael@0: if (doBufferedDrawing) { michael@0: memcpy(srcData, bufferData, srcSize.height * srcStride); michael@0: bufferData = nullptr; michael@0: srcDT->ReleaseBits(srcData); michael@0: srcDT = nullptr; michael@0: } michael@0: michael@0: drawTarget = nullptr; michael@0: textureClient->Unlock(); michael@0: michael@0: if (!mCompositableClient->AddTextureClient(textureClient)) { michael@0: NS_WARNING("Failed to add tile TextureClient [simple]"); michael@0: return SimpleTiledLayerTile(); michael@0: } michael@0: michael@0: // aTile.mCachedBuffer was set earlier michael@0: aTile.mTileBuffer = textureClient; michael@0: aTile.mManager = mManager; michael@0: aTile.mLastUpdate = TimeStamp::Now(); michael@0: michael@0: return aTile; michael@0: } michael@0: michael@0: SurfaceDescriptorTiles michael@0: SimpleTiledLayerBuffer::GetSurfaceDescriptorTiles() michael@0: { michael@0: InfallibleTArray tiles; michael@0: michael@0: for (size_t i = 0; i < mRetainedTiles.Length(); i++) { michael@0: tiles.AppendElement(mRetainedTiles[i].GetTileDescriptor()); michael@0: } michael@0: michael@0: return SurfaceDescriptorTiles(mValidRegion, mPaintedRegion, michael@0: tiles, mRetainedWidth, mRetainedHeight, michael@0: mResolution, mFrameResolution.scale); michael@0: } michael@0: michael@0: bool michael@0: SimpleTiledLayerBuffer::HasFormatChanged() const michael@0: { michael@0: return mThebesLayer->CanUseOpaqueSurface() != mLastPaintOpaque; michael@0: } michael@0: michael@0: gfxContentType michael@0: SimpleTiledLayerBuffer::GetContentType() const michael@0: { michael@0: if (mThebesLayer->CanUseOpaqueSurface()) michael@0: return gfxContentType::COLOR; michael@0: michael@0: return gfxContentType::COLOR_ALPHA; michael@0: } michael@0: michael@0: SimpleTiledContentClient::SimpleTiledContentClient(SimpleClientTiledThebesLayer* aThebesLayer, michael@0: ClientLayerManager* aManager) michael@0: : CompositableClient(aManager->AsShadowForwarder()) michael@0: , mTiledBuffer(aThebesLayer, MOZ_THIS_IN_INITIALIZER_LIST(), aManager) michael@0: { michael@0: MOZ_COUNT_CTOR(SimpleTiledContentClient); michael@0: } michael@0: michael@0: SimpleTiledContentClient::~SimpleTiledContentClient() michael@0: { michael@0: MOZ_COUNT_DTOR(SimpleTiledContentClient); michael@0: mTiledBuffer.Release(); michael@0: } michael@0: michael@0: void michael@0: SimpleTiledContentClient::UseTiledLayerBuffer() michael@0: { michael@0: mForwarder->UseTiledLayerBuffer(this, mTiledBuffer.GetSurfaceDescriptorTiles()); michael@0: mTiledBuffer.ClearPaintedRegion(); michael@0: } michael@0: michael@0: SimpleClientTiledThebesLayer::SimpleClientTiledThebesLayer(ClientLayerManager* aManager) michael@0: : ThebesLayer(aManager, michael@0: static_cast(MOZ_THIS_IN_INITIALIZER_LIST())) michael@0: , mContentClient() michael@0: { michael@0: MOZ_COUNT_CTOR(SimpleClientTiledThebesLayer); michael@0: } michael@0: michael@0: SimpleClientTiledThebesLayer::~SimpleClientTiledThebesLayer() michael@0: { michael@0: MOZ_COUNT_DTOR(SimpleClientTiledThebesLayer); michael@0: } michael@0: michael@0: void michael@0: SimpleClientTiledThebesLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs) michael@0: { michael@0: aAttrs = ThebesLayerAttributes(GetValidRegion()); michael@0: } michael@0: michael@0: void michael@0: SimpleClientTiledThebesLayer::RenderLayer() michael@0: { michael@0: LayerManager::DrawThebesLayerCallback callback = michael@0: ClientManager()->GetThebesLayerCallback(); michael@0: void *data = ClientManager()->GetThebesLayerCallbackData(); michael@0: if (!callback) { michael@0: ClientManager()->SetTransactionIncomplete(); michael@0: return; michael@0: } michael@0: michael@0: // First time? Create a content client. michael@0: if (!mContentClient) { michael@0: mContentClient = new SimpleTiledContentClient(this, ClientManager()); michael@0: michael@0: mContentClient->Connect(); michael@0: ClientManager()->AsShadowForwarder()->Attach(mContentClient, this); michael@0: MOZ_ASSERT(mContentClient->GetForwarder()); michael@0: } michael@0: michael@0: // If the format changed, nothing is valid michael@0: if (mContentClient->mTiledBuffer.HasFormatChanged()) { michael@0: mValidRegion = nsIntRegion(); michael@0: } michael@0: michael@0: nsIntRegion invalidRegion = mVisibleRegion; michael@0: invalidRegion.Sub(invalidRegion, mValidRegion); michael@0: if (invalidRegion.IsEmpty()) { michael@0: return; michael@0: } michael@0: michael@0: // Only paint the mask layer on the first transaction. michael@0: if (GetMaskLayer() && !ClientManager()->IsRepeatTransaction()) { michael@0: ToClientLayer(GetMaskLayer())->RenderLayer(); michael@0: } michael@0: michael@0: // SimpleTiledContentClient doesn't support progressive updates or the low michael@0: // precision buffer yet. michael@0: MOZ_ASSERT(!gfxPrefs::UseProgressiveTilePainting() && michael@0: !gfxPrefs::UseLowPrecisionBuffer()); michael@0: michael@0: mValidRegion = mVisibleRegion; michael@0: michael@0: NS_ASSERTION(!ClientManager()->IsRepeatTransaction(), "Didn't paint our mask layer"); michael@0: michael@0: mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion, michael@0: callback, data); michael@0: michael@0: ClientManager()->Hold(this); michael@0: michael@0: mContentClient->UseTiledLayerBuffer(); michael@0: } michael@0: michael@0: michael@0: } michael@0: }