diff --git a/dom/canvas/CanvasImageCache.cpp b/dom/canvas/CanvasImageCache.cpp index 2a906bf63945..9dcf9ee1a964 100644 --- a/dom/canvas/CanvasImageCache.cpp +++ b/dom/canvas/CanvasImageCache.cpp @@ -155,17 +155,18 @@ class ImageCacheObserver final : public nsIObserver { explicit ImageCacheObserver(ImageCache* aImageCache) : mImageCache(aImageCache) { - RegisterMemoryPressureEvent(); + RegisterObserverEvents(); } void Destroy() { - UnregisterMemoryPressureEvent(); + UnregisterObserverEvents(); mImageCache = nullptr; } NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aSomeData) override { - if (!mImageCache || strcmp(aTopic, "memory-pressure")) { + if (!mImageCache || (strcmp(aTopic, "memory-pressure") != 0 && + strcmp(aTopic, "canvas-device-reset") != 0)) { return NS_OK; } @@ -176,7 +177,7 @@ class ImageCacheObserver final : public nsIObserver { private: virtual ~ImageCacheObserver() = default; - void RegisterMemoryPressureEvent() { + void RegisterObserverEvents() { nsCOMPtr observerService = mozilla::services::GetObserverService(); @@ -184,10 +185,11 @@ class ImageCacheObserver final : public nsIObserver { if (observerService) { observerService->AddObserver(this, "memory-pressure", false); + observerService->AddObserver(this, "canvas-device-reset", false); } } - void UnregisterMemoryPressureEvent() { + void UnregisterObserverEvents() { nsCOMPtr observerService = mozilla::services::GetObserverService(); @@ -196,6 +198,7 @@ class ImageCacheObserver final : public nsIObserver { // no longer available. See bug 1029504. if (observerService) { observerService->RemoveObserver(this, "memory-pressure"); + observerService->RemoveObserver(this, "canvas-device-reset"); } } diff --git a/dom/html/HTMLCanvasElement.cpp b/dom/html/HTMLCanvasElement.cpp index 29a11dcfb763..199de1900cb7 100644 --- a/dom/html/HTMLCanvasElement.cpp +++ b/dom/html/HTMLCanvasElement.cpp @@ -265,13 +265,13 @@ HTMLCanvasElementObserver::HTMLCanvasElementObserver( HTMLCanvasElement* aElement) : mElement(aElement) { RegisterVisibilityChangeEvent(); - RegisterMemoryPressureEvent(); + RegisterObserverEvents(); } HTMLCanvasElementObserver::~HTMLCanvasElementObserver() { Destroy(); } void HTMLCanvasElementObserver::Destroy() { - UnregisterMemoryPressureEvent(); + UnregisterObserverEvents(); UnregisterVisibilityChangeEvent(); mElement = nullptr; } @@ -296,7 +296,7 @@ void HTMLCanvasElementObserver::UnregisterVisibilityChangeEvent() { this, true); } -void HTMLCanvasElementObserver::RegisterMemoryPressureEvent() { +void HTMLCanvasElementObserver::RegisterObserverEvents() { if (!mElement) { return; } @@ -306,11 +306,13 @@ void HTMLCanvasElementObserver::RegisterMemoryPressureEvent() { MOZ_ASSERT(observerService); - if (observerService) + if (observerService) { observerService->AddObserver(this, "memory-pressure", false); + observerService->AddObserver(this, "canvas-device-reset", false); + } } -void HTMLCanvasElementObserver::UnregisterMemoryPressureEvent() { +void HTMLCanvasElementObserver::UnregisterObserverEvents() { if (!mElement) { return; } @@ -321,17 +323,24 @@ void HTMLCanvasElementObserver::UnregisterMemoryPressureEvent() { // Do not assert on observerService here. This might be triggered by // the cycle collector at a late enough time, that XPCOM services are // no longer available. See bug 1029504. - if (observerService) observerService->RemoveObserver(this, "memory-pressure"); + if (observerService) { + observerService->RemoveObserver(this, "memory-pressure"); + observerService->RemoveObserver(this, "canvas-device-reset"); + } } NS_IMETHODIMP HTMLCanvasElementObserver::Observe(nsISupports*, const char* aTopic, const char16_t*) { - if (!mElement || strcmp(aTopic, "memory-pressure")) { + if (!mElement) { return NS_OK; } - mElement->OnMemoryPressure(); + if (strcmp(aTopic, "memory-pressure") == 0) { + mElement->OnMemoryPressure(); + } else if (strcmp(aTopic, "canvas-device-reset") == 0) { + mElement->OnDeviceReset(); + } return NS_OK; } @@ -399,7 +408,8 @@ HTMLCanvasElement::CreateContext(CanvasContextType aContextType) { // Add Observer for webgl canvas. if (aContextType == CanvasContextType::WebGL1 || - aContextType == CanvasContextType::WebGL2) { + aContextType == CanvasContextType::WebGL2 || + aContextType == CanvasContextType::Canvas2D) { if (!mContextObserver) { mContextObserver = new HTMLCanvasElementObserver(this); } @@ -1445,6 +1455,12 @@ void HTMLCanvasElement::OnMemoryPressure() { } } +void HTMLCanvasElement::OnDeviceReset() { + if (!mOffscreenCanvas && mCurrentContext) { + mCurrentContext->Reset(); + } +} + /* static */ void HTMLCanvasElement::SetAttrFromAsyncCanvasRenderer( AsyncCanvasRenderer* aRenderer) { diff --git a/dom/html/HTMLCanvasElement.h b/dom/html/HTMLCanvasElement.h index 7b4c181bfc46..35c0b57eef18 100644 --- a/dom/html/HTMLCanvasElement.h +++ b/dom/html/HTMLCanvasElement.h @@ -69,8 +69,8 @@ class HTMLCanvasElementObserver final : public nsIObserver, void RegisterVisibilityChangeEvent(); void UnregisterVisibilityChangeEvent(); - void RegisterMemoryPressureEvent(); - void UnregisterMemoryPressureEvent(); + void RegisterObserverEvents(); + void UnregisterObserverEvents(); private: ~HTMLCanvasElementObserver(); @@ -328,6 +328,8 @@ class HTMLCanvasElement final : public nsGenericHTMLElement, void OnMemoryPressure(); + void OnDeviceReset(); + static void SetAttrFromAsyncCanvasRenderer(AsyncCanvasRenderer* aRenderer); static void InvalidateFromAsyncCanvasRenderer(AsyncCanvasRenderer* aRenderer); diff --git a/gfx/layers/RecordedCanvasEventImpl.h b/gfx/layers/RecordedCanvasEventImpl.h index 63aef376d237..fa57905bab11 100644 --- a/gfx/layers/RecordedCanvasEventImpl.h +++ b/gfx/layers/RecordedCanvasEventImpl.h @@ -37,6 +37,7 @@ const EventType PREPARE_DATA_FOR_SURFACE = EventType(EventType::LAST + 6); const EventType GET_DATA_FOR_SURFACE = EventType(EventType::LAST + 7); const EventType ADD_SURFACE_ALIAS = EventType(EventType::LAST + 8); const EventType REMOVE_SURFACE_ALIAS = EventType(EventType::LAST + 9); +const EventType DEVICE_CHANGE_ACKNOWLEDGED = EventType(EventType::LAST + 10); class RecordedCanvasBeginTransaction final : public RecordedEventDerived { @@ -461,6 +462,38 @@ RecordedRemoveSurfaceAlias::RecordedRemoveSurfaceAlias(S& aStream) ReadElement(aStream, mSurfaceAlias); } +class RecordedDeviceChangeAcknowledged final + : public RecordedEventDerived { + public: + RecordedDeviceChangeAcknowledged() + : RecordedEventDerived(DEVICE_CHANGE_ACKNOWLEDGED) {} + + template + MOZ_IMPLICIT RecordedDeviceChangeAcknowledged(S& aStream); + + bool PlayCanvasEvent(CanvasTranslator* aTranslator) const; + + template + void Record(S& aStream) const; + + std::string GetName() const final { + return "RecordedDeviceChangeAcknowledged"; + } +}; + +inline bool RecordedDeviceChangeAcknowledged::PlayCanvasEvent( + CanvasTranslator* aTranslator) const { + aTranslator->DeviceChangeAcknowledged(); + return true; +} + +template +void RecordedDeviceChangeAcknowledged::Record(S& aStream) const {} + +template +RecordedDeviceChangeAcknowledged::RecordedDeviceChangeAcknowledged(S& aStream) + : RecordedEventDerived(DEVICE_CHANGE_ACKNOWLEDGED) {} + #define FOR_EACH_CANVAS_EVENT(f) \ f(CANVAS_BEGIN_TRANSACTION, RecordedCanvasBeginTransaction); \ f(CANVAS_END_TRANSACTION, RecordedCanvasEndTransaction); \ @@ -471,7 +504,8 @@ RecordedRemoveSurfaceAlias::RecordedRemoveSurfaceAlias(S& aStream) f(PREPARE_DATA_FOR_SURFACE, RecordedPrepareDataForSurface); \ f(GET_DATA_FOR_SURFACE, RecordedGetDataForSurface); \ f(ADD_SURFACE_ALIAS, RecordedAddSurfaceAlias); \ - f(REMOVE_SURFACE_ALIAS, RecordedRemoveSurfaceAlias); + f(REMOVE_SURFACE_ALIAS, RecordedRemoveSurfaceAlias); \ + f(DEVICE_CHANGE_ACKNOWLEDGED, RecordedDeviceChangeAcknowledged); } // namespace layers } // namespace mozilla diff --git a/gfx/layers/ipc/CanvasChild.cpp b/gfx/layers/ipc/CanvasChild.cpp index 282faab730b4..c7cfe8d1b781 100644 --- a/gfx/layers/ipc/CanvasChild.cpp +++ b/gfx/layers/ipc/CanvasChild.cpp @@ -12,6 +12,7 @@ #include "mozilla/gfx/Rect.h" #include "mozilla/gfx/Point.h" #include "mozilla/layers/CanvasDrawEventRecorder.h" +#include "nsIObserverService.h" #include "RecordedCanvasEventImpl.h" namespace mozilla { @@ -112,6 +113,16 @@ CanvasChild::CanvasChild(Endpoint&& aEndpoint) { CanvasChild::~CanvasChild() {} +ipc::IPCResult CanvasChild::RecvNotifyDeviceChanged() { + nsCOMPtr obs = mozilla::services::GetObserverService(); + if (obs) { + obs->NotifyObservers(nullptr, "canvas-device-reset", nullptr); + } + + mRecorder->RecordEvent(RecordedDeviceChangeAcknowledged()); + return IPC_OK(); +} + void CanvasChild::EnsureRecorder(TextureType aTextureType) { if (!mRecorder) { MOZ_ASSERT(mTextureType == TextureType::Unknown); diff --git a/gfx/layers/ipc/CanvasChild.h b/gfx/layers/ipc/CanvasChild.h index 93772146c200..adfccbcd8e71 100644 --- a/gfx/layers/ipc/CanvasChild.h +++ b/gfx/layers/ipc/CanvasChild.h @@ -29,6 +29,8 @@ class CanvasChild final : public PCanvasChild { explicit CanvasChild(Endpoint&& aEndpoint); + ipc::IPCResult RecvNotifyDeviceChanged(); + /** * Ensures that the DrawEventRecorder has been created. * diff --git a/gfx/layers/ipc/CanvasTranslator.cpp b/gfx/layers/ipc/CanvasTranslator.cpp index d3a35695a03f..01a0b42dc820 100644 --- a/gfx/layers/ipc/CanvasTranslator.cpp +++ b/gfx/layers/ipc/CanvasTranslator.cpp @@ -197,7 +197,13 @@ bool CanvasTranslator::TranslateRecording() { }); if (!success && !HandleExtensionEvent(eventType)) { - gfxCriticalNote << "Failed to play canvas event type: " << eventType; + if (mDeviceResetInProgress) { + // We've notified the recorder of a device change, so we are expecting + // failures. Log as a warning to prevent crash reporting being flooded. + gfxWarning() << "Failed to play canvas event type: " << eventType; + } else { + gfxCriticalNote << "Failed to play canvas event type: " << eventType; + } if (!mStream->good()) { return true; } @@ -262,11 +268,19 @@ void CanvasTranslator::EndTransaction() { mIsInTransaction = false; } +void CanvasTranslator::DeviceChangeAcknowledged() { + mDeviceResetInProgress = false; +} + bool CanvasTranslator::CheckForFreshCanvasDevice(int aLineNumber) { #if defined(XP_WIN) // If a new device has already been created, use that one. RefPtr device = gfx::DeviceManagerDx::Get()->GetCanvasDevice(); if (device && device != mDevice) { + if (mDevice) { + // We already had a device, notify child of change. + NotifyDeviceChanged(); + } mDevice = device.forget(); return true; } @@ -278,6 +292,7 @@ bool CanvasTranslator::CheckForFreshCanvasDevice(int aLineNumber) { gfxCriticalNote << "GFX: CanvasTranslator detected a device reset at " << aLineNumber; + NotifyDeviceChanged(); } RefPtr runnable = NS_NewRunnableFunction( @@ -296,6 +311,13 @@ bool CanvasTranslator::CheckForFreshCanvasDevice(int aLineNumber) { #endif } +void CanvasTranslator::NotifyDeviceChanged() { + mDeviceResetInProgress = true; + mCanvasThreadHolder->DispatchToCanvasThread( + NewRunnableMethod("CanvasTranslator::SendNotifyDeviceChanged", this, + &CanvasTranslator::SendNotifyDeviceChanged)); +} + void CanvasTranslator::AddSurfaceDescriptor(gfx::ReferencePtr aRefPtr, TextureData* aTextureData) { UniquePtr descriptor = MakeUnique(); diff --git a/gfx/layers/ipc/CanvasTranslator.h b/gfx/layers/ipc/CanvasTranslator.h index 09324fbab559..44a368ca709e 100644 --- a/gfx/layers/ipc/CanvasTranslator.h +++ b/gfx/layers/ipc/CanvasTranslator.h @@ -127,6 +127,11 @@ class CanvasTranslator final : public gfx::InlineTranslator, */ void Flush(); + /** + * Marks that device change processing in the writing process has finished. + */ + void DeviceChangeAcknowledged(); + /** * Used to send data back to the writer. This is done through the same shared * memory so the writer must wait and read the response after it has submitted @@ -259,6 +264,7 @@ class CanvasTranslator final : public gfx::InlineTranslator, bool HandleExtensionEvent(int32_t aType); bool CheckForFreshCanvasDevice(int aLineNumber); + void NotifyDeviceChanged(); RefPtr mCanvasThreadHolder; RefPtr mTranslationTaskQueue; @@ -281,6 +287,7 @@ class CanvasTranslator final : public gfx::InlineTranslator, "CanvasTranslator::mSurfaceDescriptorsMonitor"}; bool mIsValid = true; bool mIsInTransaction = false; + bool mDeviceResetInProgress = false; }; } // namespace layers diff --git a/gfx/layers/ipc/PCanvas.ipdl b/gfx/layers/ipc/PCanvas.ipdl index 32943daa60a1..1b60b860425e 100644 --- a/gfx/layers/ipc/PCanvas.ipdl +++ b/gfx/layers/ipc/PCanvas.ipdl @@ -31,6 +31,12 @@ parent: * stopped due to a timeout waiting for events. */ async ResumeTranslation(); + + child: + /** + * Notify that the canvas device used by the translator has changed. + */ + async NotifyDeviceChanged(); }; } // layers