r?jrmuizel - Record context creation failure reason.
draft
r?jrmuizel - Record context creation failure reason.
From 7a6961b658c5d0affce6b69d3259e725220df7f0 Mon Sep 17 00:00:00 2001
---
dom/canvas/WebGL2Context.cpp | 17 ++++--
dom/canvas/WebGLContext.cpp | 109 ++++++++++++++++++++++++++----------
dom/canvas/WebGLContext.h | 13 +++--
dom/canvas/WebGLContextValidate.cpp | 35 +++++++-----
4 files changed, 119 insertions(+), 55 deletions(-)
MozReview-Commit-ID: F4Uo5EUIPRf
--- a/dom/canvas/WebGL2Context.cpp
+++ b/dom/canvas/WebGL2Context.cpp
@@ -92,53 +92,58 @@ static const gl::GLFeature kRequiredFeat
gl::GLFeature::texture_storage,
gl::GLFeature::transform_feedback2,
gl::GLFeature::uniform_buffer_object,
gl::GLFeature::uniform_matrix_nonsquare,
gl::GLFeature::vertex_array_object
};
bool
-WebGLContext::InitWebGL2()
+WebGLContext::InitWebGL2(nsACString* const out_failReason)
{
MOZ_ASSERT(IsWebGL2(), "WebGLContext is not a WebGL 2 context!");
// check OpenGL features
if (!gl->IsSupported(gl::GLFeature::occlusion_query) &&
!gl->IsSupported(gl::GLFeature::occlusion_query_boolean))
{
// On desktop, we fake occlusion_query_boolean with occlusion_query if
// necessary. (See WebGL2ContextQueries.cpp)
- GenerateWarning("WebGL 2 unavailable. Requires occlusion queries.");
+ out_failReason->AssignASCII("WebGL 2 requires occlusion query support.");
return false;
}
std::vector<gl::GLFeature> missingList;
for (size_t i = 0; i < ArrayLength(kRequiredFeatures); i++) {
- if (!gl->IsSupported(kRequiredFeatures[i]))
+ if (!gl->IsSupported(kRequiredFeatures[i])) {
missingList.push_back(kRequiredFeatures[i]);
+ }
}
#ifdef XP_MACOSX
// On OSX, GL core profile is used. This requires texture swizzle
// support to emulate legacy texture formats: ALPHA, LUMINANCE,
// and LUMINANCE_ALPHA.
- if (!gl->IsSupported(gl::GLFeature::texture_swizzle))
+ if (!gl->IsSupported(gl::GLFeature::texture_swizzle)) {
missingList.push_back(gl::GLFeature::texture_swizzle);
+ }
#endif
if (missingList.size()) {
nsAutoCString exts;
for (auto itr = missingList.begin(); itr != missingList.end(); ++itr) {
exts.AppendLiteral("\n ");
exts.Append(gl::GLContext::GetFeatureName(*itr));
}
- GenerateWarning("WebGL 2 unavailable. The following required features are"
- " unavailible: %s", exts.BeginReading());
+
+ const nsPrintfCString reason("WebGL 2 requires support for the following"
+ " features: %s",
+ exts.BeginReading());
+ out_failReason->Assign(reason);
return false;
}
// we initialise WebGL 2 related stuff.
gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS,
&mGLMaxTransformFeedbackSeparateAttribs);
gl->GetUIntegerv(LOCAL_GL_MAX_UNIFORM_BUFFER_BINDINGS,
&mGLMaxUniformBufferBindings);
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -21,16 +21,17 @@
#include "ImageContainer.h"
#include "ImageEncoder.h"
#include "Layers.h"
#include "LayerUserData.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/HTMLVideoElement.h"
#include "mozilla/dom/ImageData.h"
+#include "mozilla/dom/WebGLContextEvent.h"
#include "mozilla/EnumeratedArrayCycleCollection.h"
#include "mozilla/Preferences.h"
#include "mozilla/ProcessPriorityManager.h"
#include "mozilla/Services.h"
#include "mozilla/Telemetry.h"
#include "nsContentUtils.h"
#include "nsDisplayList.h"
#include "nsError.h"
@@ -571,117 +572,128 @@ BaseCaps(const WebGLContextOptions& opti
return baseCaps;
}
////////////////////////////////////////
static already_AddRefed<gl::GLContext>
CreateGLWithEGL(const gl::SurfaceCaps& caps, gl::CreateContextFlags flags,
- WebGLContext* webgl)
+ WebGLContext* webgl, nsACString* const out_failReason)
{
const gfx::IntSize dummySize(16, 16);
RefPtr<GLContext> gl = gl::GLContextProviderEGL::CreateOffscreen(dummySize, caps,
flags);
-
if (gl && gl->IsANGLE()) {
gl = nullptr;
}
if (!gl) {
- webgl->GenerateWarning("Error during EGL OpenGL init.");
+ if (out_failReason->Length()) {
+ out_failReason->AppendLiteral("\n");
+ }
+ out_failReason->AppendLiteral("Error during EGL OpenGL init.");
return nullptr;
}
return gl.forget();
}
static already_AddRefed<GLContext>
CreateGLWithANGLE(const gl::SurfaceCaps& caps, gl::CreateContextFlags flags,
- WebGLContext* webgl)
+ WebGLContext* webgl, nsACString* const out_failReason)
{
const gfx::IntSize dummySize(16, 16);
RefPtr<GLContext> gl = gl::GLContextProviderEGL::CreateOffscreen(dummySize, caps,
flags);
-
if (gl && !gl->IsANGLE()) {
gl = nullptr;
}
if (!gl) {
- webgl->GenerateWarning("Error during ANGLE OpenGL init.");
+ if (out_failReason->Length()) {
+ out_failReason->AppendLiteral("\n");
+ }
+ out_failReason->AppendLiteral("Error during ANGLE OpenGL init.");
return nullptr;
}
return gl.forget();
}
static already_AddRefed<gl::GLContext>
CreateGLWithDefault(const gl::SurfaceCaps& caps, gl::CreateContextFlags flags,
- WebGLContext* webgl)
+ WebGLContext* webgl, nsACString* const out_failReason)
{
nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
if (!(flags & CreateContextFlags::FORCE_ENABLE_HARDWARE) &&
IsFeatureInBlacklist(gfxInfo, nsIGfxInfo::FEATURE_WEBGL_OPENGL))
{
- webgl->GenerateWarning("Refused to create native OpenGL context because of"
- " blacklisting.");
+ if (out_failReason->Length()) {
+ out_failReason->AppendASCII("\n");
+ }
+ out_failReason->AppendASCII("Refused to create native OpenGL context because of"
+ " blacklisting.");
return nullptr;
}
const gfx::IntSize dummySize(16, 16);
RefPtr<GLContext> gl = gl::GLContextProvider::CreateOffscreen(dummySize, caps, flags);
if (gl && gl->IsANGLE()) {
gl = nullptr;
}
if (!gl) {
- webgl->GenerateWarning("Error during native OpenGL init.");
+ if (out_failReason->Length()) {
+ out_failReason->AppendASCII("\n");
+ }
+ out_failReason->AppendASCII("Error during native OpenGL init.");
return nullptr;
}
return gl.forget();
}
////////////////////////////////////////
bool
WebGLContext::CreateAndInitGLWith(FnCreateGL_T fnCreateGL,
const gl::SurfaceCaps& baseCaps,
- gl::CreateContextFlags flags)
+ gl::CreateContextFlags flags,
+ nsACString* const out_failReason)
{
std::queue<gl::SurfaceCaps> fallbackCaps;
PopulateCapFallbackQueue(baseCaps, &fallbackCaps);
MOZ_RELEASE_ASSERT(!gl);
gl = nullptr;
while (!fallbackCaps.empty()) {
gl::SurfaceCaps& caps = fallbackCaps.front();
- gl = fnCreateGL(caps, flags, this);
+ gl = fnCreateGL(caps, flags, this, out_failReason);
if (gl)
break;
fallbackCaps.pop();
}
if (!gl)
return false;
- if (!InitAndValidateGL()) {
+ if (!InitAndValidateGL(out_failReason)) {
gl = nullptr;
return false;
}
return true;
}
bool
-WebGLContext::CreateAndInitGL(bool forceEnabled)
+WebGLContext::CreateAndInitGL(bool forceEnabled, nsACString* const out_failReason)
{
const bool useEGL = PR_GetEnv("MOZ_WEBGL_PREFER_EGL");
bool useANGLE = false;
#ifdef XP_WIN
const bool disableANGLE = (gfxPrefs::WebGLDisableANGLE() ||
PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL"));
useANGLE = !disableANGLE;
@@ -690,22 +702,22 @@ WebGLContext::CreateAndInitGL(bool force
gl::CreateContextFlags flags = gl::CreateContextFlags::NONE;
if (forceEnabled) flags |= gl::CreateContextFlags::FORCE_ENABLE_HARDWARE;
if (!IsWebGL2()) flags |= gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE;
if (IsWebGL2()) flags |= gl::CreateContextFlags::PREFER_ES3;
const gl::SurfaceCaps baseCaps = BaseCaps(mOptions, this);
if (useEGL)
- return CreateAndInitGLWith(CreateGLWithEGL, baseCaps, flags);
+ return CreateAndInitGLWith(CreateGLWithEGL, baseCaps, flags, out_failReason);
if (useANGLE)
- return CreateAndInitGLWith(CreateGLWithANGLE, baseCaps, flags);
+ return CreateAndInitGLWith(CreateGLWithANGLE, baseCaps, flags, out_failReason);
- return CreateAndInitGLWith(CreateGLWithDefault, baseCaps, flags);
+ return CreateAndInitGLWith(CreateGLWithDefault, baseCaps, flags, out_failReason);
}
// Fallback for resizes:
bool
WebGLContext::ResizeBackbuffer(uint32_t requestedWidth,
uint32_t requestedHeight)
{
uint32_t width = requestedWidth;
@@ -740,16 +752,43 @@ WebGLContext::ResizeBackbuffer(uint32_t
GenerateWarning("Requested size %dx%d was too large, but resize"
" to %dx%d succeeded.",
requestedWidth, requestedHeight,
width, height);
}
return true;
}
+void
+WebGLContext::ThrowEvent_WebGLContextCreationError(const nsACString& text)
+{
+ RefPtr<EventTarget> target = mCanvasElement;
+ if (!target) {
+ target = mOffscreenCanvas;
+ }
+
+ const auto kEventName = NS_LITERAL_STRING("webglcontextcreationerror");
+
+ WebGLContextEventInit eventInit;
+ // eventInit.mCancelable = true; // The spec says this, but it's silly.
+ eventInit.mStatusMessage = NS_ConvertASCIItoUTF16(text);
+
+ const RefPtr<WebGLContextEvent> event = WebGLContextEvent::Constructor(target,
+ kEventName,
+ eventInit);
+ event->SetTrusted(true);
+
+ bool didPreventDefault;
+ target->DispatchEvent(event, &didPreventDefault);
+
+ //////
+
+ GenerateWarning("Failed to create WebGL context: %s", text.BeginReading());
+}
+
NS_IMETHODIMP
WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight)
{
if (signedWidth < 0 || signedHeight < 0) {
GenerateWarning("Canvas size is too large (seems like a negative value wrapped)");
return NS_ERROR_OUT_OF_MEMORY;
}
@@ -819,63 +858,71 @@ WebGLContext::SetDimensions(int32_t sign
// generation is not 0 right now (that is, if this isn't the first
// context we're creating), we may have to dispatch a context lost
// event.
// If incrementing the generation would cause overflow,
// don't allow it. Allowing this would allow us to use
// resource handles created from older context generations.
if (!(mGeneration + 1).isValid()) {
- GenerateWarning("Too many WebGL contexts created this run.");
- return NS_ERROR_FAILURE; // exit without changing the value of mGeneration
+ // exit without changing the value of mGeneration
+ const nsLiteralCString text("Too many WebGL contexts created this run.");
+ ThrowEvent_WebGLContextCreationError(text);
+ return NS_ERROR_FAILURE;
}
// increment the generation number - Do this early because later
// in CreateOffscreenGL(), "default" objects are created that will
// pick up the old generation.
++mGeneration;
bool disabled = gfxPrefs::WebGLDisabled();
// TODO: When we have software webgl support we should use that instead.
disabled |= gfxPlatform::InSafeMode();
if (disabled) {
- GenerateWarning("WebGL creation is disabled, and so disallowed here.");
+ const nsLiteralCString text("WebGL is currently disabled.");
+ ThrowEvent_WebGLContextCreationError(text);
return NS_ERROR_FAILURE;
}
bool failIfPerfCaveat = mOptions.failIfMajorPerformanceCaveat;
if (gfxPrefs::WebGLDisableFailIfMajorPerformanceCaveat())
failIfPerfCaveat = false;
if (failIfPerfCaveat) {
nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
if (!HasAcceleratedLayers(gfxInfo)) {
- GenerateWarning("failIfMajorPerformanceCaveat: Compositor is not"
- " hardware-accelerated.");
+ const nsLiteralCString text("failIfMajorPerformanceCaveat: Compositor is not"
+ " hardware-accelerated.");
+ ThrowEvent_WebGLContextCreationError(text);
return NS_ERROR_FAILURE;
}
}
// Alright, now let's start trying.
bool forceEnabled = gfxPrefs::WebGLForceEnabled();
ScopedGfxFeatureReporter reporter("WebGL", forceEnabled);
MOZ_ASSERT(!gl);
- if (!CreateAndInitGL(forceEnabled)) {
- GenerateWarning("WebGL creation failed.");
+ nsCString failReason;
+ if (!CreateAndInitGL(forceEnabled, &failReason)) {
+ const nsPrintfCString text("WebGL creation failed: %s",
+ failReason.BeginReading());
+ ThrowEvent_WebGLContextCreationError(text);
return NS_ERROR_FAILURE;
}
MOZ_ASSERT(gl);
MOZ_ASSERT_IF(mOptions.alpha, gl->Caps().alpha);
if (!ResizeBackbuffer(width, height)) {
- GenerateWarning("Initializing WebGL backbuffer failed.");
+ const nsLiteralCString text("Initializing WebGL backbuffer failed.");
+ ThrowEvent_WebGLContextCreationError(text);
return NS_ERROR_FAILURE;
}
#ifdef DEBUG
if (gl->DebugMode())
printf_stderr("--- WebGL context created: %p\n", gl.get());
#endif
@@ -1602,31 +1649,33 @@ WebGLContext::UpdateContextLossStatus()
}
// Fall through.
}
if (mContextStatus == ContextLostAwaitingEvent) {
// The context has been lost and we haven't yet triggered the
// callback, so do that now.
-
+ const auto kEventName = NS_LITERAL_STRING("webglcontextlost");
+ const bool kCanBubble = true;
+ const bool kIsCancelable = true;
bool useDefaultHandler;
if (mCanvasElement) {
nsContentUtils::DispatchTrustedEvent(
mCanvasElement->OwnerDoc(),
static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
- NS_LITERAL_STRING("webglcontextlost"),
- true,
- true,
+ kEventName,
+ kCanBubble,
+ kIsCancelable,
&useDefaultHandler);
} else {
// OffscreenCanvas case
RefPtr<Event> event = new Event(mOffscreenCanvas, nullptr, nullptr);
- event->InitEvent(NS_LITERAL_STRING("webglcontextlost"), true, true);
+ event->InitEvent(kEventName, kCanBubble, kIsCancelable);
event->SetTrusted(true);
mOffscreenCanvas->DispatchEvent(event, &useDefaultHandler);
}
// We sent the callback, so we're just 'regular lost' now.
mContextStatus = ContextLost;
// If we're told to use the default handler, it means the script
// didn't bother to handle the event. In this case, we shouldn't
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -1196,31 +1196,34 @@ protected:
nsTArray<GLenum> mCompressedTextureFormats;
// -------------------------------------------------------------------------
// WebGL 2 specifics (implemented in WebGL2Context.cpp)
public:
virtual bool IsWebGL2() const = 0;
protected:
- bool InitWebGL2();
+ bool InitWebGL2(nsACString* const out_failReason);
- bool CreateAndInitGL(bool forceEnabled);
+ bool CreateAndInitGL(bool forceEnabled, nsACString* const out_failReason);
bool ResizeBackbuffer(uint32_t width, uint32_t height);
typedef already_AddRefed<gl::GLContext> FnCreateGL_T(const gl::SurfaceCaps& caps,
gl::CreateContextFlags flags,
- WebGLContext* webgl);
+ WebGLContext* webgl,
+ nsACString* const out_failReason);
bool CreateAndInitGLWith(FnCreateGL_T fnCreateGL, const gl::SurfaceCaps& baseCaps,
- gl::CreateContextFlags flags);
+ gl::CreateContextFlags flags,
+ nsACString* const out_failReason);
+ void ThrowEvent_WebGLContextCreationError(const nsACString& text);
// -------------------------------------------------------------------------
// Validation functions (implemented in WebGLContextValidate.cpp)
- bool InitAndValidateGL();
+ bool InitAndValidateGL(nsACString* const out_failReason);
bool ValidateBlendEquationEnum(GLenum cap, const char* info);
bool ValidateBlendFuncDstEnum(GLenum mode, const char* info);
bool ValidateBlendFuncSrcEnum(GLenum mode, const char* info);
bool ValidateBlendFuncEnumsCompatibility(GLenum sfactor, GLenum dfactor,
const char* info);
bool ValidateDataOffsetSize(WebGLintptr offset, WebGLsizeiptr size, WebGLsizeiptr bufferSize, const char* info);
bool ValidateDataRanges(WebGLintptr readOffset, WebGLintptr writeOffset, WebGLsizeiptr size, const char* info);
bool ValidateTextureTargetEnum(GLenum target, const char* info);
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -640,32 +640,35 @@ FloorPOT(int32_t x)
if (x < pot*2)
break;
pot *= 2;
}
return pot;
}
bool
-WebGLContext::InitAndValidateGL()
+WebGLContext::InitAndValidateGL(nsACString* const out_failReason)
{
- if (!gl)
- return false;
+ MOZ_RELEASE_ASSERT(gl);
// Unconditionally create a new format usage authority. This is
// important when restoring contexts and extensions need to add
// formats back into the authority.
mFormatUsage = CreateFormatUsage(gl);
- if (!mFormatUsage)
+ if (!mFormatUsage) {
+ out_failReason->AssignLiteral("Failed to create mFormatUsage.");
return false;
+ }
GLenum error = gl->fGetError();
if (error != LOCAL_GL_NO_ERROR) {
- GenerateWarning("GL error 0x%x occurred during OpenGL context"
- " initialization, before WebGL initialization!", error);
+ const nsPrintfCString reason("GL error 0x%x occurred during OpenGL context"
+ " initialization, before WebGL initialization!",
+ error);
+ out_failReason->Assign(reason);
return false;
}
mMinCapability = gfxPrefs::WebGLMinCapabilityMode();
mDisableExtensions = gfxPrefs::WebGLDisableExtensions();
mLoseContextOnMemoryPressure = gfxPrefs::WebGLLoseContextOnMemoryPressure();
mCanLoseContextInForeground = gfxPrefs::WebGLCanLoseContextInForeground();
mRestoreWhenVisible = gfxPrefs::WebGLRestoreWhenVisible();
@@ -744,32 +747,34 @@ WebGLContext::InitAndValidateGL()
gl->fEnableVertexAttribArray(0);
if (MinCapabilityMode())
mGLMaxVertexAttribs = MINVALUE_GL_MAX_VERTEX_ATTRIBS;
else
gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_ATTRIBS, &mGLMaxVertexAttribs);
if (mGLMaxVertexAttribs < 8) {
- GenerateWarning("GL_MAX_VERTEX_ATTRIBS: %d is < 8!",
- mGLMaxVertexAttribs);
+ const nsPrintfCString reason("GL_MAX_VERTEX_ATTRIBS: %d is < 8!",
+ mGLMaxVertexAttribs);
+ out_failReason->Assign(reason);
return false;
}
// Note: GL_MAX_TEXTURE_UNITS is fixed at 4 for most desktop hardware,
// even though the hardware supports much more. The
// GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS value is the accurate value.
if (MinCapabilityMode())
mGLMaxTextureUnits = MINVALUE_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS;
else
gl->fGetIntegerv(LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &mGLMaxTextureUnits);
if (mGLMaxTextureUnits < 8) {
- GenerateWarning("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: %d is < 8!",
- mGLMaxTextureUnits);
+ const nsPrintfCString reason("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: %d is < 8!",
+ mGLMaxTextureUnits);
+ out_failReason->Assign(reason);
return false;
}
mBound2DTextures.SetLength(mGLMaxTextureUnits);
mBoundCubeMapTextures.SetLength(mGLMaxTextureUnits);
mBound3DTextures.SetLength(mGLMaxTextureUnits);
mBound2DArrayTextures.SetLength(mGLMaxTextureUnits);
mBoundSamplers.SetLength(mGLMaxTextureUnits);
@@ -915,37 +920,39 @@ WebGLContext::InitAndValidateGL()
gl->fEnable(LOCAL_GL_TEXTURE_CUBE_MAP_SEAMLESS);
}
// Check the shader validator pref
mBypassShaderValidation = gfxPrefs::WebGLBypassShaderValidator();
// initialize shader translator
if (!ShInitialize()) {
- GenerateWarning("GLSL translator initialization failed!");
+ out_failReason->AssignLiteral("GLSL translator initialization failed!");
return false;
}
// Mesa can only be detected with the GL_VERSION string, of the form
// "2.1 Mesa 7.11.0"
const char* versionStr = (const char*)(gl->fGetString(LOCAL_GL_VERSION));
mIsMesa = strstr(versionStr, "Mesa");
// Notice that the point of calling fGetError here is not only to check for
// errors, but also to reset the error flags so that a subsequent WebGL
// getError call will give the correct result.
error = gl->fGetError();
if (error != LOCAL_GL_NO_ERROR) {
- GenerateWarning("GL error 0x%x occurred during WebGL context"
- " initialization!", error);
+ const nsPrintfCString reason("GL error 0x%x occurred during WebGL context"
+ " initialization!",
+ error);
+ out_failReason->Assign(reason);
return false;
}
if (IsWebGL2() &&
- !InitWebGL2())
+ !InitWebGL2(&*out_failReason))
{
// Todo: Bug 898404: Only allow WebGL2 on GL>=3.0 on desktop GL.
return false;
}
// Default value for all disabled vertex attributes is [0, 0, 0, 1]
mVertexAttribType = MakeUnique<GLenum[]>(mGLMaxVertexAttribs);
for (int32_t index = 0; index < mGLMaxVertexAttribs; ++index) {