From 3fa084ce2f7303584abbda47aaa385daedd2a3f7 Mon Sep 17 00:00:00 2001 From: smolnar Date: Thu, 30 Jun 2022 21:19:17 +0300 Subject: [PATCH] Backed out 3 changesets (bug 1771374) for causing build bustage in gfx/gl/gtest/TestColorspaces.cpp CLOSED TREE Backed out changeset 4d01f260c43a (bug 1771374) Backed out changeset fc62679f0a6c (bug 1771374) Backed out changeset f4f12470f8b7 (bug 1771374) --- dom/canvas/ClientWebGLContext.cpp | 5 +- dom/canvas/WebGLContext.cpp | 11 +- dom/canvas/WebGLIpdl.h | 7 +- dom/canvas/WebGLTypes.h | 3 +- dom/media/platforms/wmf/DXVA2Manager.cpp | 7 +- gfx/2d/Types.h | 38 +- gfx/gl/AutoMappable.h | 148 --- gfx/gl/Colorspaces.cpp | 229 ----- gfx/gl/Colorspaces.h | 658 ------------ gfx/gl/GLBlitHelper.cpp | 630 ++++-------- gfx/gl/GLBlitHelper.h | 82 +- gfx/gl/GLBlitHelperD3D.cpp | 39 +- gfx/gl/SharedSurfaceANGLE.cpp | 2 +- gfx/gl/SharedSurfaceD3D11Interop.cpp | 2 +- gfx/gl/gtest/TestColorspaces.cpp | 403 -------- gfx/gl/gtest/moz.build | 15 - gfx/gl/moz.build | 7 - gfx/ipc/GfxMessageUtils.h | 6 - gfx/layers/D3D11ShareHandleImage.cpp | 18 +- gfx/layers/D3D11ShareHandleImage.h | 11 +- gfx/layers/D3D11TextureIMFSampleImage.cpp | 6 +- gfx/layers/D3D11TextureIMFSampleImage.h | 9 +- gfx/layers/D3D9SurfaceImage.cpp | 10 +- gfx/layers/d3d11/HelpersD3D11.h | 11 - gfx/layers/d3d11/TextureD3D11.cpp | 19 +- gfx/layers/d3d11/TextureD3D11.h | 21 +- gfx/layers/ipc/LayersSurfaces.ipdlh | 2 +- gfx/thebes/gfxPlatform.cpp | 16 +- gfx/thebes/gfxPlatform.h | 2 - gfx/webrender_bindings/DCLayerTree.cpp | 963 +++++------------- gfx/webrender_bindings/DCLayerTree.h | 108 +- .../RenderD3D11TextureHost.cpp | 4 +- .../RenderD3D11TextureHost.h | 10 +- modules/libpref/init/StaticPrefList.yaml | 31 - 34 files changed, 621 insertions(+), 2912 deletions(-) delete mode 100644 gfx/gl/AutoMappable.h delete mode 100644 gfx/gl/Colorspaces.cpp delete mode 100644 gfx/gl/Colorspaces.h delete mode 100644 gfx/gl/gtest/TestColorspaces.cpp delete mode 100644 gfx/gl/gtest/moz.build diff --git a/dom/canvas/ClientWebGLContext.cpp b/dom/canvas/ClientWebGLContext.cpp index 91157966c83c..783224ec8a32 100644 --- a/dom/canvas/ClientWebGLContext.cpp +++ b/dom/canvas/ClientWebGLContext.cpp @@ -865,7 +865,10 @@ ClientWebGLContext::SetContextOptions(JSContext* cx, } if (attributes.mColorSpace.WasPassed()) { - newOpts.colorSpace = Some(attributes.mColorSpace.Value()); + newOpts.colorSpace = attributes.mColorSpace.Value(); + } + if (StaticPrefs::gfx_color_management_native_srgb()) { + newOpts.ignoreColorSpace = false; } // Don't do antialiasing if we've disabled MSAA. diff --git a/dom/canvas/WebGLContext.cpp b/dom/canvas/WebGLContext.cpp index ad9a2fd12415..137fd632084d 100644 --- a/dom/canvas/WebGLContext.cpp +++ b/dom/canvas/WebGLContext.cpp @@ -114,6 +114,7 @@ bool WebGLContextOptions::operator==(const WebGLContextOptions& r) const { eq &= (xrCompatible == r.xrCompatible); eq &= (powerPreference == r.powerPreference); eq &= (colorSpace == r.colorSpace); + eq &= (ignoreColorSpace == r.ignoreColorSpace); return eq; } @@ -899,14 +900,10 @@ constexpr auto MakeArray(Args... args) -> std::array { } inline gfx::ColorSpace2 ToColorSpace2(const WebGLContextOptions& options) { - auto ret = gfx::ColorSpace2::UNKNOWN; - if (StaticPrefs::gfx_color_management_native_srgb()) { - ret = gfx::ColorSpace2::SRGB; + if (options.ignoreColorSpace) { + return gfx::ColorSpace2::UNKNOWN; } - if (options.colorSpace) { - ret = gfx::ToColorSpace2(*options.colorSpace); - } - return ret; + return gfx::ToColorSpace2(options.colorSpace); } // - diff --git a/dom/canvas/WebGLIpdl.h b/dom/canvas/WebGLIpdl.h index b28c39557dc2..8ccaded1bfd8 100644 --- a/dom/canvas/WebGLIpdl.h +++ b/dom/canvas/WebGLIpdl.h @@ -248,12 +248,7 @@ struct ParamTraits final using T = mozilla::WebGLContextOptions; static bool Validate(const T& val) { - bool ok = true; - ok &= ValidateParam(val.powerPreference); - if (val.colorSpace) { - ok &= ValidateParam(*val.colorSpace); - } - return ok; + return ValidateParam(val.powerPreference) && ValidateParam(val.colorSpace); } }; diff --git a/dom/canvas/WebGLTypes.h b/dom/canvas/WebGLTypes.h index fc122034278b..f29ed91897c0 100644 --- a/dom/canvas/WebGLTypes.h +++ b/dom/canvas/WebGLTypes.h @@ -359,7 +359,8 @@ struct WebGLContextOptions { bool xrCompatible = false; dom::WebGLPowerPreference powerPreference = dom::WebGLPowerPreference::Default; - Maybe colorSpace; + dom::PredefinedColorSpace colorSpace = dom::PredefinedColorSpace::Srgb; + bool ignoreColorSpace = true; // Our legacy behavior. bool shouldResistFingerprinting = true; bool enableDebugRendererInfo = false; diff --git a/dom/media/platforms/wmf/DXVA2Manager.cpp b/dom/media/platforms/wmf/DXVA2Manager.cpp index 972cda21bf83..85d692d7a70a 100644 --- a/dom/media/platforms/wmf/DXVA2Manager.cpp +++ b/dom/media/platforms/wmf/DXVA2Manager.cpp @@ -1047,9 +1047,8 @@ D3D11DXVA2Manager::CopyToImage(IMFSample* aVideoSample, NS_ENSURE_TRUE(aOutImage, E_POINTER); MOZ_ASSERT(mTextureClientAllocator); - RefPtr image = - new D3D11ShareHandleImage(gfx::IntSize(mWidth, mHeight), aRegion, - ToColorSpace2(mYUVColorSpace), mColorRange); + RefPtr image = new D3D11ShareHandleImage( + gfx::IntSize(mWidth, mHeight), aRegion, mYUVColorSpace, mColorRange); // Retrieve the DXGI_FORMAT for the current video sample. RefPtr buffer; @@ -1174,7 +1173,7 @@ HRESULT D3D11DXVA2Manager::WrapTextureWithImage(IMFSample* aVideoSample, RefPtr image = new D3D11TextureIMFSampleImage( aVideoSample, texture, arrayIndex, gfx::IntSize(mWidth, mHeight), aRegion, - ToColorSpace2(mYUVColorSpace), mColorRange); + mYUVColorSpace, mColorRange); image->AllocateTextureClient(mKnowsCompositor, mIMFSampleUsageInfo); RefPtr wrapper = image->GetIMFSampleWrapper(); diff --git a/gfx/2d/Types.h b/gfx/2d/Types.h index 80149f77ab2f..6ff4341e514a 100644 --- a/gfx/2d/Types.h +++ b/gfx/2d/Types.h @@ -386,50 +386,18 @@ enum class YUVRangedColorSpace : uint8_t { // one. // Some times Worse Is Better. enum class ColorSpace2 : uint8_t { - UNKNOWN, // Really "DISPLAY". Eventually we will remove this. + UNKNOWN, // Eventually we will remove this. SRGB, - DISPLAY_P3, 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 = BT2020, + _Last = DISPLAY_P3, }; -inline ColorSpace2 ToColorSpace2(const YUVColorSpace in) { - switch (in) { - case YUVColorSpace::BT601: - return ColorSpace2::BT601_525; - case YUVColorSpace::BT709: - return ColorSpace2::BT709; - case YUVColorSpace::BT2020: - return ColorSpace2::BT2020; - case YUVColorSpace::Identity: - return ColorSpace2::SRGB; - } - MOZ_ASSERT_UNREACHABLE(); -} - -inline YUVColorSpace ToYUVColorSpace(const ColorSpace2 in) { - switch (in) { - case ColorSpace2::BT601_525: - return YUVColorSpace::BT601; - case ColorSpace2::BT709: - return YUVColorSpace::BT709; - case ColorSpace2::BT2020: - return YUVColorSpace::BT2020; - case ColorSpace2::SRGB: - return YUVColorSpace::Identity; - - case ColorSpace2::UNKNOWN: - case ColorSpace2::DISPLAY_P3: - MOZ_CRASH("Bad ColorSpace2 for ToYUVColorSpace"); - } - MOZ_ASSERT_UNREACHABLE(); -} - struct FromYUVRangedColorSpaceT final { const YUVColorSpace space; const ColorRange range; diff --git a/gfx/gl/AutoMappable.h b/gfx/gl/AutoMappable.h deleted file mode 100644 index f93b2ccb5738..000000000000 --- a/gfx/gl/AutoMappable.h +++ /dev/null @@ -1,148 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef MOZILLA_AUTO_MAPPABLE_H -#define MOZILLA_AUTO_MAPPABLE_H - -// Here be dragons. - -#include - -namespace mozilla::gfx { - -template -size_t Hash(const T&); - -template -struct StaticStdHasher { - static auto HashImpl(const T& v) { return std::hash()(v); } -}; - -template -struct StaticHasher { - static auto HashImpl(const T& v) { return v.hash(); } -}; -template -struct StaticHasher> { - static size_t HashImpl(const std::optional& v) { - if (!v) return 0; - return Hash(*v); - } -}; -template <> -struct StaticHasher : public StaticStdHasher {}; -template <> -struct StaticHasher : public StaticStdHasher {}; -template <> -struct StaticHasher : public StaticStdHasher {}; - -template -size_t Hash(const T& v) { - return StaticHasher::HashImpl(v); -} - -//- -// From Boost: -// https://www.boost.org/doc/libs/1_37_0/doc/html/hash/reference.html#boost.hash_combine - -inline size_t HashCombine(size_t seed, const size_t hash) { - seed ^= hash + 0x9e3779b9 + (seed << 6) + (seed >> 2); - return seed; -} - -// - -// See -// https://codereview.stackexchange.com/questions/136770/hashing-a-tuple-in-c17 - -template -size_t HashTupleN(const std::tuple& tup, - const std::index_sequence&) { - size_t seed = 0; - for (const auto& hash : {Hash(std::get(tup))...}) { - seed = HashCombine(seed, hash); - } - return seed; -} - -template -size_t HashTuple(const std::tuple& tup) { - return HashTupleN(tup, std::make_index_sequence()); -} - -// - - -template -auto MembersEq(const T& a, const T& b) { - const auto atup = a.Members(); - const auto btup = b.Members(); - return atup == btup; -} - -template -auto MembersLt(const T& a, const T& b) { - const auto atup = a.Members(); - const auto btup = b.Members(); - return atup == btup; -} - -template -auto MembersHash(const T& a) { - const auto atup = a.Members(); - return HashTuple(atup); -} - -template -struct MembersHasher final { - auto operator()(const T& v) const { return v.hash(); } -}; - -/** E.g.: -struct Foo { - int i; - bool b; - - auto Members() const { return std::tie(i, b); } - INLINE_AUTO_MAPPABLE(Foo) -}; -std::unordered_set easy; -**/ -#define INLINE_DERIVE_MEMBERS_EQ(T) \ - friend bool operator==(const T& a, const T& b) { \ - return mozilla::gfx::MembersEq(a, b); \ - } \ - friend bool operator!=(const T& a, const T& b) { return !operator==(a, b); } -#define INLINE_AUTO_MAPPABLE(T) \ - friend bool operator<(const T& a, const T& b) { \ - return mozilla::gfx::MembersLt(a, b); \ - } \ - INLINE_DERIVE_MEMBERS_EQ(T) \ - size_t hash() const { \ - return mozilla::gfx::MembersHash(*reinterpret_cast(this)); \ - } \ - using Hasher = mozilla::gfx::MembersHasher; - -// - - -/** E.g.: -``` -struct Foo : public AutoMappable { - int i; - bool b; - - auto Members() const { return std::tie(i, b); } -}; -std::unordered_set easy; -``` -`easy.insert({{}, 2, true});` -The initial {} is needed for aggregate initialization of AutoMappable. -Use INLINE_AUTO_MAPPABLE if this is too annoying. -**/ -template -struct AutoMappable { - INLINE_AUTO_MAPPABLE(T) -}; - -} // namespace mozilla::gfx - -#endif // MOZILLA_AUTO_MAPPABLE_H diff --git a/gfx/gl/Colorspaces.cpp b/gfx/gl/Colorspaces.cpp deleted file mode 100644 index 8ef15a58d77d..000000000000 --- a/gfx/gl/Colorspaces.cpp +++ /dev/null @@ -1,229 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -// We are going to be doing so, so many transforms, so descriptive labels are -// critical. - -#include "Colorspaces.h" - -namespace mozilla::color { - -// tf = { k * linear | linear < b -// { a * pow(linear, 1/g) - (1-a) | linear >= b -float TfFromLinear(const PiecewiseGammaDesc& desc, const float linear) { - if (linear < desc.b) { - return linear * desc.k; - } - float ret = linear; - ret = powf(ret, 1.0f / desc.g); - ret *= desc.a; - ret -= (desc.a - 1); - return ret; -} - -float LinearFromTf(const PiecewiseGammaDesc& desc, const float tf) { - const auto linear_if_low = tf / desc.k; - if (linear_if_low < desc.b) { - return linear_if_low; - } - float ret = tf; - ret += (desc.a - 1); - ret /= desc.a; - ret = powf(ret, 1.0f * desc.g); - return ret; -} - -// - - -mat3 YuvFromRgb(const YuvLumaCoeffs& yc) { - // Y is always [0,1] - // U and V are signed, and could be either [-1,+1] or [-0.5,+0.5]. - // Specs generally use [-0.5,+0.5], so we use that too. - // E.g. - // y = 0.2126*r + 0.7152*g + 0.0722*b - // u = (b - y) / (u_range = u_max - u_min) // u_min = -u_max - // = (b - y) / (u(0,0,1) - u(1,1,0)) - // = (b - y) / (2 * u(0,0,1)) - // = (b - y) / (2 * u.b)) - // = (b - y) / (2 * (1 - 0.0722)) - // = (-0.2126*r + -0.7152*g + (1-0.0722)*b) / 1.8556 - // v = (r - y) / 1.5748; - // = ((1-0.2126)*r + -0.7152*g + -0.0722*b) / 1.5748 - const auto y = vec3({yc.r, yc.g, yc.b}); - const auto u = vec3({0, 0, 1}) - y; - const auto v = vec3({1, 0, 0}) - y; - - // From rows: - return mat3({y, u / (2 * u.z()), v / (2 * v.x())}); -} - -mat4 YuvFromYcbcr(const YcbcrDesc& d) { - // E.g. - // y = (yy - 16) / (235 - 16); // 16->0, 235->1 - // u = (cb - 128) / (240 - 16); // 16->-0.5, 128->0, 240->+0.5 - // v = (cr - 128) / (240 - 16); - - const auto yRange = d.y1 - d.y0; - const auto uHalfRange = d.uPlusHalf - d.u0; - const auto uRange = 2 * uHalfRange; - - const auto ycbcrFromYuv = mat4{{vec4{{yRange, 0, 0, d.y0}}, - {{0, uRange, 0, d.u0}}, - {{0, 0, uRange, d.u0}}, - {{0, 0, 0, 1}}}}; - const auto yuvFromYcbcr = inverse(ycbcrFromYuv); - return yuvFromYcbcr; -} - -mat3 XyzFromLinearRgb(const Chromaticities& c) { - // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html - - // Given red (xr, yr), green (xg, yg), blue (xb, yb), - // and whitepoint (XW, YW, ZW) - - // [ X ] [ R ] - // [ Y ] = M x [ G ] - // [ Z ] [ B ] - - // [ Sr*Xr Sg*Xg Sb*Xb ] - // M = [ Sr*Yr Sg*Yg Sb*Yb ] - // [ Sr*Zr Sg*Zg Sb*Zb ] - - // Xr = xr / yr - // Yr = 1 - // Zr = (1 - xr - yr) / yr - - // Xg = xg / yg - // Yg = 1 - // Zg = (1 - xg - yg) / yg - - // Xb = xb / yb - // Yb = 1 - // Zb = (1 - xb - yb) / yb - - // [ Sr ] [ Xr Xg Xb ]^-1 [ XW ] - // [ Sg ] = [ Yr Yg Yb ] x [ YW ] - // [ Sb ] [ Zr Zg Zb ] [ ZW ] - - const auto xrgb = vec3({c.rx, c.gx, c.bx}); - const auto yrgb = vec3({c.ry, c.gy, c.by}); - - const auto Xrgb = xrgb / yrgb; - const auto Yrgb = vec3(1); - const auto Zrgb = (vec3(1) - xrgb - yrgb) / yrgb; - - const auto XYZrgb = mat3({Xrgb, Yrgb, Zrgb}); - const auto XYZrgb_inv = inverse(XYZrgb); - const auto XYZwhitepoint = vec3({c.wx, c.wy, 1 - c.wx - c.wy}); - const auto Srgb = XYZrgb_inv * XYZwhitepoint; - - const auto M = mat3({Srgb * Xrgb, Srgb * Yrgb, Srgb * Zrgb}); - return M; -} - -// - -ColorspaceTransform ColorspaceTransform::Create(const ColorspaceDesc& src, - const ColorspaceDesc& dst) { - auto ct = ColorspaceTransform{src, dst}; - ct.srcTf = src.tf; - ct.dstTf = dst.tf; - - const auto RgbTfFrom = [&](const ColorspaceDesc& cs) { - auto rgbFrom = mat4::Identity(); - if (cs.yuv) { - const auto yuvFromYcbcr = YuvFromYcbcr(cs.yuv->ycbcr); - const auto yuvFromRgb = YuvFromRgb(cs.yuv->yCoeffs); - const auto rgbFromYuv = inverse(yuvFromRgb); - const auto rgbFromYuv4 = mat4(rgbFromYuv); - - const auto rgbFromYcbcr = rgbFromYuv4 * yuvFromYcbcr; - rgbFrom = rgbFromYcbcr; - } - return rgbFrom; - }; - - ct.srcRgbTfFromSrc = RgbTfFrom(src); - const auto dstRgbTfFromDst = RgbTfFrom(dst); - ct.dstFromDstRgbTf = inverse(dstRgbTfFromDst); - - // - - - ct.dstRgbLinFromSrcRgbLin = mat3::Identity(); - if (!(src.chrom == dst.chrom)) { - const auto xyzFromSrcRgbLin = XyzFromLinearRgb(src.chrom); - const auto xyzFromDstRgbLin = XyzFromLinearRgb(dst.chrom); - const auto dstRgbLinFromXyz = inverse(xyzFromDstRgbLin); - ct.dstRgbLinFromSrcRgbLin = dstRgbLinFromXyz * xyzFromSrcRgbLin; - } - - return ct; -} - -vec3 ColorspaceTransform::DstFromSrc(const vec3 src) const { - const auto srcRgbTf = srcRgbTfFromSrc * vec4(src, 1); - auto srcRgbLin = srcRgbTf; - if (srcTf) { - srcRgbLin.x(LinearFromTf(*srcTf, srcRgbTf.x())); - srcRgbLin.y(LinearFromTf(*srcTf, srcRgbTf.y())); - srcRgbLin.z(LinearFromTf(*srcTf, srcRgbTf.z())); - } - - const auto dstRgbLin = dstRgbLinFromSrcRgbLin * vec3(srcRgbLin); - auto dstRgbTf = dstRgbLin; - if (dstTf) { - dstRgbTf.x(TfFromLinear(*dstTf, dstRgbLin.x())); - dstRgbTf.y(TfFromLinear(*dstTf, dstRgbLin.y())); - dstRgbTf.z(TfFromLinear(*dstTf, dstRgbLin.z())); - } - - const auto dst4 = dstFromDstRgbTf * vec4(dstRgbTf, 1); - return vec3(dst4); -} - -// - - -std::optional ColorspaceTransform::ToMat4() const { - mat4 fromSrc = srcRgbTfFromSrc; - if (srcTf) return {}; - fromSrc = mat4(dstRgbLinFromSrcRgbLin) * fromSrc; - if (dstTf) return {}; - fromSrc = dstFromDstRgbTf * fromSrc; - return fromSrc; -} - -Lut3 ColorspaceTransform::ToLut3(const ivec3 size) const { - auto lut = Lut3::Create(size); - lut.SetMap([&](const vec3& srcVal) { return DstFromSrc(srcVal); }); - return lut; -} - -vec3 Lut3::Sample(const vec3 in01) const { - const auto coord = vec3(size - 1) * in01; - const auto p0 = floor(coord); - const auto dp = coord - p0; - const auto ip0 = ivec3(p0); - - // Trilinear - const auto f000 = Fetch(ip0 + ivec3({0, 0, 0})); - const auto f100 = Fetch(ip0 + ivec3({1, 0, 0})); - const auto f010 = Fetch(ip0 + ivec3({0, 1, 0})); - const auto f110 = Fetch(ip0 + ivec3({1, 1, 0})); - const auto f001 = Fetch(ip0 + ivec3({0, 0, 1})); - const auto f101 = Fetch(ip0 + ivec3({1, 0, 1})); - const auto f011 = Fetch(ip0 + ivec3({0, 1, 1})); - const auto f111 = Fetch(ip0 + ivec3({1, 1, 1})); - - const auto fx00 = mix(f000, f100, dp.x()); - const auto fx10 = mix(f010, f110, dp.x()); - const auto fx01 = mix(f001, f101, dp.x()); - const auto fx11 = mix(f011, f111, dp.x()); - - const auto fxy0 = mix(fx00, fx10, dp.y()); - const auto fxy1 = mix(fx01, fx11, dp.y()); - - const auto fxyz = mix(fxy0, fxy1, dp.z()); - return fxyz; -} - -} // namespace mozilla::color diff --git a/gfx/gl/Colorspaces.h b/gfx/gl/Colorspaces.h deleted file mode 100644 index 04fe9e678fe0..000000000000 --- a/gfx/gl/Colorspaces.h +++ /dev/null @@ -1,658 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef MOZILLA_GFX_GL_COLORSPACES_H_ -#define MOZILLA_GFX_GL_COLORSPACES_H_ - -// Reference: https://hackmd.io/0wkiLmP7RWOFjcD13M870A - -// We are going to be doing so, so many transforms, so descriptive labels are -// critical. - -// Colorspace background info: https://hackmd.io/0wkiLmP7RWOFjcD13M870A - -#include -#include -#include -#include -#include -#include - -#include "AutoMappable.h" -#include "mozilla/Attributes.h" - -#ifdef DEBUG -# define ASSERT(EXPR) \ - do { \ - if (!(EXPR)) { \ - __builtin_trap(); \ - } \ - } while (false) -#else -# define ASSERT(EXPR) (void)(EXPR) -#endif - -namespace mozilla::color { - -struct YuvLumaCoeffs final { - float r = 0.2126; - float g = 0.7152; - float b = 0.0722; - - auto Members() const { return std::tie(r, g, b); } - INLINE_AUTO_MAPPABLE(YuvLumaCoeffs) - - static constexpr auto Rec709() { return YuvLumaCoeffs(); } - - static constexpr auto Rec2020() { - return YuvLumaCoeffs{0.2627, 0.6780, 0.0593}; - } -}; - -struct PiecewiseGammaDesc final { - // tf = { k * linear | linear < b - // { a * pow(linear, 1/g) - (1-a) | linear >= b - - // Default to Srgb - float a = 1.055; - float b = 0.04045 / 12.92; - float g = 2.4; - float k = 12.92; - - auto Members() const { return std::tie(a, b, g, k); } - INLINE_AUTO_MAPPABLE(PiecewiseGammaDesc) - - static constexpr auto Srgb() { return PiecewiseGammaDesc(); } - static constexpr auto DisplayP3() { return Srgb(); } - - static constexpr auto Rec709() { - return PiecewiseGammaDesc{ - 1.099, - 0.018, - 1.0 / 0.45, // ~2.222 - 4.5, - }; - } - static constexpr auto Rec2020_10bit() { return Rec709(); } - - static constexpr auto Rec2020_12bit() { - return PiecewiseGammaDesc{ - 1.0993, - 0.0181, - 1.0 / 0.45, // ~2.222 - 4.5, - }; - } -}; - -struct YcbcrDesc final { - float y0 = 16 / 255.0; - float y1 = 235 / 255.0; - float u0 = 128 / 255.0; - float uPlusHalf = 240 / 255.0; - - auto Members() const { return std::tie(y0, y1, u0, uPlusHalf); } - INLINE_AUTO_MAPPABLE(YcbcrDesc) - - static constexpr auto Narrow8() { // AKA limited/studio/tv - return YcbcrDesc(); - } - static constexpr auto Full8() { // AKA pc - return YcbcrDesc{ - 0 / 255.0, - 255 / 255.0, - 128 / 255.0, - 254 / 255.0, - }; - } - static constexpr auto Float() { // Best for a LUT - return YcbcrDesc{0.0, 1.0, 0.5, 1.0}; - } -}; - -struct Chromaticities final { - float rx = 0.640; - float ry = 0.330; - float gx = 0.300; - float gy = 0.600; - float bx = 0.150; - float by = 0.060; - // D65: - static constexpr float wx = 0.3127; - static constexpr float wy = 0.3290; - - auto Members() const { return std::tie(rx, ry, gx, gy, bx, by); } - INLINE_AUTO_MAPPABLE(Chromaticities) - - // - - - static constexpr auto Rec709() { // AKA limited/studio/tv - return Chromaticities(); - } - static constexpr auto Srgb() { return Rec709(); } - - static constexpr auto Rec601_625_Pal() { - auto ret = Rec709(); - ret.gx = 0.290; - return ret; - } - static constexpr auto Rec601_525_Ntsc() { - return Chromaticities{ - 0.630, 0.340, // r - 0.310, 0.595, // g - 0.155, 0.070, // b - }; - } - static constexpr auto Rec2020() { - return Chromaticities{ - 0.708, 0.292, // r - 0.170, 0.797, // g - 0.131, 0.046, // b - }; - } - static constexpr auto DisplayP3() { - return Chromaticities{ - 0.680, 0.320, // r - 0.265, 0.690, // g - 0.150, 0.060, // b - }; - } -}; - -// - - -struct YuvDesc final { - YuvLumaCoeffs yCoeffs; - YcbcrDesc ycbcr; - - auto Members() const { return std::tie(yCoeffs, ycbcr); } - INLINE_AUTO_MAPPABLE(YuvDesc); -}; - -struct ColorspaceDesc final { - Chromaticities chrom; - std::optional tf; - std::optional yuv; - - auto Members() const { return std::tie(chrom, tf, yuv); } - INLINE_AUTO_MAPPABLE(ColorspaceDesc); -}; - -// - - -template -struct avec final { - using T = TT; - static constexpr auto N = NN; - - std::array data = {}; - - // - - - constexpr avec() = default; - constexpr avec(const avec&) = default; - - constexpr avec(const avec& v, T a) { - for (int i = 0; i < N - 1; i++) { - data[i] = v[i]; - } - data[N - 1] = a; - } - constexpr avec(const avec& v, T a, T b) { - for (int i = 0; i < N - 2; i++) { - data[i] = v[i]; - } - data[N - 2] = a; - data[N - 1] = b; - } - - MOZ_IMPLICIT constexpr avec(const std::array& data) { - this->data = data; - } - - explicit constexpr avec(const T v) { - for (int i = 0; i < N; i++) { - data[i] = v; - } - } - - template - explicit constexpr avec(const avec& v) { - const auto n = std::min(N, N2); - for (int i = 0; i < n; i++) { - data[i] = static_cast(v[i]); - } - } - - // - - - const auto& operator[](const size_t n) const { return data[n]; } - auto& operator[](const size_t n) { return data[n]; } - - template - constexpr auto get() const { - return (i < N) ? data[i] : 0; - } - constexpr auto x() const { return get<0>(); } - constexpr auto y() const { return get<1>(); } - constexpr auto z() const { return get<2>(); } - constexpr auto w() const { return get<3>(); } - - constexpr auto xyz() const { return vec3({x(), y(), z()}); } - - template - void set(const T v) { - if (i < N) { - data[i] = v; - } - } - void x(const T v) { set<0>(v); } - void y(const T v) { set<1>(v); } - void z(const T v) { set<2>(v); } - void w(const T v) { set<3>(v); } - - // - - -#define _(OP) \ - friend avec operator OP(const avec a, const avec b) { \ - avec c; \ - for (int i = 0; i < N; i++) { \ - c[i] = a[i] OP b[i]; \ - } \ - return c; \ - } \ - friend avec operator OP(const avec a, const T b) { \ - avec c; \ - for (int i = 0; i < N; i++) { \ - c[i] = a[i] OP b; \ - } \ - return c; \ - } \ - friend avec operator OP(const T a, const avec b) { \ - avec c; \ - for (int i = 0; i < N; i++) { \ - c[i] = a OP b[i]; \ - } \ - return c; \ - } - _(+) - _(-) - _(*) - _(/) -#undef _ - - friend bool operator==(const avec a, const avec b) { - bool eq = true; - for (int i = 0; i < N; i++) { - eq &= (a[i] == b[i]); - } - return eq; - } -}; -using vec3 = avec; -using vec4 = avec; -using ivec3 = avec; -using ivec4 = avec; - -template -T dot(const avec& a, const avec& b) { - const auto c = a * b; - T ret = 0; - for (int i = 0; i < N; i++) { - ret += c[i]; - } - return ret; -} - -template -V mix(const V& zero, const V& one, const float val) { - return zero * (1 - val) + one * val; -} - -template -auto min(const avec& a, const avec& b) { - auto ret = avec{}; - for (int i = 0; i < ret.N; i++) { - ret[i] = std::min(a[i], b[i]); - } - return ret; -} - -template -auto max(const avec& a, const avec& b) { - auto ret = avec{}; - for (int i = 0; i < ret.N; i++) { - ret[i] = std::max(a[i], b[i]); - } - return ret; -} - -template -auto floor(const avec& a) { - auto ret = avec{}; - for (int i = 0; i < ret.N; i++) { - ret[i] = floorf(a[i]); - } - return ret; -} - -template -auto round(const avec& a) { - auto ret = avec{}; - for (int i = 0; i < ret.N; i++) { - ret[i] = roundf(a[i]); - } - return ret; -} - -template -auto abs(const avec& a) { - auto ret = avec{}; - for (int i = 0; i < ret.N; i++) { - ret[i] = std::abs(a[i]); - } - return ret; -} - -// - - -template -struct mat final { - static constexpr int y_rows = Y_Rows; - static constexpr int x_cols = X_Cols; - - static constexpr auto Identity() { - auto ret = mat{}; - for (int x = 0; x < x_cols; x++) { - for (int y = 0; y < y_rows; y++) { - ret.at(x, y) = (x == y ? 1 : 0); - } - } - return ret; - } - - std::array, Y_Rows> rows = {}; // row-major - - // - - - constexpr mat() = default; - - explicit constexpr mat(const std::array, Y_Rows>& rows) { - this->rows = rows; - } - - template - explicit constexpr mat(const mat& m) { - *this = Identity(); - for (int x = 0; x < std::min(X_Cols, X_Cols2); x++) { - for (int y = 0; y < std::min(Y_Rows, Y_Rows2); y++) { - at(x, y) = m.at(x, y); - } - } - } - - const auto& at(const int x, const int y) const { return rows.at(y)[x]; } - auto& at(const int x, const int y) { return rows.at(y)[x]; } - - friend auto operator*(const mat& a, const avec& b_colvec) { - avec c_colvec; - for (int i = 0; i < y_rows; i++) { - c_colvec[i] = dot(a.rows.at(i), b_colvec); - } - return c_colvec; - } - - friend auto operator*(const mat& a, const float b) { - mat c; - for (int x = 0; x < x_cols; x++) { - for (int y = 0; y < y_rows; y++) { - c.at(x, y) = a.at(x, y) * b; - } - } - return c; - } - friend auto operator/(const mat& a, const float b) { return a * (1 / b); } - - template - friend auto operator*(const mat& a, const mat& b) { - const auto bt = transpose(b); - const auto& b_cols = bt.rows; - - mat c; - for (int x = 0; x < BCols; x++) { - for (int y = 0; y < Y_Rows; y++) { - c.at(x, y) = dot(a.rows.at(y), b_cols.at(x)); - } - } - return c; - } -}; -using mat3 = mat<3, 3>; -using mat4 = mat<4, 4>; - -inline float determinant(const mat<1, 1>& m) { return m.at(0, 0); } -template -float determinant(const T& m) { - static_assert(T::x_cols == T::y_rows); - - float ret = 0; - for (int i = 0; i < T::x_cols; i++) { - const auto cofact = cofactor(m, i, 0); - ret += m.at(i, 0) * cofact; - } - return ret; -} - -// - - -template -float cofactor(const T& m, const int x_col, const int y_row) { - ASSERT(0 <= x_col && x_col < T::x_cols); - ASSERT(0 <= y_row && y_row < T::y_rows); - - auto cofactor = minor_val(m, x_col, y_row); - if ((x_col + y_row) % 2 == 1) { - cofactor *= -1; - } - return cofactor; -} - -// - - -// Unfortunately, can't call this `minor(...)` because there is -// `#define minor(dev) gnu_dev_minor (dev)` -// in /usr/include/x86_64-linux-gnu/sys/sysmacros.h:62 -template -float minor_val(const T& a, const int skip_x, const int skip_y) { - ASSERT(0 <= skip_x && skip_x < T::x_cols); - ASSERT(0 <= skip_y && skip_y < T::y_rows); - - // A minor matrix is a matrix without its x_col and y_row. - mat b; - - int x_skips = 0; - for (int ax = 0; ax < T::x_cols; ax++) { - if (ax == skip_x) { - x_skips = 1; - continue; - } - - int y_skips = 0; - for (int ay = 0; ay < T::y_rows; ay++) { - if (ay == skip_y) { - y_skips = 1; - continue; - } - - b.at(ax - x_skips, ay - y_skips) = a.at(ax, ay); - } - } - - const auto minor = determinant(b); - return minor; -} - -// - - -/// The matrix of cofactors. -template -auto comatrix(const T& a) { - auto b = T{}; - for (int x = 0; x < T::x_cols; x++) { - for (int y = 0; y < T::y_rows; y++) { - b.at(x, y) = cofactor(a, x, y); - } - } - return b; -} - -// - - -template -auto transpose(const T& a) { - auto b = mat{}; - for (int x = 0; x < T::x_cols; x++) { - for (int y = 0; y < T::y_rows; y++) { - b.at(y, x) = a.at(x, y); - } - } - return b; -} - -// - - -template -inline T inverse(const T& a) { - const auto det = determinant(a); - const auto comat = comatrix(a); - const auto adjugate = transpose(comat); - const auto inv = adjugate / det; - return inv; -} - -// - - -template -void ForEachIntWithin(const ivec3 size, const F& f) { - ivec3 p; - for (p.z(0); p.z() < size.z(); p.z(p.z() + 1)) { - for (p.y(0); p.y() < size.y(); p.y(p.y() + 1)) { - for (p.x(0); p.x() < size.x(); p.x(p.x() + 1)) { - f(p); - } - } - } -} -template -void ForEachSampleWithin(const ivec3 size, const F& f) { - const auto div = vec3(size - 1); - ForEachIntWithin(size, [&](const ivec3& isrc) { - const auto fsrc = vec3(isrc) / div; - f(fsrc); - }); -} - -// - - -struct Lut3 final { - ivec3 size; - std::vector data; - - // - - - static Lut3 Create(const ivec3 size) { - Lut3 lut; - lut.size = size; - lut.data.resize(size.x() * size.y() * size.z()); - return lut; - } - - // - - - /// p: [0, N-1] (clamps) - size_t Index(ivec3 p) const { - const auto scales = ivec3({1, size.x(), size.x() * size.y()}); - p = max(ivec3(0), min(p, size - 1)); // clamp - return dot(p, scales); - } - - // - - - template - void SetMap(const F& dstFromSrc01) { - ForEachIntWithin(size, [&](const ivec3 p) { - const auto i = Index(p); - const auto src01 = vec3(p) / vec3(size - 1); - const auto dstVal = dstFromSrc01(src01); - data.at(i) = dstVal; - }); - } - - // - - - /// p: [0, N-1] (clamps) - vec3 Fetch(ivec3 p) const { - const auto i = Index(p); - return data.at(i); - } - - /// in01: [0.0, 1.0] (clamps) - vec3 Sample(vec3 in01) const; -}; - -// - - -/** -Naively, it would be ideal to map directly from ycbcr to rgb, -but headroom and footroom are problematic: For e.g. narrow-range-8-bit, -our naive LUT would start at absolute y=0/255. However, values only start -at y=16/255, and depending on where your first LUT sample is, you might get -very poor approximations for y=16/255. -Further, even for full-range-8-bit, y=-0.5 is encoded as 1/255. U and v -aren't *as* important as y, but we should try be accurate for the min and -max values. Additionally, it would be embarassing to get whites/greys wrong, -so preserving u=0.0 should also be a goal. -Finally, when using non-linear transfer functions, the linear approximation of a -point between two samples will be fairly inaccurate. -We preserve min and max by choosing our input range such that min and max are -the endpoints of their LUT axis. -We preserve accuracy (at and around) mid by choosing odd sizes for dimentions. - -But also, the LUT is surprisingly robust, so check if the simple version works -before adding complexity! -**/ - -struct ColorspaceTransform final { - ColorspaceDesc srcSpace; - ColorspaceDesc dstSpace; - mat4 srcRgbTfFromSrc; - std::optional srcTf; - mat3 dstRgbLinFromSrcRgbLin; - std::optional dstTf; - mat4 dstFromDstRgbTf; - - static ColorspaceTransform Create(const ColorspaceDesc& src, - const ColorspaceDesc& dst); - - // - - - vec3 DstFromSrc(vec3 src) const; - - std::optional ToMat4() const; - - Lut3 ToLut3(const ivec3 size) const; - Lut3 ToLut3() const { - auto defaultSize = ivec3({31, 31, 15}); // Order of importance: G, R, B - if (srcSpace.yuv) { - defaultSize = ivec3({31, 15, 31}); // Y, Cb, Cr - } - return ToLut3(defaultSize); - } -}; - -} // namespace mozilla::color - -#undef ASSERT - -#endif // MOZILLA_GFX_GL_COLORSPACES_H_ diff --git a/gfx/gl/GLBlitHelper.cpp b/gfx/gl/GLBlitHelper.cpp index 54b495e1b840..3a72d677f502 100644 --- a/gfx/gl/GLBlitHelper.cpp +++ b/gfx/gl/GLBlitHelper.cpp @@ -14,9 +14,7 @@ #include "ScopedGLHelpers.h" #include "gfxUtils.h" #include "mozilla/ArrayUtils.h" -#include "mozilla/Casting.h" #include "mozilla/Preferences.h" -#include "mozilla/StaticPrefs_gfx.h" #include "mozilla/UniquePtr.h" #include "mozilla/gfx/Logging.h" #include "mozilla/gfx/Matrix.h" @@ -52,176 +50,140 @@ namespace gl { // -- -static const char kFragPreprocHeader[] = R"( - #ifdef GL_ES - #ifdef GL_FRAGMENT_PRECISION_HIGH - #define MAXP highp - #endif - #else - #define MAXP highp - #endif - #ifndef MAXP - #define MAXP mediump - #endif +const char* const kFragHeader_Tex2D = + "\ + #define SAMPLER sampler2D \n\ + #if __VERSION__ >= 130 \n\ + #define TEXTURE texture \n\ + #else \n\ + #define TEXTURE texture2D \n\ + #endif \n\ +"; +const char* const kFragHeader_Tex2DRect = + "\ + #define SAMPLER sampler2DRect \n\ + #if __VERSION__ >= 130 \n\ + #define TEXTURE texture \n\ + #else \n\ + #define TEXTURE texture2DRect \n\ + #endif \n\ +"; +const char* const kFragHeader_TexExt = + "\ + #extension GL_OES_EGL_image_external : require \n\ + #if __VERSION__ >= 130 \n\ + #define TEXTURE texture \n\ + #else \n\ + #define TEXTURE texture2D \n\ + #endif \n\ + #define SAMPLER samplerExternalOES \n\ +"; - #if __VERSION__ >= 130 - #define VARYING in - #else - #define VARYING varying - #endif - #if __VERSION__ >= 120 - #define MAT4X3 mat4x3 - #else - #define MAT4X3 mat4 - #endif -)"; - -// - - -const char* const kFragHeader_Tex2D = R"( - #define SAMPLER sampler2D - #if __VERSION__ >= 130 - #define TEXTURE texture - #else - #define TEXTURE texture2D - #endif -)"; -const char* const kFragHeader_Tex2DRect = R"( - #define SAMPLER sampler2DRect - #if __VERSION__ >= 130 - #define TEXTURE texture - #else - #define TEXTURE texture2DRect - #endif -)"; -const char* const kFragHeader_TexExt = R"( - #extension GL_OES_EGL_image_external : enable - #extension GL_OES_EGL_image_external_essl3 : enable - #if __VERSION__ >= 130 - #define TEXTURE texture - #else - #define TEXTURE texture2D - #endif - #define SAMPLER samplerExternalOES -)"; - -// - - -static const char kFragDeclHeader[] = R"( - precision PRECISION float; - #if __VERSION__ >= 130 - #define FRAG_COLOR oFragColor - out vec4 FRAG_COLOR; - #else - #define FRAG_COLOR gl_FragColor - #endif -)"; - -// - - -const char* const kFragSample_OnePlane = R"( - VARYING mediump vec2 vTexCoord0; - uniform PRECISION SAMPLER uTex0; - - vec4 metaSample() { - vec4 src = TEXTURE(uTex0, vTexCoord0); - return src; - } -)"; -// Ideally this would just change the color-matrix it uses, but this is -// acceptable debt for now. -const char* const kFragSample_OnePlane_YUV_via_GBR = R"( - VARYING mediump vec2 vTexCoord0; - uniform PRECISION SAMPLER uTex0; - - vec4 metaSample() { - vec4 src = TEXTURE(uTex0, vTexCoord0); - return src; - } -)"; -const char* const kFragSample_TwoPlane = R"( - VARYING mediump vec2 vTexCoord0; - VARYING mediump vec2 vTexCoord1; - uniform PRECISION SAMPLER uTex0; - uniform PRECISION SAMPLER uTex1; - - vec4 metaSample() { - vec4 src = TEXTURE(uTex0, vTexCoord0); // Keep r and a. - src.gb = TEXTURE(uTex1, vTexCoord1).rg; - return src; - } -)"; -const char* const kFragSample_ThreePlane = R"( - VARYING mediump vec2 vTexCoord0; - VARYING mediump vec2 vTexCoord1; - uniform PRECISION SAMPLER uTex0; - uniform PRECISION SAMPLER uTex1; - uniform PRECISION SAMPLER uTex2; - - vec4 metaSample() { - vec4 src = TEXTURE(uTex0, vTexCoord0); // Keep r and a. - src.g = TEXTURE(uTex1, vTexCoord1).r; - src.b = TEXTURE(uTex2, vTexCoord1).r; - return src; - } -)"; - -// - - -const char* const kFragConvert_None = R"( - vec3 metaConvert(vec3 src) { - return src; - } -)"; -const char* const kFragConvert_BGR = R"( - vec3 metaConvert(vec3 src) { - return src.bgr; - } -)"; -const char* const kFragConvert_ColorMatrix = R"( - uniform mediump MAT4X3 uColorMatrix; - - vec3 metaConvert(vec3 src) { - return (uColorMatrix * vec4(src, 1)).rgb; - } -)"; -const char* const kFragConvert_ColorLut = R"( - uniform PRECISION sampler3D uColorLut; - - vec3 metaConvert(vec3 src) { - // Half-texel filtering hazard! - // E.g. For texture size of 2, - // E.g. 0.5/2=0.25 is still sampling 100% of texel 0, 0% of texel 1. - // For the LUT, we need 0.5/2=0.25 to filter 25/75 texel 0 and 1. - // That is, we need to adjust our sampling point such that it's 0.25 of the - // way from texel 0's center to texel 1's center. - // We need, for N=2: - // v=0.0|N=2 => v'=0.5/2 - // v=1.0|N=2 => v'=1.5/2 - // For N=3: - // v=0.0|N=3 => v'=0.5/3 - // v=1.0|N=3 => v'=2.5/3 - // => v' = ( 0.5 + v * (3 - 1) )/3 - vec3 size = vec3(textureSize(uColorLut, 0)); - src = (0.5 + src * (size - 1.0)) / size; - return texture(uColorLut, src).rgb; - } -)"; - -// - - -static const char kFragBody[] = R"( - void main(void) { - vec4 src = metaSample(); - vec3 dst = metaConvert(src.rgb); - FRAG_COLOR = vec4(dst, src.a); - } -)"; +const char* const kFragBody_RGBA = + "\ + VARYING vec2 vTexCoord0; \n\ + uniform SAMPLER uTex0; \n\ + \n\ + void main(void) \n\ + { \n\ + FRAG_COLOR = TEXTURE(uTex0, vTexCoord0); \n\ + } \n\ +"; +const char* const kFragBody_BGRA = + "\ + VARYING vec2 vTexCoord0; \n\ + uniform SAMPLER uTex0; \n\ + \n\ + void main(void) \n\ + { \n\ + FRAG_COLOR = TEXTURE(uTex0, vTexCoord0).bgra; \n\ + } \n\ +"; +const char* const kFragBody_CrYCb = + "\ + VARYING vec2 vTexCoord0; \n\ + uniform SAMPLER uTex0; \n\ + uniform MAT4X3 uColorMatrix; \n\ + \n\ + void main(void) \n\ + { \n\ + vec4 yuv = vec4(TEXTURE(uTex0, vTexCoord0).gbr, \n\ + 1.0); \n\ + FRAG_COLOR = vec4((uColorMatrix * yuv).rgb, 1.0); \n\ + } \n\ +"; +const char* const kFragBody_NV12 = + "\ + VARYING vec2 vTexCoord0; \n\ + VARYING vec2 vTexCoord1; \n\ + uniform SAMPLER uTex0; \n\ + uniform SAMPLER uTex1; \n\ + uniform MAT4X3 uColorMatrix; \n\ + \n\ + void main(void) \n\ + { \n\ + vec4 yuv = vec4(TEXTURE(uTex0, vTexCoord0).x, \n\ + TEXTURE(uTex1, vTexCoord1).xy, \n\ + 1.0); \n\ + FRAG_COLOR = vec4((uColorMatrix * yuv).rgb, 1.0); \n\ + } \n\ +"; +const char* const kFragBody_PlanarYUV = + "\ + VARYING vec2 vTexCoord0; \n\ + VARYING vec2 vTexCoord1; \n\ + uniform SAMPLER uTex0; \n\ + uniform SAMPLER uTex1; \n\ + uniform SAMPLER uTex2; \n\ + uniform MAT4X3 uColorMatrix; \n\ + \n\ + void main(void) \n\ + { \n\ + vec4 yuv = vec4(TEXTURE(uTex0, vTexCoord0).x, \n\ + TEXTURE(uTex1, vTexCoord1).x, \n\ + TEXTURE(uTex2, vTexCoord1).x, \n\ + 1.0); \n\ + FRAG_COLOR = vec4((uColorMatrix * yuv).rgb, 1.0); \n\ + } \n\ +"; // -- +template +/*static*/ Mat Mat::Zero() { + Mat ret; + for (auto& x : ret.m) { + x = 0.0f; + } + return ret; +} + +template +/*static*/ Mat Mat::I() { + auto ret = Mat::Zero(); + for (uint8_t i = 0; i < N; i++) { + ret.at(i, i) = 1.0f; + } + return ret; +} + +template +Mat Mat::operator*(const Mat& r) const { + Mat ret; + for (uint8_t x = 0; x < N; x++) { + for (uint8_t y = 0; y < N; y++) { + float sum = 0.0f; + for (uint8_t i = 0; i < N; i++) { + sum += at(i, y) * r.at(x, i); + } + ret.at(x, y) = sum; + } + } + return ret; +} + Mat3 SubRectMat3(const float x, const float y, const float w, const float h) { - auto ret = Mat3{}; + auto ret = Mat3::Zero(); ret.at(0, 0) = w; ret.at(1, 1) = h; ret.at(2, 0) = x; @@ -250,10 +212,10 @@ Mat3 SubRectMat3(const gfx::IntRect& bigSubrect, const gfx::IntSize& smallSize, // -- ScopedSaveMultiTex::ScopedSaveMultiTex(GLContext* const gl, - const std::vector& texUnits, + const uint8_t texCount, const GLenum texTarget) : mGL(*gl), - mTexUnits(texUnits), + mTexCount(texCount), mTexTarget(texTarget), mOldTexUnit(mGL.GetIntAs(LOCAL_GL_ACTIVE_TEXTURE)) { GLenum texBinding; @@ -261,9 +223,6 @@ ScopedSaveMultiTex::ScopedSaveMultiTex(GLContext* const gl, case LOCAL_GL_TEXTURE_2D: texBinding = LOCAL_GL_TEXTURE_BINDING_2D; break; - case LOCAL_GL_TEXTURE_3D: - texBinding = LOCAL_GL_TEXTURE_BINDING_3D; - break; case LOCAL_GL_TEXTURE_RECTANGLE: texBinding = LOCAL_GL_TEXTURE_BINDING_RECTANGLE; break; @@ -274,26 +233,21 @@ ScopedSaveMultiTex::ScopedSaveMultiTex(GLContext* const gl, gfxCriticalError() << "Unhandled texTarget: " << texTarget; } - for (const auto i : IntegerRange(mTexUnits.size())) { - const auto& unit = mTexUnits[i]; - mGL.fActiveTexture(LOCAL_GL_TEXTURE0 + unit); + for (uint8_t i = 0; i < mTexCount; i++) { + mGL.fActiveTexture(LOCAL_GL_TEXTURE0 + i); if (mGL.IsSupported(GLFeature::sampler_objects)) { mOldTexSampler[i] = mGL.GetIntAs(LOCAL_GL_SAMPLER_BINDING); - mGL.fBindSampler(unit, 0); + mGL.fBindSampler(i, 0); } mOldTex[i] = mGL.GetIntAs(texBinding); } } ScopedSaveMultiTex::~ScopedSaveMultiTex() { - // Unbind in reverse order, in case we have repeats. - // Order matters because we unbound samplers during ctor, so now we have to - // make sure we rebind them in the right order. - for (const auto i : Reversed(IntegerRange(mTexUnits.size()))) { - const auto& unit = mTexUnits[i]; - mGL.fActiveTexture(LOCAL_GL_TEXTURE0 + unit); + for (uint8_t i = 0; i < mTexCount; i++) { + mGL.fActiveTexture(LOCAL_GL_TEXTURE0 + i); if (mGL.IsSupported(GLFeature::sampler_objects)) { - mGL.fBindSampler(unit, mOldTexSampler[i]); + mGL.fBindSampler(i, mOldTexSampler[i]); } mGL.fBindTexture(mTexTarget, mOldTex[i]); } @@ -425,7 +379,6 @@ DrawBlitProg::DrawBlitProg(const GLBlitHelper* const parent, const GLuint prog) mLoc_uDestMatrix(mParent.mGL->fGetUniformLocation(mProg, "uDestMatrix")), mLoc_uTexMatrix0(mParent.mGL->fGetUniformLocation(mProg, "uTexMatrix0")), mLoc_uTexMatrix1(mParent.mGL->fGetUniformLocation(mProg, "uTexMatrix1")), - mLoc_uColorLut(mParent.mGL->fGetUniformLocation(mProg, "uColorLut")), mLoc_uColorMatrix( mParent.mGL->fGetUniformLocation(mProg, "uColorMatrix")) { const auto& gl = mParent.mGL; @@ -490,35 +443,28 @@ void DrawBlitProg::Draw(const BaseArgs& args, gl->fUniformMatrix3fv(mLoc_uDestMatrix, 1, false, destMatrix.m); gl->fUniformMatrix3fv(mLoc_uTexMatrix0, 1, false, args.texMatrix0.m); - if (args.texUnitForColorLut) { - gl->fUniform1i(mLoc_uColorLut, - AssertedCast(*args.texUnitForColorLut)); - } - MOZ_ASSERT(bool(argsYUV) == (mLoc_uColorMatrix != -1)); if (argsYUV) { gl->fUniformMatrix3fv(mLoc_uTexMatrix1, 1, false, argsYUV->texMatrix1.m); - if (mLoc_uColorMatrix != -1) { - const auto& colorMatrix = - gfxUtils::YuvToRgbMatrix4x4ColumnMajor(*argsYUV->colorSpaceForMatrix); - float mat4x3[4 * 3]; - switch (mType_uColorMatrix) { - case LOCAL_GL_FLOAT_MAT4: - gl->fUniformMatrix4fv(mLoc_uColorMatrix, 1, false, colorMatrix); - break; - case LOCAL_GL_FLOAT_MAT4x3: - for (int x = 0; x < 4; x++) { - for (int y = 0; y < 3; y++) { - mat4x3[3 * x + y] = colorMatrix[4 * x + y]; - } + const auto& colorMatrix = + gfxUtils::YuvToRgbMatrix4x4ColumnMajor(argsYUV->colorSpace); + float mat4x3[4 * 3]; + switch (mType_uColorMatrix) { + case LOCAL_GL_FLOAT_MAT4: + gl->fUniformMatrix4fv(mLoc_uColorMatrix, 1, false, colorMatrix); + break; + case LOCAL_GL_FLOAT_MAT4x3: + for (int x = 0; x < 4; x++) { + for (int y = 0; y < 3; y++) { + mat4x3[3 * x + y] = colorMatrix[4 * x + y]; } - gl->fUniformMatrix4x3fv(mLoc_uColorMatrix, 1, false, mat4x3); - break; - default: - gfxCriticalError() - << "Bad mType_uColorMatrix: " << gfx::hexa(mType_uColorMatrix); - } + } + gl->fUniformMatrix4x3fv(mLoc_uColorMatrix, 1, false, mat4x3); + break; + default: + gfxCriticalError() << "Bad mType_uColorMatrix: " + << gfx::hexa(mType_uColorMatrix); } } @@ -604,17 +550,11 @@ GLBlitHelper::GLBlitHelper(GLContext* const gl) const auto glslVersion = mGL->ShadingLanguageVersion(); + // Always use 100 on ES because some devices have OES_EGL_image_external but + // not OES_EGL_image_external_essl3. We could just use 100 in that particular + // case, but this is a lot easier and is not harmful to other usages. if (mGL->IsGLES()) { - // If you run into problems on old android devices, it might be because some - // devices have OES_EGL_image_external but not OES_EGL_image_external_essl3. - // We could just use 100 in that particular case, but then we lose out on - // e.g. sampler3D. Let's just try 300 for now, and if we get regressions - // we'll add an essl100 fallback. - if (glslVersion >= 300) { - mDrawBlitProg_VersionLine = nsCString("#version 300 es\n"); - } else { - mDrawBlitProg_VersionLine = nsCString("#version 100\n"); - } + mDrawBlitProg_VersionLine = nsCString("#version 100\n"); } else if (glslVersion >= 130) { mDrawBlitProg_VersionLine = nsPrintfCString("#version %u\n", glslVersion); } @@ -685,47 +625,36 @@ const DrawBlitProg* GLBlitHelper::GetDrawBlitProg( const DrawBlitProg* GLBlitHelper::CreateDrawBlitProg( const DrawBlitProg::Key& key) const { - const auto precisionPref = StaticPrefs::gfx_blithelper_precision(); - const char* precision; - switch (precisionPref) { - case 0: - precision = "lowp"; - break; - case 1: - precision = "mediump"; - break; - default: - if (precisionPref != 2) { - NS_WARNING("gfx.blithelper.precision clamped to 2."); - } - precision = "MAXP"; - break; - } - - nsPrintfCString precisionLine("\n#define PRECISION %s\n", precision); - - // - + const char kFragHeader_Global[] = + "\ + #ifdef GL_ES \n\ + #ifdef GL_FRAGMENT_PRECISION_HIGH \n\ + precision highp float; \n\ + #else \n\ + precision mediump float; \n\ + #endif \n\ + #endif \n\ + \n\ + #if __VERSION__ >= 130 \n\ + #define VARYING in \n\ + #define FRAG_COLOR oFragColor \n\ + out vec4 FRAG_COLOR; \n\ + #else \n\ + #define VARYING varying \n\ + #define FRAG_COLOR gl_FragColor \n\ + #endif \n\ + \n\ + #if __VERSION__ >= 120 \n\ + #define MAT4X3 mat4x3 \n\ + #else \n\ + #define MAT4X3 mat4 \n\ + #endif \n\ + "; const ScopedShader fs(mGL, LOCAL_GL_FRAGMENT_SHADER); - - std::vector parts; - { - parts.push_back(mDrawBlitProg_VersionLine.get()); - parts.push_back(kFragPreprocHeader); - if (key.fragHeader) { - parts.push_back(key.fragHeader); - } - parts.push_back(precisionLine.BeginReading()); - parts.push_back(kFragDeclHeader); - for (const auto& part : key.fragParts) { - if (part) { - parts.push_back(part); - } - } - parts.push_back(kFragBody); - } - mGL->fShaderSource(fs, AssertedCast(parts.size()), parts.data(), - nullptr); + const char* const parts[] = {mDrawBlitProg_VersionLine.get(), key.fragHeader, + kFragHeader_Global, key.fragBody}; + mGL->fShaderSource(fs, ArrayLength(parts), parts, nullptr); mGL->fCompileShader(fs); const auto prog = mGL->fCreateProgram(); @@ -769,19 +698,11 @@ const DrawBlitProg* GLBlitHelper::CreateDrawBlitProg( mGL->fGetShaderInfoLog(fs, fsLogLen, nullptr, fsLog.get()); fsLog[fsLogLen] = 0; - const auto logs = - std::string("DrawBlitProg link failed:\n") + "progLog: " + progLog.get() + - "\n" + "vsLog: " + vsLog.get() + "\n" + "fsLog: " + fsLog.get() + "\n"; - gfxCriticalError() << logs; - - printf_stderr("Frag source:\n"); - int i = 0; - for (const auto& part : parts) { - printf_stderr("// parts[%i]:\n%s\n", i, part); - i += 1; - } - - MOZ_CRASH("DrawBlitProg link failed"); + gfxCriticalError() << "DrawBlitProg link failed:\n" + << "progLog: " << progLog.get() << "\n" + << "vsLog: " << vsLog.get() << "\n" + << "fsLog: " << fsLog.get() << "\n"; + MOZ_CRASH(); } // ----------------------------------------------------------------------------- @@ -944,8 +865,7 @@ bool GLBlitHelper::Blit(const java::GeckoSurfaceTexture::Ref& surfaceTexture, const auto transform3 = Mat3::I(); const auto srcOrigin = OriginPos::TopLeft; const bool yFlip = (srcOrigin != destOrigin); - const auto& prog = GetDrawBlitProg( - {kFragHeader_TexExt, {kFragSample_OnePlane, kFragConvert_None}}); + const auto& prog = GetDrawBlitProg({kFragHeader_TexExt, kFragBody_RGBA}); const DrawBlitProg::BaseArgs baseArgs = {transform3, yFlip, destSize, Nothing()}; prog->Draw(baseArgs, nullptr); @@ -975,8 +895,7 @@ bool GuessDivisors(const gfx::IntSize& ySize, const gfx::IntSize& uvSize, bool GLBlitHelper::BlitPlanarYCbCr(const PlanarYCbCrData& yuvData, const gfx::IntSize& destSize, const OriginPos destOrigin) { - const auto& prog = GetDrawBlitProg( - {kFragHeader_Tex2D, {kFragSample_ThreePlane, kFragConvert_ColorMatrix}}); + const auto& prog = GetDrawBlitProg({kFragHeader_Tex2D, kFragBody_PlanarYUV}); if (!mYuvUploads[0]) { mGL->fGenTextures(3, mYuvUploads); @@ -1038,7 +957,7 @@ bool GLBlitHelper::BlitPlanarYCbCr(const PlanarYCbCrData& yuvData, // -- - const ScopedSaveMultiTex saveTex(mGL, {0, 1, 2}, LOCAL_GL_TEXTURE_2D); + const ScopedSaveMultiTex saveTex(mGL, 3, LOCAL_GL_TEXTURE_2D); const ResetUnpackState reset(mGL); const gfx::IntSize yTexSize(yuvData.mYStride, yuvData.YDataSize().height); const gfx::IntSize uvTexSize(yuvData.mCbCrStride, @@ -1089,7 +1008,7 @@ bool GLBlitHelper::BlitPlanarYCbCr(const PlanarYCbCrData& yuvData, const DrawBlitProg::BaseArgs baseArgs = {SubRectMat3(clipRect, yTexSize), yFlip, destSize, Nothing()}; const DrawBlitProg::YUVArgs yuvArgs = { - SubRectMat3(clipRect, uvTexSize, divisors), Some(yuvData.mYUVColorSpace)}; + SubRectMat3(clipRect, uvTexSize, divisors), yuvData.mYUVColorSpace}; prog->Draw(baseArgs, &yuvArgs); return true; } @@ -1139,7 +1058,7 @@ bool GLBlitHelper::BlitImage(MacIOSurface* const iosurf, // TODO: The colorspace is known by the IOSurface, why override it? // See GetYUVColorSpace/GetFullRange() DrawBlitProg::YUVArgs yuvArgs; - yuvArgs.colorSpaceForMatrix = Some(iosurf->GetYUVColorSpace()); + yuvArgs.colorSpace = iosurf->GetYUVColorSpace(); const DrawBlitProg::YUVArgs* pYuvArgs = nullptr; @@ -1149,12 +1068,9 @@ bool GLBlitHelper::BlitImage(MacIOSurface* const iosurf, } const GLenum texTarget = LOCAL_GL_TEXTURE_RECTANGLE; + const char* const fragHeader = kFragHeader_Tex2DRect; - std::vector texUnits; - for (uint8_t i = 0; i < planes; i++) { - texUnits.push_back(i); - } - const ScopedSaveMultiTex saveTex(mGL, texUnits, texTarget); + const ScopedSaveMultiTex saveTex(mGL, planes, texTarget); const ScopedTexture tex0(mGL); const ScopedTexture tex1(mGL); const ScopedTexture tex2(mGL); @@ -1167,7 +1083,7 @@ bool GLBlitHelper::BlitImage(MacIOSurface* const iosurf, pixelFormat); } - const char* fragSample; + const char* fragBody; switch (planes) { case 1: switch (pixelFormat) { @@ -1179,11 +1095,11 @@ bool GLBlitHelper::BlitImage(MacIOSurface* const iosurf, case kCVPixelFormatType_32RGBA: case kCVPixelFormatType_64ARGB: case kCVPixelFormatType_48RGB: - fragSample = kFragSample_OnePlane; + fragBody = kFragBody_RGBA; break; case kCVPixelFormatType_422YpCbCr8: case kCVPixelFormatType_422YpCbCr8_yuvs: - fragSample = kFragSample_OnePlane_YUV_via_GBR; + fragBody = kFragBody_CrYCb; pYuvArgs = &yuvArgs; break; default: { @@ -1194,19 +1110,19 @@ bool GLBlitHelper::BlitImage(MacIOSurface* const iosurf, str = IntAsAscii(pixelFormat); } gfxCriticalError() << "Unhandled kCVPixelFormatType_*: " << str; + } // Probably YUV though - fragSample = kFragSample_OnePlane_YUV_via_GBR; + fragBody = kFragBody_CrYCb; pYuvArgs = &yuvArgs; break; - } } break; case 2: - fragSample = kFragSample_TwoPlane; + fragBody = kFragBody_NV12; pYuvArgs = &yuvArgs; break; case 3: - fragSample = kFragSample_ThreePlane; + fragBody = kFragBody_PlanarYUV; pYuvArgs = &yuvArgs; break; default: @@ -1232,10 +1148,7 @@ bool GLBlitHelper::BlitImage(MacIOSurface* const iosurf, } } - const auto& prog = GetDrawBlitProg({ - kFragHeader_Tex2DRect, - {fragSample, kFragConvert_ColorMatrix}, - }); + const auto& prog = GetDrawBlitProg({fragHeader, fragBody}); prog->Draw(baseArgs, pYuvArgs); return true; } @@ -1263,13 +1176,10 @@ void GLBlitHelper::DrawBlitTextureToFramebuffer(const GLuint srcTex, gfxCriticalError() << "Unexpected srcTarget: " << srcTarget; return; } - const auto fragConvert = srcIsBGRA ? kFragConvert_BGR : kFragConvert_None; - const auto& prog = GetDrawBlitProg({ - fragHeader, - {kFragSample_OnePlane, fragConvert}, - }); + const char* fragBody = srcIsBGRA ? kFragBody_BGRA : kFragBody_RGBA; + const auto& prog = GetDrawBlitProg({fragHeader, fragBody}); - const ScopedSaveMultiTex saveTex(mGL, {0}, srcTarget); + const ScopedSaveMultiTex saveTex(mGL, 1, srcTarget); mGL->fBindTexture(srcTarget, srcTex); const bool yFlip = false; @@ -1427,35 +1337,28 @@ bool GLBlitHelper::Blit(DMABufSurface* surface, const gfx::IntSize& destSize, // TODO: The colorspace is known by the DMABUFSurface, why override it? // See GetYUVColorSpace/GetFullRange() DrawBlitProg::YUVArgs yuvArgs; - yuvArgs.colorSpaceForMatrix = Some(surface->GetYUVColorSpace()); + yuvArgs.colorSpace = surface->GetYUVColorSpace(); const DrawBlitProg::YUVArgs* pYuvArgs = nullptr; const auto planes = surface->GetTextureCount(); const GLenum texTarget = LOCAL_GL_TEXTURE_2D; - std::vector texUnits; - for (uint8_t i = 0; i < planes; i++) { - texUnits.push_back(i); - } - const ScopedSaveMultiTex saveTex(mGL, texUnits, texTarget); + const ScopedSaveMultiTex saveTex(mGL, planes, texTarget); const auto pixelFormat = surface->GetSurfaceType(); - const char* fragSample; - auto fragConvert = kFragConvert_None; + const char* fragBody; switch (pixelFormat) { case DMABufSurface::SURFACE_RGBA: - fragSample = kFragSample_OnePlane; + fragBody = kFragBody_RGBA; break; case DMABufSurface::SURFACE_NV12: - fragSample = kFragSample_TwoPlane; + fragBody = kFragBody_NV12; pYuvArgs = &yuvArgs; - fragConvert = kFragConvert_ColorMatrix; break; case DMABufSurface::SURFACE_YUV420: - fragSample = kFragSample_ThreePlane; + fragBody = kFragBody_PlanarYUV; pYuvArgs = &yuvArgs; - fragConvert = kFragConvert_ColorMatrix; break; default: gfxCriticalError() << "Unexpected pixel format: " << pixelFormat; @@ -1473,8 +1376,7 @@ bool GLBlitHelper::Blit(DMABufSurface* surface, const gfx::IntSize& destSize, baseArgs.texMatrix0 = SubRectMat3(0, 0, 1, 1); yuvArgs.texMatrix1 = SubRectMat3(0, 0, 1, 1); - const auto& prog = - GetDrawBlitProg({kFragHeader_Tex2D, {fragSample, fragConvert}}); + const auto& prog = GetDrawBlitProg({kFragHeader_Tex2D, fragBody}); prog->Draw(baseArgs, pYuvArgs); return true; @@ -1492,105 +1394,5 @@ bool GLBlitHelper::BlitImage(layers::DMABUFSurfaceImage* srcImage, } #endif -// - - -template -static void PushUnorm(uint32_t* const out, const float inVal) { - const uint32_t mask = (1 << N) - 1; - auto fval = inVal; - fval = std::max(0.0f, std::min(fval, 1.0f)); - fval *= mask; - fval = roundf(fval); - auto ival = static_cast(fval); - ival &= mask; - - *out <<= N; - *out |= ival; -} - -static uint32_t toRgb10A2(const color::vec4& val) { - // R in LSB - uint32_t ret = 0; - PushUnorm<2>(&ret, val.w()); - PushUnorm<10>(&ret, val.z()); - PushUnorm<10>(&ret, val.y()); - PushUnorm<10>(&ret, val.x()); - return ret; -} - -std::shared_ptr GLBlitHelper::GetColorLutTex( - const ColorLutKey& key) const { - auto& weak = mColorLutTexMap[key]; - auto strong = weak.lock(); - if (!strong) { - auto& gl = *mGL; - strong = std::make_shared(gl); - weak = strong; - - const auto ct = color::ColorspaceTransform::Create(key.src, key.dst); - - // - - - const auto minLutSize = color::ivec3{2}; - const auto maxLutSize = color::ivec3{256}; - auto lutSize = minLutSize; - if (ct.srcSpace.yuv) { - lutSize.x(int(StaticPrefs::gfx_blithelper_lut_size_ycbcr_y())); - lutSize.y(int(StaticPrefs::gfx_blithelper_lut_size_ycbcr_cb())); - lutSize.z(int(StaticPrefs::gfx_blithelper_lut_size_ycbcr_cr())); - } else { - lutSize.x(int(StaticPrefs::gfx_blithelper_lut_size_rgb_r())); - lutSize.y(int(StaticPrefs::gfx_blithelper_lut_size_rgb_g())); - lutSize.z(int(StaticPrefs::gfx_blithelper_lut_size_rgb_b())); - } - lutSize = max(minLutSize, min(lutSize, maxLutSize)); // Clamp - - const auto lut = ct.ToLut3(lutSize); - const auto& size = lut.size; - - // - - - constexpr GLenum target = LOCAL_GL_TEXTURE_3D; - const auto bind = gl::ScopedBindTexture(&gl, strong->name, target); - gl.fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE); - gl.fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE); - gl.fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_R, LOCAL_GL_CLAMP_TO_EDGE); - gl.fTexParameteri(target, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR); - gl.fTexParameteri(target, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR); - - bool useFloat16 = true; - if (useFloat16) { - // Use rgba16f, which we can thankfully upload as rgba32f - static_assert(sizeof(color::vec4) == sizeof(float) * 4); - std::vector uploadData; - uploadData.reserve(lut.data.size()); - for (const auto& src : lut.data) { - const auto dst = color::vec4{src, 1}; - uploadData.push_back(dst); - } - - gl.fTexStorage3D(target, 1, LOCAL_GL_RGBA16F, size.x(), size.y(), - size.z()); - gl.fTexSubImage3D(target, 0, 0, 0, 0, size.x(), size.y(), size.z(), - LOCAL_GL_RGBA, LOCAL_GL_FLOAT, uploadData.data()); - } else { - // Use Rgb10A2 - std::vector uploadData; - uploadData.reserve(lut.data.size()); - for (const auto& src : lut.data) { - const auto dst = toRgb10A2({src, 1}); - uploadData.push_back(dst); - } - - gl.fTexStorage3D(target, 1, LOCAL_GL_RGB10_A2, size.x(), size.y(), - size.z()); - gl.fTexSubImage3D(target, 0, 0, 0, 0, size.x(), size.y(), size.z(), - LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV, - uploadData.data()); - } - } - return strong; -} - } // namespace gl } // namespace mozilla diff --git a/gfx/gl/GLBlitHelper.h b/gfx/gl/GLBlitHelper.h index bf3c1bbf1597..a34bdba9ffdf 100644 --- a/gfx/gl/GLBlitHelper.h +++ b/gfx/gl/GLBlitHelper.h @@ -7,12 +7,8 @@ #ifndef GLBLITHELPER_H_ #define GLBLITHELPER_H_ -#include #include #include -#include -#include -#include "Colorspaces.h" #include "GLConsts.h" #include "GLContextTypes.h" #include "GLTypes.h" @@ -82,9 +78,8 @@ class DMABUFSurfaceImage; namespace gl { class BindAnglePlanes; -class GLBlitHelper; class GLContext; -class Texture; +class GLBlitHelper; bool GuessDivisors(const gfx::IntSize& ySize, const gfx::IntSize& uvSize, gfx::IntSize* const out_divisors); @@ -95,27 +90,10 @@ struct Mat { float& at(const uint8_t x, const uint8_t y) { return m[N * x + y]; } - static Mat I() { - auto ret = Mat{}; - for (uint8_t i = 0; i < N; i++) { - ret.at(i, i) = 1.0f; - } - return ret; - } + static Mat Zero(); + static Mat I(); - Mat operator*(const Mat& r) const { - Mat ret; - for (uint8_t x = 0; x < N; x++) { - for (uint8_t y = 0; y < N; y++) { - float sum = 0.0f; - for (uint8_t i = 0; i < N; i++) { - sum += at(i, y) * r.at(x, i); - } - ret.at(x, y) = sum; - } - } - return ret; - } + Mat operator*(const Mat& r) const; }; typedef Mat<3> Mat3; @@ -130,18 +108,17 @@ class DrawBlitProg final { const GLint mLoc_uDestMatrix; const GLint mLoc_uTexMatrix0; const GLint mLoc_uTexMatrix1; - const GLint mLoc_uColorLut; const GLint mLoc_uColorMatrix; GLenum mType_uColorMatrix = 0; public: struct Key final { - const char* fragHeader = nullptr; - std::array fragParts = {}; + const char* const fragHeader; + const char* const fragBody; - auto Members() const { return std::tie(fragHeader, fragParts); } - friend bool operator<(const Key& a, const Key& b) { - return a.Members() < b.Members(); + bool operator<(const Key& x) const { + if (fragHeader != x.fragHeader) return fragHeader < x.fragHeader; + return fragBody < x.fragBody; } }; @@ -154,11 +131,10 @@ class DrawBlitProg final { gfx::IntSize destSize; // Always needed for (at least) setting the viewport. Maybe destRect; - Maybe texUnitForColorLut; }; struct YUVArgs final { Mat3 texMatrix1; - Maybe colorSpaceForMatrix; + gfx::YUVColorSpace colorSpace; }; void Draw(const BaseArgs& args, const YUVArgs* argsYUV = nullptr) const; @@ -166,15 +142,14 @@ class DrawBlitProg final { class ScopedSaveMultiTex final { GLContext& mGL; - const std::vector mTexUnits; + const uint8_t mTexCount; const GLenum mTexTarget; const GLuint mOldTexUnit; GLuint mOldTexSampler[3]; GLuint mOldTex[3]; public: - ScopedSaveMultiTex(GLContext* gl, const std::vector& texUnits, - GLenum texTarget); + ScopedSaveMultiTex(GLContext* gl, uint8_t texCount, GLenum texTarget); ~ScopedSaveMultiTex(); }; @@ -196,23 +171,6 @@ class GLBlitHelper final { gfx::IntSize mYuvUploads_YSize = {0, 0}; gfx::IntSize mYuvUploads_UVSize = {0, 0}; - public: - struct ColorLutKey { - color::ColorspaceDesc src; - color::ColorspaceDesc dst; - - auto Members() const { return std::tie(src, dst); } - INLINE_AUTO_MAPPABLE(ColorLutKey) - }; - - private: - mutable std::unordered_map, - ColorLutKey::Hasher> - mColorLutTexMap; - - public: - std::shared_ptr GetColorLutTex(const ColorLutKey& key) const; - #ifdef XP_WIN mutable RefPtr mD3D11; @@ -312,21 +270,13 @@ class GLBlitHelper final { #endif }; -// - -// For DrawBlitProg::Key::fragParts - extern const char* const kFragHeader_Tex2D; extern const char* const kFragHeader_Tex2DRect; extern const char* const kFragHeader_TexExt; - -extern const char* const kFragSample_OnePlane; -extern const char* const kFragSample_TwoPlane; -extern const char* const kFragSample_ThreePlane; - -extern const char* const kFragConvert_None; -extern const char* const kFragConvert_BGR; -extern const char* const kFragConvert_ColorMatrix; -extern const char* const kFragConvert_ColorLut; +extern const char* const kFragBody_RGBA; +extern const char* const kFragBody_CrYCb; +extern const char* const kFragBody_NV12; +extern const char* const kFragBody_PlanarYUV; } // namespace gl } // namespace mozilla diff --git a/gfx/gl/GLBlitHelperD3D.cpp b/gfx/gl/GLBlitHelperD3D.cpp index 7651e16490f9..9db434dec39b 100644 --- a/gfx/gl/GLBlitHelperD3D.cpp +++ b/gfx/gl/GLBlitHelperD3D.cpp @@ -84,16 +84,7 @@ class BindAnglePlanes final { const EGLAttrib* const* postAttribsList = nullptr) : mParent(*parent), mNumPlanes(numPlanes), - mMultiTex( - mParent.mGL, - [&]() { - std::vector ret; - for (int i = 0; i < numPlanes; i++) { - ret.push_back(i); - } - return ret; - }(), - LOCAL_GL_TEXTURE_EXTERNAL), + mMultiTex(mParent.mGL, mNumPlanes, LOCAL_GL_TEXTURE_EXTERNAL), mTempTexs{0}, mStreams{0}, mSuccess(true) { @@ -239,7 +230,7 @@ bool GLBlitHelper::BlitDescriptor(const layers::SurfaceDescriptorD3D10& desc, const auto srcOrigin = OriginPos::BottomLeft; const gfx::IntRect clipRect(0, 0, clipSize.width, clipSize.height); - const auto colorSpace = desc.colorSpace(); + const auto colorSpace = desc.yUVColorSpace(); if (format != gfx::SurfaceFormat::NV12 && format != gfx::SurfaceFormat::P010 && @@ -295,30 +286,13 @@ bool GLBlitHelper::BlitDescriptor(const layers::SurfaceDescriptorD3D10& desc, const gfx::IntSize uvSize(ySize.width / divisors.width, ySize.height / divisors.height); - const auto yuvColorSpace = [&]() { - switch (colorSpace) { - case gfx::ColorSpace2::UNKNOWN: - case gfx::ColorSpace2::SRGB: - case gfx::ColorSpace2::DISPLAY_P3: - MOZ_CRASH("Expected BT* colorspace"); - case gfx::ColorSpace2::BT601_525: - return gfx::YUVColorSpace::BT601; - case gfx::ColorSpace2::BT709: - return gfx::YUVColorSpace::BT709; - case gfx::ColorSpace2::BT2020: - return gfx::YUVColorSpace::BT2020; - } - MOZ_ASSERT_UNREACHABLE(); - }(); - const bool yFlip = destOrigin != srcOrigin; const DrawBlitProg::BaseArgs baseArgs = {SubRectMat3(clipRect, ySize), yFlip, destSize, Nothing()}; const DrawBlitProg::YUVArgs yuvArgs = { - SubRectMat3(clipRect, uvSize, divisors), Some(yuvColorSpace)}; + SubRectMat3(clipRect, uvSize, divisors), colorSpace}; - const auto& prog = GetDrawBlitProg( - {kFragHeader_TexExt, {kFragSample_TwoPlane, kFragConvert_ColorMatrix}}); + const auto& prog = GetDrawBlitProg({kFragHeader_TexExt, kFragBody_NV12}); prog->Draw(baseArgs, &yuvArgs); return true; } @@ -366,10 +340,9 @@ bool GLBlitHelper::BlitAngleYCbCr(const WindowsHandle (&handleList)[3], const DrawBlitProg::BaseArgs baseArgs = {SubRectMat3(clipRect, ySize), yFlip, destSize, Nothing()}; const DrawBlitProg::YUVArgs yuvArgs = { - SubRectMat3(clipRect, uvSize, divisors), Some(colorSpace)}; + SubRectMat3(clipRect, uvSize, divisors), colorSpace}; - const auto& prog = GetDrawBlitProg( - {kFragHeader_TexExt, {kFragSample_ThreePlane, kFragConvert_ColorMatrix}}); + const auto& prog = GetDrawBlitProg({kFragHeader_TexExt, kFragBody_PlanarYUV}); prog->Draw(baseArgs, &yuvArgs); return true; } diff --git a/gfx/gl/SharedSurfaceANGLE.cpp b/gfx/gl/SharedSurfaceANGLE.cpp index 9a94cd150837..d6152bbec1fc 100644 --- a/gfx/gl/SharedSurfaceANGLE.cpp +++ b/gfx/gl/SharedSurfaceANGLE.cpp @@ -143,7 +143,7 @@ SharedSurface_ANGLEShareHandle::ToSurfaceDescriptor() { const auto format = gfx::SurfaceFormat::B8G8R8A8; return Some(layers::SurfaceDescriptorD3D10( (WindowsHandle)mShareHandle, /* gpuProcessTextureId */ Nothing(), - /* arrayIndex */ 0, format, mDesc.size, mDesc.colorSpace, + /* arrayIndex */ 0, format, mDesc.size, gfx::YUVColorSpace::Identity, gfx::ColorRange::FULL)); } diff --git a/gfx/gl/SharedSurfaceD3D11Interop.cpp b/gfx/gl/SharedSurfaceD3D11Interop.cpp index 209d394ebc2e..7e9e91940faa 100644 --- a/gfx/gl/SharedSurfaceD3D11Interop.cpp +++ b/gfx/gl/SharedSurfaceD3D11Interop.cpp @@ -439,7 +439,7 @@ SharedSurface_D3D11Interop::ToSurfaceDescriptor() { const auto format = gfx::SurfaceFormat::B8G8R8A8; return Some(layers::SurfaceDescriptorD3D10( WindowsHandle(mData.dxgiHandle), /* gpuProcessTextureId */ Nothing(), - /* arrayIndex */ 0, format, mDesc.size, mDesc.colorSpace, + /* arrayIndex */ 0, format, mDesc.size, gfx::YUVColorSpace::Identity, gfx::ColorRange::FULL)); } diff --git a/gfx/gl/gtest/TestColorspaces.cpp b/gfx/gl/gtest/TestColorspaces.cpp deleted file mode 100644 index 4ece3a5d41b5..000000000000 --- a/gfx/gl/gtest/TestColorspaces.cpp +++ /dev/null @@ -1,403 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "gtest/gtest.h" -#include "Colorspaces.h" - -namespace mozilla::color { -mat4 YuvFromYcbcr(const YcbcrDesc&); -float TfFromLinear(const PiecewiseGammaDesc&, float linear); -float LinearFromTf(const PiecewiseGammaDesc&, float tf); -} // namespace mozilla::color - -using namespace mozilla::color; - -auto Calc8From8(const ColorspaceTransform& ct, const ivec3 in8) { - const auto in = vec3(in8) / vec3(255); - const auto out = ct.DstFromSrc(in); - const auto out8 = ivec3(round(out * vec3(255))); - return out8; -} - -auto Sample8From8(const Lut3& lut, const vec3 in8) { - const auto in = in8 / vec3(255); - const auto out = lut.Sample(in); - const auto out8 = ivec3(round(out * vec3(255))); - return out8; -} - -TEST(Colorspaces, YcbcrDesc_Narrow8) -{ - const auto m = YuvFromYcbcr(YcbcrDesc::Narrow8()); - - const auto Yuv8 = [&](const ivec3 ycbcr8) { - const auto ycbcr = vec4(vec3(ycbcr8) / 255, 1); - const auto yuv = m * ycbcr; - return ivec3(round(yuv * 255)); - }; - - EXPECT_EQ(Yuv8({{16, 128, 128}}), (ivec3{{0, 0, 0}})); - EXPECT_EQ(Yuv8({{17, 128, 128}}), (ivec3{{1, 0, 0}})); - // y = 0.5 => (16 + 235) / 2 = 125.5 - EXPECT_EQ(Yuv8({{125, 128, 128}}), (ivec3{{127, 0, 0}})); - EXPECT_EQ(Yuv8({{126, 128, 128}}), (ivec3{{128, 0, 0}})); - EXPECT_EQ(Yuv8({{234, 128, 128}}), (ivec3{{254, 0, 0}})); - EXPECT_EQ(Yuv8({{235, 128, 128}}), (ivec3{{255, 0, 0}})); - - // Check that we get the naive out-of-bounds behavior we'd expect: - EXPECT_EQ(Yuv8({{15, 128, 128}}), (ivec3{{-1, 0, 0}})); - EXPECT_EQ(Yuv8({{236, 128, 128}}), (ivec3{{256, 0, 0}})); -} - -TEST(Colorspaces, YcbcrDesc_Full8) -{ - const auto m = YuvFromYcbcr(YcbcrDesc::Full8()); - - const auto Yuv8 = [&](const ivec3 ycbcr8) { - const auto ycbcr = vec4(vec3(ycbcr8) / 255, 1); - const auto yuv = m * ycbcr; - return ivec3(round(yuv * 255)); - }; - - EXPECT_EQ(Yuv8({{0, 128, 128}}), (ivec3{{0, 0, 0}})); - EXPECT_EQ(Yuv8({{1, 128, 128}}), (ivec3{{1, 0, 0}})); - EXPECT_EQ(Yuv8({{127, 128, 128}}), (ivec3{{127, 0, 0}})); - EXPECT_EQ(Yuv8({{128, 128, 128}}), (ivec3{{128, 0, 0}})); - EXPECT_EQ(Yuv8({{254, 128, 128}}), (ivec3{{254, 0, 0}})); - EXPECT_EQ(Yuv8({{255, 128, 128}}), (ivec3{{255, 0, 0}})); -} - -TEST(Colorspaces, YcbcrDesc_Float) -{ - const auto m = YuvFromYcbcr(YcbcrDesc::Float()); - - const auto Yuv8 = [&](const vec3 ycbcr8) { - const auto ycbcr = vec4(vec3(ycbcr8) / 255, 1); - const auto yuv = m * ycbcr; - return ivec3(round(yuv * 255)); - }; - - EXPECT_EQ(Yuv8({{0, 0.5 * 255, 0.5 * 255}}), (ivec3{{0, 0, 0}})); - EXPECT_EQ(Yuv8({{1, 0.5 * 255, 0.5 * 255}}), (ivec3{{1, 0, 0}})); - EXPECT_EQ(Yuv8({{127, 0.5 * 255, 0.5 * 255}}), (ivec3{{127, 0, 0}})); - EXPECT_EQ(Yuv8({{128, 0.5 * 255, 0.5 * 255}}), (ivec3{{128, 0, 0}})); - EXPECT_EQ(Yuv8({{254, 0.5 * 255, 0.5 * 255}}), (ivec3{{254, 0, 0}})); - EXPECT_EQ(Yuv8({{255, 0.5 * 255, 0.5 * 255}}), (ivec3{{255, 0, 0}})); -} - -TEST(Colorspaces, ColorspaceTransform_Rec709Narrow) -{ - const auto src = ColorspaceDesc{ - Chromaticities::Rec709(), - PiecewiseGammaDesc::Rec709(), - {{YuvLumaCoeffs::Rec709(), YcbcrDesc::Narrow8()}}, - }; - const auto dst = ColorspaceDesc{ - Chromaticities::Rec709(), - PiecewiseGammaDesc::Rec709(), - {}, - }; - const auto ct = ColorspaceTransform::Create(src, dst); - - EXPECT_EQ(Calc8From8(ct, {{16, 128, 128}}), (ivec3{0})); - EXPECT_EQ(Calc8From8(ct, {{17, 128, 128}}), (ivec3{1})); - EXPECT_EQ(Calc8From8(ct, {{126, 128, 128}}), (ivec3{128})); - EXPECT_EQ(Calc8From8(ct, {{234, 128, 128}}), (ivec3{254})); - EXPECT_EQ(Calc8From8(ct, {{235, 128, 128}}), (ivec3{255})); - - // Check that we get the naive out-of-bounds behavior we'd expect: - EXPECT_EQ(Calc8From8(ct, {{15, 128, 128}}), (ivec3{-1})); - EXPECT_EQ(Calc8From8(ct, {{236, 128, 128}}), (ivec3{256})); -} - -TEST(Colorspaces, LutSample_Rec709Float) -{ - const auto src = ColorspaceDesc{ - Chromaticities::Rec709(), - PiecewiseGammaDesc::Rec709(), - {{YuvLumaCoeffs::Rec709(), YcbcrDesc::Float()}}, - }; - const auto dst = ColorspaceDesc{ - Chromaticities::Rec709(), - PiecewiseGammaDesc::Rec709(), - {}, - }; - const auto lut = ColorspaceTransform::Create(src, dst).ToLut3(); - - EXPECT_EQ(Sample8From8(lut, {{0, 0.5 * 255, 0.5 * 255}}), (ivec3{0})); - EXPECT_EQ(Sample8From8(lut, {{1, 0.5 * 255, 0.5 * 255}}), (ivec3{1})); - EXPECT_EQ(Sample8From8(lut, {{127, 0.5 * 255, 0.5 * 255}}), (ivec3{127})); - EXPECT_EQ(Sample8From8(lut, {{128, 0.5 * 255, 0.5 * 255}}), (ivec3{128})); - EXPECT_EQ(Sample8From8(lut, {{254, 0.5 * 255, 0.5 * 255}}), (ivec3{254})); - EXPECT_EQ(Sample8From8(lut, {{255, 0.5 * 255, 0.5 * 255}}), (ivec3{255})); -} - -TEST(Colorspaces, LutSample_Rec709Narrow) -{ - const auto src = ColorspaceDesc{ - Chromaticities::Rec709(), - PiecewiseGammaDesc::Rec709(), - {{YuvLumaCoeffs::Rec709(), YcbcrDesc::Narrow8()}}, - }; - const auto dst = ColorspaceDesc{ - Chromaticities::Rec709(), - PiecewiseGammaDesc::Rec709(), - {}, - }; - const auto lut = ColorspaceTransform::Create(src, dst).ToLut3(); - - EXPECT_EQ(Sample8From8(lut, {{16, 128, 128}}), (ivec3{0})); - EXPECT_EQ(Sample8From8(lut, {{17, 128, 128}}), (ivec3{1})); - EXPECT_EQ(Sample8From8(lut, {{int((235 + 16) / 2), 128, 128}}), (ivec3{127})); - EXPECT_EQ(Sample8From8(lut, {{int((235 + 16) / 2) + 1, 128, 128}}), - (ivec3{128})); - EXPECT_EQ(Sample8From8(lut, {{234, 128, 128}}), (ivec3{254})); - EXPECT_EQ(Sample8From8(lut, {{235, 128, 128}}), (ivec3{255})); -} - -TEST(Colorspaces, LutSample_Rec709Full) -{ - const auto src = ColorspaceDesc{ - Chromaticities::Rec709(), - PiecewiseGammaDesc::Rec709(), - {{YuvLumaCoeffs::Rec709(), YcbcrDesc::Full8()}}, - }; - const auto dst = ColorspaceDesc{ - Chromaticities::Rec709(), - PiecewiseGammaDesc::Rec709(), - {}, - }; - const auto lut = ColorspaceTransform::Create(src, dst).ToLut3(); - - EXPECT_EQ(Sample8From8(lut, {{0, 128, 128}}), (ivec3{0})); - EXPECT_EQ(Sample8From8(lut, {{1, 128, 128}}), (ivec3{1})); - EXPECT_EQ(Sample8From8(lut, {{16, 128, 128}}), (ivec3{16})); - EXPECT_EQ(Sample8From8(lut, {{128, 128, 128}}), (ivec3{128})); - EXPECT_EQ(Sample8From8(lut, {{235, 128, 128}}), (ivec3{235})); - EXPECT_EQ(Sample8From8(lut, {{254, 128, 128}}), (ivec3{254})); - EXPECT_EQ(Sample8From8(lut, {{255, 128, 128}}), (ivec3{255})); -} - -TEST(Colorspaces, PiecewiseGammaDesc_Srgb) -{ - const auto tf = PiecewiseGammaDesc::Srgb(); - - EXPECT_EQ(int(roundf(TfFromLinear(tf, 0x00 / 255.0) * 255)), 0x00); - EXPECT_EQ(int(roundf(TfFromLinear(tf, 0x01 / 255.0) * 255)), 0x0d); - EXPECT_EQ(int(roundf(TfFromLinear(tf, 0x37 / 255.0) * 255)), 0x80); - EXPECT_EQ(int(roundf(TfFromLinear(tf, 0x80 / 255.0) * 255)), 0xbc); - EXPECT_EQ(int(roundf(TfFromLinear(tf, 0xfd / 255.0) * 255)), 0xfe); - EXPECT_EQ(int(roundf(TfFromLinear(tf, 0xfe / 255.0) * 255)), 0xff); - EXPECT_EQ(int(roundf(TfFromLinear(tf, 0xff / 255.0) * 255)), 0xff); - - EXPECT_EQ(int(roundf(LinearFromTf(tf, 0x00 / 255.0) * 255)), 0x00); - EXPECT_EQ(int(roundf(LinearFromTf(tf, 0x01 / 255.0) * 255)), 0x00); - EXPECT_EQ(int(roundf(LinearFromTf(tf, 0x06 / 255.0) * 255)), 0x00); - EXPECT_EQ(int(roundf(LinearFromTf(tf, 0x07 / 255.0) * 255)), 0x01); - EXPECT_EQ(int(roundf(LinearFromTf(tf, 0x0d / 255.0) * 255)), 0x01); - EXPECT_EQ(int(roundf(LinearFromTf(tf, 0x80 / 255.0) * 255)), 0x37); - EXPECT_EQ(int(roundf(LinearFromTf(tf, 0xbc / 255.0) * 255)), 0x80); - EXPECT_EQ(int(roundf(LinearFromTf(tf, 0xfe / 255.0) * 255)), 0xfd); - EXPECT_EQ(int(roundf(LinearFromTf(tf, 0xff / 255.0) * 255)), 0xff); -} - -TEST(Colorspaces, PiecewiseGammaDesc_Rec709) -{ - const auto tf = PiecewiseGammaDesc::Rec709(); - - EXPECT_EQ(int(roundf(TfFromLinear(tf, 0x00 / 255.0) * 255)), 0x00); - EXPECT_EQ(int(roundf(TfFromLinear(tf, 0x01 / 255.0) * 255)), 0x05); - EXPECT_EQ(int(roundf(TfFromLinear(tf, 0x43 / 255.0) * 255)), 0x80); - EXPECT_EQ(int(roundf(TfFromLinear(tf, 0x80 / 255.0) * 255)), 0xb4); - EXPECT_EQ(int(roundf(TfFromLinear(tf, 0xfd / 255.0) * 255)), 0xfe); - EXPECT_EQ(int(roundf(TfFromLinear(tf, 0xfe / 255.0) * 255)), 0xff); - EXPECT_EQ(int(roundf(TfFromLinear(tf, 0xff / 255.0) * 255)), 0xff); - - EXPECT_EQ(int(roundf(LinearFromTf(tf, 0x00 / 255.0) * 255)), 0x00); - EXPECT_EQ(int(roundf(LinearFromTf(tf, 0x01 / 255.0) * 255)), 0x00); - EXPECT_EQ(int(roundf(LinearFromTf(tf, 0x02 / 255.0) * 255)), 0x00); - EXPECT_EQ(int(roundf(LinearFromTf(tf, 0x03 / 255.0) * 255)), 0x01); - EXPECT_EQ(int(roundf(LinearFromTf(tf, 0x05 / 255.0) * 255)), 0x01); - EXPECT_EQ(int(roundf(LinearFromTf(tf, 0x80 / 255.0) * 255)), 0x43); - EXPECT_EQ(int(roundf(LinearFromTf(tf, 0xb4 / 255.0) * 255)), 0x80); - EXPECT_EQ(int(roundf(LinearFromTf(tf, 0xfe / 255.0) * 255)), 0xfd); - EXPECT_EQ(int(roundf(LinearFromTf(tf, 0xff / 255.0) * 255)), 0xff); -} - -TEST(Colorspaces, ColorspaceTransform_PiecewiseGammaDesc) -{ - const auto src = ColorspaceDesc{ - Chromaticities::Srgb(), - {}, - {}, - }; - const auto dst = ColorspaceDesc{ - Chromaticities::Srgb(), - PiecewiseGammaDesc::Srgb(), - {}, - }; - const auto toGamma = ColorspaceTransform::Create(src, dst); - const auto toLinear = ColorspaceTransform::Create(dst, src); - - EXPECT_EQ(Calc8From8(toGamma, ivec3{0x00}), (ivec3{0x00})); - EXPECT_EQ(Calc8From8(toGamma, ivec3{0x01}), (ivec3{0x0d})); - EXPECT_EQ(Calc8From8(toGamma, ivec3{0x37}), (ivec3{0x80})); - EXPECT_EQ(Calc8From8(toGamma, ivec3{0x80}), (ivec3{0xbc})); - EXPECT_EQ(Calc8From8(toGamma, ivec3{0xfd}), (ivec3{0xfe})); - EXPECT_EQ(Calc8From8(toGamma, ivec3{0xff}), (ivec3{0xff})); - - EXPECT_EQ(Calc8From8(toLinear, ivec3{0x00}), (ivec3{0x00})); - EXPECT_EQ(Calc8From8(toLinear, ivec3{0x0d}), (ivec3{0x01})); - EXPECT_EQ(Calc8From8(toLinear, ivec3{0x80}), (ivec3{0x37})); - EXPECT_EQ(Calc8From8(toLinear, ivec3{0xbc}), (ivec3{0x80})); - EXPECT_EQ(Calc8From8(toLinear, ivec3{0xfe}), (ivec3{0xfd})); - EXPECT_EQ(Calc8From8(toLinear, ivec3{0xff}), (ivec3{0xff})); -} - -// - -// Actual end-to-end tests - -TEST(Colorspaces, SrgbFromRec709) -{ - const auto src = ColorspaceDesc{ - Chromaticities::Rec709(), - PiecewiseGammaDesc::Rec709(), - {{YuvLumaCoeffs::Rec709(), YcbcrDesc::Narrow8()}}, - }; - const auto dst = ColorspaceDesc{ - Chromaticities::Srgb(), - PiecewiseGammaDesc::Srgb(), - {}, - }; - const auto ct = ColorspaceTransform::Create(src, dst); - - EXPECT_EQ(Calc8From8(ct, ivec3{{16, 128, 128}}), (ivec3{0})); - EXPECT_EQ(Calc8From8(ct, ivec3{{17, 128, 128}}), (ivec3{3})); - EXPECT_EQ(Calc8From8(ct, ivec3{{115, 128, 128}}), (ivec3{128})); - EXPECT_EQ(Calc8From8(ct, ivec3{{126, 128, 128}}), (ivec3{140})); - EXPECT_EQ(Calc8From8(ct, ivec3{{234, 128, 128}}), (ivec3{254})); - EXPECT_EQ(Calc8From8(ct, ivec3{{235, 128, 128}}), (ivec3{255})); -} - -TEST(Colorspaces, SrgbFromDisplayP3) -{ - const auto p3C = ColorspaceDesc{ - Chromaticities::DisplayP3(), - PiecewiseGammaDesc::DisplayP3(), - }; - const auto srgbC = ColorspaceDesc{ - Chromaticities::Srgb(), - PiecewiseGammaDesc::Srgb(), - }; - const auto srgbLinearC = ColorspaceDesc{ - Chromaticities::Srgb(), - {}, - }; - const auto srgbFromP3 = ColorspaceTransform::Create(p3C, srgbC); - const auto srgbLinearFromP3 = ColorspaceTransform::Create(p3C, srgbLinearC); - - // E.g. - // https://colorjs.io/apps/convert/?color=color(display-p3%200.4%200.8%200.4)&precision=4 - auto srgb = srgbFromP3.DstFromSrc(vec3{{0.4, 0.8, 0.4}}); - EXPECT_NEAR(srgb.x(), 0.179, 0.001); - EXPECT_NEAR(srgb.y(), 0.812, 0.001); - EXPECT_NEAR(srgb.z(), 0.342, 0.001); - auto srgbLinear = srgbLinearFromP3.DstFromSrc(vec3{{0.4, 0.8, 0.4}}); - EXPECT_NEAR(srgbLinear.x(), 0.027, 0.001); - EXPECT_NEAR(srgbLinear.y(), 0.624, 0.001); - EXPECT_NEAR(srgbLinear.z(), 0.096, 0.001); -} - -// - - -struct Stats { - double mean = 0; - double variance = 0; - double min = 1.0 / 0; - double max = -1.0 / 0; - - template - static Stats For(const T& iterable) { - auto ret = Stats{}; - for (const auto& cur : iterable) { - ret.mean += cur; - ret.min = std::min(ret.min, cur); - ret.max = std::max(ret.max, cur); - } - ret.mean /= iterable.size(); - for (const auto& cur : iterable) { - ret.variance += pow(cur - ret.mean, 2); - } - ret.variance /= iterable.size(); - return ret; - } - - constexpr double standardDeviation() const { return sqrt(variance); } -}; - -static Stats StatsForLutError(const ColorspaceTransform& ct, - const ivec3 srcQuants, const ivec3 dstQuants) { - const auto lut = ct.ToLut3(); - - const auto dstScale = vec3(dstQuants - 1); - - std::vector quantErrors; - quantErrors.reserve(srcQuants.x() * srcQuants.y() * srcQuants.z()); - ForEachSampleWithin(srcQuants, [&](const vec3& src) { - const auto sampled = lut.Sample(src); - const auto actual = ct.DstFromSrc(src); - const auto isampled = ivec3(round(sampled * dstScale)); - const auto iactual = ivec3(round(actual * dstScale)); - const auto ierr = abs(isampled - iactual); - const auto quantError = dot(ierr, ivec3{1}); - quantErrors.push_back(quantError); - if (quantErrors.size() % 100000 == 0) { - printf("%zu of %zu\n", quantErrors.size(), quantErrors.capacity()); - } - }); - - const auto quantErrStats = Stats::For(quantErrors); - return quantErrStats; -} - -TEST(Colorspaces, LutError_Rec709Full_Rec709Rgb) -{ - const auto src = ColorspaceDesc{ - Chromaticities::Rec709(), - PiecewiseGammaDesc::Rec709(), - {{YuvLumaCoeffs::Rec709(), YcbcrDesc::Full8()}}, - }; - const auto dst = ColorspaceDesc{ - Chromaticities::Rec709(), - PiecewiseGammaDesc::Rec709(), - {}, - }; - const auto ct = ColorspaceTransform::Create(src, dst); - const auto stats = StatsForLutError(ct, ivec3{64}, ivec3{256}); - EXPECT_NEAR(stats.mean, 0.000, 0.001); - EXPECT_NEAR(stats.standardDeviation(), 0.008, 0.001); - EXPECT_NEAR(stats.min, 0, 0.001); - EXPECT_NEAR(stats.max, 1, 0.001); -} - -TEST(Colorspaces, LutError_Rec709Full_Srgb) -{ - const auto src = ColorspaceDesc{ - Chromaticities::Rec709(), - PiecewiseGammaDesc::Rec709(), - {{YuvLumaCoeffs::Rec709(), YcbcrDesc::Full8()}}, - }; - const auto dst = ColorspaceDesc{ - Chromaticities::Srgb(), - PiecewiseGammaDesc::Srgb(), - {}, - }; - const auto ct = ColorspaceTransform::Create(src, dst); - const auto stats = StatsForLutError(ct, ivec3{64}, ivec3{256}); - EXPECT_NEAR(stats.mean, 0.530, 0.001); - EXPECT_NEAR(stats.standardDeviation(), 1.674, 0.001); - EXPECT_NEAR(stats.min, 0, 0.001); - EXPECT_NEAR(stats.max, 17, 0.001); -} diff --git a/gfx/gl/gtest/moz.build b/gfx/gl/gtest/moz.build deleted file mode 100644 index 5b57e3ded1fd..000000000000 --- a/gfx/gl/gtest/moz.build +++ /dev/null @@ -1,15 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -LOCAL_INCLUDES += [ - "/gfx/gl", -] - -UNIFIED_SOURCES += [ - "TestColorspaces.cpp", -] - -FINAL_LIBRARY = "xul-gtest" diff --git a/gfx/gl/moz.build b/gfx/gl/moz.build index fe641cb8cab5..66ac933661f7 100644 --- a/gfx/gl/moz.build +++ b/gfx/gl/moz.build @@ -22,8 +22,6 @@ if CONFIG["MOZ_GL_PROVIDER"]: EXPORTS += [ "AndroidSurfaceTexture.h", - "AutoMappable.h", - "Colorspaces.h", "ForceDiscreteGPUHelperCGL.h", "GfxTexturesReporter.h", "GLBlitHelper.h", @@ -119,7 +117,6 @@ if CONFIG["MOZ_WAYLAND"]: UNIFIED_SOURCES += [ "AndroidSurfaceTexture.cpp", - "Colorspaces.cpp", "GfxTexturesReporter.cpp", "GLBlitHelper.cpp", "GLContext.cpp", @@ -141,10 +138,6 @@ SOURCES += [ "GLScreenBuffer.cpp", ] -TEST_DIRS += [ - "gtest", -] - include("/ipc/chromium/chromium-config.mozbuild") FINAL_LIBRARY = "xul" diff --git a/gfx/ipc/GfxMessageUtils.h b/gfx/ipc/GfxMessageUtils.h index 12b73c7c476f..c4526925f77a 100644 --- a/gfx/ipc/GfxMessageUtils.h +++ b/gfx/ipc/GfxMessageUtils.h @@ -714,12 +714,6 @@ struct ParamTraits mozilla::gfx::YUVRangedColorSpace::_First, mozilla::gfx::YUVRangedColorSpace::_Last> {}; -template <> -struct ParamTraits - : public ContiguousEnumSerializerInclusive< - mozilla::gfx::ColorSpace2, mozilla::gfx::ColorSpace2::_First, - mozilla::gfx::ColorSpace2::_Last> {}; - template <> struct ParamTraits : public ContiguousEnumSerializerCreateOrRecycleClient(mColorSpace, mColorRange, mSize); + aAllocator->CreateOrRecycleClient(mYUVColorSpace, mColorRange, mSize); if (mTextureClient) { D3D11TextureData* textureData = GetData(); MOZ_DIAGNOSTIC_ASSERT(textureData, "Wrong TextureDataType"); @@ -83,7 +83,7 @@ class MOZ_RAII D3D11TextureClientAllocationHelper : public ITextureClientAllocationHelper { public: D3D11TextureClientAllocationHelper(gfx::SurfaceFormat aFormat, - gfx::ColorSpace2 aColorSpace, + gfx::YUVColorSpace aColorSpace, gfx::ColorRange aColorRange, const gfx::IntSize& aSize, TextureAllocationFlags aAllocFlags, @@ -91,7 +91,7 @@ class MOZ_RAII D3D11TextureClientAllocationHelper TextureFlags aTextureFlags) : ITextureClientAllocationHelper(aFormat, aSize, BackendSelector::Content, aTextureFlags, aAllocFlags), - mColorSpace(aColorSpace), + mYUVColorSpace(aColorSpace), mColorRange(aColorRange), mDevice(aDevice) {} @@ -106,7 +106,7 @@ class MOZ_RAII D3D11TextureClientAllocationHelper return (aTextureClient->GetFormat() != gfx::SurfaceFormat::NV12 && aTextureClient->GetFormat() != gfx::SurfaceFormat::P010 && aTextureClient->GetFormat() != gfx::SurfaceFormat::P016) || - (textureData->mColorSpace == mColorSpace && + (textureData->GetYUVColorSpace() == mYUVColorSpace && textureData->GetColorRange() == mColorRange && textureData->GetTextureAllocationFlags() == mAllocationFlags); } @@ -118,14 +118,14 @@ class MOZ_RAII D3D11TextureClientAllocationHelper if (!data) { return nullptr; } - data->mColorSpace = mColorSpace; + data->SetYUVColorSpace(mYUVColorSpace); data->SetColorRange(mColorRange); return MakeAndAddRef(data, mTextureFlags, aAllocator->GetTextureForwarder()); } private: - const gfx::ColorSpace2 mColorSpace; + const gfx::YUVColorSpace mYUVColorSpace; const gfx::ColorRange mColorRange; const RefPtr mDevice; }; @@ -158,7 +158,7 @@ void D3D11RecycleAllocator::SetPreferredSurfaceFormat( } already_AddRefed D3D11RecycleAllocator::CreateOrRecycleClient( - gfx::ColorSpace2 aColorSpace, gfx::ColorRange aColorRange, + gfx::YUVColorSpace aColorSpace, gfx::ColorRange aColorRange, const gfx::IntSize& aSize) { // When CompositorDevice or ContentDevice is updated, // we could not reuse old D3D11Textures. It could cause video flickering. diff --git a/gfx/layers/D3D11ShareHandleImage.h b/gfx/layers/D3D11ShareHandleImage.h index e35a31a49599..730430185b92 100644 --- a/gfx/layers/D3D11ShareHandleImage.h +++ b/gfx/layers/D3D11ShareHandleImage.h @@ -28,7 +28,7 @@ class D3D11RecycleAllocator final : public TextureClientRecycleAllocator { gfx::SurfaceFormat aPreferredFormat); already_AddRefed CreateOrRecycleClient( - gfx::ColorSpace2 aColorSpace, gfx::ColorRange aColorRange, + gfx::YUVColorSpace aColorSpace, gfx::ColorRange aColorRange, const gfx::IntSize& aSize); void SetPreferredSurfaceFormat(gfx::SurfaceFormat aPreferredFormat); @@ -52,7 +52,7 @@ class D3D11RecycleAllocator final : public TextureClientRecycleAllocator { class D3D11ShareHandleImage final : public Image { public: D3D11ShareHandleImage(const gfx::IntSize& aSize, const gfx::IntRect& aRect, - gfx::ColorSpace2 aColorSpace, + gfx::YUVColorSpace aColorSpace, gfx::ColorRange aColorRange); virtual ~D3D11ShareHandleImage() = default; @@ -66,6 +66,7 @@ class D3D11ShareHandleImage final : public Image { ID3D11Texture2D* GetTexture() const; + gfx::YUVColorSpace GetYUVColorSpace() const { return mYUVColorSpace; } gfx::ColorRange GetColorRange() const { return mColorRange; } private: @@ -79,11 +80,7 @@ class D3D11ShareHandleImage final : public Image { gfx::IntSize mSize; gfx::IntRect mPictureRect; - - public: - const gfx::ColorSpace2 mColorSpace; - - private: + gfx::YUVColorSpace mYUVColorSpace; gfx::ColorRange mColorRange; RefPtr mTextureClient; RefPtr mTexture; diff --git a/gfx/layers/D3D11TextureIMFSampleImage.cpp b/gfx/layers/D3D11TextureIMFSampleImage.cpp index 738c008a04b8..c48310bbc3a5 100644 --- a/gfx/layers/D3D11TextureIMFSampleImage.cpp +++ b/gfx/layers/D3D11TextureIMFSampleImage.cpp @@ -35,14 +35,14 @@ void IMFSampleWrapper::ClearVideoSample() { mVideoSample = nullptr; } D3D11TextureIMFSampleImage::D3D11TextureIMFSampleImage( IMFSample* aVideoSample, ID3D11Texture2D* aTexture, uint32_t aArrayIndex, const gfx::IntSize& aSize, const gfx::IntRect& aRect, - gfx::ColorSpace2 aColorSpace, gfx::ColorRange aColorRange) + gfx::YUVColorSpace aColorSpace, gfx::ColorRange aColorRange) : Image(nullptr, ImageFormat::D3D11_TEXTURE_IMF_SAMPLE), mVideoSample(IMFSampleWrapper::Create(aVideoSample)), mTexture(aTexture), mArrayIndex(aArrayIndex), mSize(aSize), mPictureRect(aRect), - mColorSpace(aColorSpace), + mYUVColorSpace(aColorSpace), mColorRange(aColorRange) { MOZ_ASSERT(XRE_IsGPUProcess()); } @@ -50,7 +50,7 @@ D3D11TextureIMFSampleImage::D3D11TextureIMFSampleImage( void D3D11TextureIMFSampleImage::AllocateTextureClient( KnowsCompositor* aKnowsCompositor, RefPtr aUsageInfo) { mTextureClient = D3D11TextureData::CreateTextureClient( - mTexture, mArrayIndex, mSize, gfx::SurfaceFormat::NV12, mColorSpace, + mTexture, mArrayIndex, mSize, gfx::SurfaceFormat::NV12, mYUVColorSpace, mColorRange, aKnowsCompositor, aUsageInfo); MOZ_ASSERT(mTextureClient); } diff --git a/gfx/layers/D3D11TextureIMFSampleImage.h b/gfx/layers/D3D11TextureIMFSampleImage.h index 4077d386aa8b..5ed1baad5711 100644 --- a/gfx/layers/D3D11TextureIMFSampleImage.h +++ b/gfx/layers/D3D11TextureIMFSampleImage.h @@ -59,7 +59,7 @@ class D3D11TextureIMFSampleImage final : public Image { D3D11TextureIMFSampleImage(IMFSample* aVideoSample, ID3D11Texture2D* aTexture, uint32_t aArrayIndex, const gfx::IntSize& aSize, const gfx::IntRect& aRect, - gfx::ColorSpace2 aColorSpace, + gfx::YUVColorSpace aColorSpace, gfx::ColorRange aColorRange); virtual ~D3D11TextureIMFSampleImage() = default; @@ -74,6 +74,7 @@ class D3D11TextureIMFSampleImage final : public Image { ID3D11Texture2D* GetTexture() const; RefPtr GetIMFSampleWrapper(); + gfx::YUVColorSpace GetYUVColorSpace() const { return mYUVColorSpace; } gfx::ColorRange GetColorRange() const { return mColorRange; } private: @@ -89,15 +90,11 @@ class D3D11TextureIMFSampleImage final : public Image { // IMFTransform. RefPtr mVideoSample; RefPtr mTexture; - - public: const uint32_t mArrayIndex; const gfx::IntSize mSize; const gfx::IntRect mPictureRect; - const gfx::ColorSpace2 mColorSpace; + const gfx::YUVColorSpace mYUVColorSpace; const gfx::ColorRange mColorRange; - - private: RefPtr mTextureClient; }; diff --git a/gfx/layers/D3D9SurfaceImage.cpp b/gfx/layers/D3D9SurfaceImage.cpp index 2c8037a03fa7..8580d0a81e50 100644 --- a/gfx/layers/D3D9SurfaceImage.cpp +++ b/gfx/layers/D3D9SurfaceImage.cpp @@ -80,10 +80,10 @@ already_AddRefed DXGID3D9TextureData::GetD3D9Surface() } bool DXGID3D9TextureData::Serialize(SurfaceDescriptor& aOutDescriptor) { - SurfaceDescriptorD3D10 desc((WindowsHandle)(mHandle), - /* gpuProcessTextureId */ Nothing(), - /* arrayIndex */ 0, mFormat, GetSize(), - gfx::ColorSpace2::SRGB, gfx::ColorRange::FULL); + SurfaceDescriptorD3D10 desc( + (WindowsHandle)(mHandle), /* gpuProcessTextureId */ Nothing(), + /* arrayIndex */ 0, mFormat, GetSize(), gfx::YUVColorSpace::Identity, + gfx::ColorRange::FULL); // In reality, with D3D9 we will only ever deal with RGBA textures. bool isYUV = mFormat == gfx::SurfaceFormat::NV12 || mFormat == gfx::SurfaceFormat::P010 || @@ -91,7 +91,7 @@ bool DXGID3D9TextureData::Serialize(SurfaceDescriptor& aOutDescriptor) { if (isYUV) { gfxCriticalError() << "Unexpected YUV format for DXGID3D9TextureData: " << mFormat; - desc.colorSpace() = gfx::ColorSpace2::BT601_525; + desc.yUVColorSpace() = gfx::YUVColorSpace::BT601; desc.colorRange() = gfx::ColorRange::LIMITED; } aOutDescriptor = desc; diff --git a/gfx/layers/d3d11/HelpersD3D11.h b/gfx/layers/d3d11/HelpersD3D11.h index ef40acd0a012..337212aadc4c 100644 --- a/gfx/layers/d3d11/HelpersD3D11.h +++ b/gfx/layers/d3d11/HelpersD3D11.h @@ -8,7 +8,6 @@ #define mozilla_gfx_layers_d3d11_HelpersD3D11_h #include -#include #include "mozilla/Telemetry.h" #include "mozilla/TimeStamp.h" @@ -51,16 +50,6 @@ static inline bool WaitForFrameGPUQuery(ID3D11Device* aDevice, return success; } -inline void ClearResource(ID3D11Device* const device, ID3D11Resource* const res, - const std::array& vals) { - RefPtr rtv; - (void)device->CreateRenderTargetView(res, nullptr, getter_AddRefs(rtv)); - - RefPtr context; - device->GetImmediateContext(getter_AddRefs(context)); - context->ClearRenderTargetView(rtv, vals.data()); -} - } // namespace layers } // namespace mozilla diff --git a/gfx/layers/d3d11/TextureD3D11.cpp b/gfx/layers/d3d11/TextureD3D11.cpp index f7a2aae9165a..788153ee307d 100644 --- a/gfx/layers/d3d11/TextureD3D11.cpp +++ b/gfx/layers/d3d11/TextureD3D11.cpp @@ -402,7 +402,7 @@ bool D3D11TextureData::SerializeSpecific( } *aOutDesc = SurfaceDescriptorD3D10((WindowsHandle)sharedHandle, mGpuProcessTextureId, mArrayIndex, mFormat, - mSize, mColorSpace, mColorRange); + mSize, mYUVColorSpace, mColorRange); return true; } @@ -425,13 +425,13 @@ void D3D11TextureData::GetSubDescriptor( /* static */ already_AddRefed D3D11TextureData::CreateTextureClient( ID3D11Texture2D* aTexture, uint32_t aIndex, gfx::IntSize aSize, - gfx::SurfaceFormat aFormat, gfx::ColorSpace2 aColorSpace, + gfx::SurfaceFormat aFormat, gfx::YUVColorSpace aColorSpace, gfx::ColorRange aColorRange, KnowsCompositor* aKnowsCompositor, RefPtr aUsageInfo) { D3D11TextureData* data = new D3D11TextureData( aTexture, aIndex, aSize, aFormat, TextureAllocationFlags::ALLOC_MANUAL_SYNCHRONIZATION); - data->mColorSpace = aColorSpace; + data->SetYUVColorSpace(aColorSpace); data->SetColorRange(aColorRange); RefPtr textureClient = MakeAndAddRef( @@ -812,7 +812,7 @@ DXGITextureHostD3D11::DXGITextureHostD3D11( mSize(aDescriptor.size()), mHandle(aDescriptor.handle()), mFormat(aDescriptor.format()), - mColorSpace(aDescriptor.colorSpace()), + mYUVColorSpace(aDescriptor.yUVColorSpace()), mColorRange(aDescriptor.colorRange()), mIsLocked(false) {} @@ -963,9 +963,9 @@ void DXGITextureHostD3D11::UnlockInternal() { void DXGITextureHostD3D11::CreateRenderTexture( const wr::ExternalImageId& aExternalImageId) { - RefPtr texture = - new wr::RenderDXGITextureHost(mHandle, mGpuProcessTextureId, mArrayIndex, - mFormat, mColorSpace, mColorRange, mSize); + RefPtr texture = new wr::RenderDXGITextureHost( + mHandle, mGpuProcessTextureId, mArrayIndex, mFormat, mYUVColorSpace, + mColorRange, mSize); wr::RenderThread::Get()->RegisterExternalImage(aExternalImageId, texture.forget()); } @@ -1092,7 +1092,7 @@ void DXGITextureHostD3D11::PushDisplayItems( aBounds, aClip, true, aImageKeys[0], aImageKeys[1], GetFormat() == gfx::SurfaceFormat::NV12 ? wr::ColorDepth::Color8 : wr::ColorDepth::Color16, - wr::ToWrYuvColorSpace(ToYUVColorSpace(mColorSpace)), + wr::ToWrYuvColorSpace(mYUVColorSpace), wr::ToWrColorRange(mColorRange), aFilter, preferCompositorSurface, SupportsExternalCompositing(aBuilder.GetBackendType())); break; @@ -1109,7 +1109,8 @@ bool DXGITextureHostD3D11::SupportsExternalCompositing( return true; } // XXX Add P010 and P016 support. - if (gfx::gfxVars::UseWebRenderDCompVideoOverlayWin()) { + if (GetFormat() == gfx::SurfaceFormat::NV12 && + gfx::gfxVars::UseWebRenderDCompVideoOverlayWin()) { return true; } return false; diff --git a/gfx/layers/d3d11/TextureD3D11.h b/gfx/layers/d3d11/TextureD3D11.h index 60bebac0cd9e..72deb86a037c 100644 --- a/gfx/layers/d3d11/TextureD3D11.h +++ b/gfx/layers/d3d11/TextureD3D11.h @@ -60,7 +60,7 @@ class D3D11TextureData final : public TextureData { static already_AddRefed CreateTextureClient( ID3D11Texture2D* aTexture, uint32_t aIndex, gfx::IntSize aSize, - gfx::SurfaceFormat aFormat, gfx::ColorSpace2 aColorSpace, + gfx::SurfaceFormat aFormat, gfx::YUVColorSpace aColorSpace, gfx::ColorRange aColorRange, KnowsCompositor* aKnowsCompositor, RefPtr aUsageInfo); @@ -95,6 +95,10 @@ class D3D11TextureData final : public TextureData { bool Serialize(SurfaceDescriptor& aOutDescrptor) override; void GetSubDescriptor(RemoteDecoderVideoSubDescriptor* aOutDesc) override; + gfx::YUVColorSpace GetYUVColorSpace() const { return mYUVColorSpace; } + void SetYUVColorSpace(gfx::YUVColorSpace aColorSpace) { + mYUVColorSpace = aColorSpace; + } gfx::ColorRange GetColorRange() const { return mColorRange; } void SetColorRange(gfx::ColorRange aColorRange) { mColorRange = aColorRange; } @@ -132,11 +136,7 @@ class D3D11TextureData final : public TextureData { RefPtr mDrawTarget; const gfx::IntSize mSize; const gfx::SurfaceFormat mFormat; - - public: - gfx::ColorSpace2 mColorSpace = gfx::ColorSpace2::SRGB; - - private: + gfx::YUVColorSpace mYUVColorSpace = gfx::YUVColorSpace::Identity; gfx::ColorRange mColorRange = gfx::ColorRange::LIMITED; bool mNeedsClear = false; const bool mHasSynchronization; @@ -349,6 +349,9 @@ class DXGITextureHostD3D11 : public TextureHost { void UnlockWithoutCompositor() override; gfx::IntSize GetSize() const override { return mSize; } + gfx::YUVColorSpace GetYUVColorSpace() const override { + return mYUVColorSpace; + } gfx::ColorRange GetColorRange() const override { return mColorRange; } already_AddRefed GetAsSurface() override; @@ -389,11 +392,7 @@ class DXGITextureHostD3D11 : public TextureHost { gfx::IntSize mSize; WindowsHandle mHandle; gfx::SurfaceFormat mFormat; - - public: - const gfx::ColorSpace2 mColorSpace; - - protected: + const gfx::YUVColorSpace mYUVColorSpace; const gfx::ColorRange mColorRange; bool mIsLocked; }; diff --git a/gfx/layers/ipc/LayersSurfaces.ipdlh b/gfx/layers/ipc/LayersSurfaces.ipdlh index 800e4c54882e..bd9a406a8283 100644 --- a/gfx/layers/ipc/LayersSurfaces.ipdlh +++ b/gfx/layers/ipc/LayersSurfaces.ipdlh @@ -34,7 +34,7 @@ namespace layers { uint32_t arrayIndex; SurfaceFormat format; IntSize size; - ColorSpace2 colorSpace; + YUVColorSpace yUVColorSpace; ColorRange colorRange; }; diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index f864c730fd02..9f4a90f2bb3e 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -23,7 +23,6 @@ #include "mozilla/gfx/CanvasManagerChild.h" #include "mozilla/gfx/CanvasManagerParent.h" #include "mozilla/ClearOnShutdown.h" -#include "mozilla/EnumTypeTraits.h" #include "mozilla/StaticPrefs_accessibility.h" #include "mozilla/StaticPrefs_apz.h" #include "mozilla/StaticPrefs_bidi.h" @@ -2015,14 +2014,6 @@ const mozilla::gfx::ContentDeviceData* gfxPlatform::GetInitContentDeviceData() { return gContentDeviceInitData; } -CMSMode GfxColorManagementMode() { - const auto mode = StaticPrefs::gfx_color_management_mode(); - if (mode >= 0 && mode < UnderlyingValue(CMSMode::AllCount)) { - return CMSMode(mode); - } - return CMSMode::Off; -} - void gfxPlatform::InitializeCMS() { if (gCMSInitialized) { return; @@ -2041,7 +2032,12 @@ void gfxPlatform::InitializeCMS() { return; } - gCMSMode = GfxColorManagementMode(); + { + int32_t mode = StaticPrefs::gfx_color_management_mode(); + if (mode >= 0 && mode < int32_t(CMSMode::AllCount)) { + gCMSMode = CMSMode(mode); + } + } gCMSsRGBProfile = qcms_profile_sRGB(); diff --git a/gfx/thebes/gfxPlatform.h b/gfx/thebes/gfxPlatform.h index 406be8ebb502..e56ab49aa952 100644 --- a/gfx/thebes/gfxPlatform.h +++ b/gfx/thebes/gfxPlatform.h @@ -1027,6 +1027,4 @@ class gfxPlatform : public mozilla::layers::MemoryPressureListener { const gfxSkipChars kEmptySkipChars; }; -CMSMode GfxColorManagementMode(); - #endif /* GFX_PLATFORM_H */ diff --git a/gfx/webrender_bindings/DCLayerTree.cpp b/gfx/webrender_bindings/DCLayerTree.cpp index 162b74fd6872..7aba3ca72564 100644 --- a/gfx/webrender_bindings/DCLayerTree.cpp +++ b/gfx/webrender_bindings/DCLayerTree.cpp @@ -6,7 +6,6 @@ #include "DCLayerTree.h" -#include "GLBlitHelper.h" #include "GLContext.h" #include "GLContextEGL.h" #include "mozilla/gfx/DeviceManagerDx.h" @@ -14,8 +13,6 @@ #include "mozilla/gfx/gfxVars.h" #include "mozilla/gfx/GPUParent.h" #include "mozilla/gfx/Matrix.h" -#include "mozilla/layers/HelpersD3D11.h" -#include "mozilla/ScopeExit.h" #include "mozilla/StaticPrefs_gfx.h" #include "mozilla/webrender/RenderD3D11TextureHost.h" #include "mozilla/webrender/RenderTextureHost.h" @@ -23,7 +20,6 @@ #include "mozilla/WindowsVersion.h" #include "mozilla/Telemetry.h" #include "nsPrintfCString.h" -#include "ScopedGLHelpers.h" #include "WinUtils.h" #undef _WIN32_WINNT @@ -501,9 +497,9 @@ void DCLayerTree::CreateExternalSurface(wr::NativeSurfaceId aId, auto it = mDCSurfaces.find(aId); MOZ_RELEASE_ASSERT(it == mDCSurfaces.end()); - auto surface = MakeUnique(aIsOpaque, this); + auto surface = MakeUnique(aIsOpaque, this); if (!surface->Initialize()) { - gfxCriticalNote << "Failed to initialize DCSurfaceSwapChain: " + gfxCriticalNote << "Failed to initialize DCSurfaceVideo: " << wr::AsUint64(aId); return; } @@ -534,10 +530,10 @@ void DCLayerTree::AttachExternalImage(wr::NativeSurfaceId aId, wr::ExternalImageId aExternalImage) { auto surface_it = mDCSurfaces.find(aId); MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end()); - auto* surfaceSc = surface_it->second->AsDCSurfaceSwapChain(); - MOZ_RELEASE_ASSERT(surfaceSc); + auto* surfaceVideo = surface_it->second->AsDCSurfaceVideo(); + MOZ_RELEASE_ASSERT(surfaceVideo); - surfaceSc->AttachExternalImage(aExternalImage); + surfaceVideo->AttachExternalImage(aExternalImage); } template @@ -564,12 +560,11 @@ void DCLayerTree::AddSurface(wr::NativeSurfaceId aId, gfx::Matrix transform(aTransform.m11, aTransform.m12, aTransform.m21, aTransform.m22, aTransform.m41, aTransform.m42); - auto* surfaceSc = surface->AsDCSurfaceSwapChain(); - if (surfaceSc) { - const auto presentationTransform = surfaceSc->EnsurePresented(transform); - if (presentationTransform) { - transform = *presentationTransform; - } // else EnsurePresented failed, just limp along? + auto* surfaceVideo = surface->AsDCSurfaceVideo(); + if (surfaceVideo) { + if (surfaceVideo->CalculateSwapChainSize(transform)) { + surfaceVideo->PresentVideo(); + } } transform.PreTranslate(-virtualOffset.x, -virtualOffset.y); @@ -651,8 +646,8 @@ GLuint DCLayerTree::GetOrCreateFbo(int aWidth, int aHeight) { return fboId; } -bool DCLayerTree::EnsureVideoProcessorAtLeast(const gfx::IntSize& aInputSize, - const gfx::IntSize& aOutputSize) { +bool DCLayerTree::EnsureVideoProcessor(const gfx::IntSize& aInputSize, + const gfx::IntSize& aOutputSize) { HRESULT hr; if (!mVideoDevice || !mVideoContext) { @@ -840,444 +835,159 @@ DCTile* DCSurface::GetTile(int aX, int aY) const { return tile_it->second.get(); } -// - -// DCSurfaceSwapChain - -DCSurfaceSwapChain::DCSurfaceSwapChain(bool aIsOpaque, - DCLayerTree* aDCLayerTree) +DCSurfaceVideo::DCSurfaceVideo(bool aIsOpaque, DCLayerTree* aDCLayerTree) : DCSurface(wr::DeviceIntSize{}, wr::DeviceIntPoint{}, aIsOpaque, - aDCLayerTree) { - if (mDCLayerTree->SupportsHardwareOverlays()) { - mOverlayFormat = Some(mDCLayerTree->GetOverlayFormatForSDR()); - } -} + aDCLayerTree) {} -bool IsYuv(const DXGI_FORMAT aFormat) { +bool IsYUVSwapChainFormat(DXGI_FORMAT aFormat) { if (aFormat == DXGI_FORMAT_NV12 || aFormat == DXGI_FORMAT_YUY2) { return true; } return false; } -bool IsYuv(const gfx::SurfaceFormat aFormat) { - switch (aFormat) { - case gfx::SurfaceFormat::NV12: - case gfx::SurfaceFormat::P010: - case gfx::SurfaceFormat::P016: - case gfx::SurfaceFormat::YUV: - case gfx::SurfaceFormat::YUV422: - return true; - - case gfx::SurfaceFormat::B8G8R8A8: - case gfx::SurfaceFormat::B8G8R8X8: - case gfx::SurfaceFormat::R8G8B8A8: - case gfx::SurfaceFormat::R8G8B8X8: - case gfx::SurfaceFormat::A8R8G8B8: - case gfx::SurfaceFormat::X8R8G8B8: - - case gfx::SurfaceFormat::R8G8B8: - case gfx::SurfaceFormat::B8G8R8: - - case gfx::SurfaceFormat::R5G6B5_UINT16: - case gfx::SurfaceFormat::A8: - case gfx::SurfaceFormat::A16: - case gfx::SurfaceFormat::R8G8: - case gfx::SurfaceFormat::R16G16: - - case gfx::SurfaceFormat::HSV: - case gfx::SurfaceFormat::Lab: - case gfx::SurfaceFormat::Depth: - case gfx::SurfaceFormat::UNKNOWN: - return false; - } - MOZ_ASSERT_UNREACHABLE(); -} - -void DCSurfaceSwapChain::AttachExternalImage( - wr::ExternalImageId aExternalImage) { +void DCSurfaceVideo::AttachExternalImage(wr::ExternalImageId aExternalImage) { RenderTextureHost* texture = RenderThread::Get()->GetRenderTexture(aExternalImage); MOZ_RELEASE_ASSERT(texture); - const auto textureDxgi = texture->AsRenderDXGITextureHost(); - if (!textureDxgi) { + if (mPrevTexture == texture) { + return; + } + + // XXX if software decoded video frame format is nv12, it could be used as + // video overlay. + if (!texture || !texture->AsRenderDXGITextureHost() || + texture->AsRenderDXGITextureHost()->GetFormat() != + gfx::SurfaceFormat::NV12) { gfxCriticalNote << "Unsupported RenderTexture for overlay: " << gfx::hexa(texture); return; } - if (mSrc && mSrc->texture == textureDxgi) { - return; // Dupe. - } - - Src src = {}; - src.texture = textureDxgi; - src.format = src.texture->GetFormat(); - src.size = src.texture->GetSize(0); - src.space = { - src.texture->mColorSpace, - IsYuv(src.format) ? Some(src.texture->mColorRange) : Nothing(), - }; - - mSrc = Some(src); + mRenderTextureHost = texture; } -// - - -Maybe CreateSwapChain(ID3D11Device&, gfx::IntSize, - DXGI_FORMAT, - DXGI_COLOR_SPACE_TYPE); - -// - - -static Maybe ExactDXGIColorSpace( - const CspaceAndRange& space) { - switch (space.space) { - case gfx::ColorSpace2::BT601_525: - if (!space.yuvRange) { - return {}; - } else if (*space.yuvRange == gfx::ColorRange::FULL) { - return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601); - } else { - return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601); - } - case gfx::ColorSpace2::UNKNOWN: - case gfx::ColorSpace2::SRGB: // Gamma ~2.2 - if (!space.yuvRange) { - return Some(DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709); - } else if (*space.yuvRange == gfx::ColorRange::FULL) { - return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709); - } else { - return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709); - } - case gfx::ColorSpace2::BT709: // Gamma ~2.4 - if (!space.yuvRange) { - // This should ideally be G24, but that only exists for STUDIO not FULL. - return Some(DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709); - } else if (*space.yuvRange == gfx::ColorRange::FULL) { - // TODO return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G24_LEFT_P709); - return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709); - } else { - // TODO return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_LEFT_P709); - return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709); - } - case gfx::ColorSpace2::BT2020: - if (!space.yuvRange) { - return Some(DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020); - } else if (*space.yuvRange == gfx::ColorRange::FULL) { - // XXX Add SMPTEST2084 handling. HDR content is not handled yet by - // video overlay. - return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020); - } else { - return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020); - } - case gfx::ColorSpace2::DISPLAY_P3: - return {}; - } - MOZ_ASSERT_UNREACHABLE(); -} - -// - - -color::ColorspaceDesc ToColorspaceDesc(const CspaceAndRange& space) { - color::ColorspaceDesc ret = {}; - switch (space.space) { - case gfx::ColorSpace2::UNKNOWN: - case gfx::ColorSpace2::SRGB: - ret.chrom = color::Chromaticities::Srgb(); - ret.tf = {color::PiecewiseGammaDesc::Srgb()}; - MOZ_ASSERT(!space.yuvRange); - return ret; - - case gfx::ColorSpace2::DISPLAY_P3: - ret.chrom = color::Chromaticities::DisplayP3(); - ret.tf = {color::PiecewiseGammaDesc::DisplayP3()}; - MOZ_ASSERT(!space.yuvRange); - return ret; - - case gfx::ColorSpace2::BT601_525: - ret.chrom = color::Chromaticities::Rec601_525_Ntsc(); - ret.tf = {color::PiecewiseGammaDesc::Rec709()}; - if (space.yuvRange) { - ret.yuv = { - {color::YuvLumaCoeffs::Rec709(), color::YcbcrDesc::Narrow8()}}; - if (*space.yuvRange == gfx::ColorRange::FULL) { - ret.yuv->ycbcr = color::YcbcrDesc::Full8(); - } - } - return ret; - - case gfx::ColorSpace2::BT709: - ret.chrom = color::Chromaticities::Rec709(); - ret.tf = {color::PiecewiseGammaDesc::Rec709()}; - if (space.yuvRange) { - ret.yuv = { - {color::YuvLumaCoeffs::Rec709(), color::YcbcrDesc::Narrow8()}}; - if (*space.yuvRange == gfx::ColorRange::FULL) { - ret.yuv->ycbcr = color::YcbcrDesc::Full8(); - } - } - return ret; - - case gfx::ColorSpace2::BT2020: - ret.chrom = color::Chromaticities::Rec2020(); - ret.tf = {color::PiecewiseGammaDesc::Rec2020_10bit()}; - if (space.yuvRange) { - ret.yuv = { - {color::YuvLumaCoeffs::Rec709(), color::YcbcrDesc::Narrow8()}}; - if (*space.yuvRange == gfx::ColorRange::FULL) { - ret.yuv->ycbcr = color::YcbcrDesc::Full8(); - } - } - return ret; - } - MOZ_ASSERT_UNREACHABLE(); -} - -// - - -static CspaceTransformPlan ChooseCspaceTransformPlan( - const CspaceAndRange& srcSpace) { - // Unfortunately, it looks like the no-op - // DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709 - // => DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709 - // transform mis-translates colors if you ask VideoProcessor to resize. - // (jgilbert's RTX 3070 machine "osiris") - // Absent more investigation, let's avoid VP with non-YUV sources for now. - const auto cmsMode = GfxColorManagementMode(); - const bool doColorManagement = cmsMode != CMSMode::Off; - if (srcSpace.yuvRange && doColorManagement) { - const auto exactDxgiSpace = ExactDXGIColorSpace(srcSpace); - if (exactDxgiSpace) { - auto plan = CspaceTransformPlan::WithVideoProcessor{}; - plan.srcSpace = *exactDxgiSpace; - if (srcSpace.yuvRange) { - plan.dstYuvSpace = Some(plan.srcSpace); - plan.dstRgbSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; - } else { - plan.dstRgbSpace = plan.srcSpace; - } - return {Some(plan), {}}; - } +bool DCSurfaceVideo::CalculateSwapChainSize(gfx::Matrix& aTransform) { + if (!mRenderTextureHost) { + MOZ_ASSERT_UNREACHABLE("unexpected to be called"); + return false; } - auto plan = CspaceTransformPlan::WithGLBlitHelper{ - ToColorspaceDesc(srcSpace), - {color::Chromaticities::Srgb(), {color::PiecewiseGammaDesc::Srgb()}}, - DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709, - DXGI_FORMAT_B8G8R8A8_UNORM, - }; - switch (srcSpace.space) { - case gfx::ColorSpace2::SRGB: - case gfx::ColorSpace2::BT601_525: - case gfx::ColorSpace2::BT709: - case gfx::ColorSpace2::UNKNOWN: - break; - case gfx::ColorSpace2::BT2020: - case gfx::ColorSpace2::DISPLAY_P3: - // We know our src cspace, and we need to pick a dest cspace. - // The technically-correct thing to do is pick scrgb rgb16f, but - // bandwidth! One option for us is rec2020, which is huge, if the banding - // is acceptable. (We could even use rgb10 for this) EXCEPT, - // display-p3(1,0,0) red is rec2020(0.869 0.175 -0.005). At cost of - // clipping red by up to 0.5%, it's worth considering. Do scrgb for now - // though. + mVideoSize = mRenderTextureHost->AsRenderDXGITextureHost()->GetSize(0); - // Let's do DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020 + - // DXGI_FORMAT_R10G10B10A2_UNORM - plan = { - // Rec2020 g2.2 rgb10 - plan.srcSpace, - {color::Chromaticities::Rec2020(), - {color::PiecewiseGammaDesc::Srgb()}}, - DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020, // Note that this is srgb - // gamma! - DXGI_FORMAT_R10G10B10A2_UNORM, - }; - plan = { - // Actually, that doesn't work, so use scRGB g1.0 rgb16f - plan.srcSpace, - {color::Chromaticities::Rec709(), {}}, - DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709, // scRGB - DXGI_FORMAT_R16G16B16A16_FLOAT, - }; - break; - } - if (!doColorManagement) { - plan.dstSpace = plan.srcSpace; - plan.dstSpace.yuv = {}; - } - - return {{}, Some(plan)}; -} - -// - - -Maybe DCSurfaceSwapChain::EnsurePresented( - const gfx::Matrix& aTransform) { - MOZ_RELEASE_ASSERT(mSrc); - const auto& srcSize = mSrc->size; - - // - - - auto dstSize = srcSize; - auto presentationTransform = aTransform; + // When RenderTextureHost, swapChainSize or VideoSwapChain are updated, + // DCSurfaceVideo::PresentVideo() needs to be called. + bool needsToPresent = mPrevTexture != mRenderTextureHost; + gfx::IntSize swapChainSize = mVideoSize; + gfx::Matrix transform = aTransform; // When video is rendered to axis aligned integer rectangle, video scaling - // should be done by stretching in the VideoProcessor. - // Sotaro has observed a reduction in gpu queue tasks by doing scaling - // ourselves (via VideoProcessor) instead of having DComp handle it. + // could be done by VideoProcessor + bool scaleVideoAtVideoProcessor = false; if (StaticPrefs::gfx_webrender_dcomp_video_vp_scaling_win_AtStartup() && aTransform.PreservesAxisAlignedRectangles()) { - const auto absScales = - aTransform.ScaleFactors(); // E.g. [2, 0, 0, -2] => [2, 2] - const auto presentationTransformUnscaled = - presentationTransform.Copy().PreScale(1 / absScales.xScale, - 1 / absScales.yScale); - const auto dstSizeScaled = - gfx::IntSize::Round(gfx::Size(dstSize) * aTransform.ScaleFactors()); - const auto presentSizeOld = - presentationTransform.TransformSize(gfx::Size(dstSize)); - const auto presentSizeNew = - presentationTransformUnscaled.TransformSize(gfx::Size(dstSizeScaled)); - if (gfx::FuzzyEqual(presentSizeNew.width, presentSizeOld.width, 0.1f) && - gfx::FuzzyEqual(presentSizeNew.height, presentSizeOld.height, 0.1f)) { - dstSize = dstSizeScaled; - presentationTransform = presentationTransformUnscaled; + gfx::Size scaledSize = gfx::Size(mVideoSize) * aTransform.ScaleFactors(); + gfx::IntSize size(int32_t(std::round(scaledSize.width)), + int32_t(std::round(scaledSize.height))); + if (gfx::FuzzyEqual(scaledSize.width, size.width, 0.1f) && + gfx::FuzzyEqual(scaledSize.height, size.height, 0.1f)) { + scaleVideoAtVideoProcessor = true; + swapChainSize = size; } } - // 4:2:2 subsampled formats like YUY2 must have an even width, and 4:2:0 - // subsampled formats like NV12 must have an even width and height. - // And we should be able to pad width and height because we clip the Visual to - // the unpadded rect. Just do this unconditionally. - if (dstSize.width % 2 == 1) { - dstSize.width += 1; - } - if (dstSize.height % 2 == 1) { - dstSize.height += 1; - } - - // - - - if (mDest && mDest->dest->size != dstSize) { - mDest = Nothing(); - } - if (mDest && mDest->srcSpace != mSrc->space) { - mDest = Nothing(); - } - - // - - - // Ok, we have some options: - // * ID3D11VideoProcessor can change between colorspaces that D3D knows about. - // * But, sometimes VideoProcessor can output YUV, and sometimes it needs - // to output RGB.[1] And we can only find out by trying it. - // * Otherwise, we need to do it ourselves, via GLBlitHelper. - - // GLBlitHelper will always work. However, we probably want to prefer to use - // ID3D11VideoProcessor where we can, as an optimization. - - // From sotaro: - // > We use VideoProcessor for scaling reducing GPU usage. - // > "Video Processing" in Windows Task Manager showed that scaling by - // > VideoProcessor used less cpu than just direct composition. - - // [1]: KG: I bet whenever we need a colorspace transform, we need to pull out - // to RGB. Otherwise we might need to YUV1->RGB1->RGB2->YUV2, which is - // maybe-wasteful. Maybe it's possible if everything is a linear - // transform to multiply it all into one matrix? Gamma translation is - // non-linear though, and those cannot be a matrix. - - // So, in order, try: - // * VideoProcessor w/ YUV output - // * VideoProcessor w/ RGB output - // * GLBlitHelper (RGB output) - // Because these Src->Dest differently, we need to be stateful. - // However, if re-using the previous state fails, we can try from the top of - // the list again. - - const auto CallBlit = [&]() { - if (mDest->plan.videoProcessor) { - return CallVideoProcessorBlt(); + if (scaleVideoAtVideoProcessor) { + // 4:2:2 subsampled formats like YUY2 must have an even width, and 4:2:0 + // subsampled formats like NV12 must have an even width and height. + if (swapChainSize.width % 2 == 1) { + swapChainSize.width += 1; } - MOZ_RELEASE_ASSERT(mDest->plan.blitHelper); - return CallBlitHelper(); - }; - - bool needsPresent = mSrc->needsPresent; - if (!mDest || mDest->needsPresent) { - needsPresent = true; + if (swapChainSize.height % 2 == 1) { + swapChainSize.height += 1; + } + transform = gfx::Matrix::Translation(aTransform.GetTranslation()); } - if (needsPresent) { - // Reuse previous method? - if (mDest) { - if (!CallBlit()) { - mDest = Nothing(); + if (!mVideoSwapChain || mSwapChainSize != swapChainSize) { + needsToPresent = true; + ReleaseDecodeSwapChainResources(); + // Update mSwapChainSize before creating SwapChain + mSwapChainSize = swapChainSize; + + auto swapChainFormat = GetSwapChainFormat(); + bool useYUVSwapChain = IsYUVSwapChainFormat(swapChainFormat); + if (useYUVSwapChain) { + // Tries to create YUV SwapChain + CreateVideoSwapChain(); + if (!mVideoSwapChain) { + mFailedYuvSwapChain = true; + ReleaseDecodeSwapChainResources(); + + gfxCriticalNote << "Fallback to RGB SwapChain"; } } - - if (!mDest) { - mDest.emplace(); - mDest->srcSpace = mSrc->space; - mDest->plan = ChooseCspaceTransformPlan(mDest->srcSpace); - - const auto device = mDCLayerTree->GetDevice(); - if (mDest->plan.videoProcessor) { - const auto& plan = *mDest->plan.videoProcessor; - if (mOverlayFormat && plan.dstYuvSpace) { - mDest->dest = CreateSwapChain(*device, dstSize, *mOverlayFormat, - *plan.dstYuvSpace); - // We need to check if this works. If it does, we're going to - // immediately redundently call this again below, but that's ok. - if (!CallVideoProcessorBlt()) { - mDest->dest.reset(); - } - } - if (!mDest->dest) { - mDest->dest = CreateSwapChain( - *device, dstSize, DXGI_FORMAT_B8G8R8A8_UNORM, plan.dstRgbSpace); - } - } else if (mDest->plan.blitHelper) { - const auto& plan = *mDest->plan.blitHelper; - mDest->dest = CreateSwapChain(*device, dstSize, plan.dstDxgiFormat, - plan.dstDxgiSpace); - } - MOZ_ASSERT(mDest->dest); - mVisual->SetContent(mDest->dest->swapChain); + // Tries to create RGB SwapChain + if (!mVideoSwapChain) { + CreateVideoSwapChain(); } - - if (!CallBlit()) { - RenderThread::Get()->NotifyWebRenderError( - wr::WebRenderError::VIDEO_OVERLAY); - return {}; - } - - const auto hr = mDest->dest->swapChain->Present(0, 0); - if (FAILED(hr)) { - gfxCriticalNoteOnce << "video Present failed: " << gfx::hexa(hr); - } - - mSrc->needsPresent = false; - mDest->needsPresent = false; } - // - + aTransform = transform; - return Some(presentationTransform); + return needsToPresent; } -static Maybe CreateSwapChain( - ID3D11Device& device, const gfx::IntSize aSize, const DXGI_FORMAT aFormat, - const DXGI_COLOR_SPACE_TYPE aColorSpace) { - auto swapChain = DCSurfaceSwapChain::Dest(); - swapChain.size = aSize; - swapChain.format = aFormat; - swapChain.space = aColorSpace; +void DCSurfaceVideo::PresentVideo() { + if (!mRenderTextureHost) { + return; + } - RefPtr dxgiDevice; - device.QueryInterface((IDXGIDevice2**)getter_AddRefs(dxgiDevice)); + if (!mVideoSwapChain) { + gfxCriticalNote << "Failed to create VideoSwapChain"; + RenderThread::Get()->NotifyWebRenderError( + wr::WebRenderError::VIDEO_OVERLAY); + return; + } + + mVisual->SetContent(mVideoSwapChain); + + if (!CallVideoProcessorBlt()) { + auto swapChainFormat = GetSwapChainFormat(); + bool useYUVSwapChain = IsYUVSwapChainFormat(swapChainFormat); + if (useYUVSwapChain) { + mFailedYuvSwapChain = true; + ReleaseDecodeSwapChainResources(); + return; + } + RenderThread::Get()->NotifyWebRenderError( + wr::WebRenderError::VIDEO_OVERLAY); + return; + } + + HRESULT hr; + hr = mVideoSwapChain->Present(0, 0); + if (FAILED(hr) && hr != DXGI_STATUS_OCCLUDED) { + gfxCriticalNoteOnce << "video Present failed: " << gfx::hexa(hr); + } + + mPrevTexture = mRenderTextureHost; +} + +DXGI_FORMAT DCSurfaceVideo::GetSwapChainFormat() { + if (mFailedYuvSwapChain || !mDCLayerTree->SupportsHardwareOverlays()) { + return DXGI_FORMAT_B8G8R8A8_UNORM; + } + return mDCLayerTree->GetOverlayFormatForSDR(); +} + +bool DCSurfaceVideo::CreateVideoSwapChain() { + MOZ_ASSERT(mRenderTextureHost); + + const auto device = mDCLayerTree->GetDevice(); + + RefPtr dxgiDevice; + device->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice)); RefPtr dxgiFactoryMedia; { @@ -1286,118 +996,95 @@ static Maybe CreateSwapChain( adapter->GetParent( IID_PPV_ARGS((IDXGIFactoryMedia**)getter_AddRefs(dxgiFactoryMedia))); } - RefPtr dxgiFactory2; - { - RefPtr adapter; - dxgiDevice->GetAdapter(getter_AddRefs(adapter)); - adapter->GetParent( - IID_PPV_ARGS((IDXGIFactory2**)getter_AddRefs(dxgiFactory2))); - } - swapChain.swapChainSurfaceHandle = - MakeUnique(gfx::DeviceManagerDx::CreateDCompSurfaceHandle()); - if (!*swapChain.swapChainSurfaceHandle) { + mSwapChainSurfaceHandle = gfx::DeviceManagerDx::CreateDCompSurfaceHandle(); + if (!mSwapChainSurfaceHandle) { gfxCriticalNote << "Failed to create DCompSurfaceHandle"; - return {}; + return false; } - { - DXGI_SWAP_CHAIN_DESC1 desc = {}; - desc.Width = swapChain.size.width; - desc.Height = swapChain.size.height; - desc.Format = swapChain.format; - desc.Stereo = FALSE; - desc.SampleDesc.Count = 1; - desc.BufferCount = 2; - desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - desc.Scaling = DXGI_SCALING_STRETCH; - desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; - desc.Flags = DXGI_SWAP_CHAIN_FLAG_FULLSCREEN_VIDEO; - if (IsYuv(desc.Format)) { - desc.Flags |= DXGI_SWAP_CHAIN_FLAG_YUV_VIDEO; - } - desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; + auto swapChainFormat = GetSwapChainFormat(); - RefPtr swapChain1; - HRESULT hr; - bool useSurfaceHandle = true; - if (useSurfaceHandle) { - hr = dxgiFactoryMedia->CreateSwapChainForCompositionSurfaceHandle( - &device, *swapChain.swapChainSurfaceHandle, &desc, nullptr, - getter_AddRefs(swapChain1)); - } else { - // CreateSwapChainForComposition does not support - // e.g. DXGI_SWAP_CHAIN_FLAG_YUV_VIDEO. - MOZ_ASSERT(!desc.Flags); - hr = dxgiFactory2->CreateSwapChainForComposition( - &device, &desc, nullptr, getter_AddRefs(swapChain1)); - } - if (FAILED(hr)) { - gfxCriticalNote << "Failed to create output SwapChain: " << gfx::hexa(hr) - << " " << swapChain.size; - return {}; - } + DXGI_SWAP_CHAIN_DESC1 desc = {}; + desc.Width = mSwapChainSize.width; + desc.Height = mSwapChainSize.height; + desc.Format = swapChainFormat; + desc.Stereo = FALSE; + desc.SampleDesc.Count = 1; + desc.BufferCount = 2; + desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + desc.Scaling = DXGI_SCALING_STRETCH; + desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; + desc.Flags = DXGI_SWAP_CHAIN_FLAG_FULLSCREEN_VIDEO; + if (IsYUVSwapChainFormat(swapChainFormat)) { + desc.Flags |= DXGI_SWAP_CHAIN_FLAG_YUV_VIDEO; + } + desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; - swapChain1->QueryInterface( - static_cast(getter_AddRefs(swapChain.swapChain))); - if (!swapChain.swapChain) { - gfxCriticalNote << "Failed to get IDXGISwapChain3"; - return {}; - } + HRESULT hr; + hr = dxgiFactoryMedia->CreateSwapChainForCompositionSurfaceHandle( + device, mSwapChainSurfaceHandle, &desc, nullptr, + getter_AddRefs(mVideoSwapChain)); - const auto cmsMode = GfxColorManagementMode(); - const bool doColorManagement = cmsMode != CMSMode::Off; - if (doColorManagement) { - hr = swapChain.swapChain->SetColorSpace1(aColorSpace); - if (FAILED(hr)) { - gfxCriticalNote << "SetColorSpace1 failed: " << gfx::hexa(hr); - return {}; - } - } + if (FAILED(hr)) { + gfxCriticalNote << "Failed to create video SwapChain: " << gfx::hexa(hr) + << " " << mSwapChainSize; + return false; } - return Some(std::move(swapChain)); + mSwapChainFormat = swapChainFormat; + return true; } -bool DCSurfaceSwapChain::CallVideoProcessorBlt() const { - MOZ_RELEASE_ASSERT(mSrc); - MOZ_RELEASE_ASSERT(mDest); - MOZ_RELEASE_ASSERT(mDest->dest); +// TODO: Replace with YUVRangedColorSpace +static Maybe GetSourceDXGIColorSpace( + const gfx::YUVColorSpace aYUVColorSpace, + const gfx::ColorRange aColorRange) { + if (aYUVColorSpace == gfx::YUVColorSpace::BT601) { + if (aColorRange == gfx::ColorRange::FULL) { + return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601); + } else { + return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601); + } + } else if (aYUVColorSpace == gfx::YUVColorSpace::BT709) { + if (aColorRange == gfx::ColorRange::FULL) { + return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709); + } else { + return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709); + } + } else if (aYUVColorSpace == gfx::YUVColorSpace::BT2020) { + if (aColorRange == gfx::ColorRange::FULL) { + // XXX Add SMPTEST2084 handling. HDR content is not handled yet by + // video overlay. + return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020); + } else { + return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020); + } + } + + return Nothing(); +} + +static Maybe GetSourceDXGIColorSpace( + const gfx::YUVRangedColorSpace aYUVColorSpace) { + const auto info = FromYUVRangedColorSpace(aYUVColorSpace); + return GetSourceDXGIColorSpace(info.space, info.range); +} + +bool DCSurfaceVideo::CallVideoProcessorBlt() { + MOZ_ASSERT(mRenderTextureHost); HRESULT hr; const auto videoDevice = mDCLayerTree->GetVideoDevice(); const auto videoContext = mDCLayerTree->GetVideoContext(); - const auto& texture = mSrc->texture; + const auto texture = mRenderTextureHost->AsRenderDXGITextureHost(); - if (!mDCLayerTree->EnsureVideoProcessorAtLeast(mSrc->size, - mDest->dest->size)) { - gfxCriticalNote << "EnsureVideoProcessor Failed"; + Maybe sourceColorSpace = + GetSourceDXGIColorSpace(texture->GetYUVColorSpace()); + if (sourceColorSpace.isNothing()) { + gfxCriticalNote << "Unsupported color space"; return false; } - const auto videoProcessor = mDCLayerTree->GetVideoProcessor(); - const auto videoProcessorEnumerator = - mDCLayerTree->GetVideoProcessorEnumerator(); - - // - - // Set color spaces - - { - RefPtr videoContext1; - videoContext->QueryInterface( - (ID3D11VideoContext1**)getter_AddRefs(videoContext1)); - if (!videoContext1) { - gfxCriticalNote << "Failed to get ID3D11VideoContext1"; - return false; - } - const auto& plan = *mDest->plan.videoProcessor; - videoContext1->VideoProcessorSetStreamColorSpace1(videoProcessor, 0, - plan.srcSpace); - videoContext1->VideoProcessorSetOutputColorSpace1(videoProcessor, - mDest->dest->space); - } - - // - - // inputView RefPtr texture2D = texture->GetD3D11Texture2DWithGL(); if (!texture2D) { @@ -1405,11 +1092,50 @@ bool DCSurfaceSwapChain::CallVideoProcessorBlt() const { return false; } - if (!mSrc->texture->LockInternal()) { - gfxCriticalNote << "CallVideoProcessorBlt LockInternal failed."; + if (!mVideoSwapChain) { return false; } - const auto unlock = MakeScopeExit([&]() { mSrc->texture->Unlock(); }); + + if (!mDCLayerTree->EnsureVideoProcessor(mVideoSize, mSwapChainSize)) { + gfxCriticalNote << "EnsureVideoProcessor Failed"; + return false; + } + + RefPtr swapChain3; + mVideoSwapChain->QueryInterface( + (IDXGISwapChain3**)getter_AddRefs(swapChain3)); + if (!swapChain3) { + gfxCriticalNote << "Failed to get IDXGISwapChain3"; + return false; + } + + RefPtr videoContext1; + videoContext->QueryInterface( + (ID3D11VideoContext1**)getter_AddRefs(videoContext1)); + if (!videoContext1) { + gfxCriticalNote << "Failed to get ID3D11VideoContext1"; + return false; + } + + const auto videoProcessor = mDCLayerTree->GetVideoProcessor(); + const auto videoProcessorEnumerator = + mDCLayerTree->GetVideoProcessorEnumerator(); + + DXGI_COLOR_SPACE_TYPE inputColorSpace = sourceColorSpace.ref(); + videoContext1->VideoProcessorSetStreamColorSpace1(videoProcessor, 0, + inputColorSpace); + + DXGI_COLOR_SPACE_TYPE outputColorSpace = + IsYUVSwapChainFormat(mSwapChainFormat) + ? inputColorSpace + : DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; + hr = swapChain3->SetColorSpace1(outputColorSpace); + if (FAILED(hr)) { + gfxCriticalNote << "SetColorSpace1 failed: " << gfx::hexa(hr); + return false; + } + videoContext1->VideoProcessorSetOutputColorSpace1(videoProcessor, + outputColorSpace); D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inputDesc = {}; inputDesc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D; @@ -1425,51 +1151,6 @@ bool DCSurfaceSwapChain::CallVideoProcessorBlt() const { return false; } - // - - // outputView - - RECT destRect; - destRect.left = 0; - destRect.top = 0; - destRect.right = mDest->dest->size.width; - destRect.bottom = mDest->dest->size.height; - // KG: This causes stretching if this doesn't match `srcSize`? - - videoContext->VideoProcessorSetOutputTargetRect(videoProcessor, TRUE, - &destRect); - videoContext->VideoProcessorSetStreamDestRect(videoProcessor, 0, TRUE, - &destRect); - RECT sourceRect; - sourceRect.left = 0; - sourceRect.top = 0; - sourceRect.right = mSrc->size.width; - sourceRect.bottom = mSrc->size.height; - videoContext->VideoProcessorSetStreamSourceRect(videoProcessor, 0, TRUE, - &sourceRect); - - RefPtr outputView; - { - RefPtr backBuf; - mDest->dest->swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), - (void**)getter_AddRefs(backBuf)); - - D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outputDesc = {}; - outputDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D; - outputDesc.Texture2D.MipSlice = 0; - - hr = videoDevice->CreateVideoProcessorOutputView( - backBuf, videoProcessorEnumerator, &outputDesc, - getter_AddRefs(outputView)); - if (FAILED(hr)) { - gfxCriticalNote << "ID3D11VideoProcessorOutputView creation failed: " - << gfx::hexa(hr); - return false; - } - } - - // - - // VideoProcessorBlt - D3D11_VIDEO_PROCESSOR_STREAM stream = {}; stream.Enable = true; stream.OutputIndex = 0; @@ -1478,10 +1159,44 @@ bool DCSurfaceSwapChain::CallVideoProcessorBlt() const { stream.FutureFrames = 0; stream.pInputSurface = inputView.get(); - // KG: I guess we always copy here? The ideal case would instead be us getting - // a swapchain from dcomp, and pulling buffers out of it for e.g. webgl to - // render into. - hr = videoContext->VideoProcessorBlt(videoProcessor, outputView, 0, 1, + RECT destRect; + destRect.left = 0; + destRect.top = 0; + destRect.right = mSwapChainSize.width; + destRect.bottom = mSwapChainSize.height; + + videoContext->VideoProcessorSetOutputTargetRect(videoProcessor, TRUE, + &destRect); + videoContext->VideoProcessorSetStreamDestRect(videoProcessor, 0, TRUE, + &destRect); + RECT sourceRect; + sourceRect.left = 0; + sourceRect.top = 0; + sourceRect.right = mVideoSize.width; + sourceRect.bottom = mVideoSize.height; + videoContext->VideoProcessorSetStreamSourceRect(videoProcessor, 0, TRUE, + &sourceRect); + + if (!mOutputView) { + RefPtr backBuf; + mVideoSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), + (void**)getter_AddRefs(backBuf)); + + D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outputDesc = {}; + outputDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D; + outputDesc.Texture2D.MipSlice = 0; + + hr = videoDevice->CreateVideoProcessorOutputView( + backBuf, videoProcessorEnumerator, &outputDesc, + getter_AddRefs(mOutputView)); + if (FAILED(hr)) { + gfxCriticalNote << "ID3D11VideoProcessorOutputView creation failed: " + << gfx::hexa(hr); + return false; + } + } + + hr = videoContext->VideoProcessorBlt(videoProcessor, mOutputView, 0, 1, &stream); if (FAILED(hr)) { gfxCriticalNote << "VideoProcessorBlt failed: " << gfx::hexa(hr); @@ -1491,129 +1206,17 @@ bool DCSurfaceSwapChain::CallVideoProcessorBlt() const { return true; } -bool DCSurfaceSwapChain::CallBlitHelper() const { - MOZ_RELEASE_ASSERT(mSrc); - MOZ_RELEASE_ASSERT(mDest); - MOZ_RELEASE_ASSERT(mDest->dest); - const auto& plan = *mDest->plan.blitHelper; - - const auto gl = mDCLayerTree->GetGLContext(); - const auto& blitHelper = gl->BlitHelper(); - - const auto Mat3FromImage = [](const wr::WrExternalImage& image, - const gfx::IntSize& size) { - auto ret = gl::Mat3::I(); - ret.at(0, 0) = (image.u1 - image.u0) / size.width; // e.g. 0.8 - 0.1 - ret.at(0, 2) = image.u0 / size.width; // e.g. 0.1 - ret.at(1, 1) = (image.v1 - image.v0) / size.height; - ret.at(1, 2) = image.v0 / size.height; - return ret; - }; - - // - - // Bind LUT - - constexpr uint8_t LUT_TEX_UNIT = 3; - const auto restore3D = - gl::ScopedSaveMultiTex{gl, {LUT_TEX_UNIT}, LOCAL_GL_TEXTURE_3D}; - const auto restoreExt = - gl::ScopedSaveMultiTex{gl, {0, 1}, LOCAL_GL_TEXTURE_EXTERNAL}; - - if (plan.srcSpace != plan.dstSpace) { - if (!mDest->lut) { - mDest->lut = blitHelper->GetColorLutTex({plan.srcSpace, plan.dstSpace}); - } - MOZ_ASSERT(mDest->lut); - gl->fActiveTexture(LOCAL_GL_TEXTURE0 + LUT_TEX_UNIT); - gl->fBindTexture(LOCAL_GL_TEXTURE_3D, mDest->lut->name); +void DCSurfaceVideo::ReleaseDecodeSwapChainResources() { + mOutputView = nullptr; + mVideoSwapChain = nullptr; + mDecodeSwapChain = nullptr; + mDecodeResource = nullptr; + if (mSwapChainSurfaceHandle) { + ::CloseHandle(mSwapChainSurfaceHandle); + mSwapChainSurfaceHandle = 0; } - - // - - // Lock and bind src (image1 as needed below) - - const auto image0 = mSrc->texture->Lock(0, gl, wr::ImageRendering::Auto); - const auto size0 = mSrc->texture->GetSize(0); - const auto texCoordMat0 = Mat3FromImage(image0, size0); - const auto unlock = MakeScopeExit([&]() { mSrc->texture->Unlock(); }); - - gl->fActiveTexture(LOCAL_GL_TEXTURE0 + 0); - gl->fBindTexture(LOCAL_GL_TEXTURE_EXTERNAL, image0.handle); - - // - - // Bind swapchain RT - - RefPtr backBuf; - mDest->dest->swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), - (void**)getter_AddRefs(backBuf)); - MOZ_RELEASE_ASSERT(backBuf); - - bool clearForTesting = false; - if (clearForTesting) { - const auto device = mDCLayerTree->GetDevice(); - layers::ClearResource(device, backBuf, {1, 0, 1, 1}); - return true; - } - - const auto gle = gl::GLContextEGL::Cast(gl); - - const auto fb = gl::ScopedFramebuffer(gl); - const auto bindFb = gl::ScopedBindFramebuffer(gl, fb); - - const auto rb = gl::ScopedRenderbuffer(gl); - { - const auto bindRb = gl::ScopedBindRenderbuffer(gl, rb); - - const EGLint attribs[] = {LOCAL_EGL_NONE}; - const auto image = gle->mEgl->fCreateImage( - 0, LOCAL_EGL_D3D11_TEXTURE_ANGLE, - reinterpret_cast(backBuf.get()), attribs); - MOZ_RELEASE_ASSERT(image); - gl->fEGLImageTargetRenderbufferStorage(LOCAL_GL_RENDERBUFFER, image); - gle->mEgl->fDestroyImage(image); // Release as soon as attached to RB. - - gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, - LOCAL_GL_COLOR_ATTACHMENT0, - LOCAL_GL_RENDERBUFFER, rb); - MOZ_ASSERT(gl->IsFramebufferComplete(fb)); - } - - // - - // Draw - - auto lutUintIfNeeded = Some(LUT_TEX_UNIT); - auto fragConvert = gl::kFragConvert_ColorLut; - if (!mDest->lut) { - lutUintIfNeeded = Nothing(); - fragConvert = gl::kFragConvert_None; - } - - const auto baseArgs = gl::DrawBlitProg::BaseArgs{ - texCoordMat0, false, mDest->dest->size, {}, lutUintIfNeeded, - }; - Maybe yuvArgs; - auto fragSample = gl::kFragSample_OnePlane; - - if (IsYuv(mSrc->format)) { - const auto image1 = mSrc->texture->Lock(1, gl, wr::ImageRendering::Auto); - const auto size1 = mSrc->texture->GetSize(1); - const auto texCoordMat1 = Mat3FromImage(image1, size1); - - gl->fActiveTexture(LOCAL_GL_TEXTURE0 + 1); - gl->fBindTexture(LOCAL_GL_TEXTURE_EXTERNAL, image1.handle); - - yuvArgs = Some(gl::DrawBlitProg::YUVArgs{texCoordMat1, {}}); - fragSample = gl::kFragSample_TwoPlane; - } - - const auto dbp = blitHelper->GetDrawBlitProg( - {gl::kFragHeader_TexExt, {fragSample, fragConvert}}); - dbp->Draw(baseArgs, yuvArgs.ptrOr(nullptr)); - - return true; } -// - - DCTile::DCTile(DCLayerTree* aDCLayerTree) : mDCLayerTree(aDCLayerTree) {} DCTile::~DCTile() {} diff --git a/gfx/webrender_bindings/DCLayerTree.h b/gfx/webrender_bindings/DCLayerTree.h index 1435925cb074..8f05317060b9 100644 --- a/gfx/webrender_bindings/DCLayerTree.h +++ b/gfx/webrender_bindings/DCLayerTree.h @@ -7,14 +7,11 @@ #ifndef MOZILLA_GFX_DCLAYER_TREE_H #define MOZILLA_GFX_DCLAYER_TREE_H -#include "WinUtils.h" -#include #include #include #include #include -#include "Colorspaces.h" #include "GLTypes.h" #include "mozilla/HashFunctions.h" #include "mozilla/layers/OverlayInfo.h" @@ -37,14 +34,12 @@ struct IDCompositionVisual2; struct IDXGIDecodeSwapChain; struct IDXGIResource; struct IDXGISwapChain1; -struct IDXGISwapChain3; struct IDCompositionVirtualSurface; namespace mozilla { namespace gl { class GLContext; -class Texture; } namespace wr { @@ -55,9 +50,8 @@ namespace wr { class DCTile; class DCSurface; -class DCSurfaceSwapChain; +class DCSurfaceVideo; class RenderTextureHost; -class RenderDXGITextureHost; struct GpuOverlayInfo { bool mSupportsOverlays = false; @@ -125,8 +119,8 @@ class DCLayerTree { ID3D11VideoProcessorEnumerator* GetVideoProcessorEnumerator() const { return mVideoProcessorEnumerator; } - bool EnsureVideoProcessorAtLeast(const gfx::IntSize& aInputSize, - const gfx::IntSize& aOutputSize); + bool EnsureVideoProcessor(const gfx::IntSize& aInputSize, + const gfx::IntSize& aOutputSize); DCSurface* GetSurface(wr::NativeSurfaceId aId) const; @@ -253,7 +247,7 @@ class DCSurface { void UpdateAllocatedRect(); void DirtyAllocatedRect(); - virtual DCSurfaceSwapChain* AsDCSurfaceSwapChain() { return nullptr; } + virtual DCSurfaceVideo* AsDCSurfaceVideo() { return nullptr; } protected: DCLayerTree* mDCLayerTree; @@ -278,87 +272,33 @@ class DCSurface { RefPtr mVirtualSurface; }; -struct RaiiHANDLE final { - const HANDLE val; - - explicit RaiiHANDLE(HANDLE val) : val(val) {} - - operator HANDLE() const { return val; } - - ~RaiiHANDLE() { - if (val) { - ::CloseHandle(val); - } - } -}; - -struct CspaceAndRange final { - gfx::ColorSpace2 space; - Maybe yuvRange; - - auto Members() const { return std::tie(space, yuvRange); } - INLINE_DERIVE_MEMBERS_EQ(CspaceAndRange); -}; - -struct CspaceTransformPlan final { - struct WithVideoProcessor final { - DXGI_COLOR_SPACE_TYPE srcSpace; - Maybe dstYuvSpace; - DXGI_COLOR_SPACE_TYPE dstRgbSpace; - }; - struct WithGLBlitHelper final { - color::ColorspaceDesc srcSpace; - color::ColorspaceDesc dstSpace; - DXGI_COLOR_SPACE_TYPE dstDxgiSpace; - DXGI_FORMAT dstDxgiFormat; - }; - Maybe videoProcessor; - Maybe blitHelper; -}; - -class DCSurfaceSwapChain : public DCSurface { +class DCSurfaceVideo : public DCSurface { public: - DCSurfaceSwapChain(bool aIsOpaque, DCLayerTree* aDCLayerTree); + DCSurfaceVideo(bool aIsOpaque, DCLayerTree* aDCLayerTree); void AttachExternalImage(wr::ExternalImageId aExternalImage); - Maybe EnsurePresented(const gfx::Matrix&); + bool CalculateSwapChainSize(gfx::Matrix& aTransform); + void PresentVideo(); - DCSurfaceSwapChain* AsDCSurfaceSwapChain() override { return this; } - - struct Src final { - RefPtr texture; - gfx::IntSize size; - gfx::SurfaceFormat format; - CspaceAndRange space; - - // When RenderTextureHost, swapChainSize or VideoSwapChain are updated, - // then DCSurfaceSwapChain::Present() needs to be called. - bool needsPresent = true; - }; - struct Dest final { - RefPtr swapChain; // Destination - UniquePtr swapChainSurfaceHandle; - gfx::IntSize size; - DXGI_FORMAT format; - DXGI_COLOR_SPACE_TYPE space; - }; - struct PlanAndDest final { - CspaceAndRange srcSpace; - CspaceTransformPlan plan; - Maybe dest; - mutable std::shared_ptr lut; - bool needsPresent = true; - }; + DCSurfaceVideo* AsDCSurfaceVideo() override { return this; } protected: - bool CallVideoProcessorBlt() const; - bool CallBlitHelper() const; + DXGI_FORMAT GetSwapChainFormat(); + bool CreateVideoSwapChain(); + bool CallVideoProcessorBlt(); + void ReleaseDecodeSwapChainResources(); - // - - - Maybe mOverlayFormat; - Maybe mSrc; - Maybe mDest; + RefPtr mOutputView; + RefPtr mDecodeResource; + RefPtr mVideoSwapChain; + RefPtr mDecodeSwapChain; + HANDLE mSwapChainSurfaceHandle = 0; + gfx::IntSize mVideoSize; + gfx::IntSize mSwapChainSize; + DXGI_FORMAT mSwapChainFormat = DXGI_FORMAT_B8G8R8A8_UNORM; + bool mFailedYuvSwapChain = false; + RefPtr mRenderTextureHost; + RefPtr mPrevTexture; }; class DCTile { diff --git a/gfx/webrender_bindings/RenderD3D11TextureHost.cpp b/gfx/webrender_bindings/RenderD3D11TextureHost.cpp index 684140733316..986bf27c023c 100644 --- a/gfx/webrender_bindings/RenderD3D11TextureHost.cpp +++ b/gfx/webrender_bindings/RenderD3D11TextureHost.cpp @@ -20,7 +20,7 @@ namespace wr { RenderDXGITextureHost::RenderDXGITextureHost( WindowsHandle aHandle, Maybe& aGpuProcessTextureId, uint32_t aArrayIndex, gfx::SurfaceFormat aFormat, - gfx::ColorSpace2 aColorSpace, gfx::ColorRange aColorRange, + gfx::YUVColorSpace aYUVColorSpace, gfx::ColorRange aColorRange, gfx::IntSize aSize) : mHandle(aHandle), mGpuProcessTextureId(aGpuProcessTextureId), @@ -29,7 +29,7 @@ RenderDXGITextureHost::RenderDXGITextureHost( mStream(0), mTextureHandle{0}, mFormat(aFormat), - mColorSpace(aColorSpace), + mYUVColorSpace(aYUVColorSpace), mColorRange(aColorRange), mSize(aSize), mLocked(false) { diff --git a/gfx/webrender_bindings/RenderD3D11TextureHost.h b/gfx/webrender_bindings/RenderD3D11TextureHost.h index 220899ec0585..ab9536ca6f0e 100644 --- a/gfx/webrender_bindings/RenderD3D11TextureHost.h +++ b/gfx/webrender_bindings/RenderD3D11TextureHost.h @@ -22,8 +22,8 @@ class RenderDXGITextureHost final : public RenderTextureHostSWGL { RenderDXGITextureHost(WindowsHandle aHandle, Maybe& aGpuProcessTextureId, uint32_t aArrayIndex, gfx::SurfaceFormat aFormat, - gfx::ColorSpace2, gfx::ColorRange aColorRange, - gfx::IntSize aSize); + gfx::YUVColorSpace aYUVColorSpace, + gfx::ColorRange aColorRange, gfx::IntSize aSize); wr::WrExternalImage Lock(uint8_t aChannelIndex, gl::GLContext* aGL, wr::ImageRendering aRendering) override; @@ -58,7 +58,7 @@ class RenderDXGITextureHost final : public RenderTextureHostSWGL { PlaneInfo& aPlaneInfo) override; void UnmapPlanes() override; gfx::YUVRangedColorSpace GetYUVColorSpace() const override { - return ToYUVRangedColorSpace(ToYUVColorSpace(mColorSpace), mColorRange); + return ToYUVRangedColorSpace(mYUVColorSpace, GetColorRange()); } bool EnsureD3D11Texture2D(ID3D11Device* aDevice); @@ -108,13 +108,11 @@ class RenderDXGITextureHost final : public RenderTextureHostSWGL { // handles for Y and CbCr data. GLuint mTextureHandle[2]; - public: const gfx::SurfaceFormat mFormat; - const gfx::ColorSpace2 mColorSpace; + const gfx::YUVColorSpace mYUVColorSpace; const gfx::ColorRange mColorRange; const gfx::IntSize mSize; - private: bool mLocked; }; diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index a58cb80a5710..a7dba1c8ce27 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -5232,37 +5232,6 @@ value: false mirror: once -- name: gfx.blithelper.precision - type: RelaxedAtomicUint32 - value: 2 # { 0: lowp, 1: mediump, 2: highp } - mirror: always - -- name: gfx.blithelper.lut-size.rgb.b - type: RelaxedAtomicUint32 - value: 15 - mirror: always -- name: gfx.blithelper.lut-size.rgb.g - type: RelaxedAtomicUint32 - value: 31 - mirror: always -- name: gfx.blithelper.lut-size.rgb.r - type: RelaxedAtomicUint32 - value: 31 - mirror: always - -- name: gfx.blithelper.lut-size.ycbcr.cb - type: RelaxedAtomicUint32 - value: 15 - mirror: always -- name: gfx.blithelper.lut-size.ycbcr.cr - type: RelaxedAtomicUint32 - value: 31 - mirror: always -- name: gfx.blithelper.lut-size.ycbcr.y - type: RelaxedAtomicUint32 - value: 31 - mirror: always - # Nb: we ignore this pref on release and beta. - name: gfx.blocklist.all type: int32_t