Bug 1272808 - Expose context creation error to telemetry. r=jgilbert
MozReview-Commit-ID: JQ4rHZf71RW
--- a/dom/canvas/WebGL2Context.cpp
+++ b/dom/canvas/WebGL2Context.cpp
@@ -91,26 +91,27 @@ 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(nsACString* const out_failReason)
+WebGLContext::InitWebGL2(nsACString* const out_failReason, nsACString* const out_failureId)
{
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)
+ *out_failureId = "FEATURE_FAILURE_WEBGL2_OCCL";
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])) {
@@ -129,16 +130,17 @@ WebGLContext::InitWebGL2(nsACString* con
if (missingList.size()) {
nsAutoCString exts;
for (auto itr = missingList.begin(); itr != missingList.end(); ++itr) {
exts.AppendLiteral("\n ");
exts.Append(gl::GLContext::GetFeatureName(*itr));
}
+ *out_failureId = "FEATURE_FAILURE_WEBGL2_FEATURE";
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.
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -426,22 +426,21 @@ WebGLContext::GetHeight() const
*
* That is, try to create headless contexts based on the platform API.
* Next, create dummy-sized backbuffers for the contexts with the right
* caps. Finally, resize the backbuffer to an acceptable size given the
* requested size.
*/
static bool
-IsFeatureInBlacklist(const nsCOMPtr<nsIGfxInfo>& gfxInfo, int32_t feature)
+IsFeatureInBlacklist(const nsCOMPtr<nsIGfxInfo>& gfxInfo, int32_t feature, nsACString* const out_failureId)
{
int32_t status;
- nsCString discardFailureId;
if (!NS_SUCCEEDED(gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo, feature,
- discardFailureId, &status)))
+ *out_failureId, &status)))
return false;
return status != nsIGfxInfo::FEATURE_STATUS_OK;
}
static bool
HasAcceleratedLayers(const nsCOMPtr<nsIGfxInfo>& gfxInfo)
{
@@ -557,81 +556,87 @@ BaseCaps(const WebGLContextOptions& opti
baseCaps.surfaceAllocator = static_cast<layers::ISurfaceAllocator*>(forwarder);
} while (false);
#endif
// Done with baseCaps construction.
bool forceAllowAA = gfxPrefs::WebGLForceMSAA();
nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
+ nsCString discardFailureId;
if (!forceAllowAA &&
- IsFeatureInBlacklist(gfxInfo, nsIGfxInfo::FEATURE_WEBGL_MSAA))
+ IsFeatureInBlacklist(gfxInfo, nsIGfxInfo::FEATURE_WEBGL_MSAA, &discardFailureId))
{
webgl->GenerateWarning("Disallowing antialiased backbuffers due"
" to blacklisting.");
baseCaps.antialias = false;
}
return baseCaps;
}
////////////////////////////////////////
static already_AddRefed<gl::GLContext>
CreateGLWithEGL(const gl::SurfaceCaps& caps, gl::CreateContextFlags flags,
- WebGLContext* webgl, nsACString* const out_failReason)
+ WebGLContext* webgl, nsACString* const out_failReason,
+ nsACString* const out_failureId)
{
const gfx::IntSize dummySize(16, 16);
RefPtr<GLContext> gl = gl::GLContextProviderEGL::CreateOffscreen(dummySize, caps,
flags);
if (gl && gl->IsANGLE()) {
gl = nullptr;
}
if (!gl) {
if (out_failReason->Length()) {
out_failReason->AppendLiteral("\n");
}
out_failReason->AppendLiteral("Error during EGL OpenGL init.");
+ *out_failureId = "FEATURE_FAILURE_WEBGL_EGL_INIT";
return nullptr;
}
return gl.forget();
}
static already_AddRefed<GLContext>
CreateGLWithANGLE(const gl::SurfaceCaps& caps, gl::CreateContextFlags flags,
- WebGLContext* webgl, nsACString* const out_failReason)
+ WebGLContext* webgl, nsACString* const out_failReason,
+ nsACString* const out_failureId)
{
const gfx::IntSize dummySize(16, 16);
RefPtr<GLContext> gl = gl::GLContextProviderEGL::CreateOffscreen(dummySize, caps,
flags);
if (gl && !gl->IsANGLE()) {
gl = nullptr;
}
if (!gl) {
if (out_failReason->Length()) {
out_failReason->AppendLiteral("\n");
}
out_failReason->AppendLiteral("Error during ANGLE OpenGL init.");
+ *out_failureId = "FEATURE_FAILURE_WEBGL_ANGLE_INIT";
return nullptr;
}
return gl.forget();
}
static already_AddRefed<gl::GLContext>
CreateGLWithDefault(const gl::SurfaceCaps& caps, gl::CreateContextFlags flags,
- WebGLContext* webgl, nsACString* const out_failReason)
+ WebGLContext* webgl, nsACString* const out_failReason,
+ nsACString* const out_failureId)
{
nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
if (!(flags & CreateContextFlags::FORCE_ENABLE_HARDWARE) &&
- IsFeatureInBlacklist(gfxInfo, nsIGfxInfo::FEATURE_WEBGL_OPENGL))
+ IsFeatureInBlacklist(gfxInfo, nsIGfxInfo::FEATURE_WEBGL_OPENGL, out_failureId))
{
if (out_failReason->Length()) {
out_failReason->AppendASCII("\n");
}
out_failReason->AppendASCII("Refused to create native OpenGL context because of"
" blacklisting.");
return nullptr;
}
@@ -643,57 +648,60 @@ CreateGLWithDefault(const gl::SurfaceCap
gl = nullptr;
}
if (!gl) {
if (out_failReason->Length()) {
out_failReason->AppendASCII("\n");
}
out_failReason->AppendASCII("Error during native OpenGL init.");
+ *out_failureId = "FEATURE_FAILURE_WEBGL_DEFAULT_INIT";
return nullptr;
}
return gl.forget();
}
////////////////////////////////////////
bool
WebGLContext::CreateAndInitGLWith(FnCreateGL_T fnCreateGL,
const gl::SurfaceCaps& baseCaps,
gl::CreateContextFlags flags,
- nsACString* const out_failReason)
+ nsACString* const out_failReason,
+ nsACString* const out_failureId)
{
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, out_failReason);
+ gl = fnCreateGL(caps, flags, this, out_failReason, out_failureId);
if (gl)
break;
fallbackCaps.pop();
}
if (!gl)
return false;
- if (!InitAndValidateGL(out_failReason)) {
+ if (!InitAndValidateGL(out_failReason, out_failureId)) {
+ // The fail reason here should be specific enough for now.
gl = nullptr;
return false;
}
return true;
}
bool
-WebGLContext::CreateAndInitGL(bool forceEnabled, nsACString* const out_failReason)
+WebGLContext::CreateAndInitGL(bool forceEnabled, nsACString* const out_failReason, nsACString* const out_failureId)
{
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;
@@ -702,22 +710,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, out_failReason);
+ return CreateAndInitGLWith(CreateGLWithEGL, baseCaps, flags, out_failReason, out_failureId);
if (useANGLE)
- return CreateAndInitGLWith(CreateGLWithANGLE, baseCaps, flags, out_failReason);
+ return CreateAndInitGLWith(CreateGLWithANGLE, baseCaps, flags, out_failReason, out_failureId);
- return CreateAndInitGLWith(CreateGLWithDefault, baseCaps, flags, out_failReason);
+ return CreateAndInitGLWith(CreateGLWithDefault, baseCaps, flags, out_failReason, out_failureId);
}
// Fallback for resizes:
bool
WebGLContext::ResizeBackbuffer(uint32_t requestedWidth,
uint32_t requestedHeight)
{
uint32_t width = requestedWidth;
@@ -859,68 +867,78 @@ WebGLContext::SetDimensions(int32_t sign
// 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()) {
// exit without changing the value of mGeneration
+ Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID,
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_TOO_MANY"));
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) {
+ Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID,
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_DISABLED"));
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)) {
+ Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID,
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_PERF_CAVEAT"));
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);
nsCString failReason;
- if (!CreateAndInitGL(forceEnabled, &failReason)) {
+ nsCString failureId;
+ if (!CreateAndInitGL(forceEnabled, &failReason, &failureId)) {
+ Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID, failureId);
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)) {
+ Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID,
+ NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_RESIZE"));
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());
@@ -993,16 +1011,19 @@ WebGLContext::SetDimensions(int32_t sign
MOZ_ASSERT(gl->Caps().antialias == mOptions.antialias);
MOZ_ASSERT(gl->Caps().preserve == mOptions.preserveDrawingBuffer);
AssertCachedBindings();
AssertCachedState();
reporter.SetSuccessful();
+
+ Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID,
+ NS_LITERAL_CSTRING("SUCCESS"));
return NS_OK;
}
void
WebGLContext::ClearBackbufferIfNeeded()
{
if (!mBackbufferNeedsClear)
return;
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -1196,34 +1196,36 @@ protected:
nsTArray<GLenum> mCompressedTextureFormats;
// -------------------------------------------------------------------------
// WebGL 2 specifics (implemented in WebGL2Context.cpp)
public:
virtual bool IsWebGL2() const = 0;
protected:
- bool InitWebGL2(nsACString* const out_failReason);
+ bool InitWebGL2(nsACString* const out_failReason, nsACString* const out_failureId);
- bool CreateAndInitGL(bool forceEnabled, nsACString* const out_failReason);
+ bool CreateAndInitGL(bool forceEnabled, nsACString* const out_failReason, nsACString* const out_failureId);
bool ResizeBackbuffer(uint32_t width, uint32_t height);
typedef already_AddRefed<gl::GLContext> FnCreateGL_T(const gl::SurfaceCaps& caps,
gl::CreateContextFlags flags,
WebGLContext* webgl,
- nsACString* const out_failReason);
+ nsACString* const out_failReason,
+ nsACString* const out_failureId);
bool CreateAndInitGLWith(FnCreateGL_T fnCreateGL, const gl::SurfaceCaps& baseCaps,
gl::CreateContextFlags flags,
- nsACString* const out_failReason);
+ nsACString* const out_failReason,
+ nsACString* const out_failureId);
void ThrowEvent_WebGLContextCreationError(const nsACString& text);
// -------------------------------------------------------------------------
// Validation functions (implemented in WebGLContextValidate.cpp)
- bool InitAndValidateGL(nsACString* const out_failReason);
+ bool InitAndValidateGL(nsACString* const out_failReason, nsACString* const out_failureId);
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,31 +640,33 @@ FloorPOT(int32_t x)
if (x < pot*2)
break;
pot *= 2;
}
return pot;
}
bool
-WebGLContext::InitAndValidateGL(nsACString* const out_failReason)
+WebGLContext::InitAndValidateGL(nsACString* const out_failReason, nsACString* const out_failureId)
{
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) {
+ *out_failureId = "FEATURE_FAILURE_WEBGL_FORMAT";
out_failReason->AssignLiteral("Failed to create mFormatUsage.");
return false;
}
GLenum error = gl->fGetError();
if (error != LOCAL_GL_NO_ERROR) {
+ *out_failureId = "FEATURE_FAILURE_WEBGL_GLERR_1";
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();
@@ -747,31 +749,33 @@ WebGLContext::InitAndValidateGL(nsACStri
gl->fEnableVertexAttribArray(0);
if (MinCapabilityMode())
mGLMaxVertexAttribs = MINVALUE_GL_MAX_VERTEX_ATTRIBS;
else
gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_ATTRIBS, &mGLMaxVertexAttribs);
if (mGLMaxVertexAttribs < 8) {
+ *out_failureId = "FEATURE_FAILURE_WEBGL_V_ATRB";
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) {
+ *out_failureId = "FEATURE_FAILURE_WEBGL_T_UNIT";
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);
@@ -920,39 +924,41 @@ WebGLContext::InitAndValidateGL(nsACStri
gl->fEnable(LOCAL_GL_TEXTURE_CUBE_MAP_SEAMLESS);
}
// Check the shader validator pref
mBypassShaderValidation = gfxPrefs::WebGLBypassShaderValidator();
// initialize shader translator
if (!ShInitialize()) {
+ *out_failureId = "FEATURE_FAILURE_WEBGL_GLSL";
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) {
+ *out_failureId = "FEATURE_FAILURE_WEBGL_GLERR_2";
const nsPrintfCString reason("GL error 0x%x occurred during WebGL context"
" initialization!",
error);
out_failReason->Assign(reason);
return false;
}
if (IsWebGL2() &&
- !InitWebGL2(out_failReason))
+ !InitWebGL2(out_failReason, out_failureId))
{
// 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) {
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -1048,28 +1048,36 @@
"n_buckets": 50,
"description": "PNG image decode speed (Kbytes/sec)"
},
"CANVAS_2D_USED": {
"expires_in_version": "never",
"kind": "boolean",
"description": "2D canvas used"
},
- "CANVAS_WEBGL_USED": {
- "expires_in_version": "never",
- "kind": "boolean",
- "description": "WebGL canvas used"
+ "CANVAS_WEBGL_FAILURE_ID": {
+ "alert_emails": ["bgirard@mozilla.com"],
+ "expires_in_version": "never",
+ "kind": "count",
+ "keyed": true,
+ "description": "WebGL runtime and dynamic failure IDs. This will record a count for each context creation success or failure. Each failure id is a unique identifier that can be traced back to a particular failure branch or blocklist rule.",
+ "bug_numbers": [1272808]
},
"CANVAS_WEBGL_SUCCESS": {
"alert_emails": ["jmuizelaar@mozilla.com"],
"expires_in_version": "never",
"kind": "boolean",
"description": "WebGL1 creation success",
"bug_numbers": [1247327]
},
+ "CANVAS_WEBGL_USED": {
+ "expires_in_version": "never",
+ "kind": "boolean",
+ "description": "WebGL canvas used"
+ },
"CANVAS_WEBGL2_SUCCESS": {
"alert_emails": ["jmuizelaar@mozilla.com"],
"expires_in_version": "never",
"kind": "boolean",
"description": "WebGL2 creation success",
"bug_numbers": [1247327]
},
"TOTAL_CONTENT_PAGE_LOAD_TIME": {