Bug 1920580 - Add plumbing to allow compositor process to context reset individual canvases. r=lsalzman

This patch adds the necessary plumbing for accelerated canvas to trigger
a context lost / context restored events on the Canvas2D DOM object.
Currently we support this when the GPU process is crashed and restored,
but accelerated canvas can lose its WebGL context in a similar manner.

Note that not all accelerated canvases are impacted. Only the ones
without a recent snapshot and/or already fallen back to Skia will
require a context lost / restored event.

Differential Revision: https://phabricator.services.mozilla.com/D223209
This commit is contained in:
Andrew Osmond
2024-10-16 03:25:02 +00:00
parent 6d2b1e4699
commit 8e5657c565
10 changed files with 110 additions and 8 deletions

View File

@@ -11,6 +11,7 @@
#include "mozilla/dom/WorkerRef.h"
#include "mozilla/dom/WorkerRunnable.h"
#include "mozilla/gfx/CanvasManagerChild.h"
#include "mozilla/layers/PersistentBufferProvider.h"
using namespace mozilla::dom;
@@ -135,6 +136,31 @@ void CanvasShutdownManager::OnRemoteCanvasRestored() {
}
}
void CanvasShutdownManager::OnRemoteCanvasReset(
const nsTArray<layers::RemoteTextureOwnerId>& aOwnerIds) {
if (aOwnerIds.IsEmpty()) {
return;
}
for (const auto& canvas : mActiveCanvas) {
auto* bufferProvider = canvas->GetBufferProvider();
if (!bufferProvider) {
continue;
}
Maybe<layers::RemoteTextureOwnerId> ownerId =
bufferProvider->GetRemoteTextureOwnerId();
if (!ownerId) {
continue;
}
if (aOwnerIds.Contains(*ownerId)) {
canvas->OnRemoteCanvasLost();
canvas->OnRemoteCanvasRestored();
}
}
}
/* static */ void CanvasShutdownManager::MaybeRestoreRemoteCanvas() {
// Calling Get will recreate the CanvasManagerChild, which in turn will
// cause us to call OnRemoteCanvasRestore upon success.

View File

@@ -9,6 +9,7 @@
#include "mozilla/RefPtr.h"
#include "mozilla/StaticMutex.h"
#include "mozilla/ThreadLocal.h"
#include "mozilla/layers/LayersTypes.h"
#include <set>
namespace mozilla {
@@ -34,6 +35,8 @@ class CanvasShutdownManager final {
void OnRemoteCanvasLost();
void OnRemoteCanvasRestored();
void OnRemoteCanvasReset(
const nsTArray<layers::RemoteTextureOwnerId>& aOwnerIds);
private:
explicit CanvasShutdownManager(dom::StrongWorkerRef* aWorkerRef);

View File

@@ -153,13 +153,14 @@ PersistentBufferProviderAccelerated::Create(gfx::IntSize aSize,
}
RefPtr<PersistentBufferProviderAccelerated> provider =
new PersistentBufferProviderAccelerated(texture);
new PersistentBufferProviderAccelerated(remoteTextureOwnerId, texture);
return provider.forget();
}
PersistentBufferProviderAccelerated::PersistentBufferProviderAccelerated(
RemoteTextureOwnerId aRemoteTextureOwnerId,
const RefPtr<TextureClient>& aTexture)
: mTexture(aTexture) {
: mRemoteTextureOwnerId(aRemoteTextureOwnerId), mTexture(aTexture) {
MOZ_COUNT_CTOR(PersistentBufferProviderAccelerated);
}

View File

@@ -31,7 +31,6 @@ namespace layers {
class CompositableForwarder;
class FwdTransactionTracker;
class KnowsCompositor;
struct RemoteTextureOwnerId;
class TextureClient;
/**
@@ -80,6 +79,10 @@ class PersistentBufferProvider : public RefCounted<PersistentBufferProvider>,
virtual TextureClient* GetTextureClient() { return nullptr; }
virtual Maybe<RemoteTextureOwnerId> GetRemoteTextureOwnerId() const {
return Nothing();
}
virtual void OnMemoryPressure() {}
virtual void OnShutdown() {}
@@ -170,6 +173,10 @@ class PersistentBufferProviderAccelerated : public PersistentBufferProvider {
void ReturnSnapshot(already_AddRefed<gfx::SourceSurface> aSnapshot) override;
Maybe<RemoteTextureOwnerId> GetRemoteTextureOwnerId() const override {
return Some(mRemoteTextureOwnerId);
}
bool PreservesDrawingState() const override { return true; }
void OnShutdown() override { Destroy(); }
@@ -183,11 +190,13 @@ class PersistentBufferProviderAccelerated : public PersistentBufferProvider {
protected:
explicit PersistentBufferProviderAccelerated(
RemoteTextureOwnerId aRemoteTextureOwnerId,
const RefPtr<TextureClient>& aTexture);
~PersistentBufferProviderAccelerated() override;
void Destroy();
RemoteTextureOwnerId mRemoteTextureOwnerId;
RefPtr<TextureClient> mTexture;
RefPtr<gfx::DrawTarget> mDrawTarget;

View File

@@ -47,7 +47,8 @@ const EventType RECYCLE_BUFFER = EventType(EventType::LAST + 15);
const EventType DROP_BUFFER = EventType(EventType::LAST + 16);
const EventType PREPARE_SHMEM = EventType(EventType::LAST + 17);
const EventType PRESENT_TEXTURE = EventType(EventType::LAST + 18);
const EventType LAST_CANVAS_EVENT_TYPE = PRESENT_TEXTURE;
const EventType DEVICE_RESET_ACKNOWLEDGED = EventType(EventType::LAST + 19);
const EventType LAST_CANVAS_EVENT_TYPE = DEVICE_RESET_ACKNOWLEDGED;
class RecordedCanvasBeginTransaction final
: public RecordedEventDerived<RecordedCanvasBeginTransaction> {
@@ -485,6 +486,38 @@ template <class S>
RecordedDeviceChangeAcknowledged::RecordedDeviceChangeAcknowledged(S& aStream)
: RecordedEventDerived(DEVICE_CHANGE_ACKNOWLEDGED) {}
class RecordedDeviceResetAcknowledged final
: public RecordedEventDerived<RecordedDeviceResetAcknowledged> {
public:
RecordedDeviceResetAcknowledged()
: RecordedEventDerived(DEVICE_RESET_ACKNOWLEDGED) {}
template <class S>
MOZ_IMPLICIT RecordedDeviceResetAcknowledged(S& aStream);
bool PlayCanvasEvent(CanvasTranslator* aTranslator) const;
template <class S>
void Record(S& aStream) const;
std::string GetName() const final {
return "RecordedDeviceResetAcknowledged";
}
};
inline bool RecordedDeviceResetAcknowledged::PlayCanvasEvent(
CanvasTranslator* aTranslator) const {
aTranslator->DeviceResetAcknowledged();
return true;
}
template <class S>
void RecordedDeviceResetAcknowledged::Record(S& aStream) const {}
template <class S>
RecordedDeviceResetAcknowledged::RecordedDeviceResetAcknowledged(S& aStream)
: RecordedEventDerived(DEVICE_RESET_ACKNOWLEDGED) {}
class RecordedCanvasDrawTargetCreation final
: public RecordedEventDerived<RecordedCanvasDrawTargetCreation> {
public:
@@ -779,7 +812,8 @@ RecordedPresentTexture::RecordedPresentTexture(S& aStream)
f(RECYCLE_BUFFER, RecordedRecycleBuffer); \
f(DROP_BUFFER, RecordedDropBuffer); \
f(PREPARE_SHMEM, RecordedPrepareShmem); \
f(PRESENT_TEXTURE, RecordedPresentTexture);
f(PRESENT_TEXTURE, RecordedPresentTexture); \
f(DEVICE_RESET_ACKNOWLEDGED, RecordedDeviceResetAcknowledged);
} // namespace layers
} // namespace mozilla

View File

@@ -11,6 +11,7 @@
#include "mozilla/dom/WorkerRef.h"
#include "mozilla/dom/WorkerRunnable.h"
#include "mozilla/gfx/CanvasManagerChild.h"
#include "mozilla/gfx/CanvasShutdownManager.h"
#include "mozilla/gfx/DrawTargetRecording.h"
#include "mozilla/gfx/Tools.h"
#include "mozilla/gfx/Rect.h"
@@ -276,7 +277,7 @@ CanvasChild::CanvasChild(dom::ThreadSafeWorkerRef* aWorkerRef)
CanvasChild::~CanvasChild() { MOZ_ASSERT(!mWorkerRef); }
static void NotifyCanvasDeviceReset() {
static void NotifyCanvasDeviceChanged() {
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (obs) {
obs->NotifyObservers(nullptr, "canvas-device-reset", nullptr);
@@ -286,11 +287,23 @@ static void NotifyCanvasDeviceReset() {
ipc::IPCResult CanvasChild::RecvNotifyDeviceChanged() {
NS_ASSERT_OWNINGTHREAD(CanvasChild);
NotifyCanvasDeviceReset();
NotifyCanvasDeviceChanged();
mRecorder->RecordEvent(RecordedDeviceChangeAcknowledged());
return IPC_OK();
}
ipc::IPCResult CanvasChild::RecvNotifyDeviceReset(
const nsTArray<RemoteTextureOwnerId>& aOwnerIds) {
NS_ASSERT_OWNINGTHREAD(CanvasChild);
if (auto* manager = gfx::CanvasShutdownManager::MaybeGet()) {
manager->OnRemoteCanvasReset(aOwnerIds);
}
mRecorder->RecordEvent(RecordedDeviceResetAcknowledged());
return IPC_OK();
}
/* static */ bool CanvasChild::mDeactivated = false;
ipc::IPCResult CanvasChild::RecvDeactivate() {
@@ -301,7 +314,7 @@ ipc::IPCResult CanvasChild::RecvDeactivate() {
if (auto* cm = gfx::CanvasManagerChild::Get()) {
cm->DeactivateCanvas();
}
NotifyCanvasDeviceReset();
NotifyCanvasDeviceChanged();
return IPC_OK();
}

View File

@@ -46,6 +46,9 @@ class CanvasChild final : public PCanvasChild, public SupportsWeakPtr {
ipc::IPCResult RecvNotifyDeviceChanged();
ipc::IPCResult RecvNotifyDeviceReset(
const nsTArray<RemoteTextureOwnerId>& aOwnerIds);
ipc::IPCResult RecvDeactivate();
ipc::IPCResult RecvBlockCanvas();

View File

@@ -862,6 +862,8 @@ void CanvasTranslator::DeviceChangeAcknowledged() {
}
}
void CanvasTranslator::DeviceResetAcknowledged() { DeviceChangeAcknowledged(); }
bool CanvasTranslator::CreateReferenceTexture() {
if (mReferenceTextureData) {
mReferenceTextureData->Unlock();

View File

@@ -147,6 +147,11 @@ class CanvasTranslator final : public gfx::InlineTranslator,
*/
void DeviceChangeAcknowledged();
/**
* Marks that device reset processing in the writing process has finished.
*/
void DeviceResetAcknowledged();
/**
* Used during playback of events to create DrawTargets. For the
* CanvasTranslator this means creating TextureDatas and getting the

View File

@@ -9,6 +9,7 @@ include "mozilla/layers/LayersMessageUtils.h";
include "mozilla/layers/CanvasTranslator.h";
[MoveOnly] using mozilla::CrossProcessSemaphoreHandle from "mozilla/ipc/CrossProcessSemaphore.h";
using mozilla::layers::RemoteTextureOwnerId from "mozilla/layers/LayersTypes.h";
using mozilla::layers::TextureType from "mozilla/layers/LayersTypes.h";
[MoveOnly] using mozilla::ipc::SharedMemory::Handle from "mozilla/ipc/SharedMemory.h";
using mozilla::gfx::BackendType from "mozilla/gfx/Types.h";
@@ -72,6 +73,11 @@ child:
*/
async NotifyDeviceChanged();
/**
* Notify that the canvas device used by the translator has been reset.
*/
async NotifyDeviceReset(RemoteTextureOwnerId[] aOwners);
/**
* Deactivate remote canvas, which will cause fall back to software.
*/