Bug 998916 - check the visibility change event to defer the webgl context restore. r=jgilbert,smaug

This commit is contained in:
Jerry Shih 2014-06-23 01:16:00 +02:00
parent 1cf0c94a29
commit 03ac645b5b
6 changed files with 175 additions and 50 deletions

View File

@ -52,6 +52,7 @@
#include "mozilla/Telemetry.h"
#include "nsIObserverService.h"
#include "nsIDOMEvent.h"
#include "mozilla/Services.h"
#include "mozilla/dom/WebGLRenderingContextBinding.h"
#include "mozilla/dom/BindingUtils.h"
@ -71,24 +72,111 @@ using namespace mozilla::gfx;
using namespace mozilla::gl;
using namespace mozilla::layers;
NS_IMETHODIMP
WebGLMemoryPressureObserver::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aSomeData)
WebGLObserver::WebGLObserver(WebGLContext* aContext)
: mContext(aContext)
{
if (strcmp(aTopic, "memory-pressure"))
return NS_OK;
}
bool wantToLoseContext = true;
WebGLObserver::~WebGLObserver()
{
}
void
WebGLObserver::Destroy()
{
UnregisterMemoryPressureEvent();
UnregisterVisibilityChangeEvent();
mContext = nullptr;
}
void
WebGLObserver::RegisterVisibilityChangeEvent()
{
if (!mContext) {
return;
}
HTMLCanvasElement* canvasElement = mContext->GetCanvas();
MOZ_ASSERT(canvasElement);
if (canvasElement) {
nsIDocument* document = canvasElement->OwnerDoc();
document->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
this,
true,
false);
}
}
void
WebGLObserver::UnregisterVisibilityChangeEvent()
{
if (!mContext) {
return;
}
HTMLCanvasElement* canvasElement = mContext->GetCanvas();
if (canvasElement) {
nsIDocument* document = canvasElement->OwnerDoc();
document->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
this,
true);
}
}
void
WebGLObserver::RegisterMemoryPressureEvent()
{
if (!mContext) {
return;
}
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
MOZ_ASSERT(observerService);
if (observerService) {
observerService->AddObserver(this, "memory-pressure", false);
}
}
void
WebGLObserver::UnregisterMemoryPressureEvent()
{
if (!mContext) {
return;
}
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
MOZ_ASSERT(observerService);
if (observerService) {
observerService->RemoveObserver(this, "memory-pressure");
}
}
NS_IMETHODIMP
WebGLObserver::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aSomeData)
{
if (!mContext || strcmp(aTopic, "memory-pressure")) {
return NS_OK;
}
bool wantToLoseContext = mContext->mLoseContextOnMemoryPressure;
if (!mContext->mCanLoseContextInForeground &&
ProcessPriorityManager::CurrentProcessIsForeground())
{
wantToLoseContext = false;
} else if (!nsCRT::strcmp(aSomeData,
MOZ_UTF16("heap-minimize")))
{
wantToLoseContext = mContext->mLoseContextOnHeapMinimize;
}
if (wantToLoseContext) {
@ -98,6 +186,26 @@ WebGLMemoryPressureObserver::Observe(nsISupports* aSubject,
return NS_OK;
}
NS_IMETHODIMP
WebGLObserver::HandleEvent(nsIDOMEvent* aEvent)
{
nsAutoString type;
aEvent->GetType(type);
if (!mContext || !type.EqualsLiteral("visibilitychange")) {
return NS_OK;
}
HTMLCanvasElement* canvasElement = mContext->GetCanvas();
MOZ_ASSERT(canvasElement);
if (canvasElement && !canvasElement->OwnerDoc()->Hidden()) {
mContext->ForceRestoreContext();
}
return NS_OK;
}
WebGLContextOptions::WebGLContextOptions()
: alpha(true), depth(true), stencil(false),
premultipliedAlpha(true), antialias(true),
@ -177,8 +285,9 @@ WebGLContext::WebGLContext()
mRunContextLossTimerAgain = false;
mContextRestorer = do_CreateInstance("@mozilla.org/timer;1");
mContextStatus = ContextNotLost;
mLoseContextOnHeapMinimize = false;
mLoseContextOnMemoryPressure = false;
mCanLoseContextInForeground = true;
mRestoreWhenVisible = false;
mAlreadyGeneratedWarnings = 0;
mAlreadyWarnedAboutFakeVertexAttrib0 = false;
@ -190,6 +299,9 @@ WebGLContext::WebGLContext()
mMaxWarnings = 0;
}
mContextObserver = new WebGLObserver(this);
MOZ_RELEASE_ASSERT(mContextObserver, "Can't alloc WebGLContextObserver");
mLastUseIndex = 0;
InvalidateBufferFetching();
@ -203,6 +315,8 @@ WebGLContext::WebGLContext()
WebGLContext::~WebGLContext()
{
mContextObserver->Destroy();
DestroyResourcesAndContext();
WebGLMemoryTracker::RemoveWebGLContext(this);
TerminateContextLossTimer();
@ -212,15 +326,7 @@ WebGLContext::~WebGLContext()
void
WebGLContext::DestroyResourcesAndContext()
{
if (mMemoryPressureObserver) {
nsCOMPtr<nsIObserverService> observerService
= mozilla::services::GetObserverService();
if (observerService) {
observerService->RemoveObserver(mMemoryPressureObserver,
"memory-pressure");
}
mMemoryPressureObserver = nullptr;
}
mContextObserver->UnregisterMemoryPressureEvent();
if (!gl)
return;
@ -1281,6 +1387,10 @@ WebGLContext::UpdateContextLossStatus()
if (mLastLossWasSimulated)
return;
// Restore when the app is visible
if (mRestoreWhenVisible)
return;
ForceRestoreContext();
return;
}
@ -1314,16 +1424,22 @@ WebGLContext::UpdateContextLossStatus()
}
void
WebGLContext::ForceLoseContext()
WebGLContext::ForceLoseContext(bool simulateLosing)
{
printf_stderr("WebGL(%p)::ForceLoseContext\n", this);
MOZ_ASSERT(!IsContextLost());
mContextStatus = ContextLostAwaitingEvent;
mContextLostErrorSet = false;
mLastLossWasSimulated = false;
// Burn it all!
DestroyResourcesAndContext();
mLastLossWasSimulated = simulateLosing;
// Register visibility change observer to defer the context restoring.
// Restore the context when the app is visible.
if (mRestoreWhenVisible && !mLastLossWasSimulated) {
mContextObserver->RegisterVisibilityChangeEvent();
}
// Queue up a task, since we know the status changed.
EnqueueUpdateContextLossStatus();
@ -1336,6 +1452,8 @@ WebGLContext::ForceRestoreContext()
mContextStatus = ContextLostAwaitingRestore;
mAllowContextRestore = true; // Hey, you did say 'force'.
mContextObserver->UnregisterVisibilityChangeEvent();
// Queue up a task, since we know the status changed.
EnqueueUpdateContextLossStatus();
}

View File

@ -21,6 +21,7 @@
#include "mozilla/dom/HTMLCanvasElement.h"
#include "nsWrapperCache.h"
#include "nsIObserver.h"
#include "nsIDOMEventListener.h"
#include "nsLayoutUtils.h"
#include "GLContextProvider.h"
@ -60,7 +61,7 @@ class nsIDocShell;
namespace mozilla {
class WebGLMemoryPressureObserver;
class WebGLObserver;
class WebGLContextBoundObject;
class WebGLActiveInfo;
class WebGLExtensionBase;
@ -146,7 +147,7 @@ class WebGLContext :
friend class WebGLExtensionDrawBuffers;
friend class WebGLExtensionLoseContext;
friend class WebGLExtensionVertexArray;
friend class WebGLMemoryPressureObserver;
friend class WebGLObserver;
friend class WebGLMemoryTracker;
enum {
@ -917,8 +918,9 @@ protected:
bool mMinCapability;
bool mDisableExtensions;
bool mIsMesa;
bool mLoseContextOnHeapMinimize;
bool mLoseContextOnMemoryPressure;
bool mCanLoseContextInForeground;
bool mRestoreWhenVisible;
bool mShouldPresent;
bool mBackbufferNeedsClear;
bool mDisableFragHighP;
@ -1162,7 +1164,7 @@ protected:
GLenum type,
const GLvoid *data);
void ForceLoseContext();
void ForceLoseContext(bool simulateLosing = false);
void ForceRestoreContext();
nsTArray<WebGLRefPtr<WebGLTexture> > mBound2DTextures;
@ -1273,7 +1275,7 @@ protected:
ForceDiscreteGPUHelperCGL mForceDiscreteGPUHelper;
#endif
nsRefPtr<WebGLMemoryPressureObserver> mMemoryPressureObserver;
nsRefPtr<WebGLObserver> mContextObserver;
public:
// console logging helpers
@ -1371,19 +1373,29 @@ WebGLContext::ValidateObject(const char* info, ObjectType *aObject)
return ValidateObjectAssumeNonNull(info, aObject);
}
class WebGLMemoryPressureObserver MOZ_FINAL
// Listen visibilitychange and memory-pressure event for context lose/restore
class WebGLObserver MOZ_FINAL
: public nsIObserver
, public nsIDOMEventListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
NS_DECL_NSIDOMEVENTLISTENER
WebGLMemoryPressureObserver(WebGLContext *context)
: mContext(context)
{}
WebGLObserver(WebGLContext* aContext);
~WebGLObserver();
void Destroy();
void RegisterVisibilityChangeEvent();
void UnregisterVisibilityChangeEvent();
void RegisterMemoryPressureEvent();
void UnregisterMemoryPressureEvent();
private:
WebGLContext *mContext;
WebGLContext* mContext;
};
} // namespace mozilla

