Bug 1829026 - Update CanvasTranslator to work with DrawTargetWebgl. r=aosmond

This adds the necessary infrastructure for CanvasTranslator to allocate DrawTargetWebgl
instead of just allocating TextureData, and to use RemoteTextureMap to handle sending
the DrawTargetWebgl frames to the compositor.

This optimizes snapshot transport to use fewer copies to and from shmems when we know
the snapshot contents can be sourced from a shmem.

This adds a blocking mechanism separate from deactivation so that existing DrawTargetWebgls
can continue processing events while denying further ones from being created in the event
that allocating further DrawTargetWebgls might cause problems, but so that we don't disrupt
canvases that are already in flight.

PersistentBufferProviderAccelerated still remains the buffer provider for the new setup,
but just allocates a single RecordedTextureData internally. Since DrawTargetWebgl already
does its own swap chain management internally, we do not want to use the multiple texture
client strategy that PersistentBufferProviderShared does.

This adds a fallback mechanism such that if DrawTargetWebgl allocation fails, a TextureData
is allocated instead that still sends results to RemoteTextureMap. This has the advantage
that we don't need to synchronously block in the content process to verify if acceleration
succeeded, as the costs of such blocking are rather extreme and we must still produce the
rendered frame to ensure the user sees the correct result if the speculative acceleration
failed. It then notifies the content process asynchronously via the refresh mechanism to
try to recreate a non-accelerated buffer provider when it is ready.

There is one additional hitch in RemoteTextureMap that we need to add a mechanism to deal
with the setup of the RemoteTextureOwner. When display list building initially needs to get
the remote texture, the RemoteTextureOwner might not exist yet. In this case, we need to add
a block to ensure we wait for that to occur so that we do not render an erroneous result.
Previously, this block was handled in ClientWebGLContext. Since that is no longer used,
the block must be reinstated somewhere else until a non-blocking mechanism for display list
building to proceed with a stub texture host wrapper can be implemented.

Currently this leaves the gfx.canvas.remote and gfx.canvas.accelerated prefs as separate
toggles rather than trying to lump everything into one mechanism. While this may be desirable
in the future, currently Direct2D remote canvas is a separate acceleration mechanism that
needs to co-exist with the WebGL acceleration, and so being able to toggle both on or off
for testing is desirable.

Differential Revision: https://phabricator.services.mozilla.com/D194352
This commit is contained in:
Lee Salzman 2023-12-12 07:35:03 +00:00
parent 7f9ab71be1
commit 0978159170
28 changed files with 952 additions and 232 deletions

View File

@ -64,7 +64,6 @@
#include "nsIMemoryReporter.h"
#include "nsStyleUtil.h"
#include "CanvasImageCache.h"
#include "DrawTargetWebgl.h"
#include <algorithm>
@ -1357,6 +1356,7 @@ bool CanvasRenderingContext2D::CopyBufferProvider(
}
aTarget.CopySurface(snapshot, aCopyRect, IntPoint());
aOld.ReturnSnapshot(snapshot.forget());
return true;
}
@ -1651,13 +1651,22 @@ bool CanvasRenderingContext2D::TryAcceleratedTarget(
if (!mAllowAcceleration || GetEffectiveWillReadFrequently()) {
return false;
}
aOutDT = DrawTargetWebgl::Create(GetSize(), GetSurfaceFormat());
if (!aOutDT) {
if (!mCanvasElement) {
return false;
}
aOutProvider = new PersistentBufferProviderAccelerated(aOutDT);
return true;
WindowRenderer* renderer = WindowRendererFromCanvasElement(mCanvasElement);
if (!renderer) {
return false;
}
aOutProvider = PersistentBufferProviderAccelerated::Create(
GetSize(), GetSurfaceFormat(), renderer->AsKnowsCompositor());
if (!aOutProvider) {
return false;
}
aOutDT = aOutProvider->BorrowDrawTarget(IntRect());
MOZ_ASSERT(aOutDT);
return !!aOutDT;
}
bool CanvasRenderingContext2D::TrySharedTarget(

View File

@ -238,7 +238,7 @@ fuzzy(0-1,0-2) skip-if(cocoaWidget||winWidget||gtkWidget) needs-focus == drawFoc
# Check that captureStream() displays in a local video element
== capturestream.html wrapper.html?green.png
fuzzy-if(azureSkia,0-16,0-20) fuzzy-if(Android,0-3,0-40) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu),0-1,0-1) == 1177726-text-stroke-bounds.html 1177726-text-stroke-bounds-ref.html
fuzzy(0-235,0-3104) == 1177726-text-stroke-bounds.html 1177726-text-stroke-bounds-ref.html
# Bug 1366027
== clipped-dash-stroke-rect.html clipped-dash-stroke-rect-ref.html

View File

