Bug 1598585 Part 4: Notify the content process of a canvas device change. r=jrmuizel

This sends a message to the content process on device reset/change. The content
process clears the CanvasImageCache and canvas TextureClients and then records a
new event, so the translator knows it has finished.

Differential Revision: https://phabricator.services.mozilla.com/D60890

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Bob Owen 2020-02-24 11:21:27 +00:00
Родитель cad856864a
Коммит a8de05ad4d
9 изменённых файлов: 121 добавлений и 18 удалений

Просмотреть файл

@ -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<nsIObserverService> 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<nsIObserverService> 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");
}
}

Просмотреть файл

@ -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) {

Просмотреть файл

@ -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);

Просмотреть файл

@ -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<RecordedCanvasBeginTransaction> {
@ -461,6 +462,38 @@ RecordedRemoveSurfaceAlias::RecordedRemoveSurfaceAlias(S& aStream)
ReadElement(aStream, mSurfaceAlias);
}
class RecordedDeviceChangeAcknowledged final
: public RecordedEventDerived<RecordedDeviceChangeAcknowledged> {
public:
RecordedDeviceChangeAcknowledged()
: RecordedEventDerived(DEVICE_CHANGE_ACKNOWLEDGED) {}
template <class S>
MOZ_IMPLICIT RecordedDeviceChangeAcknowledged(S& aStream);
bool PlayCanvasEvent(CanvasTranslator* aTranslator) const;
template <class S>
void Record(S& aStream) const;
std::string GetName() const final {
return "RecordedDeviceChangeAcknowledged";
}
};
inline bool RecordedDeviceChangeAcknowledged::PlayCanvasEvent(
CanvasTranslator* aTranslator) const {
aTranslator->DeviceChangeAcknowledged();
return true;
}
template <class S>
void RecordedDeviceChangeAcknowledged::Record(S& aStream) const {}
template <class S>
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

Просмотреть файл

@ -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<PCanvasChild>&& aEndpoint) {
CanvasChild::~CanvasChild() {}
ipc::IPCResult CanvasChild::RecvNotifyDeviceChanged() {
nsCOMPtr<nsIObserverService> 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);

Просмотреть файл

@ -29,6 +29,8 @@ class CanvasChild final : public PCanvasChild {
explicit CanvasChild(Endpoint<PCanvasChild>&& aEndpoint);
ipc::IPCResult RecvNotifyDeviceChanged();
/**
* Ensures that the DrawEventRecorder has been created.
*

Просмотреть файл

@ -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<ID3D11Device> 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> 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<SurfaceDescriptor> descriptor = MakeUnique<SurfaceDescriptor>();

Просмотреть файл

@ -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<CanvasThreadHolder> mCanvasThreadHolder;
RefPtr<TaskQueue> mTranslationTaskQueue;
@ -281,6 +287,7 @@ class CanvasTranslator final : public gfx::InlineTranslator,
"CanvasTranslator::mSurfaceDescriptorsMonitor"};
bool mIsValid = true;
bool mIsInTransaction = false;
bool mDeviceResetInProgress = false;
};
} // namespace layers

Просмотреть файл

@ -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