From e722cdd3ed3557bc7ceea89ed707f0aacb4cfa8f Mon Sep 17 00:00:00 2001 From: Kelsey Gilbert Date: Mon, 2 May 2022 23:54:09 +0000 Subject: [PATCH] Bug 1703654 - Prototype display-p3 for WebGL canvas. r=lsalzman,emilio,webidl,smaug Enable (direct) external surface compositing for MacIOSurfaces. Works on Mac. Differential Revision: https://phabricator.services.mozilla.com/D144073 --- dom/canvas/ClientWebGLContext.cpp | 8 +++++++ dom/canvas/WebGLContext.cpp | 11 ++++++--- dom/canvas/WebGLTypes.h | 21 ++++++++++++++++ dom/webidl/WebGLRenderingContext.webidl | 5 ++++ gfx/2d/MacIOSurface.cpp | 24 +++++++++++++++++++ gfx/2d/MacIOSurface.h | 2 ++ gfx/2d/Types.h | 19 ++++++++++++++- gfx/gl/GLScreenBuffer.cpp | 17 +++++++++---- gfx/gl/GLScreenBuffer.h | 2 +- gfx/gl/SharedSurface.h | 19 +++++++++++++-- gfx/gl/SharedSurfaceDMABUF.h | 3 ++- gfx/gl/SharedSurfaceIO.cpp | 4 ++++ gfx/layers/NativeLayerCA.mm | 2 -- gfx/layers/ipc/LayersSurfaces.ipdlh | 3 ++- .../opengl/MacIOSurfaceTextureHostOGL.cpp | 2 +- modules/libpref/init/StaticPrefList.yaml | 5 ++++ 16 files changed, 130 insertions(+), 17 deletions(-) diff --git a/dom/canvas/ClientWebGLContext.cpp b/dom/canvas/ClientWebGLContext.cpp index 6edbffc6cddb..4d1bdb5c47cb 100644 --- a/dom/canvas/ClientWebGLContext.cpp +++ b/dom/canvas/ClientWebGLContext.cpp @@ -837,11 +837,19 @@ ClientWebGLContext::SetContextOptions(JSContext* cx, newOpts.antialias = attributes.mAntialias.Value(); } + if (attributes.mColorSpace.WasPassed()) { + newOpts.colorSpace = Some(attributes.mColorSpace.Value()); + } else if (StaticPrefs::gfx_color_management_native_srgb()) { + newOpts.colorSpace = Some(dom::PredefinedColorSpace::Srgb); + } + // Don't do antialiasing if we've disabled MSAA. if (!StaticPrefs::webgl_msaa_samples()) { newOpts.antialias = false; } + // - + if (mInitialOptions && *mInitialOptions != newOpts) { // Err if the options asked for aren't the same as what they were // originally. diff --git a/dom/canvas/WebGLContext.cpp b/dom/canvas/WebGLContext.cpp index c485757f28b8..3e08e0566a99 100644 --- a/dom/canvas/WebGLContext.cpp +++ b/dom/canvas/WebGLContext.cpp @@ -113,6 +113,7 @@ bool WebGLContextOptions::operator==(const WebGLContextOptions& r) const { eq &= (failIfMajorPerformanceCaveat == r.failIfMajorPerformanceCaveat); eq &= (xrCompatible == r.xrCompatible); eq &= (powerPreference == r.powerPreference); + eq &= (colorSpace == r.colorSpace); return eq; } @@ -875,7 +876,8 @@ bool WebGLContext::PresentInto(gl::SwapChain& swapChain) { if (!ValidateAndInitFB(nullptr)) return false; { - auto presenter = swapChain.Acquire(mDefaultFB->mSize); + const auto colorSpace = gfx::ToColorSpace2(mOptions.colorSpace); + auto presenter = swapChain.Acquire(mDefaultFB->mSize, colorSpace); if (!presenter) { GenerateWarning("Swap chain surface creation failed."); LoseContext(); @@ -916,7 +918,8 @@ bool WebGLContext::PresentIntoXR(gl::SwapChain& swapChain, const gl::MozFramebuffer& fb) { OnEndOfFrame(); - auto presenter = swapChain.Acquire(fb.mSize); + const auto colorSpace = gfx::ToColorSpace2(mOptions.colorSpace); + auto presenter = swapChain.Acquire(fb.mSize, colorSpace); if (!presenter) { GenerateWarning("Swap chain surface creation failed."); LoseContext(); @@ -1002,7 +1005,9 @@ void WebGLContext::CopyToSwapChain(WebGLFramebuffer* const srcFb, InitSwapChain(*gl, srcFb->mSwapChain, consumerType); - auto presenter = srcFb->mSwapChain.Acquire(size); + // ColorSpace will need to be part of SwapChainOptions for DTWebgl. + const auto colorSpace = gfx::ToColorSpace2(mOptions.colorSpace); + auto presenter = srcFb->mSwapChain.Acquire(size, colorSpace); if (!presenter) { GenerateWarning("Swap chain surface creation failed."); LoseContext(); diff --git a/dom/canvas/WebGLTypes.h b/dom/canvas/WebGLTypes.h index e673ff48d49d..6ffaf1d10614 100644 --- a/dom/canvas/WebGLTypes.h +++ b/dom/canvas/WebGLTypes.h @@ -364,6 +364,7 @@ struct WebGLContextOptions { bool xrCompatible = false; dom::WebGLPowerPreference powerPreference = dom::WebGLPowerPreference::Default; + Maybe colorSpace; bool shouldResistFingerprinting = true; bool enableDebugRendererInfo = false; @@ -376,6 +377,26 @@ struct WebGLContextOptions { } }; +namespace gfx { + +inline ColorSpace2 ToColorSpace2(const Maybe cs) { + if (!cs) { + return ColorSpace2::UNKNOWN; + } + + switch (*cs) { + case dom::PredefinedColorSpace::Srgb: + return ColorSpace2::SRGB; + case dom::PredefinedColorSpace::Display_p3: + return ColorSpace2::DISPLAY_P3; + case dom::PredefinedColorSpace::EndGuard_: + break; + } + MOZ_CRASH("Exhaustive switch"); +} + +} // namespace gfx + // - template diff --git a/dom/webidl/WebGLRenderingContext.webidl b/dom/webidl/WebGLRenderingContext.webidl index 1909a8904593..e0badb1d6479 100644 --- a/dom/webidl/WebGLRenderingContext.webidl +++ b/dom/webidl/WebGLRenderingContext.webidl @@ -35,6 +35,7 @@ typedef unsigned long long GLuint64EXT; // The power preference settings are documented in the WebGLContextAttributes // section of the specification. enum WebGLPowerPreference { "default", "low-power", "high-performance" }; +enum PredefinedColorSpace { "srgb", "display-p3" }; [GenerateInit] dictionary WebGLContextAttributes { @@ -51,6 +52,10 @@ dictionary WebGLContextAttributes { GLboolean preserveDrawingBuffer = false; GLboolean failIfMajorPerformanceCaveat = false; WebGLPowerPreference powerPreference = "default"; + + // We are experimenting here, though this should be close to where we end up. + [Pref="webgl.colorspaces.prototype"] + PredefinedColorSpace colorSpace; // = "srgb"; Default is gfx::ColorSpace2::UNKNOWN for now. }; [Exposed=(Window,Worker), diff --git a/gfx/2d/MacIOSurface.cpp b/gfx/2d/MacIOSurface.cpp index 2f82d273324c..c8c3e2e09c72 100644 --- a/gfx/2d/MacIOSurface.cpp +++ b/gfx/2d/MacIOSurface.cpp @@ -634,3 +634,27 @@ CGLError MacIOSurface::CGLTexImageIOSurface2D( } return err; } + +void MacIOSurface::SetColorSpace(const mozilla::gfx::ColorSpace2 cs) const { + Maybe str; + switch (cs) { + case gfx::ColorSpace2::UNKNOWN: + break; + case gfx::ColorSpace2::SRGB: + str = Some(kCGColorSpaceSRGB); + break; + case gfx::ColorSpace2::DISPLAY_P3: + str = Some(kCGColorSpaceDisplayP3); + break; + case gfx::ColorSpace2::BT601_525: // Doesn't really have a better option. + case gfx::ColorSpace2::BT709: + str = Some(kCGColorSpaceITUR_709); + break; + case gfx::ColorSpace2::BT2020: + str = Some(kCGColorSpaceITUR_2020); + break; + } + if (str) { + IOSurfaceSetValue(mIOSurfaceRef.get(), CFSTR("IOSurfaceColorSpace"), *str); + } +} diff --git a/gfx/2d/MacIOSurface.h b/gfx/2d/MacIOSurface.h index 8c9522e27f20..dc11a6f49a56 100644 --- a/gfx/2d/MacIOSurface.h +++ b/gfx/2d/MacIOSurface.h @@ -152,6 +152,8 @@ class MacIOSurface final static size_t GetMaxHeight(); CFTypeRefPtr GetIOSurfaceRef() { return mIOSurfaceRef; } + void SetColorSpace(mozilla::gfx::ColorSpace2) const; + private: CFTypeRefPtr mIOSurfaceRef; const bool mHasAlpha; diff --git a/gfx/2d/Types.h b/gfx/2d/Types.h index ec4765f30e75..62e0e2398d90 100644 --- a/gfx/2d/Types.h +++ b/gfx/2d/Types.h @@ -365,7 +365,7 @@ enum class ColorRange : uint8_t { _Last = FULL, }; -// Really "YcbcrColorSpace" +// Really "YcbcrColorColorSpace" enum class YUVRangedColorSpace : uint8_t { BT601_Narrow = 0, BT601_Full, @@ -380,6 +380,23 @@ enum class YUVRangedColorSpace : uint8_t { Default = BT709_Narrow, }; +// I can either come up with a longer "very clever" name that doesn't conflict +// with FilterSupport.h, embrace and expand FilterSupport, or rename the old +// one. +// Some times Worse Is Better. +enum class ColorSpace2 : uint8_t { + UNKNOWN, // Eventually we will remove this. + SRGB, + BT601_525, // aka smpte170m NTSC + BT709, // Same gamut as SRGB, but different gamma. + BT601_625 = + BT709, // aka bt470bg PAL. Basically BT709, just Xg is 0.290 not 0.300. + BT2020, + DISPLAY_P3, + _First = UNKNOWN, + _Last = DISPLAY_P3, +}; + struct FromYUVRangedColorSpaceT final { const YUVColorSpace space; const ColorRange range; diff --git a/gfx/gl/GLScreenBuffer.cpp b/gfx/gl/GLScreenBuffer.cpp index ef8c37416c20..1fe35bdfcd30 100644 --- a/gfx/gl/GLScreenBuffer.cpp +++ b/gfx/gl/GLScreenBuffer.cpp @@ -26,20 +26,27 @@ static constexpr size_t kPoolSize = 0; #endif -UniquePtr SwapChain::Acquire(const gfx::IntSize& size) { +UniquePtr SwapChain::Acquire( + const gfx::IntSize& size, const gfx::ColorSpace2 colorSpace) { MOZ_ASSERT(mFactory); std::shared_ptr surf; - if (!mPool.empty() && - (mPool.front()->mDesc.size != size || !mPool.front()->IsValid())) { - mPool = {}; + if (!mPool.empty()) { + // Try reuse + const auto& existingDesc = mPool.front()->mDesc; + auto newDesc = existingDesc; + newDesc.size = size; + newDesc.colorSpace = colorSpace; + if (newDesc != existingDesc || !mPool.front()->IsValid()) { + mPool = {}; + } } if (kPoolSize && mPool.size() == kPoolSize) { surf = mPool.front(); mPool.pop(); } if (!surf) { - auto uniquePtrSurf = mFactory->CreateShared(size); + auto uniquePtrSurf = mFactory->CreateShared(size, colorSpace); if (!uniquePtrSurf) return nullptr; surf.reset(uniquePtrSurf.release()); } diff --git a/gfx/gl/GLScreenBuffer.h b/gfx/gl/GLScreenBuffer.h index 17a4bd9c2f8c..d6e74d866d2d 100644 --- a/gfx/gl/GLScreenBuffer.h +++ b/gfx/gl/GLScreenBuffer.h @@ -69,7 +69,7 @@ class SwapChain final { void ClearPool(); const auto& FrontBuffer() const { return mFrontBuffer; } - UniquePtr Acquire(const gfx::IntSize&); + UniquePtr Acquire(const gfx::IntSize&, gfx::ColorSpace2); }; } // namespace gl diff --git a/gfx/gl/SharedSurface.h b/gfx/gl/SharedSurface.h index 060db717c01d..8e283695391a 100644 --- a/gfx/gl/SharedSurface.h +++ b/gfx/gl/SharedSurface.h @@ -60,9 +60,23 @@ struct PartialSharedSurfaceDesc { const SharedSurfaceType type; const layers::TextureType consumerType; const bool canRecycle; + + bool operator==(const PartialSharedSurfaceDesc& rhs) const { + return gl == rhs.gl && type == rhs.type && + consumerType == rhs.consumerType && canRecycle == rhs.canRecycle; + } }; struct SharedSurfaceDesc : public PartialSharedSurfaceDesc { gfx::IntSize size = {}; + gfx::ColorSpace2 colorSpace = gfx::ColorSpace2::UNKNOWN; + + bool operator==(const SharedSurfaceDesc& rhs) const { + return PartialSharedSurfaceDesc::operator==(rhs) && size == rhs.size && + colorSpace == rhs.colorSpace; + } + bool operator!=(const SharedSurfaceDesc& rhs) const { + return !(*this == rhs); + } }; class SharedSurface { @@ -166,8 +180,9 @@ class SurfaceFactory { const SharedSurfaceDesc&) = 0; public: - UniquePtr CreateShared(const gfx::IntSize& size) { - return CreateSharedImpl({mDesc, size}); + UniquePtr CreateShared(const gfx::IntSize& size, + gfx::ColorSpace2 cs) { + return CreateSharedImpl({mDesc, size, cs}); } }; diff --git a/gfx/gl/SharedSurfaceDMABUF.h b/gfx/gl/SharedSurfaceDMABUF.h index 29eae088f0f3..89430a8f758a 100644 --- a/gfx/gl/SharedSurfaceDMABUF.h +++ b/gfx/gl/SharedSurfaceDMABUF.h @@ -60,7 +60,8 @@ class SurfaceFactory_DMABUF : public SurfaceFactory { } bool CanCreateSurface() { - UniquePtr test = CreateShared(gfx::IntSize(1, 1)); + UniquePtr test = + CreateShared(gfx::IntSize(1, 1), gfx::ColorSpace2::SRGB); return test != nullptr; } }; diff --git a/gfx/gl/SharedSurfaceIO.cpp b/gfx/gl/SharedSurfaceIO.cpp index d1ab389f7360..1fd0f22d31f8 100644 --- a/gfx/gl/SharedSurfaceIO.cpp +++ b/gfx/gl/SharedSurfaceIO.cpp @@ -60,6 +60,10 @@ UniquePtr SharedSurface_IOSurface::Create( return nullptr; } + ioSurf->SetColorSpace(desc.colorSpace); + + // - + auto tex = MakeUnique(*desc.gl); BackTextureWithIOSurf(desc.gl, tex->name, ioSurf); diff --git a/gfx/layers/NativeLayerCA.mm b/gfx/layers/NativeLayerCA.mm index 792a4eb2ddb9..fb0e7a2fadab 100644 --- a/gfx/layers/NativeLayerCA.mm +++ b/gfx/layers/NativeLayerCA.mm @@ -1322,8 +1322,6 @@ bool NativeLayerCA::Representation::EnqueueSurface(IOSurfaceRef aSurfaceRef) { CFTypeRefPtr colorSpace = CFTypeRefPtr::WrapUnderGetRule(CVImageBufferGetColorSpace(pixelBuffer)); if (!colorSpace) { - printf("VIDEO_LOG: pixel buffer created by EnqueueSurface has no color space.\n"); - // Use our main display color space. colorSpace = CFTypeRefPtr::WrapUnderCreateRule( CGDisplayCopyColorSpace(CGMainDisplayID())); diff --git a/gfx/layers/ipc/LayersSurfaces.ipdlh b/gfx/layers/ipc/LayersSurfaces.ipdlh index 27feaa97c543..478950c5d83f 100644 --- a/gfx/layers/ipc/LayersSurfaces.ipdlh +++ b/gfx/layers/ipc/LayersSurfaces.ipdlh @@ -11,9 +11,10 @@ using mozilla::StereoMode from "ImageTypes.h"; using struct mozilla::null_t from "mozilla/ipc/IPCCore.h"; using mozilla::WindowsHandle from "mozilla/ipc/IPCTypes.h"; using mozilla::gfx::YUVColorSpace from "mozilla/gfx/Types.h"; +using mozilla::gfx::ChromaSubsampling from "mozilla/gfx/Types.h"; using mozilla::gfx::ColorDepth from "mozilla/gfx/Types.h"; using mozilla::gfx::ColorRange from "mozilla/gfx/Types.h"; -using mozilla::gfx::ChromaSubsampling from "mozilla/gfx/Types.h"; +using mozilla::gfx::ColorSpace2 from "mozilla/gfx/Types.h"; using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h"; using mozilla::gfx::IntRect from "mozilla/gfx/Rect.h"; using mozilla::gfx::IntSize from "mozilla/gfx/Point.h"; diff --git a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp index b5b9ebd4ea6c..072619f31760 100644 --- a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp +++ b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp @@ -192,7 +192,7 @@ void MacIOSurfaceTextureHostOGL::PushDisplayItems( !(mFlags & TextureFlags::NON_PREMULTIPLIED), wr::ColorF{1.0f, 1.0f, 1.0f, 1.0f}, preferCompositorSurface, - /* aSupportsExternalCompositing */ false); + /* aSupportsExternalCompositing */ true); break; } case gfx::SurfaceFormat::YUV422: { diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index 47b07623c445..355633a2c7f9 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -12670,6 +12670,11 @@ value: true mirror: always +- name: webgl.colorspaces.prototype + type: RelaxedAtomicBool + value: false + mirror: always + - name: webgl.debug.incomplete-tex-color type: RelaxedAtomicUint32 value: 0