@ -165,7 +165,13 @@ void CanvasManagerChild::DeactivateCanvas() {
}
}
void CanvasManagerChild::BlockCanvas() { mBlocked = true; }
RefPtr<layers::CanvasChild> CanvasManagerChild::GetCanvasChild() {
if (mBlocked) {
return nullptr;
}
if (!mActive) {
MOZ_ASSERT(!mCanvasChild);
return nullptr;

View File

@ -49,6 +49,7 @@ class CanvasManagerChild final : public PCanvasManagerChild {
void EndCanvasTransaction();
void ClearCachedResources();
void DeactivateCanvas();
void BlockCanvas();
RefPtr<layers::CanvasChild> GetCanvasChild();
@ -63,6 +64,7 @@ class CanvasManagerChild final : public PCanvasManagerChild {
RefPtr<webgpu::WebGPUChild> mWebGPUChild;
const uint32_t mId;
bool mActive = true;
bool mBlocked = false;
static MOZ_THREAD_LOCAL(CanvasManagerChild*) sLocalManager;
static Atomic<uint32_t> sNextId;

View File

@ -360,14 +360,15 @@ mozilla::ipc::IPCResult GPUParent::RecvInit(
// here that would normally be initialized there.
SkGraphics::Init();
if (gfxVars::RemoteCanvasEnabled()) {
bool useRemoteCanvas =
gfxVars::RemoteCanvasEnabled() || gfxVars::UseAcceleratedCanvas2D();
if (useRemoteCanvas) {
gfxGradientCache::Init();
}
#if defined(XP_WIN)
if (gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
if (DeviceManagerDx::Get()->CreateCompositorDevices() &&
gfxVars::RemoteCanvasEnabled()) {
if (DeviceManagerDx::Get()->CreateCompositorDevices() && useRemoteCanvas) {
if (DeviceManagerDx::Get()->CreateCanvasDevice()) {
gfxDWriteFont::InitDWriteSupport();
} else {

View File

@ -43,6 +43,7 @@ CanvasDrawEventRecorder::CanvasDrawEventRecorder() {
}
bool CanvasDrawEventRecorder::Init(TextureType aTextureType,
gfx::BackendType aBackendType,
UniquePtr<Helpers> aHelpers) {
mHelpers = std::move(aHelpers);
@ -91,7 +92,8 @@ bool CanvasDrawEventRecorder::Init(TextureType aTextureType,
return false;
}
if (!mHelpers->InitTranslator(aTextureType, std::move(header->handle),
if (!mHelpers->InitTranslator(aTextureType, aBackendType,
std::move(header->handle),
std::move(bufferHandles), mDefaultBufferSize,
std::move(readerSem), std::move(writerSem),
/* aUseIPDLThread */ false)) {

View File

@ -67,16 +67,13 @@ class CanvasDrawEventRecorder final : public gfx::DrawEventRecorderPrivate,
public:
virtual ~Helpers() = default;
virtual bool InitTranslator(const TextureType& aTextureType,
Handle&& aReadHandle,
nsTArray<Handle>&& aBufferHandles,
const uint64_t& aBufferSize,
CrossProcessSemaphoreHandle&& aReaderSem,
CrossProcessSemaphoreHandle&& aWriterSem,
const bool& aUseIPDLThread) = 0;
virtual bool InitTranslator(
TextureType aTextureType, gfx::BackendType aBackendType,
Handle&& aReadHandle, nsTArray<Handle>&& aBufferHandles,
uint64_t aBufferSize, CrossProcessSemaphoreHandle&& aReaderSem,
CrossProcessSemaphoreHandle&& aWriterSem, bool aUseIPDLThread) = 0;
virtual bool AddBuffer(Handle&& aBufferHandle,
const uint64_t& aBufferSize) = 0;
virtual bool AddBuffer(Handle&& aBufferHandle, uint64_t aBufferSize) = 0;
/**
* @returns true if the reader of the CanvasEventRingBuffer has permanently
@ -90,7 +87,8 @@ class CanvasDrawEventRecorder final : public gfx::DrawEventRecorderPrivate,
virtual bool RestartReader() = 0;
};
bool Init(TextureType aTextureType, UniquePtr<Helpers> aHelpers);
bool Init(TextureType aTextureType, gfx::BackendType aBackendType,
UniquePtr<Helpers> aHelpers);
/**
* Record an event for processing by the CanvasParent's CanvasTranslator.

View File

@ -6,6 +6,7 @@
#include "PersistentBufferProvider.h"
#include "mozilla/layers/RemoteTextureMap.h"
#include "mozilla/layers/TextureClient.h"
#include "mozilla/layers/TextureForwarder.h"
#include "mozilla/gfx/gfxVars.h"
@ -96,65 +97,149 @@ PersistentBufferProviderBasic::Create(gfx::IntSize aSize,
return provider.forget();
}
static already_AddRefed<TextureClient> CreateTexture(
KnowsCompositor* aKnowsCompositor, gfx::SurfaceFormat aFormat,
gfx::IntSize aSize, bool aWillReadFrequently = false,
Maybe<RemoteTextureOwnerId> aRemoteTextureOwnerId = {}) {
TextureAllocationFlags flags = ALLOC_DEFAULT;
if (aWillReadFrequently) {
flags = TextureAllocationFlags(flags | ALLOC_DO_NOT_ACCELERATE);
}
if (aRemoteTextureOwnerId) {
flags = TextureAllocationFlags(flags | ALLOC_FORCE_REMOTE);
}
RefPtr<TextureClient> tc = TextureClient::CreateForDrawing(
aKnowsCompositor, aFormat, aSize, BackendSelector::Canvas,
TextureFlags::DEFAULT | TextureFlags::NON_BLOCKING_READ_LOCK, flags);
if (tc && aRemoteTextureOwnerId) {
if (TextureData* td = tc->GetInternalData()) {
td->SetRemoteTextureOwnerId(*aRemoteTextureOwnerId);
}
}
return tc.forget();
}
// static
already_AddRefed<PersistentBufferProviderAccelerated>
PersistentBufferProviderAccelerated::Create(gfx::IntSize aSize,
gfx::SurfaceFormat aFormat,
KnowsCompositor* aKnowsCompositor) {
if (!DrawTargetWebgl::CanCreate(aSize, aFormat)) {
return nullptr;
}
if (!aKnowsCompositor || !aKnowsCompositor->GetTextureForwarder() ||
!aKnowsCompositor->GetTextureForwarder()->IPCOpen()) {
return nullptr;
}
auto remoteTextureOwnerId = RemoteTextureOwnerId::GetNext();
RefPtr<TextureClient> texture = CreateTexture(
aKnowsCompositor, aFormat, aSize, false, Some(remoteTextureOwnerId));
if (!texture) {
return nullptr;
}
RefPtr<PersistentBufferProviderAccelerated> provider =
new PersistentBufferProviderAccelerated(texture);
return provider.forget();
}
PersistentBufferProviderAccelerated::PersistentBufferProviderAccelerated(
DrawTarget* aDt)
: PersistentBufferProviderBasic(aDt) {
const RefPtr<TextureClient>& aTexture)
: mTexture(aTexture) {
MOZ_COUNT_CTOR(PersistentBufferProviderAccelerated);
MOZ_ASSERT(aDt->GetBackendType() == BackendType::WEBGL);
}
PersistentBufferProviderAccelerated::~PersistentBufferProviderAccelerated() {
MOZ_COUNT_DTOR(PersistentBufferProviderAccelerated);
Destroy();
}
inline gfx::DrawTargetWebgl*
PersistentBufferProviderAccelerated::GetDrawTargetWebgl() const {
return static_cast<gfx::DrawTargetWebgl*>(mDrawTarget.get());
}
void PersistentBufferProviderAccelerated::Destroy() {
mSnapshot = nullptr;
mDrawTarget = nullptr;
Maybe<layers::SurfaceDescriptor>
PersistentBufferProviderAccelerated::GetFrontBuffer() {
return GetDrawTargetWebgl()->GetFrontBuffer();
if (mTexture) {
if (mTexture->IsLocked()) {
MOZ_ASSERT(false);
mTexture->Unlock();
}
mTexture = nullptr;
}
}
already_AddRefed<gfx::DrawTarget>
PersistentBufferProviderAccelerated::BorrowDrawTarget(
const gfx::IntRect& aPersistedRect) {
GetDrawTargetWebgl()->BeginFrame(aPersistedRect);
return PersistentBufferProviderBasic::BorrowDrawTarget(aPersistedRect);
if (!mDrawTarget) {
if (!mTexture->Lock(OpenMode::OPEN_READ_WRITE)) {
return nullptr;
}
mDrawTarget = mTexture->BorrowDrawTarget();
if (mDrawTarget) {
mDrawTarget->ClearRect(Rect(0, 0, 0, 0));
if (!mDrawTarget->IsValid()) {
mDrawTarget = nullptr;
}
}
}
return do_AddRef(mDrawTarget);
}
bool PersistentBufferProviderAccelerated::ReturnDrawTarget(
already_AddRefed<gfx::DrawTarget> aDT) {
bool result = PersistentBufferProviderBasic::ReturnDrawTarget(std::move(aDT));
GetDrawTargetWebgl()->EndFrame();
return result;
{
RefPtr<gfx::DrawTarget> dt(aDT);
MOZ_ASSERT(mDrawTarget == dt);
if (!mDrawTarget) {
return false;
}
mDrawTarget = nullptr;
}
mTexture->Unlock();
return true;
}
already_AddRefed<gfx::SourceSurface>
PersistentBufferProviderAccelerated::BorrowSnapshot(gfx::DrawTarget* aTarget) {
mSnapshot = GetDrawTargetWebgl()->GetOptimizedSnapshot(aTarget);
if (mDrawTarget) {
MOZ_ASSERT(mTexture->IsLocked());
} else {
if (mTexture->IsLocked()) {
MOZ_ASSERT(false);
return nullptr;
}
if (!mTexture->Lock(OpenMode::OPEN_READ)) {
return nullptr;
}
}
mSnapshot = mTexture->BorrowSnapshot();
return do_AddRef(mSnapshot);
}
bool PersistentBufferProviderAccelerated::RequiresRefresh() const {
return GetDrawTargetWebgl()->RequiresRefresh();
}
void PersistentBufferProviderAccelerated::OnMemoryPressure() {
GetDrawTargetWebgl()->OnMemoryPressure();
}
static already_AddRefed<TextureClient> CreateTexture(
KnowsCompositor* aKnowsCompositor, gfx::SurfaceFormat aFormat,
gfx::IntSize aSize, bool aWillReadFrequently) {
TextureAllocationFlags flags = ALLOC_DEFAULT;
if (aWillReadFrequently) {
flags = TextureAllocationFlags(flags | ALLOC_DO_NOT_ACCELERATE);
void PersistentBufferProviderAccelerated::ReturnSnapshot(
already_AddRefed<gfx::SourceSurface> aSnapshot) {
RefPtr<SourceSurface> snapshot = aSnapshot;
MOZ_ASSERT(!snapshot || snapshot == mSnapshot);
snapshot = nullptr;
mTexture->ReturnSnapshot(mSnapshot.forget());
if (!mDrawTarget) {
mTexture->Unlock();
}
return TextureClient::CreateForDrawing(
aKnowsCompositor, aFormat, aSize, BackendSelector::Canvas,
TextureFlags::DEFAULT | TextureFlags::NON_BLOCKING_READ_LOCK, flags);
}
Maybe<SurfaceDescriptor> PersistentBufferProviderAccelerated::GetFrontBuffer() {
SurfaceDescriptor desc;
if (mTexture->GetInternalData()->Serialize(desc)) {
return Some(desc);
}
return Nothing();
}
bool PersistentBufferProviderAccelerated::RequiresRefresh() const {
return mTexture->GetInternalData()->RequiresRefresh();
}
// static
@ -198,7 +283,6 @@ PersistentBufferProviderShared::PersistentBufferProviderShared(
gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
KnowsCompositor* aKnowsCompositor, RefPtr<TextureClient>& aTexture,
bool aWillReadFrequently)
: mSize(aSize),
mFormat(aFormat),
mKnowsCompositor(aKnowsCompositor),

View File

@ -24,11 +24,11 @@ class ClientWebGLContext;
namespace gfx {
class SourceSurface;
class DrawTarget;
class DrawTargetWebgl;
} // namespace gfx
namespace layers {
struct RemoteTextureOwnerId;
class TextureClient;
/**
@ -104,12 +104,7 @@ class PersistentBufferProvider : public RefCounted<PersistentBufferProvider>,
*/
virtual bool RequiresRefresh() const { return false; }
/**
* Provide a WebGL front buffer for compositing, if available.
*/
virtual Maybe<layers::SurfaceDescriptor> GetFrontBuffer() {
return Nothing();
}
virtual Maybe<SurfaceDescriptor> GetFrontBuffer() { return Nothing(); }
};
class PersistentBufferProviderBasic : public PersistentBufferProvider {
@ -146,18 +141,17 @@ class PersistentBufferProviderBasic : public PersistentBufferProvider {
RefPtr<gfx::SourceSurface> mSnapshot;
};
class PersistentBufferProviderAccelerated
: public PersistentBufferProviderBasic {
class PersistentBufferProviderAccelerated : public PersistentBufferProvider {
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PersistentBufferProviderAccelerated,
override)
explicit PersistentBufferProviderAccelerated(gfx::DrawTarget* aTarget);
static already_AddRefed<PersistentBufferProviderAccelerated> Create(
gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
KnowsCompositor* aKnowsCompositor);
bool IsAccelerated() const override { return true; }
Maybe<layers::SurfaceDescriptor> GetFrontBuffer() override;
already_AddRefed<gfx::DrawTarget> BorrowDrawTarget(
const gfx::IntRect& aPersistedRect) override;
@ -166,14 +160,27 @@ class PersistentBufferProviderAccelerated
already_AddRefed<gfx::SourceSurface> BorrowSnapshot(
gfx::DrawTarget* aTarget) override;
void ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot) override;
bool PreservesDrawingState() const override { return true; }
void OnShutdown() override { Destroy(); }
Maybe<SurfaceDescriptor> GetFrontBuffer() override;
bool RequiresRefresh() const override;
void OnMemoryPressure() override;
protected:
explicit PersistentBufferProviderAccelerated(
const RefPtr<TextureClient>& aTexture);
~PersistentBufferProviderAccelerated() override;
gfx::DrawTargetWebgl* GetDrawTargetWebgl() const;
void Destroy();
RefPtr<TextureClient> mTexture;
RefPtr<gfx::DrawTarget> mDrawTarget;
RefPtr<gfx::SourceSurface> mSnapshot;
};
/**

View File

@ -44,7 +44,8 @@ const EventType CHECKPOINT = EventType(EventType::LAST + 13);
const EventType PAUSE_TRANSLATION = EventType(EventType::LAST + 14);
const EventType RECYCLE_BUFFER = EventType(EventType::LAST + 15);
const EventType DROP_BUFFER = EventType(EventType::LAST + 16);
const EventType LAST_CANVAS_EVENT_TYPE = DROP_BUFFER;
const EventType PREPARE_SHMEM = EventType(EventType::LAST + 17);
const EventType LAST_CANVAS_EVENT_TYPE = PREPARE_SHMEM;
class RecordedCanvasBeginTransaction final
: public RecordedEventDerived<RecordedCanvasBeginTransaction> {
@ -138,10 +139,12 @@ RecordedCanvasFlush::RecordedCanvasFlush(S& aStream)
class RecordedTextureLock final
: public RecordedEventDerived<RecordedTextureLock> {
public:
RecordedTextureLock(int64_t aTextureId, const OpenMode aMode)
RecordedTextureLock(int64_t aTextureId, const OpenMode aMode,
RemoteTextureId aId)
: RecordedEventDerived(TEXTURE_LOCK),
mTextureId(aTextureId),
mMode(aMode) {}
mMode(aMode),
mLastRemoteTextureId(aId) {}
template <class S>
MOZ_IMPLICIT RecordedTextureLock(S& aStream);
@ -156,16 +159,14 @@ class RecordedTextureLock final
private:
int64_t mTextureId;
OpenMode mMode;
RemoteTextureId mLastRemoteTextureId;
};
inline bool RecordedTextureLock::PlayCanvasEvent(
CanvasTranslator* aTranslator) const {
TextureData* textureData = aTranslator->LookupTextureData(mTextureId);
if (!textureData) {
if (!aTranslator->LockTexture(mTextureId, mMode, mLastRemoteTextureId)) {
return false;
}
textureData->Lock(mMode);
return true;
}
@ -173,6 +174,7 @@ template <class S>
void RecordedTextureLock::Record(S& aStream) const {
WriteElement(aStream, mTextureId);
WriteElement(aStream, mMode);
WriteElement(aStream, mLastRemoteTextureId.mId);
}
template <class S>
@ -181,13 +183,16 @@ RecordedTextureLock::RecordedTextureLock(S& aStream)
ReadElement(aStream, mTextureId);
ReadElementConstrained(aStream, mMode, OpenMode::OPEN_NONE,
OpenMode::OPEN_READ_WRITE_ASYNC);
ReadElement(aStream, mLastRemoteTextureId.mId);
}
class RecordedTextureUnlock final
: public RecordedEventDerived<RecordedTextureUnlock> {
public:
explicit RecordedTextureUnlock(int64_t aTextureId)
: RecordedEventDerived(TEXTURE_UNLOCK), mTextureId(aTextureId) {}
explicit RecordedTextureUnlock(int64_t aTextureId, RemoteTextureId aId)
: RecordedEventDerived(TEXTURE_UNLOCK),
mTextureId(aTextureId),
mLastRemoteTextureId(aId) {}
template <class S>
MOZ_IMPLICIT RecordedTextureUnlock(S& aStream);
@ -201,28 +206,28 @@ class RecordedTextureUnlock final
private:
int64_t mTextureId;
RemoteTextureId mLastRemoteTextureId;
};
inline bool RecordedTextureUnlock::PlayCanvasEvent(
CanvasTranslator* aTranslator) const {
TextureData* textureData = aTranslator->LookupTextureData(mTextureId);
if (!textureData) {
if (!aTranslator->UnlockTexture(mTextureId, mLastRemoteTextureId)) {
return false;
}
textureData->Unlock();
return true;
}
template <class S>
void RecordedTextureUnlock::Record(S& aStream) const {
WriteElement(aStream, mTextureId);
WriteElement(aStream, mLastRemoteTextureId.mId);
}
template <class S>
RecordedTextureUnlock::RecordedTextureUnlock(S& aStream)
: RecordedEventDerived(TEXTURE_UNLOCK) {
ReadElement(aStream, mTextureId);
ReadElement(aStream, mLastRemoteTextureId.mId);
}
class RecordedCacheDataSurface final
@ -486,8 +491,11 @@ RecordedDeviceChangeAcknowledged::RecordedDeviceChangeAcknowledged(S& aStream)
class RecordedNextTextureId final
: public RecordedEventDerived<RecordedNextTextureId> {
public:
explicit RecordedNextTextureId(int64_t aNextTextureId)
: RecordedEventDerived(NEXT_TEXTURE_ID), mNextTextureId(aNextTextureId) {}
RecordedNextTextureId(int64_t aNextTextureId,
RemoteTextureOwnerId aRemoteTextureOwnerId)
: RecordedEventDerived(NEXT_TEXTURE_ID),
mNextTextureId(aNextTextureId),
mRemoteTextureOwnerId(aRemoteTextureOwnerId) {}
template <class S>
MOZ_IMPLICIT RecordedNextTextureId(S& aStream);
@ -500,24 +508,28 @@ class RecordedNextTextureId final
std::string GetName() const final { return "RecordedNextTextureId"; }
private:
int64_t mNextTextureId;
int64_t mNextTextureId = 0;
RemoteTextureOwnerId mRemoteTextureOwnerId;
RemoteTextureId mRemoteTextureId;
};
inline bool RecordedNextTextureId::PlayCanvasEvent(
CanvasTranslator* aTranslator) const {
aTranslator->SetNextTextureId(mNextTextureId);
aTranslator->SetNextTextureId(mNextTextureId, mRemoteTextureOwnerId);
return true;
}
template <class S>
void RecordedNextTextureId::Record(S& aStream) const {
WriteElement(aStream, mNextTextureId);
WriteElement(aStream, mRemoteTextureOwnerId.mId);
}
template <class S>
RecordedNextTextureId::RecordedNextTextureId(S& aStream)
: RecordedEventDerived(NEXT_TEXTURE_ID) {
ReadElement(aStream, mNextTextureId);
ReadElement(aStream, mRemoteTextureOwnerId.mId);
}
class RecordedTextureDestruction final
@ -537,7 +549,7 @@ class RecordedTextureDestruction final
std::string GetName() const final { return "RecordedTextureDestruction"; }
private:
int64_t mTextureId;
int64_t mTextureId = 0;
};
inline bool RecordedTextureDestruction::PlayCanvasEvent(
@ -638,6 +650,43 @@ class RecordedDropBuffer final
std::string GetName() const final { return "RecordedDropAndMoveNextBuffer"; }
};
class RecordedPrepareShmem final
: public RecordedEventDerived<RecordedPrepareShmem> {
public:
explicit RecordedPrepareShmem(int64_t aTextureId)
: RecordedEventDerived(PREPARE_SHMEM), mTextureId(aTextureId) {}
template <class S>
MOZ_IMPLICIT RecordedPrepareShmem(S& aStream);
bool PlayCanvasEvent(CanvasTranslator* aTranslator) const;
template <class S>
void Record(S& aStream) const;
std::string GetName() const final { return "RecordedPrepareShmem"; }
private:
int64_t mTextureId = 0;
};
inline bool RecordedPrepareShmem::PlayCanvasEvent(
CanvasTranslator* aTranslator) const {
aTranslator->PrepareShmem(mTextureId);
return true;
}
template <class S>
void RecordedPrepareShmem::Record(S& aStream) const {
WriteElement(aStream, mTextureId);
}
template <class S>
RecordedPrepareShmem::RecordedPrepareShmem(S& aStream)
: RecordedEventDerived(PREPARE_SHMEM) {
ReadElement(aStream, mTextureId);
}
#define FOR_EACH_CANVAS_EVENT(f) \
f(CANVAS_BEGIN_TRANSACTION, RecordedCanvasBeginTransaction); \
f(CANVAS_END_TRANSACTION, RecordedCanvasEndTransaction); \
@ -655,7 +704,8 @@ class RecordedDropBuffer final
f(CHECKPOINT, RecordedCheckpoint); \
f(PAUSE_TRANSLATION, RecordedPauseTranslation); \
f(RECYCLE_BUFFER, RecordedRecycleBuffer); \
f(DROP_BUFFER, RecordedDropBuffer);
f(DROP_BUFFER, RecordedDropBuffer); \
f(PREPARE_SHMEM, RecordedPrepareShmem);
} // namespace layers
} // namespace mozilla

View File

@ -217,7 +217,7 @@ void RemoteTextureMap::Shutdown() {
}
}
RemoteTextureMap::RemoteTextureMap() : mMonitor("D3D11TextureMap::mMonitor") {}
RemoteTextureMap::RemoteTextureMap() : mMonitor("RemoteTextureMap::mMonitor") {}
RemoteTextureMap::~RemoteTextureMap() = default;
@ -314,17 +314,7 @@ void RemoteTextureMap::PushTexture(
// used by WebRender anymore.
if (front->mTextureHost &&
front->mTextureHost->NumCompositableRefs() == 0) {
// Recycle SharedTexture
if (front->mResourceWrapper) {
owner->mRecycledSharedTextures.push(
std::move(front->mResourceWrapper));
}
// Recycle BufferTextureData
if (!(front->mTextureHost->GetFlags() & TextureFlags::DUMMY_TEXTURE) &&
(front->mTextureData &&
front->mTextureData->AsBufferTextureData())) {
owner->mRecycledTextures.push(std::move(front->mTextureData));
}
owner->mReleasingTextureDataHolders.push_back(std::move(front));
owner->mUsingTextureDataHolders.pop_front();
} else if (front->mTextureHost &&
front->mTextureHost->NumCompositableRefs() >= 0) {
@ -335,6 +325,19 @@ void RemoteTextureMap::PushTexture(
owner->mUsingTextureDataHolders.pop_front();
}
}
while (!owner->mReleasingTextureDataHolders.empty()) {
auto& front = owner->mReleasingTextureDataHolders.front();
// Recycle SharedTexture
if (front->mResourceWrapper) {
owner->mRecycledSharedTextures.push(std::move(front->mResourceWrapper));
}
// Recycle BufferTextureData
if (!(front->mTextureHost->GetFlags() & TextureFlags::DUMMY_TEXTURE) &&
(front->mTextureData && front->mTextureData->AsBufferTextureData())) {
owner->mRecycledTextures.push(std::move(front->mTextureData));
}
owner->mReleasingTextureDataHolders.pop_front();
}
}
const auto info = RemoteTextureInfo(aTextureId, aOwnerId, aForPid);
@ -445,8 +448,7 @@ void RemoteTextureMap::KeepTextureDataAliveForTextureHostIfNecessary(
// SharedResourceWrapper/TextureData alive while the TextureHost is alive.
if (holder->mTextureHost &&
holder->mTextureHost->NumCompositableRefs() >= 0) {
RefPtr<nsISerialEventTarget> eventTarget =
MessageLoop::current()->SerialEventTarget();
RefPtr<nsISerialEventTarget> eventTarget = GetCurrentSerialEventTarget();
RefPtr<Runnable> runnable = NS_NewRunnableFunction(
"RemoteTextureMap::UnregisterTextureOwner::Runnable",
[data = std::move(holder->mTextureData),
@ -511,6 +513,9 @@ void RemoteTextureMap::UnregisterTextureOwner(
KeepTextureDataAliveForTextureHostIfNecessary(
lock, it->second->mUsingTextureDataHolders);
KeepTextureDataAliveForTextureHostIfNecessary(
lock, it->second->mReleasingTextureDataHolders);
releasingOwner = std::move(it->second);
mTextureOwners.erase(it);
@ -577,6 +582,9 @@ void RemoteTextureMap::UnregisterTextureOwners(
KeepTextureDataAliveForTextureHostIfNecessary(
lock, it->second->mUsingTextureDataHolders);
KeepTextureDataAliveForTextureHostIfNecessary(
lock, it->second->mReleasingTextureDataHolders);
releasingOwners.push_back(std::move(it->second));
mTextureOwners.erase(it);
}
@ -657,6 +665,19 @@ void RemoteTextureMap::UpdateTexture(const MonitorAutoLock& aProofOfLock,
UniquePtr<TextureDataHolder> holder = std::move(front);
aOwner->mWaitingTextureDataHolders.pop_front();
// If there are textures not being used by the compositor that will be
// obsoleted by this new texture, then queue them for removal later on
// the creating thread.
while (!aOwner->mUsingTextureDataHolders.empty()) {
auto& back = aOwner->mUsingTextureDataHolders.back();
if (back->mTextureHost &&
back->mTextureHost->NumCompositableRefs() == 0) {
aOwner->mReleasingTextureDataHolders.push_back(std::move(back));
aOwner->mUsingTextureDataHolders.pop_back();
continue;
}
break;
}
aOwner->mUsingTextureDataHolders.push_back(std::move(holder));
}
}
@ -696,7 +717,8 @@ RemoteTextureMap::GetAllRenderingReadyCallbacks(
bool RemoteTextureMap::GetRemoteTextureForDisplayList(
RemoteTextureHostWrapper* aTextureHostWrapper,
std::function<void(const RemoteTextureInfo&)>&& aReadyCallback) {
std::function<void(const RemoteTextureInfo&)>&& aReadyCallback,
bool aWaitForRemoteTextureOwner) {
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
MOZ_ASSERT(aTextureHostWrapper);
@ -714,8 +736,23 @@ bool RemoteTextureMap::GetRemoteTextureForDisplayList(
MonitorAutoLock lock(mMonitor);
auto* owner = GetTextureOwner(lock, ownerId, forPid);
if (!owner) {
return false;
// If there is no texture owner yet, then we might need to wait for one to
// be created, if allowed. If so, we must also wait for an initial texture
// host to be created so we can use it.
if (!owner || (aWaitForRemoteTextureOwner && !owner->mLatestTextureHost &&
owner->mWaitingTextureDataHolders.empty())) {
if (!aWaitForRemoteTextureOwner) {
return false;
}
const TimeDuration timeout = TimeDuration::FromMilliseconds(10000);
while (!owner || (!owner->mLatestTextureHost &&
owner->mWaitingTextureDataHolders.empty())) {
CVStatus status = mMonitor.Wait(timeout);
if (status == CVStatus::Timeout) {
return false;
}
owner = GetTextureOwner(lock, ownerId, forPid);
}
}
UpdateTexture(lock, owner, textureId);

View File

@ -213,7 +213,8 @@ class RemoteTextureMap {
// return true when aReadyCallback will be called.
bool GetRemoteTextureForDisplayList(
RemoteTextureHostWrapper* aTextureHostWrapper,
std::function<void(const RemoteTextureInfo&)>&& aReadyCallback);
std::function<void(const RemoteTextureInfo&)>&& aReadyCallback,
bool aWaitForRemoteTextureOwner = false);
// Get ExternalImageId of remote texture for WebRender rendering.
wr::MaybeExternalImageId GetExternalImageIdOfRemoteTexture(
@ -297,6 +298,7 @@ class RemoteTextureMap {
std::deque<UniquePtr<TextureDataHolder>> mWaitingTextureDataHolders;
// Holds TextureDataHolders that are used for building wr display list.
std::deque<UniquePtr<TextureDataHolder>> mUsingTextureDataHolders;
std::deque<UniquePtr<TextureDataHolder>> mReleasingTextureDataHolders;
// Holds async RemoteTexture ready callbacks.
std::deque<UniquePtr<RenderingReadyCallbackHolder>>
mRenderingReadyCallbackHolders;

View File

@ -318,6 +318,36 @@ static bool ShouldRemoteTextureType(TextureType aTextureType,
}
}
/* static */
TextureData* TextureData::Create(TextureType aTextureType,
gfx::SurfaceFormat aFormat,
const gfx::IntSize& aSize,
TextureAllocationFlags aAllocFlags,
gfx::BackendType aBackendType) {
switch (aTextureType) {
#ifdef XP_WIN
case TextureType::D3D11:
return D3D11TextureData::Create(aSize, aFormat, aAllocFlags);
#endif
#ifdef MOZ_WIDGET_GTK
case TextureType::DMABUF:
return DMABUFTextureData::Create(aSize, aFormat, aBackendType);
#endif
#ifdef XP_MACOSX
case TextureType::MacIOSurface:
return MacIOSurfaceTextureData::Create(aSize, aFormat, aBackendType);
#endif
#ifdef MOZ_WIDGET_ANDROID
case TextureType::AndroidNativeWindow:
return AndroidNativeWindowTextureData::Create(aSize, aFormat);
#endif
default:
return nullptr;
}
}
/* static */
TextureData* TextureData::Create(TextureForwarder* aAllocator,
gfx::SurfaceFormat aFormat, gfx::IntSize aSize,
@ -328,45 +358,33 @@ TextureData* TextureData::Create(TextureForwarder* aAllocator,
TextureType textureType =
GetTextureType(aFormat, aSize, aKnowsCompositor, aSelector, aAllocFlags);
if (ShouldRemoteTextureType(textureType, aSelector)) {
if ((aAllocFlags & ALLOC_FORCE_REMOTE) ||
ShouldRemoteTextureType(textureType, aSelector)) {
RefPtr<CanvasChild> canvasChild = aAllocator->GetCanvasChild();
if (canvasChild) {
return new RecordedTextureData(canvasChild.forget(), aSize, aFormat,
textureType);
}
if (aAllocFlags & ALLOC_FORCE_REMOTE) {
// If we must be remote, but there is no canvas child, then falling back
// is not possible.
return nullptr;
}
// We don't have a CanvasChild, but are supposed to be remote.
// Fall back to software.
textureType = TextureType::Unknown;
}
gfx::BackendType moz2DBackend = gfx::BackendType::NONE;
#if defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK)
gfx::BackendType moz2DBackend = BackendTypeForBackendSelector(
moz2DBackend = BackendTypeForBackendSelector(
aKnowsCompositor->GetCompositorBackendType(), aSelector);
#endif
switch (textureType) {
#ifdef XP_WIN
case TextureType::D3D11:
return D3D11TextureData::Create(aSize, aFormat, aAllocFlags);
#endif
#ifdef MOZ_WIDGET_GTK
case TextureType::DMABUF:
return DMABUFTextureData::Create(aSize, aFormat, moz2DBackend);
#endif
#ifdef XP_MACOSX
case TextureType::MacIOSurface:
return MacIOSurfaceTextureData::Create(aSize, aFormat, moz2DBackend);
#endif
#ifdef MOZ_WIDGET_ANDROID
case TextureType::AndroidNativeWindow:
return AndroidNativeWindowTextureData::Create(aSize, aFormat);
#endif
default:
return nullptr;
}
return TextureData::Create(textureType, aFormat, aSize, aAllocFlags,
moz2DBackend);
}
/* static */
@ -515,6 +533,7 @@ void TextureClient::Destroy() {
}
mBorrowedDrawTarget = nullptr;
mBorrowedSnapshot = false;
mReadLock = nullptr;
RefPtr<TextureChild> actor = mActor;
@ -670,6 +689,7 @@ void TextureClient::Unlock() {
mBorrowedDrawTarget = nullptr;
}
mBorrowedSnapshot = false;
if (mOpenMode & OpenMode::OPEN_WRITE) {
mUpdated = true;
@ -806,6 +826,7 @@ void TextureClient::EndDraw() {
MOZ_ASSERT(mBorrowedDrawTarget->refCount() <= mExpectedDtRefs);
mBorrowedDrawTarget = nullptr;
mBorrowedSnapshot = false;
mData->EndDraw();
}
@ -813,7 +834,9 @@ already_AddRefed<gfx::SourceSurface> TextureClient::BorrowSnapshot() {
MOZ_ASSERT(mIsLocked);
RefPtr<gfx::SourceSurface> surface = mData->BorrowSnapshot();
if (!surface) {
if (surface) {
mBorrowedSnapshot = true;
} else {
RefPtr<gfx::DrawTarget> drawTarget = BorrowDrawTarget();
if (!drawTarget) {
return nullptr;
@ -824,6 +847,15 @@ already_AddRefed<gfx::SourceSurface> TextureClient::BorrowSnapshot() {
return surface.forget();
}
void TextureClient::ReturnSnapshot(
already_AddRefed<gfx::SourceSurface> aSnapshot) {
RefPtr<gfx::SourceSurface> snapshot = aSnapshot;
if (mBorrowedSnapshot) {
mData->ReturnSnapshot(snapshot.forget());
mBorrowedSnapshot = false;
}
}
bool TextureClient::BorrowMappedData(MappedTextureData& aMap) {
MOZ_ASSERT(IsValid());
@ -1160,6 +1192,10 @@ already_AddRefed<TextureClient> TextureClient::CreateForDrawing(
if (data) {
return MakeAndAddRef<TextureClient>(data, aTextureFlags, aAllocator);
}
if (aAllocFlags & ALLOC_FORCE_REMOTE) {
// If we must be remote, but allocation failed, then don't fall back.
return nullptr;
}
// Can't do any better than a buffer texture client.
return TextureClient::CreateForRawBufferAccess(aAllocator, aFormat, aSize,

View File

@ -70,6 +70,7 @@ class SharedSurfaceTextureData;
class TextureClientPool;
#endif
class TextureForwarder;
struct RemoteTextureOwnerId;
/**
* TextureClient is the abstraction that allows us to share data between the
@ -99,6 +100,9 @@ enum TextureAllocationFlags {
// Do not use an accelerated texture type.
ALLOC_DO_NOT_ACCELERATE = 1 << 8,
// Force allocation of remote/recorded texture, or fail if not possible.
ALLOC_FORCE_REMOTE = 1 << 9,
};
enum class BackendSelector { Content, Canvas };
@ -223,6 +227,10 @@ class TextureData {
canConcurrentlyReadLock(true) {}
};
static TextureData* Create(
TextureType aTextureType, gfx::SurfaceFormat aFormat,
const gfx::IntSize& aSize, TextureAllocationFlags aAllocFlags,
gfx::BackendType aBackendType = gfx::BackendType::NONE);
static TextureData* Create(TextureForwarder* aAllocator,
gfx::SurfaceFormat aFormat, gfx::IntSize aSize,
KnowsCompositor* aKnowsCompositor,
@ -255,6 +263,8 @@ class TextureData {
return nullptr;
}
virtual void ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot) {}
virtual bool BorrowMappedData(MappedTextureData&) { return false; }
virtual bool BorrowMappedYCbCrData(MappedYCbCrTextureData&) { return false; }
@ -312,6 +322,10 @@ class TextureData {
return mozilla::ipc::FileDescriptor();
}
virtual void SetRemoteTextureOwnerId(RemoteTextureOwnerId) {}
virtual bool RequiresRefresh() const { return false; }
protected:
MOZ_COUNTED_DEFAULT_CTOR(TextureData)
};
@ -447,6 +461,8 @@ class TextureClient : public AtomicRefCountedWithFinalize<TextureClient> {
already_AddRefed<gfx::SourceSurface> BorrowSnapshot();
void ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot);
/**
* Similar to BorrowDrawTarget but provides direct access to the texture's
* bits instead of a DrawTarget.
@ -703,6 +719,7 @@ class TextureClient : public AtomicRefCountedWithFinalize<TextureClient> {
TextureData* mData;
RefPtr<gfx::DrawTarget> mBorrowedDrawTarget;
bool mBorrowedSnapshot = false;
TextureFlags mFlags;

View File

@ -30,6 +30,7 @@ RecordedTextureData::~RecordedTextureData() {
// We need the translator to drop its reference for the DrawTarget first,
// because the TextureData might need to destroy its DrawTarget within a lock.
mDT = nullptr;
mCanvasChild->CleanupTexture(mTextureId);
mCanvasChild->RecordEvent(RecordedTextureDestruction(mTextureId));
}
@ -40,15 +41,25 @@ void RecordedTextureData::FillInfo(TextureData::Info& aInfo) const {
aInfo.hasSynchronization = true;
}
void RecordedTextureData::SetRemoteTextureOwnerId(
RemoteTextureOwnerId aRemoteTextureOwnerId) {
mRemoteTextureOwnerId = aRemoteTextureOwnerId;
}
bool RecordedTextureData::Lock(OpenMode aMode) {
if (!mCanvasChild->EnsureBeginTransaction()) {
return false;
}
if (mRemoteTextureOwnerId.IsValid()) {
mLastRemoteTextureId = RemoteTextureId::GetNext();
}
if (!mDT) {
mTextureId = sNextRecordedTextureId++;
mCanvasChild->RecordEvent(RecordedNextTextureId(mTextureId));
mDT = mCanvasChild->CreateDrawTarget(mSize, mFormat);
mCanvasChild->RecordEvent(
RecordedNextTextureId(mTextureId, mRemoteTextureOwnerId));
mDT = mCanvasChild->CreateDrawTarget(mTextureId, mSize, mFormat);
if (!mDT) {
return false;
}
@ -59,7 +70,8 @@ bool RecordedTextureData::Lock(OpenMode aMode) {
return true;
}
mCanvasChild->RecordEvent(RecordedTextureLock(mTextureId, aMode));
mCanvasChild->RecordEvent(
RecordedTextureLock(mTextureId, aMode, mLastRemoteTextureId));
if (aMode & OpenMode::OPEN_WRITE) {
mCanvasChild->OnTextureWriteLock();
}
@ -75,12 +87,18 @@ void RecordedTextureData::Unlock() {
mCanvasChild->RecordEvent(RecordedCacheDataSurface(mSnapshot.get()));
}
mCanvasChild->RecordEvent(RecordedTextureUnlock(mTextureId));
mCanvasChild->RecordEvent(
RecordedTextureUnlock(mTextureId, mLastRemoteTextureId));
mLockedMode = OpenMode::OPEN_NONE;
}
already_AddRefed<gfx::DrawTarget> RecordedTextureData::BorrowDrawTarget() {
mSnapshot = nullptr;
if (RefPtr<gfx::SourceSurface> wrapper = do_AddRef(mSnapshotWrapper)) {
mCanvasChild->DetachSurface(wrapper);
mSnapshotWrapper = nullptr;
}
return do_AddRef(mDT);
}
@ -95,23 +113,39 @@ void RecordedTextureData::EndDraw() {
}
already_AddRefed<gfx::SourceSurface> RecordedTextureData::BorrowSnapshot() {
if (RefPtr<gfx::SourceSurface> wrapper = do_AddRef(mSnapshotWrapper)) {
return wrapper.forget();
}
// There are some failure scenarios where we have no DrawTarget and
// BorrowSnapshot is called in an attempt to copy to a new texture.
if (!mDT) {
return nullptr;
}
if (mSnapshot) {
return mCanvasChild->WrapSurface(mSnapshot);
}
RefPtr<gfx::SourceSurface> wrapper = mCanvasChild->WrapSurface(
mSnapshot ? mSnapshot : mDT->Snapshot(), mTextureId);
mSnapshotWrapper = wrapper;
return wrapper.forget();
}
return mCanvasChild->WrapSurface(mDT->Snapshot());
void RecordedTextureData::ReturnSnapshot(
already_AddRefed<gfx::SourceSurface> aSnapshot) {
RefPtr<gfx::SourceSurface> snapshot = aSnapshot;
if (RefPtr<gfx::SourceSurface> wrapper = do_AddRef(mSnapshotWrapper)) {
mCanvasChild->DetachSurface(wrapper);
}
}
void RecordedTextureData::Deallocate(LayersIPCChannel* aAllocator) {}
bool RecordedTextureData::Serialize(SurfaceDescriptor& aDescriptor) {
aDescriptor = SurfaceDescriptorRecorded(mTextureId);
if (mRemoteTextureOwnerId.IsValid()) {
aDescriptor = SurfaceDescriptorRemoteTexture(mLastRemoteTextureId,
mRemoteTextureOwnerId);
} else {
aDescriptor = SurfaceDescriptorRecorded(mTextureId);
}
return true;
}
@ -125,5 +159,9 @@ TextureFlags RecordedTextureData::GetTextureFlags() const {
return TextureFlags::WAIT_HOST_USAGE_END;
}
bool RecordedTextureData::RequiresRefresh() const {
return mCanvasChild->RequiresRefresh(mTextureId);
}
} // namespace layers
} // namespace mozilla

View File

@ -32,6 +32,8 @@ class RecordedTextureData final : public TextureData {
already_AddRefed<gfx::SourceSurface> BorrowSnapshot() final;
void ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot) final;
void Deallocate(LayersIPCChannel* aAllocator) final;
bool Serialize(SurfaceDescriptor& aDescriptor) final;
@ -40,6 +42,11 @@ class RecordedTextureData final : public TextureData {
TextureFlags GetTextureFlags() const final;
void SetRemoteTextureOwnerId(
RemoteTextureOwnerId aRemoteTextureOwnerId) final;
bool RequiresRefresh() const final;
private:
DISALLOW_COPY_AND_ASSIGN(RecordedTextureData);
@ -51,7 +58,10 @@ class RecordedTextureData final : public TextureData {
gfx::SurfaceFormat mFormat;
RefPtr<gfx::DrawTarget> mDT;
RefPtr<gfx::SourceSurface> mSnapshot;
ThreadSafeWeakPtr<gfx::SourceSurface> mSnapshotWrapper;
OpenMode mLockedMode;
layers::RemoteTextureId mLastRemoteTextureId;
layers::RemoteTextureOwnerId mRemoteTextureOwnerId;
};
} // namespace layers

View File

@ -15,6 +15,7 @@
#include "mozilla/ipc/Endpoint.h"
#include "mozilla/ipc/ProcessChild.h"
#include "mozilla/layers/CanvasDrawEventRecorder.h"
#include "mozilla/layers/ImageDataSerializer.h"
#include "mozilla/layers/SourceSurfaceSharedData.h"
#include "mozilla/Maybe.h"
#include "nsIObserverService.h"
@ -30,22 +31,22 @@ class RecorderHelpers final : public CanvasDrawEventRecorder::Helpers {
~RecorderHelpers() override = default;
bool InitTranslator(const TextureType& aTextureType, Handle&& aReadHandle,
nsTArray<Handle>&& aBufferHandles,
const uint64_t& aBufferSize,
bool InitTranslator(TextureType aTextureType, gfx::BackendType aBackendType,
Handle&& aReadHandle, nsTArray<Handle>&& aBufferHandles,
uint64_t aBufferSize,
CrossProcessSemaphoreHandle&& aReaderSem,
CrossProcessSemaphoreHandle&& aWriterSem,
const bool& aUseIPDLThread) override {
bool aUseIPDLThread) override {
if (!mCanvasChild) {
return false;
}
return mCanvasChild->SendInitTranslator(
aTextureType, std::move(aReadHandle), std::move(aBufferHandles),
aBufferSize, std::move(aReaderSem), std::move(aWriterSem),
aUseIPDLThread);
aTextureType, aBackendType, std::move(aReadHandle),
std::move(aBufferHandles), aBufferSize, std::move(aReaderSem),
std::move(aWriterSem), aUseIPDLThread);
}
bool AddBuffer(Handle&& aBufferHandle, const uint64_t& aBufferSize) override {
bool AddBuffer(Handle&& aBufferHandle, uint64_t aBufferSize) override {
if (!mCanvasChild) {
return false;
}
@ -75,10 +76,11 @@ class SourceSurfaceCanvasRecording final : public gfx::SourceSurface {
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceCanvasRecording, final)
SourceSurfaceCanvasRecording(
const RefPtr<gfx::SourceSurface>& aRecordedSuface,
int64_t aTextureId, const RefPtr<gfx::SourceSurface>& aRecordedSuface,
CanvasChild* aCanvasChild,
const RefPtr<CanvasDrawEventRecorder>& aRecorder)
: mRecordedSurface(aRecordedSuface),
: mTextureId(aTextureId),
mRecordedSurface(aRecordedSuface),
mCanvasChild(aCanvasChild),
mRecorder(aRecorder) {
// It's important that AddStoredObject is called first because that will
@ -119,11 +121,14 @@ class SourceSurfaceCanvasRecording final : public gfx::SourceSurface {
return do_AddRef(mDataSourceSurface);
}
void DrawTargetWillChange() { mDetached = true; }
private:
void EnsureDataSurfaceOnMainThread() {
// The data can only be retrieved on the main thread.
if (!mDataSourceSurface && NS_IsMainThread()) {
mDataSourceSurface = mCanvasChild->GetDataSurface(mRecordedSurface);
mDataSourceSurface =
mCanvasChild->GetDataSurface(mTextureId, mRecordedSurface, mDetached);
}
}
@ -141,10 +146,12 @@ class SourceSurfaceCanvasRecording final : public gfx::SourceSurface {
aRecorder = nullptr;
}
int64_t mTextureId;
RefPtr<gfx::SourceSurface> mRecordedSurface;
RefPtr<CanvasChild> mCanvasChild;
RefPtr<CanvasDrawEventRecorder> mRecorder;
RefPtr<gfx::DataSourceSurface> mDataSourceSurface;
bool mDetached = false;
};
CanvasChild::CanvasChild() = default;
@ -176,11 +183,21 @@ ipc::IPCResult CanvasChild::RecvDeactivate() {
return IPC_OK();
}
ipc::IPCResult CanvasChild::RecvBlockCanvas() {
if (auto* cm = gfx::CanvasManagerChild::Get()) {
cm->BlockCanvas();
}
return IPC_OK();
}
void CanvasChild::EnsureRecorder(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
TextureType aTextureType) {
if (!mRecorder) {
gfx::BackendType backendType =
gfxPlatform::GetPlatform()->GetPreferredCanvasBackend();
auto recorder = MakeRefPtr<CanvasDrawEventRecorder>();
if (!recorder->Init(aTextureType, MakeUnique<RecorderHelpers>(this))) {
if (!recorder->Init(aTextureType, backendType,
MakeUnique<RecorderHelpers>(this))) {
return;
}
@ -276,19 +293,26 @@ bool CanvasChild::ShouldBeCleanedUp() const {
}
already_AddRefed<gfx::DrawTarget> CanvasChild::CreateDrawTarget(
gfx::IntSize aSize, gfx::SurfaceFormat aFormat) {
int64_t aTextureId, gfx::IntSize aSize, gfx::SurfaceFormat aFormat) {
RefPtr<gfx::DrawTarget> dummyDt = gfx::Factory::CreateDrawTarget(
gfx::BackendType::SKIA, gfx::IntSize(1, 1), aFormat);
RefPtr<gfx::DrawTarget> dt = MakeAndAddRef<gfx::DrawTargetRecording>(
mRecorder, dummyDt, gfx::IntRect(gfx::IntPoint(0, 0), aSize));
mTextureInfo.insert({aTextureId, {}});
return dt.forget();
}
bool CanvasChild::EnsureDataSurfaceShmem(gfx::IntSize aSize,
gfx::SurfaceFormat aFormat) {
size_t dataFormatWidth = aSize.width * BytesPerPixel(aFormat);
size_t sizeRequired =
ipc::SharedMemory::PageAlignedSize(dataFormatWidth * aSize.height);
ImageDataSerializer::ComputeRGBBufferSize(aSize, aFormat);
if (!sizeRequired) {
return false;
}
sizeRequired = ipc::SharedMemory::PageAlignedSize(sizeRequired);
if (!mDataSurfaceShmemAvailable || mDataSurfaceShmem->Size() < sizeRequired) {
RecordEvent(RecordedPauseTranslation());
auto dataSurfaceShmem = MakeRefPtr<ipc::SharedMemoryBasic>();
@ -328,7 +352,7 @@ int64_t CanvasChild::CreateCheckpoint() {
}
already_AddRefed<gfx::DataSourceSurface> CanvasChild::GetDataSurface(
const gfx::SourceSurface* aSurface) {
int64_t aTextureId, const gfx::SourceSurface* aSurface, bool aDetached) {
MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aSurface);
@ -344,6 +368,26 @@ already_AddRefed<gfx::DataSourceSurface> CanvasChild::GetDataSurface(
return nullptr;
}
// Shmem is only valid if the surface is the latest snapshot (not detached).
if (!aDetached) {
// If there is a shmem associated with this snapshot id, then we want to try
// use that directly without having to allocate a new shmem for retrieval.
auto it = mTextureInfo.find(aTextureId);
if (it != mTextureInfo.end() && it->second.mSnapshotShmem.IsReadable()) {
ipc::Shmem& shmem = it->second.mSnapshotShmem;
mRecorder->RecordEvent(RecordedPrepareShmem(aTextureId));
uint32_t checkpoint = mRecorder->CreateCheckpoint();
mRecorder->WaitForCheckpoint(checkpoint);
gfx::IntSize size = aSurface->GetSize();
gfx::SurfaceFormat format = aSurface->GetFormat();
auto stride = ImageDataSerializer::ComputeRGBStride(format, size.width);
RefPtr<gfx::DataSourceSurface> dataSurface =
gfx::Factory::CreateWrappingDataSourceSurface(shmem.get<uint8_t>(),
stride, size, format);
return dataSurface.forget();
}
}
RecordEvent(RecordedPrepareDataForSurface(aSurface));
gfx::IntSize ssSize = aSurface->GetSize();
@ -362,11 +406,11 @@ already_AddRefed<gfx::DataSourceSurface> CanvasChild::GetDataSurface(
auto* data = static_cast<uint8_t*>(mDataSurfaceShmem->memory());
auto* closure = new DataShmemHolder{do_AddRef(mDataSurfaceShmem), this};
auto dataFormatWidth = ssSize.width * BytesPerPixel(ssFormat);
auto stride = ImageDataSerializer::ComputeRGBStride(ssFormat, ssSize.width);
RefPtr<gfx::DataSourceSurface> dataSurface =
gfx::Factory::CreateWrappingDataSourceSurface(
data, dataFormatWidth, ssSize, ssFormat,
data, stride, ssSize, ssFormat,
[](void* aClosure) {
auto* shmemHolder = static_cast<DataShmemHolder*>(aClosure);
shmemHolder->canvasChild->ReturnDataSurfaceShmem(
@ -380,12 +424,13 @@ already_AddRefed<gfx::DataSourceSurface> CanvasChild::GetDataSurface(
}
already_AddRefed<gfx::SourceSurface> CanvasChild::WrapSurface(
const RefPtr<gfx::SourceSurface>& aSurface) {
const RefPtr<gfx::SourceSurface>& aSurface, int64_t aTextureId) {
if (!aSurface) {
return nullptr;
}
return MakeAndAddRef<SourceSurfaceCanvasRecording>(aSurface, this, mRecorder);
return MakeAndAddRef<SourceSurfaceCanvasRecording>(aTextureId, aSurface, this,
mRecorder);
}
void CanvasChild::ReturnDataSurfaceShmem(
@ -398,5 +443,44 @@ void CanvasChild::ReturnDataSurfaceShmem(
}
}
void CanvasChild::DetachSurface(const RefPtr<gfx::SourceSurface>& aSurface) {
if (auto* surface =
static_cast<SourceSurfaceCanvasRecording*>(aSurface.get())) {
surface->DrawTargetWillChange();
}
}
ipc::IPCResult CanvasChild::RecvNotifyRequiresRefresh(int64_t aTextureId) {
auto it = mTextureInfo.find(aTextureId);
if (it != mTextureInfo.end()) {
it->second.mRequiresRefresh = true;
}
return IPC_OK();
}
bool CanvasChild::RequiresRefresh(int64_t aTextureId) const {
auto it = mTextureInfo.find(aTextureId);
if (it != mTextureInfo.end()) {
return it->second.mRequiresRefresh;
}
return false;
}
ipc::IPCResult CanvasChild::RecvSnapshotShmem(
int64_t aTextureId, Shmem&& aShmem, SnapshotShmemResolver&& aResolve) {
auto it = mTextureInfo.find(aTextureId);
if (it != mTextureInfo.end()) {
it->second.mSnapshotShmem = std::move(aShmem);
aResolve(true);
} else {
aResolve(false);
}
return IPC_OK();
}
void CanvasChild::CleanupTexture(int64_t aTextureId) {
mTextureInfo.erase(aTextureId);
}
} // namespace layers
} // namespace mozilla

View File

@ -42,6 +42,13 @@ class CanvasChild final : public PCanvasChild, public SupportsWeakPtr {
ipc::IPCResult RecvDeactivate();
ipc::IPCResult RecvBlockCanvas();
ipc::IPCResult RecvNotifyRequiresRefresh(int64_t aTextureId);
ipc::IPCResult RecvSnapshotShmem(int64_t aTextureId, Shmem&& aShmem,
SnapshotShmemResolver&& aResolve);
/**
* Ensures that the DrawEventRecorder has been created.
*
@ -92,12 +99,13 @@ class CanvasChild final : public PCanvasChild, public SupportsWeakPtr {
/**
* Create a DrawTargetRecording for a canvas texture.
* @param aTextureId the id of the new texture
* @param aSize size for the DrawTarget
* @param aFormat SurfaceFormat for the DrawTarget
* @returns newly created DrawTargetRecording
*/
already_AddRefed<gfx::DrawTarget> CreateDrawTarget(
gfx::IntSize aSize, gfx::SurfaceFormat aFormat);
int64_t aTextureId, gfx::IntSize aSize, gfx::SurfaceFormat aFormat);
/**
* Record an event for processing by the CanvasParent's CanvasTranslator.
@ -111,21 +119,33 @@ class CanvasChild final : public PCanvasChild, public SupportsWeakPtr {
* Wrap the given surface, so that we can provide a DataSourceSurface if
* required.
* @param aSurface the SourceSurface to wrap
* @param aTextureId the texture id of the source TextureData
* @returns a SourceSurface that can provide a DataSourceSurface if required
*/
already_AddRefed<gfx::SourceSurface> WrapSurface(
const RefPtr<gfx::SourceSurface>& aSurface);
const RefPtr<gfx::SourceSurface>& aSurface, int64_t aTextureId);
/**
* The DrawTargetRecording is about to change, so detach the old snapshot.
*/
void DetachSurface(const RefPtr<gfx::SourceSurface>& aSurface);
/**
* Get DataSourceSurface from the translated equivalent version of aSurface in
* the GPU process.
* @param aTextureId the source TextureData to read from
* @param aSurface the SourceSurface in this process for which we need a
* DataSourceSurface
* @param aDetached whether the surface is old
* @returns a DataSourceSurface created from data for aSurface retrieve from
* GPU process
*/
already_AddRefed<gfx::DataSourceSurface> GetDataSurface(
const gfx::SourceSurface* aSurface);
int64_t aTextureId, const gfx::SourceSurface* aSurface, bool aDetached);
bool RequiresRefresh(int64_t aTextureId) const;
void CleanupTexture(int64_t aTextureId);
protected:
void ActorDestroy(ActorDestroyReason aWhy) final;
@ -153,6 +173,11 @@ class CanvasChild final : public PCanvasChild, public SupportsWeakPtr {
int64_t mLastWriteLockCheckpoint = 0;
uint32_t mTransactionsSinceGetDataSurface = kCacheDataSurfaceThreshold;
std::vector<RefPtr<gfx::SourceSurface>> mLastTransactionExternalSurfaces;
struct TextureInfo {
ipc::Shmem mSnapshotShmem;
bool mRequiresRefresh = false;
};
std::unordered_map<int64_t, TextureInfo> mTextureInfo;
bool mIsInTransaction = false;
bool mHasOutstandingWriteLock = false;
bool mDormant = false;

View File

@ -10,17 +10,21 @@
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/CanvasManagerParent.h"
#include "mozilla/gfx/CanvasRenderThread.h"
#include "mozilla/gfx/DrawTargetWebgl.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/gfx/GPUParent.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/ipc/Endpoint.h"
#include "mozilla/layers/BufferTexture.h"
#include "mozilla/layers/CanvasTranslator.h"
#include "mozilla/layers/ImageDataSerializer.h"
#include "mozilla/layers/SharedSurfacesParent.h"
#include "mozilla/layers/TextureClient.h"
#include "mozilla/StaticPrefs_gfx.h"
#include "mozilla/SyncRunnable.h"
#include "mozilla/TaskQueue.h"
#include "mozilla/Telemetry.h"
#include "GLContext.h"
#include "RecordedCanvasEventImpl.h"
#if defined(XP_WIN)
@ -32,6 +36,7 @@ namespace mozilla {
namespace layers {
TextureData* CanvasTranslator::CreateTextureData(TextureType aTextureType,
gfx::BackendType aBackendType,
const gfx::IntSize& aSize,
gfx::SurfaceFormat aFormat) {
TextureData* textureData = nullptr;
@ -43,8 +48,16 @@ TextureData* CanvasTranslator::CreateTextureData(TextureType aTextureType,
break;
}
#endif
case TextureType::Unknown:
textureData = BufferTextureData::Create(
aSize, aFormat, gfx::BackendType::SKIA, LayersBackend::LAYERS_WR,
TextureFlags::DEALLOCATE_CLIENT | TextureFlags::REMOTE_TEXTURE,
ALLOC_CLEAR_BUFFER, nullptr);
break;
default:
MOZ_CRASH("Unsupported TextureType for CanvasTranslator.");
textureData = TextureData::Create(aTextureType, aFormat, aSize,
ALLOC_CLEAR_BUFFER, aBackendType);
break;
}
return textureData;
@ -64,12 +77,7 @@ CanvasTranslator::CanvasTranslator(
Telemetry::ScalarAdd(Telemetry::ScalarID::GFX_CANVAS_REMOTE_ACTIVATED, 1);
}
CanvasTranslator::~CanvasTranslator() {
// The textures need to be the last thing holding their DrawTargets, so that
// they can destroy them within a lock.
mDrawTargets.Clear();
mBaseDT = nullptr;
}
CanvasTranslator::~CanvasTranslator() = default;
void CanvasTranslator::DispatchToTaskQueue(
already_AddRefed<nsIRunnable> aRunnable) {
@ -102,16 +110,30 @@ static bool CreateAndMapShmem(RefPtr<ipc::SharedMemoryBasic>& aShmem,
return true;
}
bool CanvasTranslator::EnsureSharedContextWebgl() {
if (!mSharedContext || mSharedContext->IsContextLost()) {
mSharedContext = gfx::SharedContextWebgl::Create();
if (!mSharedContext || mSharedContext->IsContextLost()) {
mSharedContext = nullptr;
BlockCanvas();
return false;
}
}
return true;
}
mozilla::ipc::IPCResult CanvasTranslator::RecvInitTranslator(
const TextureType& aTextureType, Handle&& aReadHandle,
nsTArray<Handle>&& aBufferHandles, uint64_t aBufferSize,
CrossProcessSemaphoreHandle&& aReaderSem,
TextureType aTextureType, gfx::BackendType aBackendType,
Handle&& aReadHandle, nsTArray<Handle>&& aBufferHandles,
uint64_t aBufferSize, CrossProcessSemaphoreHandle&& aReaderSem,
CrossProcessSemaphoreHandle&& aWriterSem, bool aUseIPDLThread) {
if (mHeaderShmem) {
return IPC_FAIL(this, "RecvInitTranslator called twice.");
}
mTextureType = aTextureType;
mBackendType = aBackendType;
mOtherPid = OtherPid();
mHeaderShmem = MakeAndAddRef<ipc::SharedMemoryBasic>();
if (!CreateAndMapShmem(mHeaderShmem, std::move(aReadHandle),
@ -127,12 +149,15 @@ mozilla::ipc::IPCResult CanvasTranslator::RecvInitTranslator(
mReaderSemaphore.reset(CrossProcessSemaphore::Create(std::move(aReaderSem)));
mReaderSemaphore->CloseHandle();
#if defined(XP_WIN)
if (!CheckForFreshCanvasDevice(__LINE__)) {
gfxCriticalNote << "GFX: CanvasTranslator failed to get device";
return IPC_OK();
}
#endif
if (gfx::gfxVars::UseAcceleratedCanvas2D() && !EnsureSharedContextWebgl()) {
gfxCriticalNote
<< "GFX: CanvasTranslator failed creating WebGL shared context";
}
if (!aUseIPDLThread) {
mTranslationTaskQueue = gfx::CanvasRenderThread::CreateWorkerTaskQueue();
@ -259,24 +284,29 @@ void CanvasTranslator::GetDataSurface(uint64_t aSurfaceRef) {
auto dstSize = surface->GetSize();
auto srcSize = map->GetSurface()->GetSize();
int32_t dataFormatWidth = dstSize.width * BytesPerPixel(surface->GetFormat());
gfx::SurfaceFormat format = surface->GetFormat();
int32_t bpp = BytesPerPixel(format);
int32_t dataFormatWidth = dstSize.width * bpp;
int32_t srcStride = map->GetStride();
if (dataFormatWidth > srcStride || srcSize != dstSize) {
return;
}
auto requiredSize = dataFormatWidth * dstSize.height;
int32_t dstStride =
ImageDataSerializer::ComputeRGBStride(format, dstSize.width);
auto requiredSize =
ImageDataSerializer::ComputeRGBBufferSize(dstSize, format);
if (requiredSize <= 0 || size_t(requiredSize) > mDataSurfaceShmem->Size()) {
return;
}
char* dst = static_cast<char*>(mDataSurfaceShmem->memory());
const char* src = reinterpret_cast<char*>(map->GetData());
const char* endSrc = src + (srcSize.height * srcStride);
uint8_t* dst = static_cast<uint8_t*>(mDataSurfaceShmem->memory());
const uint8_t* src = map->GetData();
const uint8_t* endSrc = src + (srcSize.height * srcStride);
while (src < endSrc) {
memcpy(dst, src, dataFormatWidth);
src += srcStride;
dst += dataFormatWidth;
dst += dstStride;
}
}
@ -308,6 +338,9 @@ void CanvasTranslator::ActorDestroy(ActorDestroyReason why) {
void CanvasTranslator::FinishShutdown() {
MOZ_ASSERT(gfx::CanvasRenderThread::IsInCanvasRenderThread());
ClearTextureInfo();
gfx::CanvasManagerParent::RemoveReplayTextures(this);
}
@ -316,7 +349,8 @@ bool CanvasTranslator::CheckDeactivated() {
return true;
}
if (NS_WARN_IF(!gfx::gfxVars::RemoteCanvasEnabled())) {
if (NS_WARN_IF(!gfx::gfxVars::RemoteCanvasEnabled() &&
!gfx::gfxVars::UseAcceleratedCanvas2D())) {
Deactivate();
}
@ -337,18 +371,31 @@ void CanvasTranslator::Deactivate() {
&CanvasTranslator::SendDeactivate));
// Unlock all of our textures.
for (auto const& entry : mTextureDatas) {
entry.second->Unlock();
for (auto const& entry : mTextureInfo) {
if (entry.second.mTextureData) {
entry.second.mTextureData->Unlock();
}
}
// Disable remote canvas for all.
gfx::CanvasManagerParent::DisableRemoteCanvas();
}
void CanvasTranslator::BlockCanvas() {
if (mDeactivated || mBlocked) {
return;
}
mBlocked = true;
gfx::CanvasRenderThread::Dispatch(
NewRunnableMethod("CanvasTranslator::SendBlockCanvas", this,
&CanvasTranslator::SendBlockCanvas));
}
void CanvasTranslator::CheckAndSignalWriter() {
do {
switch (mHeader->writerState) {
case State::Processing:
case State::Failed:
return;
case State::AboutToWait:
// The writer is making a decision about whether to wait. So, we must
@ -432,6 +479,15 @@ bool CanvasTranslator::ReadNextEvent(EventType& aEventType) {
void CanvasTranslator::TranslateRecording() {
MOZ_ASSERT(IsInTaskQueue());
if (mSharedContext && EnsureSharedContextWebgl()) {
mSharedContext->EnterTlsScope();
}
auto exitTlsScope = MakeScopeExit([&] {
if (mSharedContext) {
mSharedContext->ExitTlsScope();
}
});
mHeader->readerState = State::Processing;
EventType eventType;
while (ReadNextEvent(eventType)) {
@ -531,25 +587,32 @@ bool CanvasTranslator::CreateReferenceTexture() {
mReferenceTextureData->Unlock();
}
mReferenceTextureData.reset(CreateTextureData(
mTextureType, gfx::IntSize(1, 1), gfx::SurfaceFormat::B8G8R8A8));
mReferenceTextureData.reset(CreateTextureData(mTextureType, mBackendType,
gfx::IntSize(1, 1),
gfx::SurfaceFormat::B8G8R8A8));
if (!mReferenceTextureData) {
return false;
}
mReferenceTextureData->Lock(OpenMode::OPEN_READ_WRITE);
mBaseDT = mReferenceTextureData->BorrowDrawTarget();
if (!mBaseDT) {
// We might get a null draw target due to a device failure, just return
// false so that we can recover.
return false;
}
mBackendType = mBaseDT->GetBackendType();
return true;
}
bool CanvasTranslator::CheckForFreshCanvasDevice(int aLineNumber) {
// If not on D3D11, we are not dependent on a fresh device for DT creation if
// one already exists.
if (mBaseDT && mTextureType != TextureType::D3D11) {
return false;
}
#if defined(XP_WIN)
// If a new device has already been created, use that one.
RefPtr<ID3D11Device> device = gfx::DeviceManagerDx::Get()->GetCanvasDevice();
@ -589,11 +652,9 @@ bool CanvasTranslator::CheckForFreshCanvasDevice(int aLineNumber) {
Deactivate();
return false;
}
#endif
return CreateReferenceTexture();
#else
return false;
#endif
}
void CanvasTranslator::NotifyDeviceChanged() {
@ -603,29 +664,118 @@ void CanvasTranslator::NotifyDeviceChanged() {
&CanvasTranslator::SendNotifyDeviceChanged));
}
gfx::DrawTargetWebgl* CanvasTranslator::GetDrawTargetWebgl(
int64_t aTextureId) const {
auto result = mTextureInfo.find(aTextureId);
if (result != mTextureInfo.end() && result->second.mDrawTarget &&
result->second.mDrawTarget->GetBackendType() == gfx::BackendType::WEBGL) {
return static_cast<gfx::DrawTargetWebgl*>(result->second.mDrawTarget.get());
}
return nullptr;
}
void CanvasTranslator::NotifyRequiresRefresh(int64_t aTextureId,
bool aDispatch) {
if (aDispatch) {
DispatchToTaskQueue(NewRunnableMethod<int64_t, bool>(
"CanvasTranslator::NotifyRequiresRefresh", this,
&CanvasTranslator::NotifyRequiresRefresh, aTextureId, false));
return;
}
if (mTextureInfo.find(aTextureId) != mTextureInfo.end()) {
Unused << SendNotifyRequiresRefresh(aTextureId);
}
}
void CanvasTranslator::CacheSnapshotShmem(int64_t aTextureId, bool aDispatch) {
if (aDispatch) {
DispatchToTaskQueue(NewRunnableMethod<int64_t, bool>(
"CanvasTranslator::CacheSnapshotShmem", this,
&CanvasTranslator::CacheSnapshotShmem, aTextureId, false));
return;
}
if (gfx::DrawTargetWebgl* webgl = GetDrawTargetWebgl(aTextureId)) {
if (Maybe<Shmem> shmem = webgl->GetShmem()) {
// Lock the DT so that it doesn't get removed while shmem is in transit.
mTextureInfo[aTextureId].mLocked++;
nsCOMPtr<nsIThread> thread =
gfx::CanvasRenderThread::GetCanvasRenderThread();
RefPtr<CanvasTranslator> translator = this;
SendSnapshotShmem(aTextureId, std::move(*shmem))
->Then(
thread, __func__,
[=](bool) { translator->RemoveTexture(aTextureId); },
[=](ipc::ResponseRejectReason) {
translator->RemoveTexture(aTextureId);
});
}
}
}
void CanvasTranslator::PrepareShmem(int64_t aTextureId) {
if (gfx::DrawTargetWebgl* webgl = GetDrawTargetWebgl(aTextureId)) {
webgl->PrepareData();
}
}
already_AddRefed<gfx::DrawTarget> CanvasTranslator::CreateDrawTarget(
gfx::ReferencePtr aRefPtr, const gfx::IntSize& aSize,
gfx::SurfaceFormat aFormat) {
MOZ_DIAGNOSTIC_ASSERT(mNextTextureId >= 0, "No texture ID set");
RefPtr<gfx::DrawTarget> dt;
do {
TextureData* textureData = CreateTextureData(mTextureType, aSize, aFormat);
if (textureData) {
MOZ_DIAGNOSTIC_ASSERT(mNextTextureId >= 0, "No texture ID set");
textureData->Lock(OpenMode::OPEN_READ_WRITE);
mTextureDatas[mNextTextureId] = UniquePtr<TextureData>(textureData);
gfx::CanvasManagerParent::AddReplayTexture(this, mNextTextureId,
textureData);
dt = textureData->BorrowDrawTarget();
if (mNextRemoteTextureOwnerId.IsValid()) {
if (EnsureSharedContextWebgl()) {
mSharedContext->EnterTlsScope();
}
} while (!dt && CheckForFreshCanvasDevice(__LINE__));
if (RefPtr<gfx::DrawTargetWebgl> webgl = gfx::DrawTargetWebgl::Create(
aSize, aFormat, this, mSharedContext)) {
webgl->BeginFrame(gfx::IntRect());
dt = webgl.forget().downcast<gfx::DrawTarget>();
if (dt) {
TextureInfo& info = mTextureInfo[mNextTextureId];
info.mDrawTarget = dt;
info.mRemoteTextureOwnerId = mNextRemoteTextureOwnerId;
CacheSnapshotShmem(mNextTextureId);
}
}
if (!dt) {
NotifyRequiresRefresh(mNextTextureId);
}
}
if (!dt) {
do {
TextureData* textureData =
CreateTextureData(mTextureType, mBackendType, aSize, aFormat);
if (textureData) {
TextureInfo& info = mTextureInfo[mNextTextureId];
info.mTextureData = UniquePtr<TextureData>(textureData);
info.mRemoteTextureOwnerId = mNextRemoteTextureOwnerId;
if (textureData->Lock(OpenMode::OPEN_READ_WRITE)) {
dt = textureData->BorrowDrawTarget();
}
}
} while (!dt && CheckForFreshCanvasDevice(__LINE__));
}
AddDrawTarget(aRefPtr, dt);
mNextTextureId = -1;
mNextRemoteTextureOwnerId = RemoteTextureOwnerId();
return dt.forget();
}
void CanvasTranslator::RemoveTexture(int64_t aTextureId) {
mTextureDatas.erase(aTextureId);
{
// Don't erase the texture if still in use
auto result = mTextureInfo.find(aTextureId);
if (result == mTextureInfo.end() || --result->second.mLocked > 0) {
return;
}
mTextureInfo.erase(result);
}
// It is possible that the texture from the content process has never been
// forwarded from the GPU process, so make sure its descriptor is removed.
@ -633,11 +783,112 @@ void CanvasTranslator::RemoveTexture(int64_t aTextureId) {
}
TextureData* CanvasTranslator::LookupTextureData(int64_t aTextureId) {
TextureMap::const_iterator result = mTextureDatas.find(aTextureId);
if (result == mTextureDatas.end()) {
auto result = mTextureInfo.find(aTextureId);
if (result == mTextureInfo.end()) {
return nullptr;
}
return result->second.get();
return result->second.mTextureData.get();
}
bool CanvasTranslator::LockTexture(int64_t aTextureId, OpenMode aMode,
RemoteTextureId aId) {
auto result = mTextureInfo.find(aTextureId);
if (result == mTextureInfo.end()) {
return false;
}
if (result->second.mDrawTarget &&
result->second.mDrawTarget->GetBackendType() == gfx::BackendType::WEBGL) {
gfx::DrawTargetWebgl* webgl =
static_cast<gfx::DrawTargetWebgl*>(result->second.mDrawTarget.get());
webgl->BeginFrame(webgl->GetRect());
} else if (TextureData* data = result->second.mTextureData.get()) {
if (!data->Lock(aMode)) {
return false;
}
}
return true;
}
bool CanvasTranslator::UnlockTexture(int64_t aTextureId, RemoteTextureId aId) {
auto result = mTextureInfo.find(aTextureId);
if (result == mTextureInfo.end()) {
return false;
}
RemoteTextureOwnerId ownerId = result->second.mRemoteTextureOwnerId;
if (result->second.mDrawTarget &&
result->second.mDrawTarget->GetBackendType() == gfx::BackendType::WEBGL) {
gfx::DrawTargetWebgl* webgl =
static_cast<gfx::DrawTargetWebgl*>(result->second.mDrawTarget.get());
webgl->EndFrame();
webgl->CopyToSwapChain(aId, ownerId, mOtherPid);
if (!result->second.mNotifiedRequiresRefresh && webgl->RequiresRefresh()) {
result->second.mNotifiedRequiresRefresh = true;
NotifyRequiresRefresh(aTextureId);
}
} else if (TextureData* data = result->second.mTextureData.get()) {
if (aId.IsValid()) {
PushRemoteTexture(data, aId, ownerId);
data->Unlock();
} else {
data->Unlock();
gfx::CanvasManagerParent::AddReplayTexture(this, aTextureId, data);
}
}
return true;
}
bool CanvasTranslator::PushRemoteTexture(TextureData* aData,
RemoteTextureId aId,
RemoteTextureOwnerId aOwnerId) {
if (!mRemoteTextureOwner) {
mRemoteTextureOwner = new RemoteTextureOwnerClient(mOtherPid);
}
if (!mRemoteTextureOwner->IsRegistered(aOwnerId)) {
mRemoteTextureOwner->RegisterTextureOwner(
aOwnerId,
/* aIsSyncMode */ gfx::gfxVars::WebglOopAsyncPresentForceSync());
}
TextureData::Info info;
aData->FillInfo(info);
UniquePtr<TextureData> dstData;
if (mTextureType == TextureType::Unknown) {
dstData = mRemoteTextureOwner->CreateOrRecycleBufferTextureData(
aOwnerId, info.size, info.format);
} else {
dstData.reset(
CreateTextureData(mTextureType, mBackendType, info.size, info.format));
}
bool success = false;
// Source data is already locked.
if (dstData && dstData->Lock(OpenMode::OPEN_WRITE)) {
if (RefPtr<gfx::DrawTarget> dstDT = dstData->BorrowDrawTarget()) {
if (RefPtr<gfx::DrawTarget> srcDT = aData->BorrowDrawTarget()) {
if (RefPtr<gfx::SourceSurface> snapshot = srcDT->Snapshot()) {
dstDT->CopySurface(snapshot, snapshot->GetRect(),
gfx::IntPoint(0, 0));
success = true;
}
}
}
dstData->Unlock();
}
if (success) {
mRemoteTextureOwner->PushTexture(aId, aOwnerId, std::move(dstData));
} else {
mRemoteTextureOwner->PushDummyTexture(aId, aOwnerId);
}
return success;
}
void CanvasTranslator::ClearTextureInfo() {
mTextureInfo.clear();
mDrawTargets.Clear();
mSharedContext = nullptr;
mBaseDT = nullptr;
if (mRemoteTextureOwner) {
mRemoteTextureOwner->UnregisterAllTextureOwners();
mRemoteTextureOwner = nullptr;
}
}
already_AddRefed<gfx::SourceSurface> CanvasTranslator::LookupExternalSurface(

View File

@ -17,6 +17,7 @@
#include "mozilla/layers/CanvasDrawEventRecorder.h"
#include "mozilla/layers/LayersSurfaces.h"
#include "mozilla/layers/PCanvasParent.h"
#include "mozilla/layers/RemoteTextureMap.h"
#include "mozilla/ipc/CrossProcessSemaphore.h"
#include "mozilla/Monitor.h"
#include "mozilla/UniquePtr.h"
@ -26,6 +27,11 @@ namespace mozilla {
using EventType = gfx::RecordedEvent::EventType;
class TaskQueue;
namespace gfx {
class DrawTargetWebgl;
class SharedContextWebgl;
} // namespace gfx
namespace layers {
class SharedSurfacesHolder;
@ -63,6 +69,7 @@ class CanvasTranslator final : public gfx::InlineTranslator,
* CanvasEventRingBuffer.
*
* @param aTextureType the TextureType the translator will create
* @param aBackendType the BackendType for texture data
* @param aHeaderHandle handle for the control header
* @param aBufferHandles handles for the initial buffers for translation
* @param aBufferSize size of buffers and the default size
@ -71,13 +78,11 @@ class CanvasTranslator final : public gfx::InlineTranslator,
* @param aUseIPDLThread if true, use the IPDL thread instead of the worker
* pool for translation requests
*/
ipc::IPCResult RecvInitTranslator(const TextureType& aTextureType,
Handle&& aReadHandle,
nsTArray<Handle>&& aBufferHandles,
uint64_t aBufferSize,
CrossProcessSemaphoreHandle&& aReaderSem,
CrossProcessSemaphoreHandle&& aWriterSem,
bool aUseIPDLThread);
ipc::IPCResult RecvInitTranslator(
TextureType aTextureType, gfx::BackendType aBackendType,
Handle&& aReadHandle, nsTArray<Handle>&& aBufferHandles,
uint64_t aBufferSize, CrossProcessSemaphoreHandle&& aReaderSem,
CrossProcessSemaphoreHandle&& aWriterSem, bool aUseIPDLThread);
/**
* Restart the translation from a Stopped state.
@ -134,8 +139,9 @@ class CanvasTranslator final : public gfx::InlineTranslator,
* Set the texture ID that will be used as a lookup for the texture created by
* the next CreateDrawTarget.
*/
void SetNextTextureId(int64_t aNextTextureId) {
void SetNextTextureId(int64_t aNextTextureId, RemoteTextureOwnerId aOwnerId) {
mNextTextureId = aNextTextureId;
mNextRemoteTextureOwnerId = aOwnerId;
}
/**
@ -175,6 +181,12 @@ class CanvasTranslator final : public gfx::InlineTranslator,
*/
void RemoveTexture(int64_t aTextureId);
bool LockTexture(int64_t aTextureId, OpenMode aMode, RemoteTextureId aId);
bool UnlockTexture(int64_t aTextureId, RemoteTextureId aId);
bool PushRemoteTexture(TextureData* aData, RemoteTextureId aId,
RemoteTextureOwnerId aOwnerId);
/**
* Overriden to remove any DataSourceSurfaces associated with the RefPtr.
*
@ -256,6 +268,8 @@ class CanvasTranslator final : public gfx::InlineTranslator,
UniquePtr<gfx::DataSourceSurface::ScopedMap> GetPreparedMap(
gfx::ReferencePtr aSurface);
void PrepareShmem(int64_t aTextureId);
void RecycleBuffer();
void NextBuffer();
@ -281,11 +295,14 @@ class CanvasTranslator final : public gfx::InlineTranslator,
void Deactivate();
void BlockCanvas();
TextureData* CreateTextureData(TextureType aTextureType,
gfx::BackendType aBackendType,
const gfx::IntSize& aSize,
gfx::SurfaceFormat aFormat);
void AddSurfaceDescriptor(int64_t aTextureId, TextureData* atextureData);
void ClearTextureInfo();
bool HandleExtensionEvent(int32_t aType);
@ -293,11 +310,18 @@ class CanvasTranslator final : public gfx::InlineTranslator,
bool CheckForFreshCanvasDevice(int aLineNumber);
void NotifyDeviceChanged();
bool EnsureSharedContextWebgl();
gfx::DrawTargetWebgl* GetDrawTargetWebgl(int64_t aTextureId) const;
void NotifyRequiresRefresh(int64_t aTextureId, bool aDispatch = true);
void CacheSnapshotShmem(int64_t aTextureId, bool aDispatch = true);
RefPtr<TaskQueue> mTranslationTaskQueue;
RefPtr<SharedSurfacesHolder> mSharedSurfacesHolder;
#if defined(XP_WIN)
RefPtr<ID3D11Device> mDevice;
#endif
RefPtr<gfx::SharedContextWebgl> mSharedContext;
RefPtr<RemoteTextureOwnerClient> mRemoteTextureOwner;
size_t mDefaultBufferSize = 0;
uint32_t mMaxSpinCount;
@ -329,13 +353,23 @@ class CanvasTranslator final : public gfx::InlineTranslator,
// Sometimes during device reset our reference DrawTarget can be null, so we
// hold the BackendType separately.
gfx::BackendType mBackendType = gfx::BackendType::NONE;
typedef std::unordered_map<int64_t, UniquePtr<TextureData>> TextureMap;
TextureMap mTextureDatas;
base::ProcessId mOtherPid = base::kInvalidProcessId;
struct TextureInfo {
UniquePtr<TextureData> mTextureData;
RefPtr<gfx::DrawTarget> mDrawTarget;
RemoteTextureOwnerId mRemoteTextureOwnerId;
bool mNotifiedRequiresRefresh = false;
// Ref-count of how active uses of the DT. Avoids deletion when locked.
int32_t mLocked = 1;
};
std::unordered_map<int64_t, TextureInfo> mTextureInfo;
int64_t mNextTextureId = -1;
RemoteTextureOwnerId mNextRemoteTextureOwnerId;
nsRefPtrHashtable<nsPtrHashKey<void>, gfx::DataSourceSurface> mDataSurfaces;
gfx::ReferencePtr mMappedSurface;
UniquePtr<gfx::DataSourceSurface::ScopedMap> mPreparedMap;
Atomic<bool> mDeactivated{false};
Atomic<bool> mBlocked{false};
bool mIsInTransaction = false;
bool mDeviceResetInProgress = false;
};

View File

@ -517,7 +517,7 @@ PTextureChild* CompositorBridgeChild::CreateTexture(
}
already_AddRefed<CanvasChild> CompositorBridgeChild::GetCanvasChild() {
MOZ_ASSERT(gfx::gfxVars::RemoteCanvasEnabled());
MOZ_ASSERT(gfxPlatform::UseRemoteCanvas());
if (auto* cm = gfx::CanvasManagerChild::Get()) {
return cm->GetCanvasChild().forget();
}

View File

@ -11,6 +11,7 @@ include "mozilla/layers/CanvasTranslator.h";
[MoveOnly] using mozilla::CrossProcessSemaphoreHandle from "mozilla/ipc/CrossProcessSemaphore.h";
using mozilla::layers::TextureType from "mozilla/layers/LayersTypes.h";
[MoveOnly] using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h";
using mozilla::gfx::BackendType from "mozilla/gfx/Types.h";
namespace mozilla {
namespace layers {
@ -33,8 +34,9 @@ parent:
* aUseIPDLThread if true, use the IPDL thread instead of the worker pool for
* translation requests
*/
async InitTranslator(TextureType aTextureType, Handle aHeaderHandle,
Handle[] aBufferHandles, uint64_t aBufferSize,
async InitTranslator(TextureType aTextureType, BackendType aBackendType,
Handle aHeaderHandle, Handle[] aBufferHandles,
uint64_t aBufferSize,
CrossProcessSemaphoreHandle aReaderSem,
CrossProcessSemaphoreHandle aWriterSem,
bool aUseIPDLThread);
@ -67,6 +69,22 @@ child:
* Deactivate remote canvas, which will cause fall back to software.
*/
async Deactivate();
/**
* Block further accelerated canvases from being created, but allow existing
* canvases to continue processing.
*/
async BlockCanvas();
/**
* Notify that a remote accelerated canvas requires a fallback refresh.
*/
async NotifyRequiresRefresh(int64_t aTextureId);
/**
* Cache the shmem of the framebuffer for snapshotting.
*/
async SnapshotShmem(int64_t aTextureId, Shmem aShmem) returns (bool aSuccess);
};
} // layers

View File

@ -143,6 +143,7 @@ void WebRenderImageHost::PushPendingRemoteTexture(
// Clear when RemoteTextureOwner is different.
mPendingRemoteTextureWrappers.clear();
mWaitingReadyCallback = false;
mWaitForRemoteTextureOwner = true;
}
}
@ -222,7 +223,8 @@ void WebRenderImageHost::UseRemoteTexture() {
std::function<void(const RemoteTextureInfo&)> function;
RemoteTextureMap::Get()->GetRemoteTextureForDisplayList(
wrapper, std::move(function));
wrapper, std::move(function), mWaitForRemoteTextureOwner);
mWaitForRemoteTextureOwner = false;
}
if (!texture ||

View File

@ -88,6 +88,7 @@ class WebRenderImageHost : public CompositableHost, public ImageComposite {
std::deque<CompositableTextureHostRef> mPendingRemoteTextureWrappers;
bool mWaitingReadyCallback = false;
bool mWaitForRemoteTextureOwner = true;
Maybe<RemoteTextureOwnerId> mRemoteTextureOwnerIdOfPushCallback;
base::ProcessId mForPidOfPushCallback;

View File

@ -1189,7 +1189,8 @@ bool gfxPlatform::IsHeadless() {
/* static */
bool gfxPlatform::UseRemoteCanvas() {
return XRE_IsContentProcess() && gfx::gfxVars::RemoteCanvasEnabled();
return XRE_IsContentProcess() && (gfx::gfxVars::RemoteCanvasEnabled() ||
gfx::gfxVars::UseAcceleratedCanvas2D());
}
/* static */
@ -3865,13 +3866,18 @@ void gfxPlatform::DisableGPUProcess() {
}
/* static */ void gfxPlatform::DisableRemoteCanvas() {
if (!gfxVars::RemoteCanvasEnabled()) {
return;
if (gfxVars::RemoteCanvasEnabled()) {
gfxConfig::ForceDisable(Feature::REMOTE_CANVAS, FeatureStatus::Failed,
"Disabled by runtime error",
"FEATURE_REMOTE_CANVAS_RUNTIME_ERROR"_ns);
gfxVars::SetRemoteCanvasEnabled(false);
}
if (gfxVars::UseAcceleratedCanvas2D()) {
gfxConfig::ForceDisable(Feature::ACCELERATED_CANVAS2D,
FeatureStatus::Failed, "Disabled by runtime error",
"FEATURE_ACCELERATED_CANVAS2D_RUNTIME_ERROR"_ns);
gfxVars::SetUseAcceleratedCanvas2D(false);
}
gfxConfig::ForceDisable(Feature::REMOTE_CANVAS, FeatureStatus::Failed,
"Disabled by runtime error",
"FEATURE_REMOTE_CANVAS_RUNTIME_ERROR"_ns);
gfxVars::SetRemoteCanvasEnabled(false);
}
void gfxPlatform::ImportCachedContentDeviceData() {

View File

@ -104,7 +104,7 @@ fuzzy(0-1,0-43) == 1201272-1.html 1201272-1-ref.html
== 1303534-1.html 1303534-1-ref.html
fuzzy-if(cocoaWidget,0-1,0-1410) == 1304353-text-global-alpha-1.html 1304353-text-global-alpha-1-ref.html
fuzzy-if(cocoaWidget,0-1,0-1302) fuzzy-if(winWidget,0-1,0-578) == 1304353-text-global-alpha-2.html 1304353-text-global-alpha-2-ref.html
fuzzy(0-1,0-1302) == 1304353-text-global-alpha-2.html 1304353-text-global-alpha-2-ref.html
fuzzy-if(winWidget,0-94,0-1575) fuzzy-if(cocoaWidget,0-1,0-34) == 1304353-text-global-composite-op-1.html 1304353-text-global-composite-op-1-ref.html
== text-indent-1a.html text-indent-1-ref.html

View File

@ -36,8 +36,8 @@ fails-if(useDrawSnapshot) == background-scale-with-viewbox-1.html background-sca
== canvas-drawImage-scale-1b.html lime100x100-ref.html
== canvas-drawImage-scale-1c.html lime100x100-ref.html
fuzzy(0-1,0-2) fuzzy-if(!remoteCanvas,0-1,0-529) fuzzy-if(remoteCanvas,0-97,0-745) fuzzy-if(Android&&device,0-64,0-1212) == canvas-drawImage-scale-2a.html canvas-drawImage-scale-2-ref.html
fuzzy(0-1,0-2) fuzzy-if(!remoteCanvas,0-1,0-529) fuzzy-if(remoteCanvas,0-97,0-745) fuzzy-if(Android&&device,0-64,0-1212) == canvas-drawImage-scale-2b.html canvas-drawImage-scale-2-ref.html
fuzzy(0-192,0-1813) == canvas-drawImage-scale-2a.html canvas-drawImage-scale-2-ref.html
fuzzy(0-192,0-1813) == canvas-drawImage-scale-2b.html canvas-drawImage-scale-2-ref.html
fuzzy-if(winWidget,0-1,0-10000) fuzzy-if(azureSkia,0-1,0-10000) fuzzy-if(Android,0-1,0-10000) == canvas-drawImage-alpha-1.html canvas-drawImage-alpha-1-ref.html
#Same as scale-2a but with globalAlpha:

View File

@ -2,6 +2,6 @@
# e.g. filter: blur(3px) grayscale(0.5) invert(0.2);
# Some platforms render this complex filter chain a little differently, and that's ok.
fuzzy(4-6,12000-19950) fuzzy-if(swgl,5-10,13600-20260) fuzzy-if(Android&&device&&!swgl,6-6,19986-19986) == long-chain.html long-chain-ref.html # Win10: Bug 1258241
fuzzy(4-10,12000-20260) == long-chain.html long-chain-ref.html # Win10: Bug 1258241
== moz-element.html moz-element-ref.html
fuzzy-if(!useDrawSnapshot,13-15,7670-7982) fuzzy-if(!useDrawSnapshot&&swgl,11-12,14052-14056) fuzzy-if(Android&&device&&!swgl,13-13,13505-13505) == same-filter.html same-filter-ref.html
fuzzy(0-15,0-14056) == same-filter.html same-filter-ref.html