/* -*- 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/. */
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();
} returntrue;
}
if (dt) { // This is simply to ensure the DrawTarget gets initialized, and will detect // a device reset, even if we're on the main thread.
dt->ClearRect(Rect(0, 0, 0, 0));
}
if (!dt || !dt->IsValid()) { return nullptr;
}
RefPtr<PersistentBufferProviderBasic> provider = new PersistentBufferProviderBasic(dt);
if (IsActivityTracked()) { if (auto* cm = CanvasManagerChild::Get()) {
cm->GetActiveResourceTracker()->RemoveObject(this);
} else {
MOZ_ASSERT_UNREACHABLE("Tracked but no CanvasManagerChild!");
}
}
if (mKnowsCompositor == aKnowsCompositor) { // The forwarder should not change most of the time. returntrue;
}
if (IsActivityTracked()) { if (auto* cm = CanvasManagerChild::Get()) {
cm->GetActiveResourceTracker()->RemoveObject(this);
} else {
MOZ_ASSERT_UNREACHABLE("Tracked but no CanvasManagerChild!");
}
}
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);
MOZ_ASSERT(newTexture); if (!newTexture) { returnfalse;
}
// 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)) { returnfalse;
}
if (!prevTexture->Lock(OpenMode::OPEN_READ)) {
newTexture->Unlock(); returnfalse;
}
auto* cm = CanvasManagerChild::Get(); if (NS_WARN_IF(!cm)) { return nullptr;
}
MOZ_ASSERT(!mSnapshot);
if (IsActivityTracked()) {
cm->GetActiveResourceTracker()->MarkUsed(this);
} else {
cm->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() >= mMaxAllowedTextures) { // 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(mWindowID); // ...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) {
gfxCriticalNote << "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;
}
}
MOZ_ASSERT(newTexture); if (newTexture) { if (mTextures.append(newTexture)) {
tex = newTexture;
mBack = Some<uint32_t>(mTextures.length() - 1);
}
}
}
if (!tex) { return nullptr;
}
if (mPermanentBackBuffer) { // If we have a permanent back buffer lock the selected one and switch to // the permanent one before borrowing the DrawTarget. We will copy back into // the selected one when ReturnDrawTarget is called, before we make it the // new front buffer. if (!tex->Lock(OpenMode::OPEN_WRITE)) { return nullptr;
}
tex = mPermanentBackBuffer;
} else { // Copy from the previous back buffer if required.
Maybe<TextureClientAutoLock> autoReadLock;
TextureClient* previous = nullptr; if (mBack != previousBackBuffer && !aPersistedRect.IsEmpty()) { if (tex->HasSynchronization()) { // We are about to read lock a texture that is in use by the compositor // and has synchronization. To prevent possible future contention we // switch to using a permanent back buffer.
mPermanentBackBuffer = CreateTexture(mKnowsCompositor, mFormat, mSize,
mWillReadFrequently); if (!mPermanentBackBuffer) { return nullptr;
} if (!tex->Lock(OpenMode::OPEN_WRITE)) { return nullptr;
}
tex = mPermanentBackBuffer;
}
previous = GetTexture(previousBackBuffer); if (previous) {
autoReadLock.emplace(previous, OpenMode::OPEN_READ);
}
}
if (!tex->Lock(OpenMode::OPEN_READ_WRITE)) { return nullptr;
}
mDrawTarget = tex->BorrowDrawTarget(); if (mDrawTarget) { // This is simply to ensure the DrawTarget gets initialized, and will detect // a device reset, even if we're on the main thread.
mDrawTarget->ClearRect(Rect(0, 0, 0, 0));
if (!mDrawTarget->IsValid()) {
mDrawTarget = nullptr;
}
}
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);
TextureClient* back = GetTexture(mBack);
MOZ_ASSERT(back);
mDrawTarget = nullptr;
dt = nullptr;
// If we have a permanent back buffer we have actually been drawing to that, // so now we must copy to the shared one. if (mPermanentBackBuffer && back) {
DebugOnly<bool> success =
mPermanentBackBuffer->CopyToTextureClient(back, nullptr, nullptr);
MOZ_ASSERT(success);
// Let our permanent back buffer know that we have finished drawing.
mPermanentBackBuffer->EndDraw();
}
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) {
gfxCriticalNote
<< "PersistentBufferProviderShared: front buffer unavailable"; return nullptr;
}
// Sometimes, for example on tab switch, we re-forward our texture. So if we // are and it is still read locked, then borrow and return our DrawTarget to // force it to be copied to a texture that we will safely read lock. if (texture->IsReadLocked()) {
RefPtr<DrawTarget> dt =
BorrowDrawTarget(IntRect(0, 0, mSize.width, mSize.height));
// If we failed to borrow a DrawTarget then all our textures must be read // locked or we failed to create one, so we'll just return the current front // buffer even though that might lead to contention. if (dt) {
ReturnDrawTarget(dt.forget());
texture = GetTexture(mFront); if (!texture) {
gfxCriticalNote
<< "PersistentBufferProviderShared: front buffer unavailable"; return nullptr;
}
}
} else { // If it isn't read locked then make sure it is set as updated, so that we // will always read lock even if we've forwarded the texture before.
texture->SetUpdated();
}
return texture;
}
already_AddRefed<gfx::SourceSurface>
PersistentBufferProviderShared::BorrowSnapshot(gfx::DrawTarget* aTarget) { // If we have a permanent back buffer we can always use that to snapshot. if (mPermanentBackBuffer) {
mSnapshot = mPermanentBackBuffer->BorrowSnapshot(); return do_AddRef(mSnapshot);
}
if (mDrawTarget) { auto back = GetTexture(mBack); if (NS_WARN_IF(!back) || NS_WARN_IF(!back->IsLocked())) { return nullptr;
}
mSnapshot = back->BorrowSnapshot(); return do_AddRef(mSnapshot);
}
auto front = GetTexture(mFront); if (NS_WARN_IF(!front) || NS_WARN_IF(front->IsLocked())) { return nullptr;
}
if (front->IsReadLocked() && front->HasSynchronization()) { // We are about to read lock a texture that is in use by the compositor and // has synchronization. To prevent possible future contention we switch to // using a permanent back buffer.
mPermanentBackBuffer =
CreateTexture(mKnowsCompositor, mFormat, mSize, mWillReadFrequently); if (!mPermanentBackBuffer ||
!mPermanentBackBuffer->Lock(OpenMode::OPEN_READ_WRITE)) { return nullptr;
}
if (!front->Lock(OpenMode::OPEN_READ)) { return nullptr;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.