Bug 710163: fix EXT_lose_context semantics r=bjacob

The EXT_lose_context extension spec has had updates from Khronos which break our
current implementation. Primarily, it is mostly asynchronous now with more
heavily defined behavior.

NOTE: This patch will not pass on our current copy of context-lost.html and
context-lost-restored.html see bug for more info.
This commit is contained in:
Doug Sherk 2012-01-04 16:12:03 -05:00
parent 0824cb64ed
commit f0ca08618c
3 changed files with 134 additions and 48 deletions

View File

@ -292,11 +292,13 @@ WebGLContext::WebGLContext()
WebGLMemoryReporter::AddWebGLContext(this);
mContextLost = false;
mAllowRestore = false;
mAllowRestore = true;
mRobustnessTimerRunning = false;
mDrawSinceRobustnessTimerSet = false;
mContextRestorer = do_CreateInstance("@mozilla.org/timer;1");
mContextStatus = ContextStable;
mContextLostErrorSet = false;
mContextLostDueToTest = false;
}
WebGLContext::~WebGLContext()
@ -948,7 +950,7 @@ bool WebGLContext::IsExtensionSupported(WebGLExtensionID ei)
// We always support this extension.
isSupported = true;
break;
case WebGL_WEBGL_EXT_lose_context:
case WebGL_MOZ_WEBGL_lose_context:
// We always support this extension.
isSupported = true;
break;
@ -980,9 +982,9 @@ WebGLContext::GetExtension(const nsAString& aName, nsIWebGLExtension **retval)
if (IsExtensionSupported(WebGL_OES_standard_derivatives))
ei = WebGL_OES_standard_derivatives;
}
else if (aName.EqualsLiteral("WEBGL_EXT_lose_context")) {
if (IsExtensionSupported(WebGL_WEBGL_EXT_lose_context))
ei = WebGL_WEBGL_EXT_lose_context;
else if (aName.EqualsLiteral("MOZ_WEBGL_lose_context")) {
if (IsExtensionSupported(WebGL_MOZ_WEBGL_lose_context))
ei = WebGL_MOZ_WEBGL_lose_context;
}
if (ei != WebGLExtensionID_Max) {
@ -991,7 +993,7 @@ WebGLContext::GetExtension(const nsAString& aName, nsIWebGLExtension **retval)
case WebGL_OES_standard_derivatives:
mEnabledExtensions[ei] = new WebGLExtensionStandardDerivatives(this);
break;
case WebGL_WEBGL_EXT_lose_context:
case WebGL_MOZ_WEBGL_lose_context:
mEnabledExtensions[ei] = new WebGLExtensionLoseContext(this);
break;
// create an extension for any types that don't
@ -1097,10 +1099,69 @@ WebGLContext::EnsureBackbufferClearedAsNeeded()
Invalidate();
}
// We use this timer for many things. Here are the things that it is activated for:
// 1) If a script is using the MOZ_WEBGL_lose_context extension.
// 2) If we are using EGL and _NOT ANGLE_, we query periodically to see if the
// CONTEXT_LOST_WEBGL error has been triggered.
// 3) If we are using ANGLE, or anything that supports ARB_robustness, query the
// GPU periodically to see if the reset status bit has been set.
// In all of these situations, we use this timer to send the script context lost
// and restored events asynchronously. For example, if it triggers a context loss,
// the webglcontextlost event will be sent to it the next time the robustness timer
// fires.
// Note that this timer mechanism is not used unless one of these 3 criteria
// are met.
// At a bare minimum, from context lost to context restores, it would take 3
// full timer iterations: detection, webglcontextlost, webglcontextrestored.
NS_IMETHODIMP
WebGLContext::Notify(nsITimer* timer)
{
TerminateRobustnessTimer();
// If the context has been lost and we're waiting for it to be restored, do
// that now.
if (mContextStatus == ContextLostAwaitingEvent) {
bool defaultAction;
nsContentUtils::DispatchTrustedEvent(HTMLCanvasElement()->OwnerDoc(),
(nsIDOMHTMLCanvasElement*) HTMLCanvasElement(),
NS_LITERAL_STRING("webglcontextlost"),
PR_TRUE,
PR_TRUE,
&defaultAction);
// If the script didn't handle the event, we don't allow restores.
if (defaultAction)
mAllowRestore = false;
// If the script handled the event and we are allowing restores, then
// mark it to be restored. Otherwise, leave it as context lost
// (unusable).
if (!defaultAction && mAllowRestore) {
ForceRestoreContext();
// Restart the timer so that it will be restored on the next
// callback.
SetupRobustnessTimer();
} else {
mContextStatus = ContextLost;
}
} else if (mContextStatus == ContextLostAwaitingRestore) {
// Try to restore the context. If it fails, try again later.
if (NS_FAILED(SetDimensions(mWidth, mHeight))) {
SetupRobustnessTimer();
return NS_OK;
}
mContextStatus = ContextStable;
nsContentUtils::DispatchTrustedEvent(HTMLCanvasElement()->OwnerDoc(),
(nsIDOMHTMLCanvasElement*) HTMLCanvasElement(),
NS_LITERAL_STRING("webglcontextrestored"),
PR_TRUE,
PR_TRUE);
// Set all flags back to the state they were in before the context was
// lost.
mContextLostErrorSet = false;
mContextLostDueToTest = false;
mAllowRestore = true;
}
MaybeRestoreContext();
return NS_OK;
}
@ -1108,18 +1169,25 @@ WebGLContext::Notify(nsITimer* timer)
void
WebGLContext::MaybeRestoreContext()
{
if (mContextLost || mAllowRestore)
// Don't try to handle it if we already know it's busted.
if (mContextStatus != ContextStable || gl == nsnull)
return;
bool isEGL = gl->GetContextType() == GLContext::ContextTypeEGL,
isANGLE = gl->IsANGLE();
// If was lost due to a forced context loss, don't try to handle it.
// Also, we also don't try to handle if if we don't have robustness.
// Note that the code in this function is used only for situations where
// we have an actual context loss, and not a simulated one.
if (mContextLostDueToTest ||
(!mHasRobustness && !isEGL))
return;
GLContext::ContextResetARB resetStatus = GLContext::CONTEXT_NO_ERROR;
if (mHasRobustness) {
gl->MakeCurrent();
resetStatus = (GLContext::ContextResetARB) gl->fGetGraphicsResetStatus();
// This call is safe as it does not actually interact with GL, so the
// context does not have to be current.
} else if (isEGL) {
// Simulate a ARB_robustness guilty context loss for when we
// get an EGL_CONTEXT_LOST error. It may not actually be guilty,
@ -1143,10 +1211,11 @@ WebGLContext::MaybeRestoreContext()
// run it again some time later.
if (mDrawSinceRobustnessTimerSet)
SetupRobustnessTimer();
return;
break;
case GLContext::CONTEXT_GUILTY_CONTEXT_RESET_ARB:
NS_WARNING("WebGL content on the page caused the graphics card to reset; not restoring the context");
return;
mAllowRestore = false;
break;
case GLContext::CONTEXT_INNOCENT_CONTEXT_RESET_ARB:
break;
case GLContext::CONTEXT_UNKNOWN_CONTEXT_RESET_ARB:
@ -1156,45 +1225,25 @@ WebGLContext::MaybeRestoreContext()
// This means that we can't restore it or risk restoring a guilty context. Should this ever change,
// we can get rid of the whole IsANGLE() junk from GLContext.h since, as of writing, this is the
// only use for it. See ANGLE issue 261.
return;
mAllowRestore = false;
}
break;
}
ForceRestoreContext();
}
void
WebGLContext::ForceLoseContext()
{
TerminateRobustnessTimer();
mWebGLError = LOCAL_GL_CONTEXT_LOST;
bool defaultAction;
mContextLost = true;
mContextStatus = ContextLostAwaitingEvent;
// Queue up a task to restore the event.
SetupRobustnessTimer();
DestroyResourcesAndContext();
nsContentUtils::DispatchTrustedEvent(HTMLCanvasElement()->OwnerDoc(),
(nsIDOMHTMLCanvasElement*) HTMLCanvasElement(),
NS_LITERAL_STRING("webglcontextlost"),
PR_TRUE,
PR_TRUE,
&defaultAction);
if (defaultAction)
mAllowRestore = false;
}
void
WebGLContext::ForceRestoreContext()
{
mContextLost = false;
mAllowRestore = false;
SetDimensions(mHeight, mWidth);
nsContentUtils::DispatchTrustedEvent(HTMLCanvasElement()->OwnerDoc(),
(nsIDOMHTMLCanvasElement*) HTMLCanvasElement(),
NS_LITERAL_STRING("webglcontextrestored"),
PR_TRUE,
PR_TRUE);
mContextStatus = ContextLostAwaitingRestore;
}
//
@ -1471,8 +1520,8 @@ WebGLContext::GetSupportedExtensions(nsIVariant **retval)
extList.InsertElementAt(extList.Length(), "OES_texture_float");
if (IsExtensionSupported(WebGL_OES_standard_derivatives))
extList.InsertElementAt(extList.Length(), "OES_standard_derivatives");
if (IsExtensionSupported(WebGL_WEBGL_EXT_lose_context))
extList.InsertElementAt(extList.Length(), "WEBGL_EXT_lose_context");
if (IsExtensionSupported(WebGL_MOZ_WEBGL_lose_context))
extList.InsertElementAt(extList.Length(), "MOZ_WEBGL_lose_context");
nsresult rv;
if (extList.Length() > 0) {
@ -1491,7 +1540,13 @@ WebGLContext::GetSupportedExtensions(nsIVariant **retval)
NS_IMETHODIMP
WebGLContext::IsContextLost(WebGLboolean *retval)
{
*retval = mContextLost;
*retval = mContextStatus != ContextStable;
return NS_OK;
}
// Internalized version of IsContextLost.
bool
WebGLContext::IsContextStable()
{
return mContextStatus == ContextStable;
}

View File

@ -575,10 +575,17 @@ public:
return mMinCapability;
}
// See the comment over WebGLContext::Notify() for more information on this.
bool ShouldEnableRobustnessTimer() {
return mHasRobustness ||
IsExtensionEnabled(WebGL_MOZ_WEBGL_lose_context) ||
(gl != nsnull && gl->GetContextType() == gl::GLContext::ContextTypeEGL);
}
// Sets up the GL_ARB_robustness timer if it isn't already, so that if the
// driver gets restarted, the context may get reset with it.
void SetupRobustnessTimer() {
if (mContextLost || (!mHasRobustness && gl->GetContextType() != gl::GLContext::ContextTypeEGL))
if (!ShouldEnableRobustnessTimer())
return;
// If the timer was already running, don't restart it here. Instead,
@ -668,11 +675,30 @@ protected:
PRInt32 mGLMaxFragmentUniformVectors;
PRInt32 mGLMaxVertexUniformVectors;
// Represents current status, or state, of the context. That is, is it lost
// or stable and what part of the context lost process are we currently at.
// This is used to support the WebGL spec's asyncronous nature in handling
// context loss.
enum ContextStatus {
// The context is stable; there either are none or we don't know of any.
ContextStable,
// The context has been lost, but we have not yet sent an event to the
// script informing it of this.
ContextLostAwaitingEvent,
// The context has been lost, and we have sent the script an event
// informing it of this.
ContextLost,
// The context is lost, an event has been sent to the script, and the
// script correctly handled the event. We are waiting for the context to
// be restored.
ContextLostAwaitingRestore
};
// extensions
enum WebGLExtensionID {
WebGL_OES_texture_float,
WebGL_OES_standard_derivatives,
WebGL_WEBGL_EXT_lose_context,
WebGL_MOZ_WEBGL_lose_context,
WebGLExtensionID_Max
};
nsCOMPtr<WebGLExtension> mEnabledExtensions[WebGLExtensionID_Max];
@ -805,6 +831,7 @@ protected:
const GLvoid *data);
void MaybeRestoreContext();
bool IsContextStable();
void ForceLoseContext();
void ForceRestoreContext();
@ -861,10 +888,12 @@ protected:
int mBackbufferClearingStatus;
nsCOMPtr<nsITimer> mContextRestorer;
bool mContextLost;
bool mAllowRestore;
bool mRobustnessTimerRunning;
bool mDrawSinceRobustnessTimerSet;
ContextStatus mContextStatus;
bool mContextLostErrorSet;
bool mContextLostDueToTest;
public:
// console logging helpers

View File

@ -2533,9 +2533,12 @@ WebGLContext::CreateTexture(nsIWebGLTexture **retval)
NS_IMETHODIMP
WebGLContext::GetError(WebGLenum *_retval)
{
if (!mContextLost) {
if (mContextStatus == ContextStable) {
MakeContextCurrent();
UpdateWebGLErrorAndClearGLError();
} else if (!mContextLostErrorSet) {
mWebGLError = LOCAL_GL_CONTEXT_LOST;
mContextLostErrorSet = true;
}
*_retval = mWebGLError;
@ -5145,11 +5148,10 @@ WebGLContext::TexSubImage2D_dom(WebGLenum target, WebGLint level,
bool
WebGLContext::LoseContext()
{
if (mContextLost) {
if (mContextLost)
return false;
}
mAllowRestore = true;
mContextLostDueToTest = true;
ForceLoseContext();
return true;
@ -5158,7 +5160,7 @@ WebGLContext::LoseContext()
bool
WebGLContext::RestoreContext()
{
if (!mContextLost || !mAllowRestore) {
if (IsContextStable() || !mAllowRestore) {
return false;
}