Bug 1431013 - Activate powerPreference attribute only when has events;
In WebGL spec mention, this `powerPreference` attribute will be ignored if no event listeners exist which would handle both "webglcontextlost" and "webglcontextrestored" events dispatched to the canvas element.
MozReview-Commit-ID: EZZysuu4yet
--- a/dom/canvas/CanvasRenderingContextHelper.cpp
+++ b/dom/canvas/CanvasRenderingContextHelper.cpp
@@ -187,16 +187,17 @@ CanvasRenderingContextHelper::GetContext
CallQueryInterface(context, &cp);
if (!cp) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
mCurrentContext = context.forget();
mCurrentContextType = contextType;
+ mCurrentContext->SetEventBits(mContextEventBits);
nsresult rv = UpdateContext(aCx, aContextOptions, aRv);
if (NS_FAILED(rv)) {
// See bug 645792 and bug 1215072.
// We want to throw only if dictionary initialization fails,
// so only in case aRv has been set to some error value.
if (contextType == CanvasContextType::WebGL1)
Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_SUCCESS, 0);
--- a/dom/canvas/CanvasRenderingContextHelper.h
+++ b/dom/canvas/CanvasRenderingContextHelper.h
@@ -25,16 +25,23 @@ class EncodeCompleteCallback;
enum class CanvasContextType : uint8_t {
NoContext,
Canvas2D,
WebGL1,
WebGL2,
ImageBitmap
};
+enum class CanvasEventType : uint8_t {
+ NONE = 0,
+ WEBGL_CONTEXT_LOST = 1 << 0,
+ WEBGL_CONTEXT_RESTORED = 1 << 1
+};
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CanvasEventType)
+
/**
* Povides common RenderingContext functionality used by both OffscreenCanvas
* and HTMLCanvasElement.
*/
class CanvasRenderingContextHelper
{
public:
virtual already_AddRefed<nsISupports>
@@ -68,16 +75,17 @@ protected:
CreateContext(CanvasContextType aContextType);
already_AddRefed<nsICanvasRenderingContextInternal>
CreateContextHelper(CanvasContextType aContextType,
layers::LayersBackend aCompositorBackend);
virtual nsIntSize GetWidthHeight() = 0;
+ CanvasEventType mContextEventBits = CanvasEventType::NONE;
CanvasContextType mCurrentContextType;
nsCOMPtr<nsICanvasRenderingContextInternal> mCurrentContext;
};
} // namespace dom
} // namespace mozilla
#endif // MOZILLA_DOM_CANVASRENDERINGCONTEXTHELPER_H_
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -89,16 +89,22 @@
namespace mozilla {
using namespace mozilla::dom;
using namespace mozilla::gfx;
using namespace mozilla::gl;
using namespace mozilla::layers;
+#define POWER_PREFERENCE_EVENT(event, power, flags) ((power == dom::WebGLPowerPreference::High_performance) && \
+ (((event) & \
+ (CanvasEventType::WEBGL_CONTEXT_LOST|CanvasEventType::WEBGL_CONTEXT_RESTORED)) == \
+ (CanvasEventType::WEBGL_CONTEXT_LOST|CanvasEventType::WEBGL_CONTEXT_RESTORED)) ? \
+ flags | gl::CreateContextFlags::HIGH_POWER : flags)
+
WebGLContextOptions::WebGLContextOptions()
: alpha(true)
, depth(true)
, stencil(false)
, premultipliedAlpha(true)
, antialias(true)
, preserveDrawingBuffer(false)
, failIfMajorPerformanceCaveat(false)
@@ -501,22 +507,27 @@ BaseCaps(const WebGLContextOptions& opti
return baseCaps;
}
////////////////////////////////////////
static already_AddRefed<gl::GLContext>
CreateGLWithEGL(const gl::SurfaceCaps& caps, gl::CreateContextFlags flags,
WebGLContext* webgl,
+ dom::WebGLPowerPreference powerPreference,
std::vector<WebGLContext::FailureReason>* const out_failReasons)
{
const gfx::IntSize dummySize(16, 16);
nsCString failureId;
RefPtr<GLContext> gl = gl::GLContextProviderEGL::CreateOffscreen(dummySize, caps,
- flags, &failureId);
+ POWER_PREFERENCE_EVENT(
+ webgl->GetEventBits(),
+ powerPreference,
+ flags),
+ &failureId);
if (gl && gl->IsANGLE()) {
gl = nullptr;
}
if (!gl) {
out_failReasons->push_back(WebGLContext::FailureReason(
failureId,
"Error during EGL OpenGL init."
@@ -525,22 +536,27 @@ CreateGLWithEGL(const gl::SurfaceCaps& c
}
return gl.forget();
}
static already_AddRefed<GLContext>
CreateGLWithANGLE(const gl::SurfaceCaps& caps, gl::CreateContextFlags flags,
WebGLContext* webgl,
+ dom::WebGLPowerPreference powerPreference,
std::vector<WebGLContext::FailureReason>* const out_failReasons)
{
const gfx::IntSize dummySize(16, 16);
nsCString failureId;
RefPtr<GLContext> gl = gl::GLContextProviderEGL::CreateOffscreen(dummySize, caps,
- flags, &failureId);
+ POWER_PREFERENCE_EVENT(
+ webgl->GetEventBits(),
+ powerPreference,
+ flags),
+ &failureId);
if (gl && !gl->IsANGLE()) {
gl = nullptr;
}
if (!gl) {
out_failReasons->push_back(WebGLContext::FailureReason(
failureId,
"Error during ANGLE OpenGL init."
@@ -549,22 +565,27 @@ CreateGLWithANGLE(const gl::SurfaceCaps&
}
return gl.forget();
}
static already_AddRefed<gl::GLContext>
CreateGLWithDefault(const gl::SurfaceCaps& caps, gl::CreateContextFlags flags,
WebGLContext* webgl,
+ dom::WebGLPowerPreference powerPreference,
std::vector<WebGLContext::FailureReason>* const out_failReasons)
{
const gfx::IntSize dummySize(16, 16);
nsCString failureId;
RefPtr<GLContext> gl = gl::GLContextProvider::CreateOffscreen(dummySize, caps,
- flags, &failureId);
+ POWER_PREFERENCE_EVENT(
+ webgl->GetEventBits(),
+ powerPreference,
+ flags),
+ &failureId);
if (gl && gl->IsANGLE()) {
gl = nullptr;
}
if (!gl) {
out_failReasons->push_back(WebGLContext::FailureReason(
failureId,
"Error during native OpenGL init."
@@ -585,17 +606,18 @@ WebGLContext::CreateAndInitGLWith(FnCrea
{
std::queue<gl::SurfaceCaps> fallbackCaps;
PopulateCapFallbackQueue(baseCaps, &fallbackCaps);
MOZ_RELEASE_ASSERT(!gl, "GFX: Already have a context.");
RefPtr<gl::GLContext> potentialGL;
while (!fallbackCaps.empty()) {
const gl::SurfaceCaps& caps = fallbackCaps.front();
- potentialGL = fnCreateGL(caps, flags, this, out_failReasons);
+ potentialGL = fnCreateGL(caps, flags, this, mOptions.powerPreference,
+ out_failReasons);
if (potentialGL)
break;
fallbackCaps.pop();
}
if (!potentialGL) {
out_failReasons->push_back(FailureReason("FEATURE_FAILURE_WEBGL_EXHAUSTED_CAPS",
"Exhausted GL driver caps."));
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -316,16 +316,17 @@ class WebGLContext
};
const uint32_t mMaxPerfWarnings;
mutable uint64_t mNumPerfWarnings;
const uint32_t mMaxAcceptableFBStatusInvals;
uint64_t mNextFenceId = 1;
uint64_t mCompletedFenceId = 0;
+ dom::CanvasEventType mEventBits = dom::CanvasEventType::NONE;
public:
WebGLContext();
protected:
virtual ~WebGLContext();
public:
@@ -384,16 +385,20 @@ public:
NS_IMETHOD Redraw(const gfxRect&) override {
return NS_ERROR_NOT_IMPLEMENTED;
}
void SynthesizeGLError(GLenum err) const;
void SynthesizeGLError(GLenum err, const char* fmt, ...) const MOZ_FORMAT_PRINTF(3, 4);
+ virtual void SetEventBits(dom::CanvasEventType aEventBits) override {
+ mEventBits = aEventBits;
+ }
+
void ErrorInvalidEnum(const char* fmt = 0, ...) const MOZ_FORMAT_PRINTF(2, 3);
void ErrorInvalidOperation(const char* fmt = 0, ...) const MOZ_FORMAT_PRINTF(2, 3);
void ErrorInvalidValue(const char* fmt = 0, ...) const MOZ_FORMAT_PRINTF(2, 3);
void ErrorInvalidFramebufferOperation(const char* fmt = 0, ...) const MOZ_FORMAT_PRINTF(2, 3);
void ErrorInvalidEnumInfo(const char* info, GLenum enumValue) const;
void ErrorInvalidEnumInfo(const char* info, const char* funcName,
GLenum enumValue) const;
void ErrorOutOfMemory(const char* fmt = 0, ...) const MOZ_FORMAT_PRINTF(2, 3);
@@ -489,16 +494,18 @@ public:
void UpdateContextLossStatus();
void EnqueueUpdateContextLossStatus();
bool TryToRestoreContext();
void AssertCachedBindings() const;
void AssertCachedGlobalState() const;
+ CanvasEventType GetEventBits() { return mEventBits; }
+
dom::HTMLCanvasElement* GetCanvas() const { return mCanvasElement; }
nsIDocument* GetOwnerDoc() const;
// WebIDL WebGLRenderingContext API
void Commit();
void GetCanvas(Nullable<dom::OwningHTMLCanvasElementOrOffscreenCanvas>& retval);
private:
gfx::IntSize DrawingBufferSize(const char* funcName);
@@ -1592,16 +1599,17 @@ protected:
bool InitWebGL2(FailureReason* const out_failReason);
bool CreateAndInitGL(bool forceEnabled,
std::vector<FailureReason>* const out_failReasons);
typedef already_AddRefed<gl::GLContext> FnCreateGL_T(const gl::SurfaceCaps& caps,
gl::CreateContextFlags flags,
WebGLContext* webgl,
+ dom::WebGLPowerPreference powerPreference,
std::vector<FailureReason>* const out_failReasons);
bool CreateAndInitGLWith(FnCreateGL_T fnCreateGL, const gl::SurfaceCaps& baseCaps,
gl::CreateContextFlags flags,
std::vector<FailureReason>* const out_failReasons);
void ThrowEvent_WebGLContextCreationError(const nsACString& text);
--- a/dom/canvas/nsICanvasRenderingContextInternal.h
+++ b/dom/canvas/nsICanvasRenderingContextInternal.h
@@ -31,16 +31,18 @@ class Layer;
class LayerManager;
class WebRenderCanvasData;
} // namespace layers
namespace gfx {
class SourceSurface;
} // namespace gfx
} // namespace mozilla
+using mozilla::dom::CanvasEventType;
+
class nsICanvasRenderingContextInternal :
public nsISupports,
public nsAPostRefreshObserver
{
public:
typedef mozilla::layers::CanvasLayer CanvasLayer;
typedef mozilla::layers::CanvasRenderer CanvasRenderer;
typedef mozilla::layers::Layer Layer;
@@ -175,16 +177,18 @@ public:
// Given a point, return hit region ID if it exists or an empty string if it doesn't
virtual nsString GetHitRegion(const mozilla::gfx::Point& point) { return nsString(); }
virtual void OnVisibilityChange() {}
virtual void OnMemoryPressure() {}
+ virtual void SetEventBits(mozilla::dom::CanvasEventType aEventBits) {}
+
//
// shmem support
//
// If this context can be set to use Mozilla's Shmem segments as its backing
// store, this will set it to that state. Note that if you have drawn
// anything into this canvas before changing the shmem state, it will be
// lost.
--- a/dom/html/HTMLCanvasElement.cpp
+++ b/dom/html/HTMLCanvasElement.cpp
@@ -616,16 +616,35 @@ nsresult HTMLCanvasElement::GetEventTarg
evt->region = mCurrentContext->GetHitRegion(hitpoint);
aVisitor.mCanHandle = true;
}
}
return nsGenericHTMLElement::GetEventTargetParent(aVisitor);
}
+void
+HTMLCanvasElement::AddEventListener(const nsAString& aType,
+ EventListener* aCallback,
+ const AddEventListenerOptionsOrBoolean& aOptions,
+ const Nullable<bool>& aWantsUntrusted,
+ ErrorResult& aRv)
+{
+ if (aType == NS_LITERAL_STRING("webglcontextlost")) {
+ mContextEventBits |= CanvasEventType::WEBGL_CONTEXT_LOST;
+ }
+
+ if (aType == NS_LITERAL_STRING("webglcontextrestored")) {
+ mContextEventBits |= CanvasEventType::WEBGL_CONTEXT_RESTORED;
+ }
+
+ nsGenericHTMLElement::AddEventListener(aType, aCallback, aOptions, aWantsUntrusted,
+ aRv);
+}
+
nsChangeHint
HTMLCanvasElement::GetAttributeChangeHint(const nsAtom* aAttribute,
int32_t aModType) const
{
nsChangeHint retval =
nsGenericHTMLElement::GetAttributeChangeHint(aAttribute, aModType);
if (aAttribute == nsGkAtoms::width ||
aAttribute == nsGkAtoms::height)
--- a/dom/html/HTMLCanvasElement.h
+++ b/dom/html/HTMLCanvasElement.h
@@ -297,16 +297,23 @@ public:
virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
bool aPreallocateChildren) const override;
nsresult CopyInnerTo(mozilla::dom::Element* aDest,
bool aPreallocateChildren);
virtual nsresult GetEventTargetParent(
mozilla::EventChainPreVisitor& aVisitor) override;
+ using nsIDOMEventTarget::AddEventListener;
+ virtual void AddEventListener(const nsAString& aType,
+ EventListener* aCallback,
+ const AddEventListenerOptionsOrBoolean& aOptions,
+ const Nullable<bool>& aWantsUntrusted,
+ ErrorResult& aRv) override;
+
/*
* Helpers called by various users of Canvas
*/
already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
Layer *aOldLayer,
LayerManager *aManager);
bool UpdateWebRenderCanvasData(nsDisplayListBuilder* aBuilder,
--- a/gfx/gl/GLContextTypes.h
+++ b/gfx/gl/GLContextTypes.h
@@ -52,15 +52,17 @@ enum class CreateContextFlags : int8_t {
FORCE_ENABLE_HARDWARE = 1 << 1,
/* Don't force discrete GPU to be used (if applicable) */
ALLOW_OFFLINE_RENDERER = 1 << 2,
// Ask for ES3 if possible
PREFER_ES3 = 1 << 3,
NO_VALIDATION = 1 << 4,
PREFER_ROBUSTNESS = 1 << 5,
+
+ HIGH_POWER = 1 << 6,
};
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CreateContextFlags)
} /* namespace gl */
} /* namespace mozilla */
#endif /* GLCONTEXT_TYPES_H_ */