зеркало из https://github.com/mozilla/gecko-dev.git
445 строки
11 KiB
C++
445 строки
11 KiB
C++
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
* 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/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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
//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,
|
|
CompositableForwarder* aFwd)
|
|
{
|
|
if (!aFwd || !aFwd->IPCOpen()) {
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<TextureClient> texture = TextureClient::CreateForDrawing(
|
|
aFwd, aFormat, aSize,
|
|
BackendSelector::Canvas,
|
|
TextureFlags::DEFAULT,
|
|
TextureAllocationFlags::ALLOC_DEFAULT
|
|
);
|
|
|
|
if (!texture) {
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<PersistentBufferProviderShared> provider =
|
|
new PersistentBufferProviderShared(aSize, aFormat, aFwd, texture);
|
|
return provider.forget();
|
|
}
|
|
|
|
PersistentBufferProviderShared::PersistentBufferProviderShared(gfx::IntSize aSize,
|
|
gfx::SurfaceFormat aFormat,
|
|
CompositableForwarder* aFwd,
|
|
RefPtr<TextureClient>& aTexture)
|
|
|
|
: mSize(aSize)
|
|
, mFormat(aFormat)
|
|
, mFwd(aFwd)
|
|
, mFront(Nothing())
|
|
{
|
|
if (mTextures.append(aTexture)) {
|
|
mBack = Some<uint32_t>(0);
|
|
}
|
|
MOZ_COUNT_CTOR(PersistentBufferProviderShared);
|
|
}
|
|
|
|
PersistentBufferProviderShared::~PersistentBufferProviderShared()
|
|
{
|
|
MOZ_COUNT_DTOR(PersistentBufferProviderShared);
|
|
|
|
if (IsActivityTracked()) {
|
|
mFwd->GetActiveResourceTracker().RemoveObject(this);
|
|
}
|
|
|
|
Destroy();
|
|
}
|
|
|
|
bool
|
|
PersistentBufferProviderShared::SetForwarder(CompositableForwarder* aFwd)
|
|
{
|
|
MOZ_ASSERT(aFwd);
|
|
if (!aFwd) {
|
|
return false;
|
|
}
|
|
|
|
if (mFwd == aFwd) {
|
|
// The forwarder should not change most of the time.
|
|
return true;
|
|
}
|
|
|
|
if (IsActivityTracked()) {
|
|
mFwd->GetActiveResourceTracker().RemoveObject(this);
|
|
}
|
|
|
|
if (mFwd->AsTextureForwarder() != aFwd->AsTextureForwarder() ||
|
|
mFwd->GetCompositorBackendType() != aFwd->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(
|
|
aFwd, 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;
|
|
}
|
|
}
|
|
|
|
mFwd = aFwd;
|
|
|
|
return true;
|
|
}
|
|
|
|
TextureClient*
|
|
PersistentBufferProviderShared::GetTexture(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 (!mFwd->IPCOpen()) {
|
|
return nullptr;
|
|
}
|
|
|
|
MOZ_ASSERT(!mSnapshot);
|
|
|
|
if (IsActivityTracked()) {
|
|
mFwd->GetActiveResourceTracker().MarkUsed(this);
|
|
} else {
|
|
mFwd->GetActiveResourceTracker().AddObject(this);
|
|
}
|
|
|
|
if (mDrawTarget) {
|
|
RefPtr<gfx::DrawTarget> dt(mDrawTarget);
|
|
return dt.forget();
|
|
}
|
|
|
|
mFront = Nothing();
|
|
|
|
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.
|
|
MOZ_ASSERT(false);
|
|
// 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 as not released any of them.
|
|
// This seems to happen, however, in some edge cases such as just after a
|
|
// device reset (cf. Bug 1291163).
|
|
// 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(
|
|
mFwd, 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()
|
|
{
|
|
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 (uint32_t i = 0; i < mTextures.length(); ++i) {
|
|
TextureClient* texture = mTextures[i];
|
|
if (texture && texture->IsLocked()) {
|
|
MOZ_ASSERT(false);
|
|
texture->Unlock();
|
|
}
|
|
}
|
|
|
|
mTextures.clear();
|
|
}
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|