View File

@ -3878,8 +3878,7 @@ WebGLContext::LoseContext()
if (IsContextLost())
return ErrorInvalidOperation("loseContext: Context is already lost.");
ForceLoseContext();
mLastLossWasSimulated = true;
ForceLoseContext(true);
}
void

View File

@ -8,7 +8,7 @@
using namespace mozilla;
NS_IMPL_ISUPPORTS(WebGLMemoryPressureObserver, nsIObserver)
NS_IMPL_ISUPPORTS(WebGLObserver, nsIObserver)
NS_IMETHODIMP
WebGLMemoryTracker::CollectReports(nsIHandleReportCallback* aHandleReport,

View File

@ -1553,8 +1553,9 @@ WebGLContext::InitAndValidateGL()
mMinCapability = Preferences::GetBool("webgl.min_capability_mode", false);
mDisableExtensions = Preferences::GetBool("webgl.disable-extensions", false);
mLoseContextOnHeapMinimize = Preferences::GetBool("webgl.lose-context-on-heap-minimize", false);
mLoseContextOnMemoryPressure = Preferences::GetBool("webgl.lose-context-on-memory-preasure", false);
mCanLoseContextInForeground = Preferences::GetBool("webgl.can-lose-context-in-foreground", true);
mRestoreWhenVisible = Preferences::GetBool("webgl.restore-context-when-visible", true);
if (MinCapabilityMode()) {
mDisableFragHighP = true;
@ -1778,19 +1779,13 @@ WebGLContext::InitAndValidateGL()
VertexAttrib4f(index, 0, 0, 0, 1);
}
mMemoryPressureObserver
= new WebGLMemoryPressureObserver(this);
nsCOMPtr<nsIObserverService> observerService
= mozilla::services::GetObserverService();
if (observerService) {
observerService->AddObserver(mMemoryPressureObserver,
"memory-pressure",
false);
}
mDefaultVertexArray = WebGLVertexArray::Create(this);
mDefaultVertexArray->mAttribs.SetLength(mGLMaxVertexAttribs);
mBoundVertexArray = mDefaultVertexArray;
if (mLoseContextOnMemoryPressure) {
mContextObserver->RegisterMemoryPressureEvent();
}
return true;
}

View File

@ -3716,8 +3716,9 @@ pref("webgl.msaa-force", false);
pref("webgl.prefer-16bpp", false);
pref("webgl.default-no-alpha", false);
pref("webgl.force-layers-readback", false);
pref("webgl.lose-context-on-heap-minimize", false);
pref("webgl.lose-context-on-memory-preasure", false);
pref("webgl.can-lose-context-in-foreground", true);
pref("webgl.restore-context-when-visible", true);
pref("webgl.max-warnings-per-context", 32);
pref("webgl.enable-draft-extensions", false);
pref("webgl.enable-privileged-extensions", false);