From e23c37371147c5ff5ac7d825ccaa90e096cd036e Mon Sep 17 00:00:00 2001 From: Kelsey Gilbert Date: Sat, 9 Mar 2024 05:35:20 +0000 Subject: [PATCH] Bug 1883225 - webgl.texSubImage(video): re-enable gpu-blit for RGBA, and RGB iff RGB8+SRGB8 renderable. r=gfx-reviewers,lsalzman Differential Revision: https://phabricator.services.mozilla.com/D203698 --- dom/canvas/ClientWebGLContext.cpp | 14 +++--- dom/canvas/TexUnpackBlob.cpp | 80 ++++++++++++++++++++++++------- dom/canvas/TexUnpackBlob.h | 2 +- dom/canvas/WebGLContext.cpp | 33 ++++++++++--- dom/canvas/WebGLContext.h | 3 +- dom/canvas/WebGLIpdl.h | 29 +++++++++++ dom/canvas/WebGLTypes.h | 33 +++++++++++-- 7 files changed, 156 insertions(+), 38 deletions(-) diff --git a/dom/canvas/ClientWebGLContext.cpp b/dom/canvas/ClientWebGLContext.cpp index c506926f2143..c97a4fa229ca 100644 --- a/dom/canvas/ClientWebGLContext.cpp +++ b/dom/canvas/ClientWebGLContext.cpp @@ -1268,8 +1268,9 @@ RefPtr ClientWebGLContext::BackBufferSnapshot() { if (!DoReadPixels(desc, pixels)) return nullptr; // RGBA->BGRA and flip-y. - MOZ_RELEASE_ASSERT(gfx::SwizzleYFlipData(pixels.data(), stride, gfx::SurfaceFormat::R8G8B8A8, - pixels.data(), stride, gfx::SurfaceFormat::B8G8R8A8, {size.x, size.y})); + MOZ_RELEASE_ASSERT(gfx::SwizzleYFlipData( + pixels.data(), stride, gfx::SurfaceFormat::R8G8B8A8, pixels.data(), + stride, gfx::SurfaceFormat::B8G8R8A8, {size.x, size.y})); } return surf; @@ -4356,12 +4357,9 @@ void ClientWebGLContext::TexImage(uint8_t funcDims, GLenum imageTarget, const auto& contextInfo = mNotLost->info; const auto fallbackReason = [&]() -> Maybe { - if (!respecFormat) { - return Some(std::string{ - "Fast uploads not supported for TexSubImage. Use TexImage."}); - } - auto fallbackReason = BlitPreventReason( - level, offset, respecFormat, pi, *desc, contextInfo.isRgb8Renderable); + auto fallbackReason = + BlitPreventReason(level, offset, respecFormat, pi, *desc, + contextInfo.optionalRenderableFormatBits); if (fallbackReason) return fallbackReason; const bool canUploadViaSd = contextInfo.uploadableSdTypes[sdType]; diff --git a/dom/canvas/TexUnpackBlob.cpp b/dom/canvas/TexUnpackBlob.cpp index e9e64fe9b99e..e13dc1c06487 100644 --- a/dom/canvas/TexUnpackBlob.cpp +++ b/dom/canvas/TexUnpackBlob.cpp @@ -668,11 +668,10 @@ bool TexUnpackImage::Validate(const WebGLContext* const webgl, return ValidateUnpackPixels(webgl, pi, fullRows, *this); } -Maybe BlitPreventReason(const int32_t level, const ivec3& offset, - const GLenum internalFormat, - const webgl::PackingInfo& pi, - const TexUnpackBlobDesc& desc, - const bool isRgb8Renderable) { +Maybe BlitPreventReason( + const int32_t level, const ivec3& offset, const GLenum internalFormat, + const webgl::PackingInfo& pi, const TexUnpackBlobDesc& desc, + const OptionalRenderableFormatBits optionalRenderableFormatBits) { const auto& size = desc.size; const auto& unpacking = desc.unpacking; @@ -705,26 +704,71 @@ Maybe BlitPreventReason(const int32_t level, const ivec3& offset, const auto formatReason = [&]() -> const char* { if (pi.type != LOCAL_GL_UNSIGNED_BYTE) { - return "`type` must be `UNSIGNED_BYTE`"; + return "`unpackType` must be `UNSIGNED_BYTE`"; } - switch (internalFormat) { + switch (pi.format) { case LOCAL_GL_RGBA: - case LOCAL_GL_RGBA8: - return nullptr; + return nullptr; // All internalFormats for unpackFormat=RGBA are + // renderable. case LOCAL_GL_RGB: - case LOCAL_GL_RGB8: - if (isRgb8Renderable) { - return nullptr; - } break; + + default: + return "`unpackFormat` must be `RGBA` or maybe `RGB`"; } - if (isRgb8Renderable) { - return "effective format must be RGB8 or RGBA8"; - } else { - return "effective format must be RGBA8"; + + // - + + struct { + OptionalRenderableFormatBits bits; + const char* errorMsg; + } required; + + switch (internalFormat) { + case LOCAL_GL_RGB565: + return nullptr; + case LOCAL_GL_RGB: + case LOCAL_GL_RGB8: + required = { + OptionalRenderableFormatBits::RGB8, + "Unavailable, as blitting internalFormats RGB or RGB8 requires " + "that RGB8 must be a renderable format.", + }; + break; + case LOCAL_GL_SRGB: + case LOCAL_GL_SRGB8: + required = { + OptionalRenderableFormatBits::SRGB8, + "Unavailable, as blitting internalFormats SRGB or SRGB8 requires " + "that SRGB8 must be a renderable format.", + }; + break; + case 0: + // texSubImage, so internalFormat is unknown, and could be anything! + required = { + OptionalRenderableFormatBits::RGB8 | + OptionalRenderableFormatBits::SRGB8, + "Unavailable, as blitting texSubImage with unpackFormat=RGB " + "requires that RGB8 and SRGB8 must be renderable formats.", + }; + break; + default: + gfxCriticalError() + << "Unexpected internalFormat for unpackFormat=RGB: 0x" + << gfx::hexa(internalFormat); + return "Unexpected internalFormat for unpackFormat=RGB"; } + + const auto availableBits = optionalRenderableFormatBits; + if ((required.bits | availableBits) != availableBits) { + return required.errorMsg; + } + + // - + + return nullptr; }(); if (formatReason) return formatReason; @@ -756,7 +800,7 @@ bool TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec, const auto reason = BlitPreventReason(level, {xOffset, yOffset, zOffset}, dui->internalFormat, - pi, mDesc, webgl->mIsRgb8Renderable); + pi, mDesc, webgl->mOptionalRenderableFormatBits); if (reason) { webgl->GeneratePerfWarning( "Failed to hit GPU-copy fast-path." diff --git a/dom/canvas/TexUnpackBlob.h b/dom/canvas/TexUnpackBlob.h index 02cb8dec41cf..dc850fe5d442 100644 --- a/dom/canvas/TexUnpackBlob.h +++ b/dom/canvas/TexUnpackBlob.h @@ -45,7 +45,7 @@ Maybe BlitPreventReason(int32_t level, const ivec3& offset, GLenum internalFormat, const webgl::PackingInfo&, const TexUnpackBlobDesc&, - bool isRgb8Renderable); + OptionalRenderableFormatBits); class TexUnpackBlob { public: diff --git a/dom/canvas/WebGLContext.cpp b/dom/canvas/WebGLContext.cpp index c1e6cbbed2b9..ec19bfef3d22 100644 --- a/dom/canvas/WebGLContext.cpp +++ b/dom/canvas/WebGLContext.cpp @@ -653,7 +653,7 @@ RefPtr WebGLContext::Create(HostWebGLContext* host, out->limits = *webgl->mLimits; out->uploadableSdTypes = UploadableSdTypes(); out->vendor = webgl->gl->Vendor(); - out->isRgb8Renderable = webgl->mIsRgb8Renderable; + out->optionalRenderableFormatBits = webgl->mOptionalRenderableFormatBits; return webgl; } @@ -710,15 +710,36 @@ void WebGLContext::FinishInit() { const auto tex = gl::ScopedTexture(gl); const auto fb = gl::ScopedFramebuffer(gl); gl->fBindTexture(LOCAL_GL_TEXTURE_2D, tex); - gl->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGB, 1, 1, 0, LOCAL_GL_RGB, - LOCAL_GL_UNSIGNED_BYTE, nullptr); - gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fb); gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_TEXTURE_2D, tex, 0); - const auto status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER); - mIsRgb8Renderable = (status == LOCAL_GL_FRAMEBUFFER_COMPLETE); + const auto IsRenderable = [&](const GLint internalFormat, + const GLenum unpackFormat) { + gl->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, internalFormat, 1, 1, 0, + unpackFormat, LOCAL_GL_UNSIGNED_BYTE, nullptr); + const auto status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER); + return (status == LOCAL_GL_FRAMEBUFFER_COMPLETE); + }; + + if (IsRenderable(LOCAL_GL_RGB, LOCAL_GL_RGB)) { + mOptionalRenderableFormatBits |= + webgl::OptionalRenderableFormatBits::RGB8; + } + if (gl->IsSupported(gl::GLFeature::sRGB)) { + struct { + GLint internal; + GLenum unpack; + } formats = {LOCAL_GL_SRGB8, LOCAL_GL_RGB}; + const bool isEs2 = (gl->IsGLES() && gl->Version() < 300); + if (isEs2) { + formats = {LOCAL_GL_SRGB, LOCAL_GL_SRGB}; + } + if (IsRenderable(formats.internal, formats.unpack)) { + mOptionalRenderableFormatBits |= + webgl::OptionalRenderableFormatBits::SRGB8; + } + } } ////// diff --git a/dom/canvas/WebGLContext.h b/dom/canvas/WebGLContext.h index a4757232490e..5ab584174cb4 100644 --- a/dom/canvas/WebGLContext.h +++ b/dom/canvas/WebGLContext.h @@ -317,7 +317,8 @@ class WebGLContext : public VRefCounted, public SupportsWeakPtr { webgl::InitContextResult* out); private: - bool mIsRgb8Renderable = false; + webgl::OptionalRenderableFormatBits mOptionalRenderableFormatBits = + webgl::OptionalRenderableFormatBits{0}; void FinishInit(); protected: diff --git a/dom/canvas/WebGLIpdl.h b/dom/canvas/WebGLIpdl.h index af2777605a03..b3179d9703e3 100644 --- a/dom/canvas/WebGLIpdl.h +++ b/dom/canvas/WebGLIpdl.h @@ -641,6 +641,35 @@ struct ParamTraits> final { } }; +// - + +template +struct ParamTraits_IsEnumCase { + using T = TT; + + static void Write(IPC::MessageWriter* const writer, const T& in) { + MOZ_RELEASE_ASSERT(IsEnumCase(in)); + WriteParam(writer, mozilla::UnderlyingValue(in)); + } + + static bool Read(IPC::MessageReader* const reader, T* const out) { + std::underlying_type_t rawVal; + if (!ReadParam(reader, &rawVal)) return false; + *out = static_cast(rawVal); + return IsEnumCase(*out); + } +}; + +// - + +#define USE_IS_ENUM_CASE(T) \ + template <> \ + struct ParamTraits : public ParamTraits_IsEnumCase {}; + +USE_IS_ENUM_CASE(mozilla::webgl::OptionalRenderableFormatBits) + +#undef USE_IS_ENUM_CASE + } // namespace IPC #endif diff --git a/dom/canvas/WebGLTypes.h b/dom/canvas/WebGLTypes.h index c91108f46d49..fa6bd63ab8b8 100644 --- a/dom/canvas/WebGLTypes.h +++ b/dom/canvas/WebGLTypes.h @@ -19,12 +19,14 @@ #include "ImageContainer.h" #include "mozilla/Casting.h" #include "mozilla/CheckedInt.h" +#include "mozilla/EnumTypeTraits.h" #include "mozilla/MathAlgorithms.h" #include "mozilla/Range.h" #include "mozilla/RefCounted.h" #include "mozilla/Result.h" #include "mozilla/ResultVariant.h" #include "mozilla/Span.h" +#include "mozilla/TypedEnumBits.h" #include "mozilla/gfx/2D.h" #include "mozilla/gfx/BuildConstants.h" #include "mozilla/gfx/Logging.h" @@ -679,18 +681,41 @@ struct Padded { // - +enum class OptionalRenderableFormatBits : uint8_t { + RGB8 = (1 << 0), + SRGB8 = (1 << 1), +}; +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(OptionalRenderableFormatBits) +inline constexpr bool IsEnumCase(const OptionalRenderableFormatBits raw) { + auto rawWithoutValidBits = UnderlyingValue(raw); + auto bit = decltype(rawWithoutValidBits){1}; + while (bit) { + switch (OptionalRenderableFormatBits{bit}) { + // -Werror=switch ensures exhaustive. + case OptionalRenderableFormatBits::RGB8: + case OptionalRenderableFormatBits::SRGB8: + rawWithoutValidBits &= ~bit; + break; + } + bit <<= 1; + } + return rawWithoutValidBits == 0; +} + +// - + struct InitContextResult final { Padded error; // MINGW 32-bit needs this padding. WebGLContextOptions options; gl::GLVendor vendor; - bool isRgb8Renderable; + OptionalRenderableFormatBits optionalRenderableFormatBits; uint8_t _padding = {}; - webgl::Limits limits; + Limits limits; EnumMask uploadableSdTypes; auto MutTiedFields() { - return std::tie(error, options, vendor, isRgb8Renderable, _padding, limits, - uploadableSdTypes); + return std::tie(error, options, vendor, optionalRenderableFormatBits, + _padding, limits, uploadableSdTypes); } };