forked from mirrors/gecko-dev
		
	MozReview-Commit-ID: JJXYkF5PR5K --HG-- extra : rebase_source : 86217890ee8a6e5d26b1b26cc063d42c6793c903
		
			
				
	
	
		
			486 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			486 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | 
						|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | 
						|
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
#include "PersistentBufferProvider.h"
 | 
						|
 | 
						|
#include "Layers.h"
 | 
						|
#include "mozilla/layers/ShadowLayers.h"
 | 
						|
#include "mozilla/layers/TextureClient.h"
 | 
						|
#include "mozilla/gfx/Logging.h"
 | 
						|
#include "pratom.h"
 | 
						|
#include "gfxPlatform.h"
 | 
						|
 | 
						|
namespace mozilla {
 | 
						|
 | 
						|
using namespace gfx;
 | 
						|
 | 
						|
namespace layers {
 | 
						|
 | 
						|
PersistentBufferProviderBasic::PersistentBufferProviderBasic(DrawTarget* aDt)
 | 
						|
: mDrawTarget(aDt)
 | 
						|
{
 | 
						|
  MOZ_COUNT_CTOR(PersistentBufferProviderBasic);
 | 
						|
}
 | 
						|
 | 
						|
PersistentBufferProviderBasic::~PersistentBufferProviderBasic()
 | 
						|
{
 | 
						|
  MOZ_COUNT_DTOR(PersistentBufferProviderBasic);
 | 
						|
  Destroy();
 | 
						|
}
 | 
						|
 | 
						|
already_AddRefed<gfx::DrawTarget>
 | 
						|
PersistentBufferProviderBasic::BorrowDrawTarget(const gfx::IntRect& aPersistedRect)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(!mSnapshot);
 | 
						|
  RefPtr<gfx::DrawTarget> dt(mDrawTarget);
 | 
						|
  return dt.forget();
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
PersistentBufferProviderBasic::ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT)
 | 
						|
{
 | 
						|
  RefPtr<gfx::DrawTarget> dt(aDT);
 | 
						|
  MOZ_ASSERT(mDrawTarget == dt);
 | 
						|
  if (dt) {
 | 
						|
    // Since SkiaGL default to storing drawing command until flush
 | 
						|
    // we have to flush it before present.
 | 
						|
    dt->Flush();
 | 
						|
  }
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
already_AddRefed<gfx::SourceSurface>
 | 
						|
PersistentBufferProviderBasic::BorrowSnapshot()
 | 
						|
{
 | 
						|
  mSnapshot = mDrawTarget->Snapshot();
 | 
						|
  RefPtr<SourceSurface> snapshot = mSnapshot;
 | 
						|
  return snapshot.forget();
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
PersistentBufferProviderBasic::ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot)
 | 
						|
{
 | 
						|
  RefPtr<SourceSurface> snapshot = aSnapshot;
 | 
						|
  MOZ_ASSERT(!snapshot || snapshot == mSnapshot);
 | 
						|
  mSnapshot = nullptr;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
PersistentBufferProviderBasic::Destroy()
 | 
						|
{
 | 
						|
  mSnapshot = nullptr;
 | 
						|
  mDrawTarget = nullptr;
 | 
						|
}
 | 
						|
 | 
						|
//static
 | 
						|
already_AddRefed<PersistentBufferProviderBasic>
 | 
						|
PersistentBufferProviderBasic::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
 | 
						|
                                      gfx::BackendType aBackend)
 | 
						|
{
 | 
						|
  RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(aBackend, aSize, aFormat);
 | 
						|
 | 
						|
  if (!dt) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<PersistentBufferProviderBasic> provider =
 | 
						|
    new PersistentBufferProviderBasic(dt);
 | 
						|
 | 
						|
  return provider.forget();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
//static
 | 
						|
already_AddRefed<PersistentBufferProviderShared>
 | 
						|
PersistentBufferProviderShared::Create(gfx::IntSize aSize,
 | 
						|
                                       gfx::SurfaceFormat aFormat,
 | 
						|
                                       KnowsCompositor* aKnowsCompositor)
 | 
						|
{
 | 
						|
  if (!aKnowsCompositor || !aKnowsCompositor->GetTextureForwarder()->IPCOpen()) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<TextureClient> texture = TextureClient::CreateForDrawing(
 | 
						|
    aKnowsCompositor, aFormat, aSize,
 | 
						|
    BackendSelector::Canvas,
 | 
						|
    TextureFlags::DEFAULT,
 | 
						|
    TextureAllocationFlags::ALLOC_DEFAULT
 | 
						|
  );
 | 
						|
 | 
						|
  if (!texture) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<PersistentBufferProviderShared> provider =
 | 
						|
    new PersistentBufferProviderShared(aSize, aFormat, aKnowsCompositor, texture);
 | 
						|
  return provider.forget();
 | 
						|
}
 | 
						|
 | 
						|
PersistentBufferProviderShared::PersistentBufferProviderShared(gfx::IntSize aSize,
 | 
						|
                                                               gfx::SurfaceFormat aFormat,
 | 
						|
                                                               KnowsCompositor* aKnowsCompositor,
 | 
						|
                                                               RefPtr<TextureClient>& aTexture)
 | 
						|
 | 
						|
: mSize(aSize)
 | 
						|
, mFormat(aFormat)
 | 
						|
, mKnowsCompositor(aKnowsCompositor)
 | 
						|
, mFront(Nothing())
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aKnowsCompositor);
 | 
						|
  if (mTextures.append(aTexture)) {
 | 
						|
    mBack = Some<uint32_t>(0);
 | 
						|
  }
 | 
						|
  MOZ_COUNT_CTOR(PersistentBufferProviderShared);
 | 
						|
}
 | 
						|
 | 
						|
PersistentBufferProviderShared::~PersistentBufferProviderShared()
 | 
						|
{
 | 
						|
  MOZ_COUNT_DTOR(PersistentBufferProviderShared);
 | 
						|
 | 
						|
  if (IsActivityTracked()) {
 | 
						|
    mKnowsCompositor->GetActiveResourceTracker()->RemoveObject(this);
 | 
						|
  }
 | 
						|
 | 
						|
  Destroy();
 | 
						|
}
 | 
						|
 | 
						|
LayersBackend
 | 
						|
PersistentBufferProviderShared::GetType()
 | 
						|
{
 | 
						|
  if (mKnowsCompositor->GetCompositorBackendType() == LayersBackend::LAYERS_WR) {
 | 
						|
    return LayersBackend::LAYERS_WR;
 | 
						|
  } else {
 | 
						|
    return LayersBackend::LAYERS_CLIENT;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
PersistentBufferProviderShared::SetKnowsCompositor(KnowsCompositor* aKnowsCompositor)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aKnowsCompositor);
 | 
						|
  if (!aKnowsCompositor) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  if (mKnowsCompositor == aKnowsCompositor) {
 | 
						|
    // The forwarder should not change most of the time.
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  if (IsActivityTracked()) {
 | 
						|
    mKnowsCompositor->GetActiveResourceTracker()->RemoveObject(this);
 | 
						|
  }
 | 
						|
 | 
						|
  if (mKnowsCompositor->GetTextureForwarder() != aKnowsCompositor->GetTextureForwarder() ||
 | 
						|
      mKnowsCompositor->GetCompositorBackendType() != aKnowsCompositor->GetCompositorBackendType()) {
 | 
						|
    // We are going to be used with an different and/or incompatible forwarder.
 | 
						|
    // This should be extremely rare. We have to copy the front buffer into a
 | 
						|
    // texture that is compatible with the new forwarder.
 | 
						|
 | 
						|
    // Grab the current front buffer.
 | 
						|
    RefPtr<TextureClient> prevTexture = GetTexture(mFront);
 | 
						|
 | 
						|
    // Get rid of everything else
 | 
						|
    Destroy();
 | 
						|
 | 
						|
    if (prevTexture) {
 | 
						|
      RefPtr<TextureClient> newTexture = TextureClient::CreateForDrawing(
 | 
						|
        aKnowsCompositor, mFormat, mSize,
 | 
						|
        BackendSelector::Canvas,
 | 
						|
        TextureFlags::DEFAULT,
 | 
						|
        TextureAllocationFlags::ALLOC_DEFAULT
 | 
						|
      );
 | 
						|
 | 
						|
      MOZ_ASSERT(newTexture);
 | 
						|
      if (!newTexture) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
 | 
						|
      // If we early-return in one of the following branches, we will
 | 
						|
      // leave the buffer provider in an empty state, since we called
 | 
						|
      // Destroy. Not ideal but at least we won't try to use it with a
 | 
						|
      // an incompatible ipc channel.
 | 
						|
 | 
						|
      if (!newTexture->Lock(OpenMode::OPEN_WRITE)) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
 | 
						|
      if (!prevTexture->Lock(OpenMode::OPEN_READ)) {
 | 
						|
        newTexture->Unlock();
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
 | 
						|
      bool success = prevTexture->CopyToTextureClient(newTexture, nullptr, nullptr);
 | 
						|
 | 
						|
      prevTexture->Unlock();
 | 
						|
      newTexture->Unlock();
 | 
						|
 | 
						|
      if (!success) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
 | 
						|
      if (!mTextures.append(newTexture)) {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
      mFront = Some<uint32_t>(mTextures.length() - 1);
 | 
						|
      mBack = mFront;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  mKnowsCompositor = aKnowsCompositor;
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
TextureClient*
 | 
						|
PersistentBufferProviderShared::GetTexture(const Maybe<uint32_t>& aIndex)
 | 
						|
{
 | 
						|
  if (aIndex.isNothing() || !CheckIndex(aIndex.value())) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
  return mTextures[aIndex.value()];
 | 
						|
}
 | 
						|
 | 
						|
already_AddRefed<gfx::DrawTarget>
 | 
						|
PersistentBufferProviderShared::BorrowDrawTarget(const gfx::IntRect& aPersistedRect)
 | 
						|
{
 | 
						|
  if (!mKnowsCompositor->GetTextureForwarder()->IPCOpen()) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  MOZ_ASSERT(!mSnapshot);
 | 
						|
 | 
						|
  if (IsActivityTracked()) {
 | 
						|
    mKnowsCompositor->GetActiveResourceTracker()->MarkUsed(this);
 | 
						|
  } else {
 | 
						|
    mKnowsCompositor->GetActiveResourceTracker()->AddObject(this);
 | 
						|
  }
 | 
						|
 | 
						|
  if (mDrawTarget) {
 | 
						|
    RefPtr<gfx::DrawTarget> dt(mDrawTarget);
 | 
						|
    return dt.forget();
 | 
						|
  }
 | 
						|
 | 
						|
  auto previousBackBuffer = mBack;
 | 
						|
 | 
						|
  TextureClient* tex = GetTexture(mBack);
 | 
						|
 | 
						|
  // First try to reuse the current back buffer. If we can do that it means
 | 
						|
  // we can skip copying its content to the new back buffer.
 | 
						|
  if (tex && tex->IsReadLocked()) {
 | 
						|
    // The back buffer is currently used by the compositor, we can't draw
 | 
						|
    // into it.
 | 
						|
    tex = nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!tex) {
 | 
						|
    // Try to grab an already allocated texture if any is available.
 | 
						|
    for (uint32_t i = 0; i < mTextures.length(); ++i) {
 | 
						|
      if (!mTextures[i]->IsReadLocked()) {
 | 
						|
        mBack = Some(i);
 | 
						|
        tex = mTextures[i];
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (!tex) {
 | 
						|
    // We have to allocate a new texture.
 | 
						|
    if (mTextures.length() >= 4) {
 | 
						|
      // We should never need to buffer that many textures, something's wrong.
 | 
						|
      // In theory we throttle the main thread when the compositor can't keep up,
 | 
						|
      // so we shoud never get in a situation where we sent 4 textures to the
 | 
						|
      // compositor and the latter has not released any of them.
 | 
						|
      // In practice, though, the throttling mechanism appears to have some issues,
 | 
						|
      // especially when switching between layer managers (during tab-switch).
 | 
						|
      // To make sure we don't get too far ahead of the compositor, we send a
 | 
						|
      // sync ping to the compositor thread...
 | 
						|
      mKnowsCompositor->SyncWithCompositor();
 | 
						|
      // ...and try again.
 | 
						|
      for (uint32_t i = 0; i < mTextures.length(); ++i) {
 | 
						|
        if (!mTextures[i]->IsReadLocked()) {
 | 
						|
          gfxCriticalNote << "Managed to allocate after flush.";
 | 
						|
          mBack = Some(i);
 | 
						|
          tex = mTextures[i];
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      if (!tex) {
 | 
						|
        gfxCriticalError() << "Unexpected BufferProvider over-production.";
 | 
						|
        // It would be pretty bad to keep piling textures up at this point so we
 | 
						|
        // call NotifyInactive to remove some of our textures.
 | 
						|
        NotifyInactive();
 | 
						|
        // Give up now. The caller can fall-back to a non-shared buffer provider.
 | 
						|
        return nullptr;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    RefPtr<TextureClient> newTexture = TextureClient::CreateForDrawing(
 | 
						|
      mKnowsCompositor, mFormat, mSize,
 | 
						|
      BackendSelector::Canvas,
 | 
						|
      TextureFlags::DEFAULT,
 | 
						|
      TextureAllocationFlags::ALLOC_DEFAULT
 | 
						|
    );
 | 
						|
 | 
						|
    MOZ_ASSERT(newTexture);
 | 
						|
    if (newTexture) {
 | 
						|
      if (mTextures.append(newTexture)) {
 | 
						|
        tex = newTexture;
 | 
						|
        mBack = Some<uint32_t>(mTextures.length() - 1);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (!tex || !tex->Lock(OpenMode::OPEN_READ_WRITE)) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  if (mBack != previousBackBuffer && !aPersistedRect.IsEmpty()) {
 | 
						|
    TextureClient* previous = GetTexture(previousBackBuffer);
 | 
						|
    if (previous && previous->Lock(OpenMode::OPEN_READ)) {
 | 
						|
      DebugOnly<bool> success = previous->CopyToTextureClient(tex, &aPersistedRect, nullptr);
 | 
						|
      MOZ_ASSERT(success);
 | 
						|
 | 
						|
      previous->Unlock();
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  mDrawTarget = tex->BorrowDrawTarget();
 | 
						|
 | 
						|
  RefPtr<gfx::DrawTarget> dt(mDrawTarget);
 | 
						|
  return dt.forget();
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
PersistentBufferProviderShared::ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT)
 | 
						|
{
 | 
						|
  RefPtr<gfx::DrawTarget> dt(aDT);
 | 
						|
  MOZ_ASSERT(mDrawTarget == dt);
 | 
						|
  // Can't change the current front buffer while its snapshot is borrowed!
 | 
						|
  MOZ_ASSERT(!mSnapshot);
 | 
						|
 | 
						|
  mDrawTarget = nullptr;
 | 
						|
  dt = nullptr;
 | 
						|
 | 
						|
  TextureClient* back = GetTexture(mBack);
 | 
						|
  MOZ_ASSERT(back);
 | 
						|
 | 
						|
  if (back) {
 | 
						|
    back->Unlock();
 | 
						|
    mFront = mBack;
 | 
						|
  }
 | 
						|
 | 
						|
  return !!back;
 | 
						|
}
 | 
						|
 | 
						|
TextureClient*
 | 
						|
PersistentBufferProviderShared::GetTextureClient()
 | 
						|
{
 | 
						|
  // Can't access the front buffer while drawing.
 | 
						|
  MOZ_ASSERT(!mDrawTarget);
 | 
						|
  TextureClient* texture = GetTexture(mFront);
 | 
						|
  if (texture) {
 | 
						|
    texture->EnableReadLock();
 | 
						|
  } else {
 | 
						|
    gfxCriticalNote << "PersistentBufferProviderShared: front buffer unavailable";
 | 
						|
  }
 | 
						|
  return texture;
 | 
						|
}
 | 
						|
 | 
						|
already_AddRefed<gfx::SourceSurface>
 | 
						|
PersistentBufferProviderShared::BorrowSnapshot()
 | 
						|
{
 | 
						|
  MOZ_ASSERT(!mDrawTarget);
 | 
						|
 | 
						|
  auto front = GetTexture(mFront);
 | 
						|
  if (!front || front->IsLocked()) {
 | 
						|
    MOZ_ASSERT(false);
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!front->Lock(OpenMode::OPEN_READ)) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<DrawTarget> dt = front->BorrowDrawTarget();
 | 
						|
 | 
						|
  if (!dt) {
 | 
						|
    front->Unlock();
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  mSnapshot = dt->Snapshot();
 | 
						|
 | 
						|
  RefPtr<SourceSurface> snapshot = mSnapshot;
 | 
						|
  return snapshot.forget();
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
PersistentBufferProviderShared::ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot)
 | 
						|
{
 | 
						|
  RefPtr<SourceSurface> snapshot = aSnapshot;
 | 
						|
  MOZ_ASSERT(!snapshot || snapshot == mSnapshot);
 | 
						|
 | 
						|
  mSnapshot = nullptr;
 | 
						|
  snapshot = nullptr;
 | 
						|
 | 
						|
  auto front = GetTexture(mFront);
 | 
						|
  if (front) {
 | 
						|
    front->Unlock();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
PersistentBufferProviderShared::NotifyInactive()
 | 
						|
{
 | 
						|
  ClearCachedResources();
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
PersistentBufferProviderShared::ClearCachedResources()
 | 
						|
{
 | 
						|
  RefPtr<TextureClient> front = GetTexture(mFront);
 | 
						|
  RefPtr<TextureClient> back = GetTexture(mBack);
 | 
						|
 | 
						|
  // Clear all textures (except the front and back ones that we just kept).
 | 
						|
  mTextures.clear();
 | 
						|
 | 
						|
  if (back) {
 | 
						|
    if (mTextures.append(back)) {
 | 
						|
      mBack = Some<uint32_t>(0);
 | 
						|
    }
 | 
						|
    if (front == back) {
 | 
						|
      mFront = mBack;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (front && front != back) {
 | 
						|
    if (mTextures.append(front)) {
 | 
						|
      mFront = Some<uint32_t>(mTextures.length() - 1);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
PersistentBufferProviderShared::Destroy()
 | 
						|
{
 | 
						|
  mSnapshot = nullptr;
 | 
						|
  mDrawTarget = nullptr;
 | 
						|
 | 
						|
  for (auto& mTexture : mTextures) {
 | 
						|
    TextureClient* texture = mTexture;
 | 
						|
    if (texture && texture->IsLocked()) {
 | 
						|
      MOZ_ASSERT(false);
 | 
						|
      texture->Unlock();
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  mTextures.clear();
 | 
						|
}
 | 
						|
 | 
						|
} // namespace layers
 | 
						|
} // namespace mozilla
 |