diff --git a/dom/canvas/WebGLContext.h b/dom/canvas/WebGLContext.h index b55047126fc0..c3c8b0753a73 100644 --- a/dom/canvas/WebGLContext.h +++ b/dom/canvas/WebGLContext.h @@ -1694,7 +1694,8 @@ class WebGLContext : public nsICanvasRenderingContextInternal, // Enable an extension if it's supported. Return the extension on success. WebGLExtensionBase* EnableSupportedExtension(dom::CallerType callerType, - WebGLExtensionID ext); + WebGLExtensionID ext, + bool explict = true); public: // returns true if the extension has been enabled by calling getExtension. @@ -1702,6 +1703,10 @@ class WebGLContext : public nsICanvasRenderingContextInternal, return mExtensions[ext]; } + bool IsExtensionExplicit(const WebGLExtensionID ext) const; + + void WarnIfImplicit(const WebGLExtensionID ext); + protected: // returns true if the extension is supported for this caller type (this // decides what getSupportedExtensions exposes) diff --git a/dom/canvas/WebGLContextDraw.cpp b/dom/canvas/WebGLContextDraw.cpp index c57e49bcaef1..5c14442e02d2 100644 --- a/dom/canvas/WebGLContextDraw.cpp +++ b/dom/canvas/WebGLContextDraw.cpp @@ -333,13 +333,15 @@ const webgl::CachedDrawFetchLimits* ValidateDraw(WebGLContext* const webgl, if (!webgl->BindCurFBForDraw()) return nullptr; const auto& fb = webgl->mBoundDrawFramebuffer; - if (fb && !webgl->IsExtensionEnabled(WebGLExtensionID::EXT_float_blend) && - webgl->mBlendEnabled) { + if (fb && webgl->mBlendEnabled) { const auto& info = *fb->GetCompletenessInfo(); if (info.hasFloat32) { - webgl->ErrorInvalidOperation( - "Float32 blending requires EXT_float_blend."); - return nullptr; + if (!webgl->IsExtensionEnabled(WebGLExtensionID::EXT_float_blend)) { + webgl->ErrorInvalidOperation( + "Float32 blending requires EXT_float_blend."); + return nullptr; + } + webgl->WarnIfImplicit(WebGLExtensionID::EXT_float_blend); } } diff --git a/dom/canvas/WebGLContextExtensions.cpp b/dom/canvas/WebGLContextExtensions.cpp index 4d4481e9ae3e..60cffa8626a6 100644 --- a/dom/canvas/WebGLContextExtensions.cpp +++ b/dom/canvas/WebGLContextExtensions.cpp @@ -235,20 +235,44 @@ bool WebGLContext::IsExtensionSupported(WebGLExtensionID ext) const { return false; } +bool WebGLContext::IsExtensionExplicit(const WebGLExtensionID ext) const { + MOZ_ASSERT(ext < WebGLExtensionID::Max); + return mExtensions[ext] && mExtensions[ext]->IsExplicit(); +} + +void WebGLContext::WarnIfImplicit(const WebGLExtensionID ext) { + MOZ_ASSERT(ext < WebGLExtensionID::Max); + const auto& extension = mExtensions[ext]; + if (!extension || extension->IsExplicit()) return; + + GenerateWarning( + "Using format enabled by implicitly enabled extension: %s. " + "For maximal portability enable it explicitly.", + GetExtensionString(ext)); + // Don't spam warnings + extension->SetExplicit(); +} + static bool CompareWebGLExtensionName(const nsACString& name, const char* other) { return name.Equals(other, nsCaseInsensitiveCStringComparator()); } WebGLExtensionBase* WebGLContext::EnableSupportedExtension( - dom::CallerType callerType, WebGLExtensionID ext) { + dom::CallerType callerType, WebGLExtensionID ext, bool explict) { if (!IsExtensionEnabled(ext)) { if (!IsExtensionSupported(callerType, ext)) return nullptr; EnableExtension(ext); } - return mExtensions[ext]; + const auto extension = mExtensions[ext]; + MOZ_ASSERT(extension); + if (explict && !extension->IsExplicit()) { + extension->SetExplicit(); + } + + return extension; } void WebGLContext::GetExtension(JSContext* cx, const nsAString& wideName, @@ -285,22 +309,25 @@ void WebGLContext::GetExtension(JSContext* cx, const nsAString& wideName, // Step 4: Enable any implied extensions. switch (ext) { case WebGLExtensionID::EXT_color_buffer_float: - EnableSupportedExtension(callerType, WebGLExtensionID::EXT_float_blend); + EnableSupportedExtension(callerType, WebGLExtensionID::EXT_float_blend, + false); break; case WebGLExtensionID::OES_texture_float: - EnableSupportedExtension(callerType, - WebGLExtensionID::WEBGL_color_buffer_float); - EnableSupportedExtension(callerType, WebGLExtensionID::EXT_float_blend); + EnableSupportedExtension( + callerType, WebGLExtensionID::WEBGL_color_buffer_float, false); + EnableSupportedExtension(callerType, WebGLExtensionID::EXT_float_blend, + false); break; case WebGLExtensionID::OES_texture_half_float: - EnableSupportedExtension(callerType, - WebGLExtensionID::EXT_color_buffer_half_float); + EnableSupportedExtension( + callerType, WebGLExtensionID::EXT_color_buffer_half_float, false); break; case WebGLExtensionID::WEBGL_color_buffer_float: - EnableSupportedExtension(callerType, WebGLExtensionID::EXT_float_blend); + EnableSupportedExtension(callerType, WebGLExtensionID::EXT_float_blend, + false); break; default: diff --git a/dom/canvas/WebGLContextFramebufferOperations.cpp b/dom/canvas/WebGLContextFramebufferOperations.cpp index 77f5e7cfdb12..9ce71f5c5d65 100644 --- a/dom/canvas/WebGLContextFramebufferOperations.cpp +++ b/dom/canvas/WebGLContextFramebufferOperations.cpp @@ -72,11 +72,27 @@ void WebGLContext::ClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) { const FuncScope funcScope(*this, "clearColor"); if (IsContextLost()) return; - const bool supportsFloatColorBuffers = - (IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_float) || - IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float) || - IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float)); - if (!supportsFloatColorBuffers) { + if (IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_float)) { + MOZ_ASSERT(IsExtensionExplicit(WebGLExtensionID::EXT_color_buffer_float)); + + } else if (IsExtensionEnabled( + WebGLExtensionID::EXT_color_buffer_half_float) || + IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float)) { + const bool explict = + (IsExtensionExplicit(WebGLExtensionID::EXT_color_buffer_half_float) || + IsExtensionExplicit(WebGLExtensionID::WEBGL_color_buffer_float)); + const bool wouldHaveClamped = + (r != GLClampFloat(r) || g != GLClampFloat(g) || b != GLClampFloat(b) || + a != GLClampFloat(a)); + if (!explict && wouldHaveClamped) { + if (IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float)) { + WarnIfImplicit(WebGLExtensionID::EXT_color_buffer_half_float); + } else if (IsExtensionEnabled( + WebGLExtensionID::WEBGL_color_buffer_float)) { + WarnIfImplicit(WebGLExtensionID::WEBGL_color_buffer_float); + } + } + } else { r = GLClampFloat(r); g = GLClampFloat(g); b = GLClampFloat(b); diff --git a/dom/canvas/WebGLExtensionColorBufferFloat.cpp b/dom/canvas/WebGLExtensionColorBufferFloat.cpp index 8fea9d86a416..1642977dd942 100644 --- a/dom/canvas/WebGLExtensionColorBufferFloat.cpp +++ b/dom/canvas/WebGLExtensionColorBufferFloat.cpp @@ -15,13 +15,18 @@ WebGLExtensionColorBufferFloat::WebGLExtensionColorBufferFloat( WebGLContext* webgl) : WebGLExtensionBase(webgl) { MOZ_ASSERT(IsSupported(webgl), "Don't construct extension if unsupported."); + SetRenderable(webgl::FormatRenderableState::Implicit( + WebGLExtensionID::WEBGL_color_buffer_float)); +} - auto& fua = webgl->mFormatUsage; +void WebGLExtensionColorBufferFloat::SetRenderable( + const webgl::FormatRenderableState state) { + auto& fua = mContext->mFormatUsage; - auto fnUpdateUsage = [&fua](GLenum sizedFormat, - webgl::EffectiveFormat effFormat) { + auto fnUpdateUsage = [&](GLenum sizedFormat, + webgl::EffectiveFormat effFormat) { auto usage = fua->EditUsage(effFormat); - usage->SetRenderable(); + usage->SetRenderable(state); fua->AllowRBFormat(sizedFormat, usage); }; @@ -33,6 +38,10 @@ WebGLExtensionColorBufferFloat::WebGLExtensionColorBufferFloat( #undef FOO } +void WebGLExtensionColorBufferFloat::OnSetExplicit() { + SetRenderable(webgl::FormatRenderableState::Explicit()); +} + WebGLExtensionColorBufferFloat::~WebGLExtensionColorBufferFloat() {} bool WebGLExtensionColorBufferFloat::IsSupported(const WebGLContext* webgl) { diff --git a/dom/canvas/WebGLExtensionColorBufferHalfFloat.cpp b/dom/canvas/WebGLExtensionColorBufferHalfFloat.cpp index c065e2282b75..004eea330d9f 100644 --- a/dom/canvas/WebGLExtensionColorBufferHalfFloat.cpp +++ b/dom/canvas/WebGLExtensionColorBufferHalfFloat.cpp @@ -15,15 +15,19 @@ WebGLExtensionColorBufferHalfFloat::WebGLExtensionColorBufferHalfFloat( WebGLContext* webgl) : WebGLExtensionBase(webgl) { MOZ_ASSERT(IsSupported(webgl), "Don't construct extension if unsupported."); + SetRenderable(webgl::FormatRenderableState::Implicit( + WebGLExtensionID::EXT_color_buffer_half_float)); +} - auto& fua = webgl->mFormatUsage; +void WebGLExtensionColorBufferHalfFloat::SetRenderable( + const webgl::FormatRenderableState state) { + auto& fua = mContext->mFormatUsage; - auto fnUpdateUsage = [&fua](GLenum sizedFormat, - webgl::EffectiveFormat effFormat, - const bool renderable) { + auto fnUpdateUsage = [&](GLenum sizedFormat, webgl::EffectiveFormat effFormat, + const bool renderable) { auto usage = fua->EditUsage(effFormat); if (renderable) { - usage->SetRenderable(); + usage->SetRenderable(state); } fua->AllowRBFormat(sizedFormat, usage, renderable); }; @@ -37,6 +41,10 @@ WebGLExtensionColorBufferHalfFloat::WebGLExtensionColorBufferHalfFloat( #undef FOO } +void WebGLExtensionColorBufferHalfFloat::OnSetExplicit() { + SetRenderable(webgl::FormatRenderableState::Explicit()); +} + WebGLExtensionColorBufferHalfFloat::~WebGLExtensionColorBufferHalfFloat() {} bool WebGLExtensionColorBufferHalfFloat::IsSupported( diff --git a/dom/canvas/WebGLExtensions.cpp b/dom/canvas/WebGLExtensions.cpp index 74a19bbe6333..baeefc900dc3 100644 --- a/dom/canvas/WebGLExtensions.cpp +++ b/dom/canvas/WebGLExtensions.cpp @@ -12,8 +12,8 @@ namespace mozilla { -WebGLExtensionBase::WebGLExtensionBase(WebGLContext* context) - : WebGLContextBoundObject(context), mIsLost(false) {} +WebGLExtensionBase::WebGLExtensionBase(WebGLContext* webgl) + : WebGLContextBoundObject(webgl), mIsLost(false), mIsExplicit(false) {} WebGLExtensionBase::~WebGLExtensionBase() {} diff --git a/dom/canvas/WebGLExtensions.h b/dom/canvas/WebGLExtensions.h index bdd2185447c4..00fe56d1ef20 100644 --- a/dom/canvas/WebGLExtensions.h +++ b/dom/canvas/WebGLExtensions.h @@ -12,6 +12,7 @@ #include "nsWrapperCache.h" #include "WebGLObjectModel.h" #include "WebGLTypes.h" +#include "WebGLFormats.h" namespace mozilla { class ErrorResult; @@ -42,6 +43,13 @@ class WebGLExtensionBase : public nsWrapperCache, void MarkLost(); + void SetExplicit() { + mIsExplicit = true; + OnSetExplicit(); + } + + bool IsExplicit() { return mIsExplicit; } + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLExtensionBase) NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLExtensionBase) @@ -51,6 +59,10 @@ class WebGLExtensionBase : public nsWrapperCache, virtual void OnMarkLost() {} bool mIsLost; + + virtual void OnSetExplicit() {} + + bool mIsExplicit; }; #define DECL_WEBGL_EXTENSION_GOOP \ @@ -321,6 +333,10 @@ class WebGLExtensionColorBufferFloat : public WebGLExtensionBase { static bool IsSupported(const WebGLContext*); + void SetRenderable(const webgl::FormatRenderableState); + + void OnSetExplicit() override; + DECL_WEBGL_EXTENSION_GOOP }; @@ -331,6 +347,10 @@ class WebGLExtensionColorBufferHalfFloat : public WebGLExtensionBase { static bool IsSupported(const WebGLContext*); + void SetRenderable(const webgl::FormatRenderableState); + + void OnSetExplicit() override; + DECL_WEBGL_EXTENSION_GOOP }; diff --git a/dom/canvas/WebGLFormats.cpp b/dom/canvas/WebGLFormats.cpp index f2dd2228cd9e..78d77b72b07e 100644 --- a/dom/canvas/WebGLFormats.cpp +++ b/dom/canvas/WebGLFormats.cpp @@ -765,8 +765,10 @@ static bool AddUnsizedFormats(FormatUsageAuthority* fua, gl::GLContext* gl) { // clang-format on } -void FormatUsageInfo::SetRenderable() { - this->isRenderable = true; +void FormatUsageInfo::SetRenderable(const FormatRenderableState& state) { + if (!renderableState.IsExplicit()) { + renderableState = state; + } #ifdef DEBUG const auto format = this->format; @@ -1125,6 +1127,11 @@ void FormatUsageAuthority::AllowRBFormat(GLenum sizedFormat, MOZ_ASSERT(usage->format->sizedFormat); MOZ_ASSERT(usage->IsRenderable() || !expectRenderable); + const auto& found = mRBFormatMap.find(sizedFormat); + if (found != mRBFormatMap.end()) { + MOZ_ASSERT(found->second == usage); + return; + } AlwaysInsert(mRBFormatMap, sizedFormat, usage); } diff --git a/dom/canvas/WebGLFormats.h b/dom/canvas/WebGLFormats.h index d48e69ec07ad..df3cc5731dd1 100644 --- a/dom/canvas/WebGLFormats.h +++ b/dom/canvas/WebGLFormats.h @@ -309,11 +309,35 @@ GLenum ComponentType(const FormatInfo* format); */ //////////////////////////////////////// +struct FormatRenderableState final { + private: + enum class RenderableState { + Disabled, + Implicit, + Explicit, + }; + + public: + RenderableState state = RenderableState::Disabled; + WebGLExtensionID extid = WebGLExtensionID::Max; + + static FormatRenderableState Explicit() { + return {RenderableState::Explicit}; + } + + static FormatRenderableState Implicit(WebGLExtensionID extid) { + return {RenderableState::Implicit, extid}; + } + + bool IsRenderable() const { return state != RenderableState::Disabled; } + bool IsExplicit() const { return state == RenderableState::Explicit; } +}; + struct FormatUsageInfo { const FormatInfo* const format; private: - bool isRenderable = false; + FormatRenderableState renderableState; public: bool isFilterable = false; @@ -338,8 +362,14 @@ struct FormatUsageInfo { } } - bool IsRenderable() const { return isRenderable; } - void SetRenderable(); + bool IsRenderable() const { return renderableState.IsRenderable(); } + void SetRenderable( + const FormatRenderableState& state = FormatRenderableState::Explicit()); + bool IsExplicitlyRenderable() const { return renderableState.IsExplicit(); } + WebGLExtensionID GetExtensionID() const { + MOZ_ASSERT(renderableState.extid != WebGLExtensionID::Max); + return renderableState.extid; + } bool IsUnpackValid(const PackingInfo& key, const DriverUnpackInfo** const out_value) const; diff --git a/dom/canvas/WebGLFramebuffer.cpp b/dom/canvas/WebGLFramebuffer.cpp index 6beffc8e875a..79c04f8733d1 100644 --- a/dom/canvas/WebGLFramebuffer.cpp +++ b/dom/canvas/WebGLFramebuffer.cpp @@ -143,6 +143,9 @@ bool WebGLFBAttachPoint::IsComplete(WebGLContext* webgl, fnWriteErrorInfo(info.BeginReading()); return false; } + if (!formatUsage->IsExplicitlyRenderable()) { + webgl->WarnIfImplicit(formatUsage->GetExtensionID()); + } const auto format = formatUsage->format; diff --git a/dom/canvas/WebGLTexture.cpp b/dom/canvas/WebGLTexture.cpp index 1fdca53de981..50c4132147cd 100644 --- a/dom/canvas/WebGLTexture.cpp +++ b/dom/canvas/WebGLTexture.cpp @@ -775,6 +775,10 @@ void WebGLTexture::GenerateMipmap() { return; } + if (usage->IsRenderable() && !usage->IsExplicitlyRenderable()) { + mContext->WarnIfImplicit(usage->GetExtensionID()); + } + // Done with validation. Do the operation. gl::GLContext* gl = mContext->gl; diff --git a/dom/canvas/WebGLTextureUpload.cpp b/dom/canvas/WebGLTextureUpload.cpp index f6ab719f88e8..e47849bd2e99 100644 --- a/dom/canvas/WebGLTextureUpload.cpp +++ b/dom/canvas/WebGLTextureUpload.cpp @@ -1670,12 +1670,14 @@ ScopedCopyTexImageSource::ScopedCopyTexImageSource( if (webgl->IsExtensionEnabled( WebGLExtensionID::WEBGL_color_buffer_float)) { sizedFormat = LOCAL_GL_RGBA32F; + webgl->WarnIfImplicit(WebGLExtensionID::WEBGL_color_buffer_float); break; } if (webgl->IsExtensionEnabled( WebGLExtensionID::EXT_color_buffer_half_float)) { sizedFormat = LOCAL_GL_RGBA16F; + webgl->WarnIfImplicit(WebGLExtensionID::EXT_color_buffer_half_float); break; } MOZ_CRASH("GFX: Should be able to request CopyTexImage from Float.");