diff options
Diffstat (limited to 'gfx/layers/composite')
-rw-r--r-- | gfx/layers/composite/CompositableHost.cpp | 69 | ||||
-rw-r--r-- | gfx/layers/composite/CompositableHost.h | 131 | ||||
-rw-r--r-- | gfx/layers/composite/Diagnostics.h | 29 | ||||
-rw-r--r-- | gfx/layers/composite/FontData.h | 304 | ||||
-rw-r--r-- | gfx/layers/composite/FrameUniformityData.cpp | 38 | ||||
-rw-r--r-- | gfx/layers/composite/FrameUniformityData.h | 49 | ||||
-rw-r--r-- | gfx/layers/composite/GPUVideoTextureHost.cpp | 224 | ||||
-rw-r--r-- | gfx/layers/composite/GPUVideoTextureHost.h | 84 | ||||
-rw-r--r-- | gfx/layers/composite/ImageComposite.cpp | 378 | ||||
-rw-r--r-- | gfx/layers/composite/ImageComposite.h | 139 | ||||
-rw-r--r-- | gfx/layers/composite/RemoteTextureHostWrapper.cpp | 230 | ||||
-rw-r--r-- | gfx/layers/composite/RemoteTextureHostWrapper.h | 120 | ||||
-rw-r--r-- | gfx/layers/composite/TextureHost.cpp | 840 | ||||
-rw-r--r-- | gfx/layers/composite/TextureHost.h | 1009 |
14 files changed, 3644 insertions, 0 deletions
diff --git a/gfx/layers/composite/CompositableHost.cpp b/gfx/layers/composite/CompositableHost.cpp new file mode 100644 index 0000000000..a64f55d716 --- /dev/null +++ b/gfx/layers/composite/CompositableHost.cpp @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "CompositableHost.h" +#include <map> // for _Rb_tree_iterator, map, etc +#include <utility> // for pair +#include "Effects.h" // for EffectMask, Effect, etc +#include "gfxUtils.h" +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor +#include "mozilla/layers/TextureHost.h" // for TextureHost, etc +#include "mozilla/layers/WebRenderImageHost.h" +#include "mozilla/RefPtr.h" // for nsRefPtr +#include "nsDebug.h" // for NS_WARNING +#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc +#include "gfxPlatform.h" // for gfxPlatform +#include "IPDLActor.h" + +namespace mozilla { + +using namespace gfx; + +namespace layers { + +class Compositor; + +CompositableHost::CompositableHost(const TextureInfo& aTextureInfo) + : mTextureInfo(aTextureInfo), mCompositorBridgeID(0) { + MOZ_COUNT_CTOR(CompositableHost); +} + +CompositableHost::~CompositableHost() { MOZ_COUNT_DTOR(CompositableHost); } + +void CompositableHost::UseTextureHost(const nsTArray<TimedTexture>& aTextures) { +} + +void CompositableHost::RemoveTextureHost(TextureHost* aTexture) {} + +/* static */ +already_AddRefed<CompositableHost> CompositableHost::Create( + const TextureInfo& aTextureInfo) { + RefPtr<CompositableHost> result; + switch (aTextureInfo.mCompositableType) { + case CompositableType::IMAGE: + result = new WebRenderImageHost(aTextureInfo); + break; + default: + NS_ERROR("Unknown CompositableType"); + } + return result.forget(); +} + +void CompositableHost::DumpTextureHost(std::stringstream& aStream, + TextureHost* aTexture) { + if (!aTexture) { + return; + } + RefPtr<gfx::DataSourceSurface> dSurf = aTexture->GetAsSurface(); + if (!dSurf) { + return; + } + aStream << gfxUtils::GetAsDataURI(dSurf).get(); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/composite/CompositableHost.h b/gfx/layers/composite/CompositableHost.h new file mode 100644 index 0000000000..66d3a33ad7 --- /dev/null +++ b/gfx/layers/composite/CompositableHost.h @@ -0,0 +1,131 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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_BUFFERHOST_H +#define MOZILLA_GFX_BUFFERHOST_H + +#include <stdint.h> // for uint64_t +#include <stdio.h> // for FILE +#include "gfxRect.h" // for gfxRect +#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc +#include "mozilla/Attributes.h" // for override +#include "mozilla/RefPtr.h" // for RefPtr, RefCounted, etc +// #include "mozilla/gfx/MatrixFwd.h" // for Matrix4x4 +#include "mozilla/gfx/Polygon.h" // for Polygon +#include "mozilla/gfx/Rect.h" // for Rect +#include "mozilla/ipc/ProtocolUtils.h" +#include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc +// #include "mozilla/layers/LayersTypes.h" // for LayerRenderState, etc +#include "mozilla/layers/LayersMessages.h" +#include "mozilla/layers/TextureHost.h" // for TextureHost +#include "nsCOMPtr.h" // for already_AddRefed +#include "nscore.h" // for nsACString +#include "Units.h" // for CSSToScreenScale + +namespace mozilla { + +namespace layers { + +class WebRenderImageHost; + +struct ImageCompositeNotificationInfo { + base::ProcessId mImageBridgeProcessId; + ImageCompositeNotification mNotification; +}; + +struct AsyncCompositableRef { + AsyncCompositableRef() : mProcessId(base::kInvalidProcessId) {} + AsyncCompositableRef(base::ProcessId aProcessId, + const CompositableHandle& aHandle) + : mProcessId(aProcessId), mHandle(aHandle) {} + explicit operator bool() const { return !!mHandle; } + base::ProcessId mProcessId; + CompositableHandle mHandle; +}; + +/** + * The compositor-side counterpart to CompositableClient. Responsible for + * updating textures and data about textures from IPC and how textures are + * composited (tiling, double buffering, etc.). + * + * Update (for images/canvases) and UpdateThebes (for Thebes) are called during + * the layers transaction to update the Compositbale's textures from the + * content side. The actual update (and any syncronous upload) is done by the + * TextureHost, but it is coordinated by the CompositableHost. + * + * Composite is called by the owning layer when it is composited. + * CompositableHost will use its TextureHost(s) and call Compositor::DrawQuad to + * do the actual rendering. + */ +class CompositableHost { + protected: + virtual ~CompositableHost(); + + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositableHost) + explicit CompositableHost(const TextureInfo& aTextureInfo); + + static already_AddRefed<CompositableHost> Create( + const TextureInfo& aTextureInfo); + + virtual WebRenderImageHost* AsWebRenderImageHost() { return nullptr; } + + virtual void Dump(std::stringstream& aStream, const char* aPrefix = "", + bool aDumpHtml = false) {} + static void DumpTextureHost(std::stringstream& aStream, + TextureHost* aTexture); + + struct TimedTexture { + CompositableTextureHostRef mTexture; + TimeStamp mTimeStamp; + gfx::IntRect mPictureRect; + int32_t mFrameID; + int32_t mProducerID; + }; + virtual void UseTextureHost(const nsTArray<TimedTexture>& aTextures); + virtual void UseRemoteTexture(const RemoteTextureId aTextureId, + const RemoteTextureOwnerId aOwnerId, + const base::ProcessId aForPid, + const gfx::IntSize aSize, + const TextureFlags aFlags) = 0; + virtual void RemoveTextureHost(TextureHost* aTexture); + + // Enable remote texture push callback + virtual void EnableRemoteTexturePushCallback( + const RemoteTextureOwnerId aOwnerId, const base::ProcessId aForPid, + const gfx::IntSize aSize, const TextureFlags aFlags) = 0; + // Called from RemoteTextureMap when a new remote texture is pushed + virtual void NotifyPushTexture(const RemoteTextureId aTextureId, + const RemoteTextureOwnerId aOwnerId, + const base::ProcessId aForPid) = 0; + + uint64_t GetCompositorBridgeID() const { return mCompositorBridgeID; } + + const AsyncCompositableRef& GetAsyncRef() const { return mAsyncRef; } + void SetAsyncRef(const AsyncCompositableRef& aRef) { mAsyncRef = aRef; } + + void SetCompositorBridgeID(uint64_t aID) { mCompositorBridgeID = aID; } + + /// Called when shutting down the layer tree. + /// This is a good place to clear all potential gpu resources before the + /// widget is is destroyed. + virtual void CleanupResources() {} + + virtual void OnReleased() {} + + virtual uint32_t GetDroppedFrames() { return 0; } + + protected: + protected: + TextureInfo mTextureInfo; + AsyncCompositableRef mAsyncRef; + uint64_t mCompositorBridgeID; +}; + +} // namespace layers +} // namespace mozilla + +#endif diff --git a/gfx/layers/composite/Diagnostics.h b/gfx/layers/composite/Diagnostics.h new file mode 100644 index 0000000000..b348128a20 --- /dev/null +++ b/gfx/layers/composite/Diagnostics.h @@ -0,0 +1,29 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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_layers_composite_Diagnostics_h +#define mozilla_gfx_layers_composite_Diagnostics_h + +#include "mozilla/Maybe.h" +#include <cstdint> + +namespace mozilla { +namespace layers { + +// These statistics are collected by layers backends, preferably by the GPU +struct GPUStats { + GPUStats() : mInvalidPixels(0), mScreenPixels(0), mPixelsFilled(0) {} + + uint32_t mInvalidPixels; + uint32_t mScreenPixels; + uint32_t mPixelsFilled; + Maybe<float> mDrawTime; +}; + +} // namespace layers +} // namespace mozilla + +#endif // mozilla_gfx_layers_composite_Diagnostics_h diff --git a/gfx/layers/composite/FontData.h b/gfx/layers/composite/FontData.h new file mode 100644 index 0000000000..aa315f2519 --- /dev/null +++ b/gfx/layers/composite/FontData.h @@ -0,0 +1,304 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +// This is explicitly not guarded as we want only 1 file to include this and +// it's good if things break if someone else does. + +namespace mozilla { +namespace layers { +namespace normal_font { +const unsigned char sFontPNG[] = { + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, + 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, + 0x8, 0x0, 0x0, 0x0, 0x0, 0x79, 0x19, 0xf7, 0xba, 0x0, 0x0, 0xb, + 0xfe, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xed, 0x5d, 0xd1, 0xb6, 0xe3, + 0x20, 0x8, 0x74, 0x72, 0xf2, 0xff, 0xbf, 0x3c, 0xfb, 0xd0, 0xbb, 0xbd, + 0x51, 0x8, 0x48, 0xd4, 0xc4, 0xde, 0xda, 0xb3, 0x67, 0x77, 0x6d, 0x13, + 0x63, 0x10, 0x81, 0x41, 0x40, 0x30, 0x7d, 0xf7, 0x67, 0x4b, 0x8b, 0x0, + 0xdf, 0xfd, 0xd9, 0x2f, 0xdc, 0x3, 0xc6, 0xda, 0x76, 0x67, 0x29, 0xa5, + 0x94, 0xb8, 0x38, 0x20, 0x42, 0x33, 0xc0, 0x68, 0xaa, 0x37, 0x64, 0x14, + 0x47, 0xc2, 0x67, 0x13, 0x80, 0x5, 0xbf, 0x91, 0xb0, 0x5f, 0x9f, 0x73, + 0x71, 0x40, 0x39, 0x61, 0x1c, 0xcd, 0xff, 0x39, 0x7d, 0x8, 0x1e, 0x29, + 0x72, 0x3f, 0x1, 0x90, 0x12, 0x8a, 0xf9, 0xc, 0x52, 0x20, 0x9f, 0x51, + 0x2, 0x8e, 0xd0, 0x99, 0x8d, 0x3, 0x7a, 0x2f, 0x2, 0x7a, 0x4, 0xa4, + 0x90, 0xba, 0x8, 0x68, 0x1, 0x28, 0x32, 0x1a, 0x87, 0x6f, 0x50, 0x71, + 0x7f, 0x5f, 0x29, 0xf, 0x85, 0xa3, 0x18, 0x5b, 0x10, 0xc, 0x10, 0x80, + 0xf2, 0x1d, 0x79, 0x4e, 0x5e, 0x8f, 0xfc, 0xaf, 0x1, 0xb0, 0x60, 0x50, + 0x4, 0x17, 0x40, 0xc6, 0xd3, 0xf4, 0xd5, 0x2e, 0x3a, 0xdb, 0x1, 0x5d, + 0x85, 0x78, 0x8a, 0xbd, 0x7e, 0x33, 0xc3, 0x24, 0xe4, 0x52, 0x70, 0xff, + 0xbc, 0xf5, 0x8f, 0xf0, 0x82, 0xe2, 0x5c, 0x1c, 0xd0, 0x9b, 0x83, 0x1c, + 0x7a, 0x50, 0xb4, 0x33, 0xa9, 0xf9, 0xf5, 0x58, 0x0, 0xb, 0xe, 0x2f, + 0x2, 0x2c, 0x2, 0x7c, 0x3d, 0x1, 0xea, 0x8c, 0xb9, 0xc, 0x4f, 0x16, + 0x70, 0x14, 0x21, 0xc5, 0x8e, 0x60, 0xfb, 0x1, 0xe, 0x80, 0xf5, 0xc2, + 0x52, 0x8c, 0xb6, 0x9, 0x52, 0xdc, 0xfb, 0xc6, 0x35, 0x4, 0x40, 0xa, + 0x81, 0x8d, 0x66, 0xbd, 0xce, 0xc9, 0x8, 0x80, 0x6f, 0x93, 0x1, 0x6d, + 0xef, 0xcf, 0xf1, 0xf4, 0xc2, 0xc0, 0xf9, 0xd9, 0xdb, 0xe7, 0x7f, 0x30, + 0x5, 0x50, 0x98, 0x6b, 0x48, 0xe8, 0x9, 0xaf, 0x77, 0x14, 0xf8, 0xbe, + 0xf5, 0x7d, 0xa0, 0xfa, 0xb, 0x18, 0xa2, 0x6f, 0x3e, 0x9e, 0xc2, 0x5c, + 0x65, 0x5f, 0xe3, 0x75, 0xa7, 0x30, 0x87, 0x3d, 0xa, 0xd8, 0x42, 0x8b, + 0x8a, 0x3f, 0x81, 0xb6, 0x7f, 0xa1, 0xa6, 0x8f, 0x80, 0x7, 0xa2, 0x5d, + 0x8, 0xd2, 0xd1, 0xfb, 0x7f, 0x4a, 0x4a, 0x6e, 0xfe, 0xc, 0x3c, 0xab, + 0xa6, 0x9e, 0x31, 0x84, 0x4c, 0x9e, 0xa3, 0xfb, 0x45, 0x47, 0x7f, 0xc5, + 0xc2, 0x2, 0xcb, 0x1f, 0xb0, 0x38, 0x60, 0x11, 0x60, 0x7a, 0x2, 0xe0, + 0x6f, 0x11, 0x0, 0x1e, 0x5a, 0x85, 0x3, 0x97, 0x5f, 0xde, 0x1, 0x94, + 0x5f, 0x9c, 0x93, 0x2b, 0xf2, 0x73, 0x77, 0x68, 0xd1, 0xce, 0x1, 0x60, + 0x6e, 0x3b, 0x82, 0x44, 0x26, 0x5c, 0xe1, 0x6d, 0x5f, 0xd3, 0xb2, 0x43, + 0x91, 0xb4, 0xcd, 0xb0, 0x27, 0x97, 0x0, 0x12, 0x4, 0x53, 0xc, 0x54, + 0x25, 0x44, 0x6f, 0x4d, 0x95, 0x77, 0xe7, 0x13, 0x80, 0xd2, 0x30, 0xf4, + 0x2d, 0x1f, 0xf0, 0xb4, 0x45, 0x47, 0x11, 0x13, 0xb7, 0xda, 0x49, 0x7b, + 0xf7, 0x29, 0x22, 0x12, 0xc1, 0x23, 0xa2, 0x3, 0x1b, 0xec, 0xd, 0x30, + 0xc7, 0x7f, 0x3f, 0x4b, 0x82, 0x9d, 0x18, 0xe0, 0x2, 0x1, 0xca, 0xdd, + 0xd9, 0x32, 0xe4, 0xe2, 0x35, 0xb6, 0xdf, 0xab, 0xd0, 0x86, 0xaf, 0x59, + 0xf0, 0x80, 0x4b, 0xcc, 0xe0, 0xd6, 0xe1, 0x7e, 0x41, 0x4, 0xb8, 0xf, + 0x11, 0xf8, 0x9a, 0x37, 0x1a, 0x9c, 0xc1, 0x27, 0x6d, 0xd1, 0xe, 0x5e, + 0x22, 0x80, 0x21, 0x99, 0xcc, 0x89, 0xc, 0xee, 0x72, 0x28, 0xbe, 0x1d, + 0x10, 0x17, 0xd3, 0x60, 0x4a, 0x7c, 0x7, 0xee, 0x10, 0x0, 0x81, 0x71, + 0x73, 0x1c, 0xdd, 0x86, 0xe8, 0xf, 0x86, 0x1a, 0xfb, 0x50, 0xc8, 0x77, + 0xe3, 0xe3, 0xb7, 0xfb, 0x57, 0xdd, 0x5c, 0x78, 0x76, 0x86, 0xd5, 0x99, + 0x8d, 0xe1, 0xee, 0x1, 0x2d, 0x7f, 0xc0, 0x82, 0xc3, 0x8b, 0x0, 0xdf, + 0xfd, 0xd9, 0x15, 0xcb, 0x1a, 0x22, 0xb6, 0x94, 0x56, 0xbb, 0x94, 0x62, + 0xa5, 0x62, 0x80, 0x68, 0x9a, 0xbf, 0xf7, 0xdd, 0xf9, 0xd2, 0xed, 0x82, + 0x2c, 0x52, 0x54, 0x40, 0x15, 0xc8, 0xb7, 0xcb, 0xd0, 0x88, 0xb0, 0xc6, + 0x6d, 0x6c, 0x5c, 0x5e, 0xcf, 0xe2, 0xe, 0xbf, 0xff, 0x36, 0x59, 0xe, + 0x7b, 0x3c, 0x9b, 0x7b, 0x3, 0x3b, 0x68, 0xfd, 0x96, 0x94, 0x92, 0x8a, + 0x0, 0x12, 0x33, 0x5c, 0xbc, 0xb4, 0x42, 0xe9, 0x81, 0xa1, 0xf0, 0xb, + 0x96, 0x70, 0x4f, 0x2e, 0xa9, 0xd4, 0x64, 0xeb, 0x79, 0x33, 0x80, 0xa4, + 0xf0, 0x63, 0xc6, 0x72, 0x16, 0x76, 0xdb, 0x2f, 0x30, 0x98, 0x83, 0x5, + 0x29, 0x4d, 0x1b, 0x7b, 0x86, 0xc2, 0xd6, 0x7d, 0x88, 0x63, 0x93, 0x8c, + 0xa7, 0x6, 0x63, 0x4, 0x90, 0x8f, 0x28, 0xd7, 0xac, 0x39, 0x0, 0xfa, + 0xbd, 0x83, 0x85, 0x43, 0x25, 0x6, 0xc6, 0xfc, 0x68, 0xf1, 0x73, 0x7, + 0xd5, 0xde, 0x24, 0x60, 0xfe, 0xcf, 0x57, 0xc8, 0x9, 0x1, 0xc6, 0x58, + 0xde, 0x5f, 0x22, 0xd9, 0xe3, 0xc5, 0x12, 0x40, 0x63, 0xb0, 0xb4, 0x97, + 0x14, 0xc7, 0xce, 0xc6, 0xfc, 0x85, 0xfb, 0x33, 0x96, 0xa4, 0xdb, 0x9f, + 0x92, 0xae, 0x60, 0xe9, 0x69, 0x57, 0x6f, 0x2b, 0x5a, 0xd1, 0x6e, 0x33, + 0xa5, 0xa6, 0xeb, 0x5b, 0xad, 0x84, 0x47, 0xc1, 0xd7, 0x74, 0xf8, 0x6d, + 0xfb, 0xf2, 0xf7, 0x5f, 0x70, 0x78, 0xa1, 0xc1, 0x45, 0x80, 0x45, 0x80, + 0xaf, 0xf7, 0x7, 0x28, 0xd1, 0x9c, 0xb4, 0xc0, 0x45, 0x91, 0x4c, 0xaa, + 0xda, 0xd, 0x87, 0x6f, 0xdc, 0xfb, 0x99, 0x3f, 0x12, 0x34, 0xf1, 0xbb, + 0x7b, 0xfd, 0xff, 0x76, 0xfe, 0x6f, 0xb9, 0xbd, 0xc8, 0x23, 0x1, 0xde, + 0x9b, 0x78, 0xbf, 0xf0, 0x4e, 0xdb, 0xeb, 0xcb, 0x9e, 0x50, 0x98, 0x5e, + 0x25, 0xe0, 0x37, 0xd1, 0x1b, 0x63, 0xda, 0x47, 0xcb, 0x2c, 0xb5, 0x6e, + 0xff, 0x79, 0x63, 0xdb, 0x90, 0x7a, 0x7f, 0xb9, 0xfd, 0xfe, 0xbf, 0x12, + 0x82, 0xbc, 0x2e, 0xf7, 0xf6, 0xb4, 0x83, 0xf7, 0x8f, 0x54, 0xde, 0x25, + 0x5a, 0x67, 0xf6, 0xe5, 0xf6, 0x42, 0x63, 0xc7, 0x98, 0x87, 0x32, 0xe4, + 0xe3, 0xd3, 0x6c, 0x21, 0x22, 0x42, 0xa2, 0xfd, 0x8d, 0x48, 0x6d, 0x8f, + 0x4c, 0x4, 0xed, 0x29, 0x42, 0x41, 0x1, 0xfc, 0xbc, 0x84, 0xfe, 0xaf, + 0xdc, 0x60, 0xee, 0xcd, 0xee, 0xa, 0x53, 0x6a, 0x3e, 0xbe, 0xd0, 0x9a, + 0xad, 0xf0, 0x50, 0x1c, 0x1e, 0xc2, 0xe0, 0x4b, 0x81, 0x2e, 0xb, 0x50, + 0x44, 0x30, 0xb8, 0x70, 0x18, 0x85, 0x2c, 0x9c, 0xd8, 0x44, 0x66, 0xd7, + 0xfd, 0xf6, 0x2d, 0xfd, 0x6c, 0x60, 0x57, 0xf7, 0x49, 0x28, 0x33, 0x16, + 0x61, 0x10, 0x84, 0x6e, 0x40, 0x8d, 0x60, 0xf3, 0x78, 0xfe, 0x78, 0x79, + 0xf1, 0xfc, 0xfd, 0xcd, 0x84, 0xb9, 0xfa, 0x3c, 0xf4, 0x51, 0xae, 0x69, + 0xc2, 0x5e, 0xe3, 0x5e, 0xcd, 0x8, 0x22, 0x22, 0x53, 0xa8, 0x6f, 0x13, + 0x14, 0xe3, 0x63, 0x15, 0xd5, 0xb5, 0xe7, 0x1f, 0x63, 0x79, 0xbe, 0xd2, + 0x12, 0xfc, 0x55, 0x0, 0x5f, 0x8a, 0x8b, 0x97, 0x3f, 0x60, 0xa1, 0xc1, + 0x45, 0x80, 0x45, 0x80, 0x2f, 0xf7, 0x7, 0x54, 0xe1, 0xfd, 0xf3, 0x8d, + 0x82, 0x12, 0xfe, 0x97, 0x96, 0xa4, 0x5, 0xcf, 0x7f, 0x77, 0xc5, 0x60, + 0xf8, 0x7, 0xe4, 0xe3, 0x2d, 0xbf, 0x3e, 0x82, 0x7b, 0xd1, 0x7b, 0x1d, + 0xde, 0x17, 0xe, 0x88, 0x77, 0xdb, 0xdb, 0xcd, 0x67, 0x79, 0x7d, 0xd1, + 0x13, 0xe5, 0xde, 0x20, 0xa5, 0xbb, 0xc1, 0xc1, 0x16, 0x96, 0xf7, 0xc0, + 0x89, 0xd3, 0x3d, 0x8b, 0xf, 0xf0, 0xf3, 0x67, 0xdb, 0x94, 0x2f, 0x8e, + 0x86, 0x99, 0x48, 0x99, 0x8, 0xc4, 0x4e, 0xb6, 0x22, 0xf7, 0xf1, 0x32, + 0x20, 0x6, 0xcf, 0x47, 0x18, 0x3a, 0x74, 0x6a, 0x89, 0x79, 0x78, 0xbf, + 0xf8, 0x9d, 0x68, 0xe3, 0x80, 0x17, 0x58, 0x3d, 0x40, 0x56, 0x5, 0xba, + 0x34, 0x6, 0xcf, 0xc6, 0x84, 0x60, 0x45, 0x3d, 0x38, 0xb0, 0xc5, 0x3f, + 0x50, 0xb1, 0x1e, 0x22, 0xc1, 0xf7, 0x21, 0x77, 0x87, 0xcc, 0x37, 0xb8, + 0xb4, 0x4, 0x1a, 0x53, 0x1e, 0x30, 0xb3, 0xc1, 0xbd, 0x55, 0xe1, 0xf5, + 0x23, 0x5, 0xd0, 0xba, 0xe6, 0xf9, 0x2b, 0xfd, 0x6b, 0x16, 0x29, 0x38, + 0x74, 0x7a, 0xf6, 0x3a, 0xbc, 0x7e, 0xe0, 0x33, 0x57, 0x6, 0x8, 0x4, + 0x4f, 0xd7, 0x89, 0x6d, 0x5f, 0x31, 0x14, 0xaa, 0xea, 0x4f, 0x9e, 0x19, + 0x23, 0x82, 0xad, 0x17, 0x54, 0xc8, 0x80, 0xbe, 0x19, 0x1e, 0x93, 0x9b, + 0xc2, 0x3, 0xec, 0x9c, 0xc1, 0x2c, 0x60, 0x8e, 0xae, 0x32, 0x81, 0x86, + 0xcb, 0x21, 0xb2, 0xd0, 0xe0, 0x22, 0xc0, 0xf, 0x1, 0xf0, 0xed, 0x4, + 0xf0, 0xec, 0x88, 0xba, 0xd2, 0x4f, 0xd7, 0x3b, 0x40, 0xe7, 0x76, 0x7c, + 0x9, 0xc, 0x16, 0x83, 0x51, 0x31, 0xdb, 0x23, 0x3c, 0xbf, 0x4d, 0x6, + 0x88, 0x3, 0xb, 0x0, 0xf3, 0x77, 0x79, 0x79, 0x79, 0x9e, 0x81, 0xe8, + 0xce, 0xea, 0xa0, 0x66, 0x86, 0x4d, 0xd3, 0x39, 0x1, 0xc7, 0x3e, 0x61, + 0x8f, 0x7f, 0x57, 0x67, 0xcc, 0xa, 0x77, 0x57, 0x32, 0x3c, 0x60, 0xdc, + 0x4f, 0x25, 0x40, 0x43, 0x44, 0x2c, 0x4, 0xb9, 0xc4, 0x45, 0x8f, 0x38, + 0x42, 0x58, 0x7b, 0xfc, 0x15, 0xc9, 0xd3, 0x74, 0xd9, 0xb4, 0xad, 0xd4, + 0x35, 0x3b, 0x2f, 0x2a, 0x25, 0xe1, 0x3e, 0x66, 0x9, 0xb6, 0xaa, 0x85, + 0xd1, 0x6a, 0x65, 0x78, 0x41, 0x45, 0x36, 0x3e, 0x63, 0xf8, 0x89, 0x19, + 0x5d, 0xcd, 0xd7, 0x8a, 0xa2, 0xaa, 0x70, 0x27, 0x1, 0x8d, 0x9a, 0xef, + 0x82, 0x8b, 0x65, 0x20, 0x7, 0x10, 0x5, 0x8d, 0x8b, 0x9c, 0x94, 0x12, + 0xee, 0x97, 0x29, 0x2e, 0x74, 0xf2, 0xfe, 0xa2, 0x29, 0x31, 0xe7, 0x3e, + 0x95, 0xeb, 0xd4, 0xe3, 0xd3, 0xc8, 0x1f, 0x1c, 0x73, 0xed, 0x47, 0x60, + 0x81, 0xc9, 0x4c, 0xef, 0x7, 0x38, 0x20, 0x96, 0x1a, 0xb, 0xfe, 0x39, + 0x2, 0x2c, 0x38, 0xbc, 0x8, 0xb0, 0x8, 0xf0, 0xa7, 0x8, 0x70, 0xbb, + 0x54, 0xb7, 0x4f, 0x33, 0x88, 0xfa, 0xb3, 0x3f, 0xef, 0x98, 0x1d, 0x17, + 0xa, 0x79, 0xa5, 0xc6, 0x52, 0x51, 0x4e, 0x4f, 0xc1, 0xcb, 0x79, 0x81, + 0x3, 0x17, 0xef, 0xcb, 0x19, 0xc1, 0x69, 0xf3, 0xdd, 0x86, 0x71, 0x7d, + 0x51, 0x8f, 0x12, 0x23, 0xeb, 0x50, 0xed, 0x1a, 0x5e, 0x36, 0x2b, 0xe, + 0x78, 0x78, 0xff, 0x82, 0x59, 0xb, 0xaf, 0x58, 0x39, 0xda, 0xd6, 0xa8, + 0xc5, 0x15, 0xaa, 0x4b, 0x8c, 0x41, 0x2a, 0x6, 0x4d, 0x71, 0x58, 0xfb, + 0xc5, 0xda, 0xde, 0x6a, 0x13, 0x8d, 0x41, 0x5a, 0x15, 0x26, 0xb6, 0xb, + 0x6f, 0x0, 0x44, 0x5f, 0xb8, 0xd, 0xd9, 0x4, 0x93, 0x9d, 0x29, 0x18, + 0xd8, 0x3a, 0xd3, 0xf2, 0x82, 0x16, 0xf0, 0xce, 0xf8, 0x8c, 0x4f, 0x11, + 0x4c, 0x8a, 0xd9, 0x45, 0x57, 0x5b, 0x8d, 0xdb, 0x2d, 0x5c, 0xc8, 0x16, + 0x35, 0xab, 0xce, 0x99, 0xf0, 0x88, 0xe0, 0xc0, 0xf0, 0x9a, 0xa2, 0x12, + 0xef, 0x9b, 0x54, 0xd6, 0xfc, 0x1, 0xc9, 0xf4, 0xf, 0xb8, 0x27, 0x82, + 0xd2, 0x3c, 0x42, 0x62, 0x6c, 0x7c, 0xc0, 0x9e, 0x3c, 0x17, 0xa7, 0xeb, + 0x24, 0x55, 0xda, 0xf4, 0xae, 0xe7, 0x69, 0xb3, 0xa7, 0xc0, 0xad, 0x61, + 0xcf, 0x9, 0xd, 0xa1, 0xb1, 0x3c, 0xcf, 0xd9, 0x2d, 0x41, 0xb7, 0x1e, + 0xb, 0xbd, 0xf8, 0x0, 0x4e, 0x44, 0xee, 0x85, 0x6, 0x3b, 0x31, 0x5, + 0xc6, 0xdd, 0xff, 0x11, 0x1c, 0x30, 0xb2, 0x32, 0xcf, 0xf3, 0x4, 0x78, + 0x78, 0x4, 0xfb, 0x15, 0x3d, 0x1b, 0x14, 0x34, 0x66, 0x21, 0x20, 0x50, + 0xad, 0x54, 0x86, 0xf3, 0x2, 0x5, 0xe7, 0x8a, 0x8d, 0xa7, 0xbf, 0x5a, + 0xa9, 0xb3, 0xd1, 0x74, 0xfe, 0xce, 0x73, 0x46, 0x59, 0x6a, 0x2e, 0x87, + 0xa7, 0x6e, 0x2a, 0xaf, 0x53, 0x80, 0xd0, 0x41, 0x93, 0xfb, 0x18, 0x6b, + 0x23, 0x9f, 0x81, 0xb2, 0xd6, 0x17, 0x12, 0xa3, 0x24, 0x66, 0xcb, 0x74, + 0x98, 0xd9, 0xe3, 0xf1, 0xc3, 0xdb, 0x82, 0xb1, 0xcf, 0x32, 0xd8, 0x7c, + 0xaa, 0x52, 0xc8, 0x7b, 0xdc, 0xa1, 0x31, 0xb6, 0x54, 0xfa, 0xeb, 0x78, + 0x2, 0xbc, 0x32, 0xec, 0x99, 0x64, 0x6c, 0x32, 0x4e, 0x57, 0x3a, 0xaf, + 0x50, 0xec, 0xa, 0x7, 0x8c, 0x3e, 0x81, 0x13, 0xc4, 0xfb, 0xaf, 0x54, + 0xe6, 0x27, 0x9c, 0x3c, 0xfd, 0xb2, 0xa2, 0xdb, 0x98, 0xc8, 0x4f, 0xb2, + 0x6, 0x7b, 0x57, 0xf7, 0xd8, 0xaa, 0x41, 0x7e, 0x37, 0xa1, 0x8, 0xc7, + 0x75, 0xad, 0xa0, 0x61, 0x8b, 0x2, 0x6e, 0x98, 0x9f, 0x8b, 0xc6, 0x11, + 0xe4, 0x20, 0xe1, 0xb0, 0x53, 0x3c, 0x78, 0x99, 0x6a, 0x8a, 0x15, 0x4, + 0x7c, 0xa9, 0xe5, 0x5f, 0x19, 0x30, 0xdc, 0xe, 0xe8, 0xae, 0x2, 0x59, + 0x1, 0xf2, 0x3d, 0xbc, 0x7a, 0x2c, 0xe9, 0xd0, 0xe8, 0xa2, 0x65, 0x13, + 0x83, 0x5c, 0x60, 0x80, 0xf, 0xfb, 0x80, 0xe3, 0xef, 0xf8, 0xd3, 0x40, + 0x6b, 0xf9, 0x3, 0x16, 0x1, 0x3e, 0x90, 0xeb, 0x8f, 0xba, 0x75, 0x9b, + 0x60, 0x38, 0x68, 0xbc, 0x20, 0xfa, 0x80, 0xcc, 0x6d, 0xed, 0x9f, 0x3d, + 0xee, 0x9d, 0x3f, 0xd8, 0x38, 0x3e, 0x69, 0x46, 0xbc, 0x77, 0xa7, 0xd1, + 0x7, 0x7b, 0x50, 0x3b, 0xc, 0x0, 0xe7, 0x4, 0x70, 0x2d, 0xa7, 0xbe, + 0x72, 0x53, 0x3f, 0xc4, 0x17, 0xae, 0x7d, 0x13, 0xb5, 0x2c, 0x72, 0xcb, + 0x11, 0x2c, 0xfc, 0x1, 0xd7, 0xd5, 0xc4, 0x18, 0x3b, 0x80, 0xe0, 0x7b, + 0xcb, 0xd0, 0x3, 0x6b, 0x48, 0x66, 0x78, 0xaa, 0xb0, 0x4, 0xf3, 0xfd, + 0x78, 0xf7, 0x7c, 0x1, 0x3a, 0x33, 0x46, 0xaf, 0xde, 0x7f, 0xf, 0x45, + 0x6d, 0x55, 0xb3, 0x7f, 0x7b, 0xb4, 0xc, 0x9c, 0x88, 0x3b, 0xb5, 0x0, + 0xba, 0x40, 0x2b, 0x9e, 0x1e, 0xd2, 0x78, 0x1, 0xe, 0xca, 0xd3, 0xef, + 0x52, 0xaa, 0x5e, 0x2, 0x1d, 0xe8, 0x91, 0xbd, 0x82, 0x5f, 0xe, 0x84, + 0x87, 0x3f, 0xa9, 0x36, 0x17, 0xfb, 0x72, 0xfe, 0xf9, 0x3e, 0x60, 0xf9, + 0x9a, 0xf9, 0x1a, 0x57, 0x8a, 0xe7, 0x57, 0x84, 0x48, 0x5c, 0x77, 0x88, + 0xc, 0x10, 0x60, 0x1c, 0xc1, 0x42, 0xfe, 0xfb, 0xe0, 0xaa, 0x3f, 0xe0, + 0x3, 0xc, 0xb7, 0xae, 0x5a, 0xc0, 0x96, 0xa9, 0xb, 0xb, 0x2c, 0x2, + 0x2c, 0x2, 0x2c, 0x2, 0x2c, 0x2, 0x7c, 0xd3, 0x67, 0xff, 0xb6, 0x17, + 0xbe, 0x52, 0x43, 0xc4, 0x2f, 0xd9, 0x27, 0x2e, 0x86, 0xb1, 0x97, 0x57, + 0xe6, 0xed, 0x81, 0x96, 0xe2, 0x6e, 0x87, 0x56, 0xf6, 0xc6, 0x40, 0xcd, + 0xe6, 0x72, 0x92, 0x25, 0xc, 0x3, 0xdb, 0xf5, 0xce, 0x79, 0x7f, 0x17, + 0x28, 0x78, 0x5, 0x9, 0x9d, 0x76, 0xe2, 0x9f, 0x3c, 0xd, 0x27, 0x7d, + 0x1c, 0x45, 0x82, 0x41, 0x4d, 0xe9, 0x30, 0xfb, 0xfd, 0x81, 0x63, 0xb0, + 0x69, 0x23, 0xb4, 0x14, 0xfd, 0xb9, 0x4, 0xa0, 0x8e, 0xce, 0x2c, 0xe0, + 0x1a, 0xf2, 0x89, 0xb8, 0xd1, 0xe6, 0xc8, 0xa2, 0xb1, 0x65, 0xd7, 0x71, + 0xbc, 0x6d, 0x45, 0x77, 0xef, 0xe1, 0x45, 0x81, 0x82, 0x27, 0x50, 0x54, + 0x7b, 0x87, 0x13, 0x41, 0x51, 0x75, 0xa4, 0x26, 0x4f, 0xfc, 0x37, 0x49, + 0xd6, 0x5b, 0xa8, 0xa8, 0x8b, 0xdd, 0x78, 0xe0, 0xa2, 0xe5, 0x4e, 0x50, + 0xc7, 0xe, 0xb6, 0xe1, 0xdf, 0x54, 0xd4, 0x14, 0x35, 0xdf, 0x5f, 0x1c, + 0x4f, 0xa0, 0xbc, 0x2d, 0xfd, 0x18, 0x21, 0x1b, 0xdf, 0x9b, 0x24, 0x64, + 0x99, 0x72, 0xe3, 0x48, 0x6e, 0x2f, 0xbb, 0xdc, 0xb9, 0xc0, 0x5d, 0x71, + 0x3c, 0x19, 0x63, 0xb5, 0x16, 0x50, 0x17, 0x9d, 0xa1, 0x5, 0xf0, 0xa, + 0x6a, 0x39, 0xed, 0x41, 0xf5, 0xdf, 0x44, 0xb4, 0x80, 0x26, 0xd6, 0x58, + 0xdf, 0xa1, 0xd3, 0x5f, 0xd, 0x1, 0x9c, 0x73, 0x85, 0x9d, 0x3e, 0xa3, + 0x6a, 0x30, 0x35, 0xd6, 0x2c, 0x6d, 0xb6, 0x3, 0x7a, 0x7b, 0x8, 0xbc, + 0x83, 0x93, 0xbe, 0xcf, 0x14, 0x9e, 0xcb, 0x5, 0xb3, 0x4d, 0x3e, 0xbe, + 0xf1, 0xd8, 0x60, 0xb9, 0xc4, 0x16, 0x1, 0x16, 0x1, 0xfa, 0x20, 0xec, + 0xcf, 0x25, 0x80, 0x13, 0xb6, 0xe8, 0xd4, 0x92, 0xab, 0x88, 0x33, 0xb, + 0xa6, 0x7f, 0x47, 0xb, 0x2, 0x94, 0xf9, 0xe6, 0x79, 0xfb, 0xff, 0xb3, + 0x71, 0x2a, 0x4, 0xa5, 0x61, 0xe5, 0x5, 0x36, 0x22, 0x18, 0x5c, 0x1e, + 0xb, 0x71, 0x80, 0x63, 0xfb, 0xeb, 0xd7, 0x83, 0x67, 0x6d, 0xc7, 0xf4, + 0xaa, 0x58, 0x2, 0x22, 0x82, 0x83, 0xee, 0x84, 0xb4, 0xac, 0xa, 0x36, + 0x5e, 0x2f, 0xec, 0x2e, 0xd5, 0xb6, 0x3f, 0x10, 0x80, 0x88, 0x15, 0x77, + 0xe7, 0xc3, 0x87, 0x11, 0xaa, 0xee, 0x0, 0x83, 0x49, 0x49, 0x24, 0x3, + 0xa0, 0x8a, 0x50, 0x59, 0x14, 0xc6, 0xaa, 0x80, 0xff, 0xd1, 0x39, 0x8a, + 0xd2, 0xab, 0xf0, 0x26, 0x88, 0x3c, 0xc9, 0x12, 0xe, 0xe2, 0x10, 0x5d, + 0xfc, 0xdb, 0x3e, 0x34, 0xff, 0x3, 0x66, 0xdd, 0xc9, 0xb6, 0x2b, 0x1c, + 0x85, 0xce, 0xdb, 0xdb, 0x8, 0x49, 0x10, 0xf7, 0x81, 0x5, 0x5, 0xf8, + 0x23, 0xc9, 0x90, 0xb5, 0x7f, 0x4f, 0x8d, 0x4b, 0xc5, 0xef, 0x8f, 0xdb, + 0x1, 0xcd, 0xa6, 0x67, 0x9d, 0xd4, 0x36, 0xfc, 0xb2, 0x59, 0x73, 0x1b, + 0xb4, 0x4c, 0x87, 0x1a, 0xef, 0x9a, 0x58, 0xb3, 0x95, 0x33, 0x4e, 0x4f, + 0xa1, 0xdd, 0x4f, 0x84, 0x38, 0x5b, 0x5e, 0x9f, 0x23, 0x21, 0x6, 0xb5, + 0xb2, 0x2e, 0x2c, 0xce, 0xd9, 0xcd, 0x24, 0x57, 0xf9, 0xfb, 0x60, 0x30, + 0xa4, 0x79, 0x18, 0xa6, 0xca, 0x5d, 0xcd, 0x17, 0xc8, 0xf3, 0x5b, 0x63, + 0xb7, 0xe7, 0x1b, 0xe4, 0x3e, 0xc7, 0x3b, 0x38, 0x60, 0x6a, 0x40, 0xbe, + 0xfc, 0x1, 0xaa, 0xd4, 0xe, 0xc6, 0x4b, 0x77, 0x57, 0x1a, 0x5e, 0xd5, + 0xda, 0x5e, 0x37, 0xff, 0x27, 0x0, 0x1d, 0x1d, 0x22, 0x4c, 0x19, 0x11, + 0x7f, 0x8d, 0xc8, 0x33, 0x5d, 0x5b, 0xd1, 0x7b, 0x7e, 0x76, 0x6c, 0x73, + 0xcb, 0xcd, 0x83, 0x96, 0xc0, 0x47, 0xe5, 0x51, 0xa9, 0xc9, 0xf3, 0xdd, + 0xdb, 0xb9, 0x11, 0xa8, 0x9d, 0x67, 0x78, 0x7e, 0x80, 0xa1, 0xdb, 0x6e, + 0x1b, 0xdc, 0xae, 0xb1, 0x74, 0xe7, 0x76, 0xa1, 0xea, 0xd4, 0xf3, 0xc, + 0x3, 0x60, 0xa0, 0x2f, 0x77, 0x6d, 0x77, 0x71, 0x59, 0xd1, 0x9a, 0x66, + 0x8d, 0x6c, 0xf2, 0xb0, 0xaf, 0xde, 0xed, 0xb9, 0x3f, 0xfb, 0x5d, 0x62, + 0x26, 0x55, 0x7, 0xf3, 0x46, 0xdb, 0xed, 0x4, 0xe8, 0x7d, 0xcc, 0x89, + 0xd6, 0xe6, 0x75, 0x7, 0xc0, 0x58, 0x19, 0xb0, 0xcb, 0x53, 0x5e, 0x47, + 0xb4, 0xe7, 0x5d, 0x12, 0xf, 0x38, 0x44, 0x90, 0x66, 0xa2, 0xc8, 0xaf, + 0xef, 0x6c, 0xe8, 0xbf, 0x98, 0xd6, 0xe, 0x78, 0xc4, 0x66, 0x9b, 0xc9, + 0x50, 0x7c, 0x64, 0x6f, 0x70, 0x26, 0x2d, 0xf9, 0x8, 0x1, 0x66, 0xe2, + 0x80, 0xe5, 0xf, 0xd0, 0x25, 0xa3, 0x3, 0x6f, 0xe1, 0x41, 0xf0, 0xd6, + 0x7c, 0xef, 0x26, 0x88, 0x1f, 0xd2, 0x31, 0x5b, 0xd, 0x9c, 0x75, 0xfc, + 0x5, 0x25, 0xe6, 0x76, 0xfd, 0x5, 0x51, 0x19, 0x11, 0x84, 0xf8, 0x33, + 0x9d, 0xe1, 0xf2, 0x9, 0x58, 0x40, 0x98, 0xda, 0x22, 0xb0, 0x50, 0xc4, + 0xcb, 0x37, 0xb6, 0x35, 0x86, 0x65, 0x51, 0x75, 0xa8, 0xb7, 0x65, 0x7a, + 0x3a, 0x9c, 0x3d, 0xa9, 0xdb, 0xe9, 0x96, 0xcf, 0xa4, 0xb5, 0x5d, 0xe5, + 0x93, 0x69, 0xd1, 0xb1, 0x8c, 0x3c, 0x6e, 0x93, 0xab, 0x4e, 0xdd, 0xcc, + 0x3c, 0xc4, 0xef, 0xb7, 0xb6, 0xb, 0x21, 0xf5, 0x33, 0xa0, 0x7e, 0xa6, + 0x41, 0x99, 0xb0, 0x60, 0x47, 0xa8, 0xdc, 0xbf, 0x31, 0xa2, 0x9d, 0x27, + 0x30, 0xb6, 0x40, 0xdf, 0x99, 0x53, 0xe6, 0xc5, 0x1, 0x4a, 0x69, 0xb3, + 0xc1, 0x86, 0xda, 0xf0, 0xf7, 0xf, 0xd5, 0xeb, 0xdf, 0xa8, 0x64, 0x7b, + 0x73, 0xf0, 0xf6, 0xee, 0xd3, 0x96, 0xf0, 0xf1, 0x64, 0xf1, 0xfb, 0x97, + 0x80, 0xac, 0xae, 0x3f, 0x58, 0x15, 0x2b, 0x1e, 0xa4, 0xc3, 0xf6, 0xf5, + 0xa6, 0x8e, 0x41, 0xca, 0xc4, 0xdf, 0xb, 0x9a, 0xdb, 0x49, 0xd4, 0x34, + 0x91, 0xd7, 0x67, 0x87, 0x98, 0xc4, 0xda, 0x72, 0xbe, 0x69, 0x95, 0x8c, + 0x4, 0x9f, 0xb6, 0x3, 0x5e, 0x23, 0xb0, 0xc2, 0x90, 0xa2, 0x6d, 0xbb, + 0xe0, 0xc4, 0xab, 0x82, 0xe2, 0x3a, 0x7c, 0xfd, 0x51, 0x38, 0xbc, 0x8, + 0xb0, 0x8, 0xb0, 0x8, 0xf0, 0xa7, 0x9, 0x80, 0x4f, 0x23, 0x40, 0xe7, + 0x0, 0x11, 0xc4, 0x83, 0x63, 0x1b, 0x1c, 0x40, 0x3d, 0x22, 0x44, 0x9a, + 0x3, 0x44, 0x10, 0x33, 0xc5, 0x9b, 0x1d, 0x40, 0xb0, 0xcc, 0xf6, 0xd0, + 0xdd, 0xcb, 0xe, 0xf8, 0x39, 0x61, 0xa2, 0xc9, 0xf0, 0xaa, 0x68, 0x67, + 0xb1, 0x79, 0x9d, 0xfb, 0x6f, 0x8e, 0x10, 0xd1, 0x22, 0x8b, 0x7b, 0xb7, + 0x8f, 0x5f, 0xf6, 0xee, 0xbf, 0x59, 0x8, 0xb6, 0xee, 0x4e, 0x5f, 0xd9, + 0xcd, 0xe6, 0x6c, 0x4b, 0xc0, 0x11, 0xfa, 0x1d, 0xda, 0xa8, 0x97, 0xe3, + 0xd1, 0x76, 0x2b, 0x1, 0x40, 0xe9, 0xa3, 0xeb, 0xdd, 0xb6, 0xe1, 0x6a, + 0x7, 0x96, 0x6a, 0x34, 0x84, 0x46, 0xf3, 0x80, 0x60, 0x2, 0xcc, 0xc3, + 0x4, 0xa0, 0x88, 0xef, 0xef, 0xdf, 0x4e, 0x19, 0x4, 0x9f, 0x4b, 0xb, + 0x3c, 0x61, 0x7, 0x4c, 0x65, 0x7b, 0x3c, 0x1, 0x86, 0xa6, 0xa, 0xa2, + 0x7b, 0x82, 0x0, 0x53, 0x71, 0xc0, 0x72, 0x89, 0x2d, 0x7f, 0xc0, 0x22, + 0x80, 0x62, 0x9a, 0xc5, 0x2, 0x44, 0x7a, 0x27, 0x94, 0xdc, 0x19, 0x20, + 0xf2, 0x3a, 0x78, 0xd9, 0xc6, 0xfb, 0xbd, 0xfd, 0x5, 0x63, 0xfd, 0x3, + 0xe2, 0x7a, 0x67, 0x7a, 0x3a, 0x63, 0xab, 0x9, 0x5, 0xaf, 0xf3, 0xb4, + 0x4d, 0x46, 0xf8, 0x24, 0xa7, 0x66, 0x44, 0x6b, 0x5b, 0x24, 0x7e, 0xe6, + 0x17, 0x88, 0xf8, 0x81, 0x58, 0x5b, 0xa9, 0x17, 0x21, 0xba, 0x3f, 0xfe, + 0xfe, 0x8a, 0x16, 0x2f, 0xf7, 0x92, 0x46, 0xfa, 0x7, 0xb4, 0x19, 0xba, + 0x3f, 0xd9, 0xd8, 0x40, 0x83, 0xe9, 0x86, 0xbd, 0x5a, 0xc5, 0x67, 0xd4, + 0xcf, 0xcc, 0xb4, 0xb, 0x0, 0x94, 0xbf, 0xdf, 0xbe, 0x3d, 0xae, 0xd6, + 0x96, 0x7b, 0xd0, 0x36, 0x9e, 0xc0, 0xe, 0xe8, 0x1e, 0x1f, 0x62, 0xf7, + 0x58, 0xfc, 0xfe, 0x8, 0x16, 0xf0, 0x78, 0xa2, 0xaf, 0x7b, 0x0, 0xae, + 0x84, 0x70, 0xaa, 0xe6, 0xf6, 0xf6, 0x12, 0x2b, 0xe3, 0x2b, 0xae, 0x2f, + 0xea, 0x33, 0x6, 0xdb, 0x22, 0x20, 0xa, 0x91, 0xf0, 0x81, 0xbf, 0xf0, + 0xf1, 0x2, 0x70, 0xc6, 0x97, 0xd0, 0x98, 0x5b, 0xa6, 0x38, 0x25, 0x34, + 0x3e, 0xfe, 0xf5, 0x13, 0x43, 0xbf, 0x7f, 0xbd, 0x3f, 0xe0, 0x1f, 0x5f, + 0x4e, 0x4b, 0x19, 0x56, 0xcb, 0xb5, 0x20, 0x0, 0x0, 0x0, 0x0, 0x49, + 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82}; +const unsigned short sGlyphWidths[256] = { + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 4, 3, 5, 7, 7, 12, + 9, 2, 4, 4, 5, 8, 4, 4, 4, 4, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 4, 4, 8, 8, 8, 7, 13, 9, 9, 9, 9, 9, 8, 10, 9, 3, 6, 9, + 7, 11, 9, 10, 9, 10, 9, 9, 7, 9, 9, 13, 7, 9, 7, 4, 4, 4, 5, + 7, 4, 7, 7, 7, 7, 7, 3, 7, 7, 3, 3, 7, 3, 11, 7, 7, 7, 7, + 4, 7, 4, 7, 5, 9, 7, 7, 7, 4, 3, 4, 8, 10, 7, 10, 3, 7, 4, + 13, 7, 7, 4, 14, 9, 4, 13, 10, 7, 10, 10, 3, 3, 4, 4, 5, 7, 13, + 4, 13, 7, 4, 12, 10, 7, 9, 4, 3, 7, 7, 7, 7, 3, 7, 4, 10, 4, + 7, 8, 4, 10, 7, 5, 7, 4, 4, 4, 7, 7, 4, 4, 4, 5, 7, 11, 11, + 11, 8, 9, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 3, 3, 3, 3, 9, + 9, 10, 10, 10, 10, 10, 8, 10, 9, 9, 9, 9, 9, 9, 9, 7, 7, 7, 7, + 7, 7, 12, 7, 7, 7, 7, 7, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7}; +} // namespace normal_font + +const FontBitmapInfo sDefaultCompositorFont = { + mozilla::Nothing(), + mozilla::Some(&normal_font::sGlyphWidths[0]), + 256, + 256, + 16, + 16, + 0, + normal_font::sFontPNG, + sizeof(normal_font::sFontPNG)}; + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/composite/FrameUniformityData.cpp b/gfx/layers/composite/FrameUniformityData.cpp new file mode 100644 index 0000000000..2589de292e --- /dev/null +++ b/gfx/layers/composite/FrameUniformityData.cpp @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "FrameUniformityData.h" + +#include "mozilla/TimeStamp.h" +#include "mozilla/dom/APZTestDataBinding.h" +#include "mozilla/dom/ToJSValue.h" +#include "nsTArray.h" + +namespace mozilla { +namespace layers { + +using namespace gfx; + +bool FrameUniformityData::ToJS(JS::MutableHandle<JS::Value> aOutValue, + JSContext* aContext) { + dom::FrameUniformityResults results; + dom::Sequence<dom::FrameUniformity>& layers = + results.mLayerUniformities.Construct(); + + for (const auto& [layerAddr, uniformity] : mUniformities) { + // FIXME: Make this infallible after bug 968520 is done. + MOZ_ALWAYS_TRUE(layers.AppendElement(fallible)); + dom::FrameUniformity& entry = layers.LastElement(); + + entry.mLayerAddress.Construct() = layerAddr; + entry.mFrameUniformity.Construct() = uniformity; + } + + return dom::ToJSValue(aContext, results, aOutValue); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/composite/FrameUniformityData.h b/gfx/layers/composite/FrameUniformityData.h new file mode 100644 index 0000000000..d1e69065af --- /dev/null +++ b/gfx/layers/composite/FrameUniformityData.h @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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_layers_FrameUniformityData_h_ +#define mozilla_layers_FrameUniformityData_h_ + +#include "ipc/IPCMessageUtils.h" +#include "js/TypeDecls.h" +#include "ipc/IPCMessageUtilsSpecializations.h" +#include "mozilla/RefPtr.h" +#include "mozilla/UniquePtr.h" +#include "nsTArray.h" + +namespace mozilla { +namespace layers { + +class FrameUniformityData { + friend struct IPC::ParamTraits<FrameUniformityData>; + + public: + bool ToJS(JS::MutableHandle<JS::Value> aOutValue, JSContext* aContext); + // Contains the calculated frame uniformities + std::map<uintptr_t, float> mUniformities; +}; + +} // namespace layers +} // namespace mozilla + +namespace IPC { +template <> +struct ParamTraits<mozilla::layers::FrameUniformityData> { + typedef mozilla::layers::FrameUniformityData paramType; + + static void Write(MessageWriter* aWriter, const paramType& aParam) { + WriteParam(aWriter, aParam.mUniformities); + } + + static bool Read(MessageReader* aReader, paramType* aResult) { + return ParamTraitsStd<std::map<uintptr_t, float>>::Read( + aReader, &aResult->mUniformities); + } +}; + +} // namespace IPC + +#endif // mozilla_layers_FrameUniformityData_h_ diff --git a/gfx/layers/composite/GPUVideoTextureHost.cpp b/gfx/layers/composite/GPUVideoTextureHost.cpp new file mode 100644 index 0000000000..c3c0547648 --- /dev/null +++ b/gfx/layers/composite/GPUVideoTextureHost.cpp @@ -0,0 +1,224 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "GPUVideoTextureHost.h" + +#include "ImageContainer.h" +#include "mozilla/RemoteDecoderManagerParent.h" +#include "mozilla/layers/ImageBridgeParent.h" +#include "mozilla/layers/VideoBridgeParent.h" +#include "mozilla/webrender/RenderTextureHostWrapper.h" +#include "mozilla/webrender/RenderThread.h" + +namespace mozilla { +namespace layers { + +GPUVideoTextureHost::GPUVideoTextureHost( + TextureFlags aFlags, const SurfaceDescriptorGPUVideo& aDescriptor) + : TextureHost(TextureHostType::Unknown, aFlags), mDescriptor(aDescriptor) { + MOZ_COUNT_CTOR(GPUVideoTextureHost); +} + +GPUVideoTextureHost::~GPUVideoTextureHost() { + MOZ_COUNT_DTOR(GPUVideoTextureHost); +} + +GPUVideoTextureHost* GPUVideoTextureHost::CreateFromDescriptor( + TextureFlags aFlags, const SurfaceDescriptorGPUVideo& aDescriptor) { + return new GPUVideoTextureHost(aFlags, aDescriptor); +} + +TextureHost* GPUVideoTextureHost::EnsureWrappedTextureHost() { + if (mWrappedTextureHost) { + return mWrappedTextureHost; + } + + const auto& sd = + static_cast<const SurfaceDescriptorRemoteDecoder&>(mDescriptor); + VideoBridgeParent* parent = VideoBridgeParent::GetSingleton(sd.source()); + if (!parent) { + // The VideoBridge went away. This can happen if the RDD process + // crashes. + return nullptr; + } + mWrappedTextureHost = parent->LookupTexture(sd.handle()); + + if (!mWrappedTextureHost) { + // The TextureHost hasn't been registered yet. This is due to a race + // between the ImageBridge (content) and the VideoBridge (RDD) and the + // ImageBridge won. See bug + // https://bugzilla.mozilla.org/show_bug.cgi?id=1630733#c14 for more + // details. + return nullptr; + } + + if (mExternalImageId.isSome()) { + // External image id is allocated by mWrappedTextureHost. + mWrappedTextureHost->EnsureRenderTexture(Nothing()); + MOZ_ASSERT(mWrappedTextureHost->mExternalImageId.isSome()); + auto wrappedId = mWrappedTextureHost->mExternalImageId.ref(); + + RefPtr<wr::RenderTextureHost> texture = + new wr::RenderTextureHostWrapper(wrappedId); + wr::RenderThread::Get()->RegisterExternalImage(mExternalImageId.ref(), + texture.forget()); + } + + return mWrappedTextureHost; +} + +bool GPUVideoTextureHost::IsValid() { return !!EnsureWrappedTextureHost(); } + +gfx::YUVColorSpace GPUVideoTextureHost::GetYUVColorSpace() const { + MOZ_ASSERT(mWrappedTextureHost, "Image isn't valid yet"); + if (!mWrappedTextureHost) { + return TextureHost::GetYUVColorSpace(); + } + return mWrappedTextureHost->GetYUVColorSpace(); +} + +gfx::ColorDepth GPUVideoTextureHost::GetColorDepth() const { + MOZ_ASSERT(mWrappedTextureHost, "Image isn't valid yet"); + if (!mWrappedTextureHost) { + return TextureHost::GetColorDepth(); + } + return mWrappedTextureHost->GetColorDepth(); +} + +gfx::ColorRange GPUVideoTextureHost::GetColorRange() const { + MOZ_ASSERT(mWrappedTextureHost, "Image isn't valid yet"); + if (!mWrappedTextureHost) { + return TextureHost::GetColorRange(); + } + return mWrappedTextureHost->GetColorRange(); +} + +gfx::IntSize GPUVideoTextureHost::GetSize() const { + MOZ_ASSERT(mWrappedTextureHost, "Image isn't valid yet"); + if (!mWrappedTextureHost) { + return gfx::IntSize(); + } + return mWrappedTextureHost->GetSize(); +} + +gfx::SurfaceFormat GPUVideoTextureHost::GetFormat() const { + MOZ_ASSERT(mWrappedTextureHost, "Image isn't valid yet"); + if (!mWrappedTextureHost) { + return gfx::SurfaceFormat::UNKNOWN; + } + return mWrappedTextureHost->GetFormat(); +} + +void GPUVideoTextureHost::CreateRenderTexture( + const wr::ExternalImageId& aExternalImageId) { + MOZ_ASSERT(mExternalImageId.isSome()); + + // When mWrappedTextureHost already exist, call CreateRenderTexture() here. + // In other cases, EnsureWrappedTextureHost() handles CreateRenderTexture(). + + if (mWrappedTextureHost) { + // External image id is allocated by mWrappedTextureHost. + mWrappedTextureHost->EnsureRenderTexture(Nothing()); + MOZ_ASSERT(mWrappedTextureHost->mExternalImageId.isSome()); + auto wrappedId = mWrappedTextureHost->mExternalImageId.ref(); + + RefPtr<wr::RenderTextureHost> texture = + new wr::RenderTextureHostWrapper(wrappedId); + wr::RenderThread::Get()->RegisterExternalImage(mExternalImageId.ref(), + texture.forget()); + return; + } + + EnsureWrappedTextureHost(); +} + +void GPUVideoTextureHost::MaybeDestroyRenderTexture() { + if (mExternalImageId.isNothing() || !mWrappedTextureHost) { + // RenderTextureHost was not created + return; + } + // When GPUVideoTextureHost created RenderTextureHost, delete it here. + TextureHost::DestroyRenderTexture(mExternalImageId.ref()); +} + +uint32_t GPUVideoTextureHost::NumSubTextures() { + if (!EnsureWrappedTextureHost()) { + return 0; + } + return EnsureWrappedTextureHost()->NumSubTextures(); +} + +void GPUVideoTextureHost::PushResourceUpdates( + wr::TransactionBuilder& aResources, ResourceUpdateOp aOp, + const Range<wr::ImageKey>& aImageKeys, const wr::ExternalImageId& aExtID) { + MOZ_ASSERT(EnsureWrappedTextureHost(), "Image isn't valid yet"); + if (!EnsureWrappedTextureHost()) { + return; + } + EnsureWrappedTextureHost()->PushResourceUpdates(aResources, aOp, aImageKeys, + aExtID); +} + +void GPUVideoTextureHost::PushDisplayItems( + wr::DisplayListBuilder& aBuilder, const wr::LayoutRect& aBounds, + const wr::LayoutRect& aClip, wr::ImageRendering aFilter, + const Range<wr::ImageKey>& aImageKeys, PushDisplayItemFlagSet aFlags) { + MOZ_ASSERT(EnsureWrappedTextureHost(), "Image isn't valid yet"); + MOZ_ASSERT(aImageKeys.length() > 0); + if (!EnsureWrappedTextureHost()) { + return; + } + + EnsureWrappedTextureHost()->PushDisplayItems(aBuilder, aBounds, aClip, + aFilter, aImageKeys, aFlags); +} + +bool GPUVideoTextureHost::SupportsExternalCompositing( + WebRenderBackend aBackend) { + if (!EnsureWrappedTextureHost()) { + return false; + } + return EnsureWrappedTextureHost()->SupportsExternalCompositing(aBackend); +} + +void GPUVideoTextureHost::UnbindTextureSource() { + if (EnsureWrappedTextureHost()) { + EnsureWrappedTextureHost()->UnbindTextureSource(); + } + // Handle read unlock + TextureHost::UnbindTextureSource(); +} + +void GPUVideoTextureHost::NotifyNotUsed() { + if (EnsureWrappedTextureHost()) { + EnsureWrappedTextureHost()->NotifyNotUsed(); + } + TextureHost::NotifyNotUsed(); +} + +bool GPUVideoTextureHost::IsWrappingBufferTextureHost() { + if (EnsureWrappedTextureHost()) { + return EnsureWrappedTextureHost()->IsWrappingBufferTextureHost(); + } + return false; +} + +bool GPUVideoTextureHost::IsWrappingSurfaceTextureHost() { + if (EnsureWrappedTextureHost()) { + return EnsureWrappedTextureHost()->IsWrappingSurfaceTextureHost(); + } + return false; +} + +TextureHostType GPUVideoTextureHost::GetTextureHostType() { + if (!mWrappedTextureHost) { + return TextureHostType::Unknown; + } + return mWrappedTextureHost->GetTextureHostType(); +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/composite/GPUVideoTextureHost.h b/gfx/layers/composite/GPUVideoTextureHost.h new file mode 100644 index 0000000000..a9194e6a0f --- /dev/null +++ b/gfx/layers/composite/GPUVideoTextureHost.h @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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_GPUVIDEOTEXTUREHOST_H +#define MOZILLA_GFX_GPUVIDEOTEXTUREHOST_H + +#include "mozilla/layers/TextureHost.h" + +namespace mozilla { +namespace layers { + +class GPUVideoTextureHost : public TextureHost { + public: + static GPUVideoTextureHost* CreateFromDescriptor( + TextureFlags aFlags, const SurfaceDescriptorGPUVideo& aDescriptor); + + virtual ~GPUVideoTextureHost(); + + void DeallocateDeviceData() override {} + + gfx::SurfaceFormat GetFormat() const override; + + already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override { + return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING) + } + + gfx::YUVColorSpace GetYUVColorSpace() const override; + gfx::ColorDepth GetColorDepth() const override; + gfx::ColorRange GetColorRange() const override; + + gfx::IntSize GetSize() const override; + + bool IsValid() override; + +#ifdef MOZ_LAYERS_HAVE_LOG + const char* Name() override { return "GPUVideoTextureHost"; } +#endif + + void CreateRenderTexture( + const wr::ExternalImageId& aExternalImageId) override; + + void MaybeDestroyRenderTexture() override; + + uint32_t NumSubTextures() override; + + void PushResourceUpdates(wr::TransactionBuilder& aResources, + ResourceUpdateOp aOp, + const Range<wr::ImageKey>& aImageKeys, + const wr::ExternalImageId& aExtID) override; + + void PushDisplayItems(wr::DisplayListBuilder& aBuilder, + const wr::LayoutRect& aBounds, + const wr::LayoutRect& aClip, wr::ImageRendering aFilter, + const Range<wr::ImageKey>& aImageKeys, + PushDisplayItemFlagSet aFlags) override; + + bool SupportsExternalCompositing(WebRenderBackend aBackend) override; + + void UnbindTextureSource() override; + + void NotifyNotUsed() override; + + bool IsWrappingBufferTextureHost() override; + bool IsWrappingSurfaceTextureHost() override; + + TextureHostType GetTextureHostType() override; + + protected: + GPUVideoTextureHost(TextureFlags aFlags, + const SurfaceDescriptorGPUVideo& aDescriptor); + + TextureHost* EnsureWrappedTextureHost(); + + RefPtr<TextureHost> mWrappedTextureHost; + SurfaceDescriptorGPUVideo mDescriptor; +}; + +} // namespace layers +} // namespace mozilla + +#endif // MOZILLA_GFX_GPUVIDEOTEXTUREHOST_H diff --git a/gfx/layers/composite/ImageComposite.cpp b/gfx/layers/composite/ImageComposite.cpp new file mode 100644 index 0000000000..7c36519843 --- /dev/null +++ b/gfx/layers/composite/ImageComposite.cpp @@ -0,0 +1,378 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "ImageComposite.h" + +#include <inttypes.h> + +#include "mozilla/ProfilerMarkers.h" +#include "gfxPlatform.h" +#include "nsPrintfCString.h" + +namespace mozilla { + +using namespace gfx; + +namespace layers { + +/* static */ const float ImageComposite::BIAS_TIME_MS = 1.0f; + +ImageComposite::ImageComposite() = default; + +ImageComposite::~ImageComposite() = default; + +TimeStamp ImageComposite::GetBiasedTime(const TimeStamp& aInput) const { + switch (mBias) { + case ImageComposite::BIAS_NEGATIVE: + return aInput - TimeDuration::FromMilliseconds(BIAS_TIME_MS); + case ImageComposite::BIAS_POSITIVE: + return aInput + TimeDuration::FromMilliseconds(BIAS_TIME_MS); + default: + return aInput; + } +} + +void ImageComposite::UpdateBias(size_t aImageIndex, bool aFrameChanged) { + MOZ_ASSERT(aImageIndex < ImagesCount()); + + TimeStamp compositionTime = GetCompositionTime(); + TimeStamp compositedImageTime = mImages[aImageIndex].mTimeStamp; + TimeStamp nextImageTime = aImageIndex + 1 < ImagesCount() + ? mImages[aImageIndex + 1].mTimeStamp + : TimeStamp(); + + if (profiler_thread_is_being_profiled_for_markers() && compositedImageTime && + nextImageTime) { + TimeDuration offsetCurrent = compositedImageTime - compositionTime; + TimeDuration offsetNext = nextImageTime - compositionTime; + nsPrintfCString str("current %.2lfms, next %.2lfms", + offsetCurrent.ToMilliseconds(), + offsetNext.ToMilliseconds()); + PROFILER_MARKER_TEXT("Video frame offsets", GRAPHICS, {}, str); + } + + if (compositedImageTime.IsNull()) { + mBias = ImageComposite::BIAS_NONE; + return; + } + TimeDuration threshold = TimeDuration::FromMilliseconds(1.5); + if (compositionTime - compositedImageTime < threshold && + compositionTime - compositedImageTime > -threshold) { + // The chosen frame's time is very close to the composition time (probably + // just before the current composition time, but due to previously set + // negative bias, it could be just after the current composition time too). + // If the inter-frame time is almost exactly equal to (a multiple of) + // the inter-composition time, then we're in a dangerous situation because + // jitter might cause frames to fall one side or the other of the + // composition times, causing many frames to be skipped or duplicated. + // Try to prevent that by adding a negative bias to the frame times during + // the next composite; that should ensure the next frame's time is treated + // as falling just before a composite time. + mBias = ImageComposite::BIAS_NEGATIVE; + return; + } + if (!nextImageTime.IsNull() && nextImageTime - compositionTime < threshold && + nextImageTime - compositionTime > -threshold) { + // The next frame's time is very close to our composition time (probably + // just after the current composition time, but due to previously set + // positive bias, it could be just before the current composition time too). + // We're in a dangerous situation because jitter might cause frames to + // fall one side or the other of the composition times, causing many frames + // to be skipped or duplicated. + // Specifically, the next composite is at risk of picking the "next + 1" + // frame rather than the "next" frame, which would cause the "next" frame to + // be skipped. Try to prevent that by adding a positive bias to the frame + // times during the next composite; if the inter-frame time is almost + // exactly equal to the inter-composition time, that should ensure that the + // next + 1 frame falls just *after* the next composition time, and the next + // composite should then pick the next frame rather than the next + 1 frame. + mBias = ImageComposite::BIAS_POSITIVE; + return; + } + if (aFrameChanged) { + // The current and next video frames are a sufficient distance from the + // composition time and we can reliably pick the right frame without bias. + // Reset the bias. + // We only do this when the frame changed. Otherwise, when playing a 30fps + // video on a 60fps display, we'd keep resetting the bias during the "middle + // frames". + mBias = ImageComposite::BIAS_NONE; + } +} + +int ImageComposite::ChooseImageIndex() { + // ChooseImageIndex is called for all images in the layer when it is visible. + // Change to this behaviour would break dropped frames counting calculation: + // We rely on this assumption to determine if during successive runs an + // image is returned that isn't the one following immediately the previous one + if (mImages.IsEmpty()) { + return -1; + } + + TimeStamp compositionTime = GetCompositionTime(); + auto compositionOpportunityId = GetCompositionOpportunityId(); + if (compositionTime && + compositionOpportunityId != mLastChooseImageIndexComposition) { + // We are inside a composition, in the first call to ChooseImageIndex during + // this composition. + // Find the newest frame whose biased timestamp is at or before + // `compositionTime`. + uint32_t imageIndex = 0; + while (imageIndex + 1 < mImages.Length() && + mImages[imageIndex + 1].mTextureHost->IsValid() && + GetBiasedTime(mImages[imageIndex + 1].mTimeStamp) <= + compositionTime) { + ++imageIndex; + } + + if (!mImages[imageIndex].mTextureHost->IsValid()) { + // Still not ready to be shown. + return -1; + } + + bool wasVisibleAtPreviousComposition = + compositionOpportunityId == mLastChooseImageIndexComposition.Next(); + + bool frameChanged = + UpdateCompositedFrame(imageIndex, wasVisibleAtPreviousComposition); + UpdateBias(imageIndex, frameChanged); + + mLastChooseImageIndexComposition = compositionOpportunityId; + + return imageIndex; + } + + // We've been called before during this composition, or we're not in a + // composition. Just return the last image we picked (if it's one of the + // current images). + for (uint32_t i = 0; i < mImages.Length(); ++i) { + if (mImages[i].mFrameID == mLastFrameID && + mImages[i].mProducerID == mLastProducerID) { + return i; + } + } + + return 0; +} + +const ImageComposite::TimedImage* ImageComposite::ChooseImage() { + int index = ChooseImageIndex(); + return index >= 0 ? &mImages[index] : nullptr; +} + +void ImageComposite::RemoveImagesWithTextureHost(TextureHost* aTexture) { + for (int32_t i = mImages.Length() - 1; i >= 0; --i) { + if (mImages[i].mTextureHost == aTexture) { + aTexture->UnbindTextureSource(); + mImages.RemoveElementAt(i); + } + } +} + +void ImageComposite::ClearImages() { mImages.Clear(); } + +void ImageComposite::SetImages(nsTArray<TimedImage>&& aNewImages) { + if (!aNewImages.IsEmpty()) { + DetectTimeStampJitter(&aNewImages[0]); + + // Frames older than the first frame in aNewImages that we haven't shown yet + // will never be shown. + CountSkippedFrames(&aNewImages[0]); + + if (profiler_thread_is_being_profiled_for_markers()) { + int len = aNewImages.Length(); + const auto& first = aNewImages[0]; + const auto& last = aNewImages.LastElement(); + nsPrintfCString str("%d %s, frameID %" PRId32 " (prod %" PRId32 + ") to frameID %" PRId32 " (prod %" PRId32 ")", + len, len == 1 ? "image" : "images", first.mFrameID, + first.mProducerID, last.mFrameID, last.mProducerID); + PROFILER_MARKER_TEXT("ImageComposite::SetImages", GRAPHICS, {}, str); + } + } + mImages = std::move(aNewImages); +} + +// Returns whether the frame changed. +bool ImageComposite::UpdateCompositedFrame( + int aImageIndex, bool aWasVisibleAtPreviousComposition) { + MOZ_RELEASE_ASSERT(aImageIndex >= 0); + MOZ_RELEASE_ASSERT(aImageIndex < static_cast<int>(mImages.Length())); + const TimedImage& image = mImages[aImageIndex]; + + auto compositionOpportunityId = GetCompositionOpportunityId(); + TimeStamp compositionTime = GetCompositionTime(); + MOZ_RELEASE_ASSERT(compositionTime, + "Should only be called during a composition"); + + nsCString descr; + if (profiler_thread_is_being_profiled_for_markers()) { + nsCString relativeTimeString; + if (image.mTimeStamp) { + relativeTimeString.AppendPrintf( + " [relative timestamp %.1lfms]", + (image.mTimeStamp - compositionTime).ToMilliseconds()); + } + int remainingImages = mImages.Length() - 1 - aImageIndex; + static const char* kBiasStrings[] = {"NONE", "NEGATIVE", "POSITIVE"}; + descr.AppendPrintf( + "frameID %" PRId32 " (producerID %" PRId32 ") [composite %" PRIu64 + "] [bias %s] [%d remaining %s]%s", + image.mFrameID, image.mProducerID, compositionOpportunityId.mId, + kBiasStrings[mBias], remainingImages, + remainingImages == 1 ? "image" : "images", relativeTimeString.get()); + if (mLastProducerID != image.mProducerID) { + descr.AppendPrintf(", previous producerID: %" PRId32, mLastProducerID); + } else if (mLastFrameID != image.mFrameID) { + descr.AppendPrintf(", previous frameID: %" PRId32, mLastFrameID); + } else { + descr.AppendLiteral(", no change"); + } + } + PROFILER_MARKER_TEXT("UpdateCompositedFrame", GRAPHICS, {}, descr); + + if (mLastFrameID == image.mFrameID && mLastProducerID == image.mProducerID) { + // The frame didn't change. + return false; + } + + CountSkippedFrames(&image); + + int32_t dropped = mSkippedFramesSinceLastComposite; + mSkippedFramesSinceLastComposite = 0; + + if (!aWasVisibleAtPreviousComposition) { + // This video was not part of the on-screen scene during the previous + // composition opportunity, for example it may have been scrolled off-screen + // or in a background tab, or compositing might have been paused. + // Ignore any skipped frames and don't count them as dropped. + dropped = 0; + } + + if (dropped > 0) { + mDroppedFrames += dropped; + if (profiler_thread_is_being_profiled_for_markers()) { + const char* frameOrFrames = dropped == 1 ? "frame" : "frames"; + nsPrintfCString text("%" PRId32 " %s dropped: %" PRId32 " -> %" PRId32 + " (producer %" PRId32 ")", + dropped, frameOrFrames, mLastFrameID, image.mFrameID, + mLastProducerID); + PROFILER_MARKER_TEXT("Video frames dropped", GRAPHICS, {}, text); + } + } + + mLastFrameID = image.mFrameID; + mLastProducerID = image.mProducerID; + mLastFrameUpdateComposition = compositionOpportunityId; + + return true; +} + +void ImageComposite::OnFinishRendering(int aImageIndex, + const TimedImage* aImage, + base::ProcessId aProcessId, + const CompositableHandle& aHandle) { + if (mLastFrameUpdateComposition != GetCompositionOpportunityId()) { + // The frame did not change in this composition. + return; + } + + if (aHandle) { + ImageCompositeNotificationInfo info; + info.mImageBridgeProcessId = aProcessId; + info.mNotification = ImageCompositeNotification( + aHandle, aImage->mTimeStamp, GetCompositionTime(), mLastFrameID, + mLastProducerID); + AppendImageCompositeNotification(info); + } +} + +const ImageComposite::TimedImage* ImageComposite::GetImage( + size_t aIndex) const { + if (aIndex >= mImages.Length()) { + return nullptr; + } + return &mImages[aIndex]; +} + +void ImageComposite::CountSkippedFrames(const TimedImage* aImage) { + if (aImage->mProducerID != mLastProducerID) { + // Switched producers. + return; + } + + if (mImages.IsEmpty() || aImage->mFrameID <= mLastFrameID + 1) { + // No frames were skipped. + return; + } + + uint32_t targetFrameRate = gfxPlatform::TargetFrameRate(); + if (targetFrameRate == 0) { + // Can't know whether we could have reasonably displayed all video frames. + return; + } + + double targetFrameDurationMS = 1000.0 / targetFrameRate; + + // Count how many images in mImages were skipped between mLastFrameID and + // aImage.mFrameID. Only count frames for which we can estimate a duration by + // looking at the next frame's timestamp, and only if the video frame rate is + // no faster than the target frame rate. + int32_t skipped = 0; + for (size_t i = 0; i + 1 < mImages.Length(); i++) { + const auto& img = mImages[i]; + if (img.mProducerID != aImage->mProducerID || + img.mFrameID <= mLastFrameID || img.mFrameID >= aImage->mFrameID) { + continue; + } + + // We skipped img! Estimate img's time duration. + const auto& next = mImages[i + 1]; + if (next.mProducerID != aImage->mProducerID) { + continue; + } + + MOZ_ASSERT(next.mFrameID > img.mFrameID); + TimeDuration duration = next.mTimeStamp - img.mTimeStamp; + if (floor(duration.ToMilliseconds()) >= floor(targetFrameDurationMS)) { + // Count the frame. + skipped++; + } + } + + mSkippedFramesSinceLastComposite += skipped; +} + +void ImageComposite::DetectTimeStampJitter(const TimedImage* aNewImage) { + if (!profiler_thread_is_being_profiled_for_markers() || + aNewImage->mTimeStamp.IsNull()) { + return; + } + + // Find aNewImage in mImages and compute its timestamp delta, if found. + // Ideally, a given video frame should never change its timestamp (jitter + // should be zero). However, we re-adjust video frame timestamps based on the + // audio clock. If the audio clock drifts compared to the system clock, or if + // there are bugs or inaccuracies in the computation of these timestamps, + // jitter will be non-zero. + Maybe<TimeDuration> jitter; + for (const auto& img : mImages) { + if (img.mProducerID == aNewImage->mProducerID && + img.mFrameID == aNewImage->mFrameID) { + if (!img.mTimeStamp.IsNull()) { + jitter = Some(aNewImage->mTimeStamp - img.mTimeStamp); + } + break; + } + } + if (jitter) { + nsPrintfCString text("%.2lfms", jitter->ToMilliseconds()); + PROFILER_MARKER_TEXT("VideoFrameTimeStampJitter", GRAPHICS, {}, text); + } +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/composite/ImageComposite.h b/gfx/layers/composite/ImageComposite.h new file mode 100644 index 0000000000..05e812c8b3 --- /dev/null +++ b/gfx/layers/composite/ImageComposite.h @@ -0,0 +1,139 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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_IMAGECOMPOSITE_H +#define MOZILLA_GFX_IMAGECOMPOSITE_H + +#include "CompositableHost.h" // for CompositableTextureHostRef +#include "mozilla/gfx/2D.h" +#include "mozilla/TimeStamp.h" // for TimeStamp +#include "nsTArray.h" + +namespace mozilla { +namespace layers { + +/** + * Implements Image selection logic. + */ +class ImageComposite { + public: + static const float BIAS_TIME_MS; + + explicit ImageComposite(); + virtual ~ImageComposite(); + + int32_t GetFrameID() { + const TimedImage* img = ChooseImage(); + return img ? img->mFrameID : -1; + } + + int32_t GetProducerID() { + const TimedImage* img = ChooseImage(); + return img ? img->mProducerID : -1; + } + + int32_t GetLastFrameID() const { return mLastFrameID; } + int32_t GetLastProducerID() const { return mLastProducerID; } + uint32_t GetDroppedFramesAndReset() { + uint32_t dropped = mDroppedFrames; + mDroppedFrames = 0; + return dropped; + } + + enum Bias { + // Don't apply bias to frame times + BIAS_NONE, + // Apply a negative bias to frame times to keep them before the vsync time + BIAS_NEGATIVE, + // Apply a positive bias to frame times to keep them after the vsync time + BIAS_POSITIVE, + }; + + protected: + virtual TimeStamp GetCompositionTime() const = 0; + virtual CompositionOpportunityId GetCompositionOpportunityId() const = 0; + virtual void AppendImageCompositeNotification( + const ImageCompositeNotificationInfo& aInfo) const = 0; + + struct TimedImage { + CompositableTextureHostRef mTextureHost; + TimeStamp mTimeStamp; + gfx::IntRect mPictureRect; + int32_t mFrameID; + int32_t mProducerID; + }; + + /** + * ChooseImage is guaranteed to return the same TimedImage every time it's + * called during the same composition, up to the end of Composite() --- + * it depends only on mImages, mCompositor->GetCompositionTime(), and mBias. + * mBias is updated at the end of Composite(). + */ + const TimedImage* ChooseImage(); + int ChooseImageIndex(); + const TimedImage* GetImage(size_t aIndex) const; + size_t ImagesCount() const { return mImages.Length(); } + const nsTArray<TimedImage>& Images() const { return mImages; } + + void RemoveImagesWithTextureHost(TextureHost* aTexture); + void ClearImages(); + void SetImages(nsTArray<TimedImage>&& aNewImages); + + protected: + // Send ImageComposite notifications and update the ChooseImage bias. + void OnFinishRendering(int aImageIndex, const TimedImage* aImage, + base::ProcessId aProcessId, + const CompositableHandle& aHandle); + + int32_t mLastFrameID = -1; + int32_t mLastProducerID = -1; + + private: + nsTArray<TimedImage> mImages; + TimeStamp GetBiasedTime(const TimeStamp& aInput) const; + + // Called when we know that frames older than aImage will never get another + // chance to be displayed. + // Counts frames in mImages that were skipped, and adds the skipped count to + // mSkippedFramesSinceLastComposite. + void CountSkippedFrames(const TimedImage* aImage); + + // Update mLastFrameID and mLastProducerID, and report dropped frames. + // Returns whether the frame changed. + bool UpdateCompositedFrame(int aImageIndex, + bool aWasVisibleAtPreviousComposition); + + void UpdateBias(size_t aImageIndex, bool aFrameUpdated); + + // Emit a profiler marker about video frame timestamp jitter. + void DetectTimeStampJitter(const TimedImage* aNewImage); + + /** + * Bias to apply to the next frame. + */ + Bias mBias = BIAS_NONE; + + // Video frames that were in mImages but that will never reach the screen. + // This count is reset in UpdateCompositedFrame. + int32_t mSkippedFramesSinceLastComposite = 0; + + // The number of dropped frames since the last call to + // GetDroppedFramesAndReset(). Not all skipped frames are considered "dropped" + // - for example, videos that are scrolled out of view or videos in background + // tabs are expected to skip frames, and those skipped frames are not counted + // as dropped frames. + uint32_t mDroppedFrames = 0; + + // The composition opportunity IDs of the last calls to ChooseImageIndex and + // UpdateCompositedFrame, respectively. + CompositionOpportunityId mLastChooseImageIndexComposition; + CompositionOpportunityId mLastFrameUpdateComposition; +}; + +} // namespace layers +} // namespace mozilla + +#endif // MOZILLA_GFX_IMAGECOMPOSITE_H diff --git a/gfx/layers/composite/RemoteTextureHostWrapper.cpp b/gfx/layers/composite/RemoteTextureHostWrapper.cpp new file mode 100644 index 0000000000..59eae27aa4 --- /dev/null +++ b/gfx/layers/composite/RemoteTextureHostWrapper.cpp @@ -0,0 +1,230 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "RemoteTextureHostWrapper.h" + +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/layers/AsyncImagePipelineManager.h" +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/layers/RemoteTextureMap.h" +#include "mozilla/layers/WebRenderTextureHost.h" +#include "mozilla/StaticPrefs_webgl.h" +#include "mozilla/webrender/RenderTextureHostWrapper.h" +#include "mozilla/webrender/RenderThread.h" + +namespace mozilla::layers { + +/* static */ +RefPtr<TextureHost> RemoteTextureHostWrapper::Create( + const RemoteTextureId aTextureId, const RemoteTextureOwnerId aOwnerId, + const base::ProcessId aForPid, const gfx::IntSize aSize, + const TextureFlags aFlags) { + RefPtr<TextureHost> textureHost = + new RemoteTextureHostWrapper(aTextureId, aOwnerId, aForPid, aSize, + aFlags | TextureFlags::REMOTE_TEXTURE); + auto externalImageId = AsyncImagePipelineManager::GetNextExternalImageId(); + textureHost = new WebRenderTextureHost(aFlags, textureHost, externalImageId); + return textureHost; +} + +RemoteTextureHostWrapper::RemoteTextureHostWrapper( + const RemoteTextureId aTextureId, const RemoteTextureOwnerId aOwnerId, + const base::ProcessId aForPid, const gfx::IntSize aSize, + const TextureFlags aFlags) + : TextureHost(TextureHostType::Unknown, aFlags), + mTextureId(aTextureId), + mOwnerId(aOwnerId), + mForPid(aForPid), + mSize(aSize) { + MOZ_COUNT_CTOR(RemoteTextureHostWrapper); +} + +RemoteTextureHostWrapper::~RemoteTextureHostWrapper() { + MOZ_COUNT_DTOR(RemoteTextureHostWrapper); +} + +bool RemoteTextureHostWrapper::IsValid() { + return !!mRemoteTextureForDisplayList; +} + +gfx::YUVColorSpace RemoteTextureHostWrapper::GetYUVColorSpace() const { + MOZ_ASSERT(mRemoteTextureForDisplayList, "TextureHost isn't valid yet"); + if (!mRemoteTextureForDisplayList) { + return TextureHost::GetYUVColorSpace(); + } + return mRemoteTextureForDisplayList->GetYUVColorSpace(); +} + +gfx::ColorDepth RemoteTextureHostWrapper::GetColorDepth() const { + MOZ_ASSERT(mRemoteTextureForDisplayList, "TextureHost isn't valid yet"); + if (!mRemoteTextureForDisplayList) { + return TextureHost::GetColorDepth(); + } + return mRemoteTextureForDisplayList->GetColorDepth(); +} + +gfx::ColorRange RemoteTextureHostWrapper::GetColorRange() const { + MOZ_ASSERT(mRemoteTextureForDisplayList, "TextureHost isn't valid yet"); + if (!mRemoteTextureForDisplayList) { + return TextureHost::GetColorRange(); + } + return mRemoteTextureForDisplayList->GetColorRange(); +} + +gfx::IntSize RemoteTextureHostWrapper::GetSize() const { + MOZ_ASSERT(mRemoteTextureForDisplayList, "TextureHost isn't valid yet"); + if (!mRemoteTextureForDisplayList) { + return gfx::IntSize(); + } + return mRemoteTextureForDisplayList->GetSize(); +} + +gfx::SurfaceFormat RemoteTextureHostWrapper::GetFormat() const { + MOZ_ASSERT(mRemoteTextureForDisplayList, "TextureHost isn't valid yet"); + if (!mRemoteTextureForDisplayList) { + return gfx::SurfaceFormat::UNKNOWN; + } + return mRemoteTextureForDisplayList->GetFormat(); +} + +void RemoteTextureHostWrapper::CreateRenderTexture( + const wr::ExternalImageId& aExternalImageId) { + MOZ_ASSERT(mExternalImageId.isSome()); + MOZ_ASSERT(mRemoteTextureForDisplayList); + MOZ_ASSERT(mRemoteTextureForDisplayList->mExternalImageId.isSome()); + + if (mIsSyncMode) { + // sync mode + // mRemoteTextureForDisplayList is also used for WebRender rendering. + auto wrappedId = mRemoteTextureForDisplayList->mExternalImageId.ref(); + RefPtr<wr::RenderTextureHost> texture = + new wr::RenderTextureHostWrapper(wrappedId); + wr::RenderThread::Get()->RegisterExternalImage(mExternalImageId.ref(), + texture.forget()); + } else { + // async mode + // mRemoteTextureForDisplayList could be previous remote texture's + // TextureHost that is compatible to the mTextureId's TextureHost. + // mRemoteTextureForDisplayList might not be used WebRender rendering. + RefPtr<wr::RenderTextureHost> texture = + new wr::RenderTextureHostWrapper(mTextureId, mOwnerId, mForPid); + wr::RenderThread::Get()->RegisterExternalImage(mExternalImageId.ref(), + texture.forget()); + } +} + +void RemoteTextureHostWrapper::MaybeDestroyRenderTexture() { + if (mExternalImageId.isNothing() || !mRemoteTextureForDisplayList) { + // RenderTextureHost was not created + return; + } + TextureHost::DestroyRenderTexture(mExternalImageId.ref()); +} + +uint32_t RemoteTextureHostWrapper::NumSubTextures() { + if (!mRemoteTextureForDisplayList) { + return 0; + } + return mRemoteTextureForDisplayList->NumSubTextures(); +} + +void RemoteTextureHostWrapper::PushResourceUpdates( + wr::TransactionBuilder& aResources, ResourceUpdateOp aOp, + const Range<wr::ImageKey>& aImageKeys, const wr::ExternalImageId& aExtID) { + MOZ_ASSERT(mRemoteTextureForDisplayList, "TextureHost isn't valid yet"); + if (!mRemoteTextureForDisplayList) { + return; + } + mRemoteTextureForDisplayList->PushResourceUpdates(aResources, aOp, aImageKeys, + aExtID); +} + +void RemoteTextureHostWrapper::PushDisplayItems( + wr::DisplayListBuilder& aBuilder, const wr::LayoutRect& aBounds, + const wr::LayoutRect& aClip, wr::ImageRendering aFilter, + const Range<wr::ImageKey>& aImageKeys, PushDisplayItemFlagSet aFlags) { + MOZ_ASSERT(mRemoteTextureForDisplayList, "TextureHost isn't valid yet"); + MOZ_ASSERT(aImageKeys.length() > 0); + if (!mRemoteTextureForDisplayList) { + return; + } + mRemoteTextureForDisplayList->PushDisplayItems(aBuilder, aBounds, aClip, + aFilter, aImageKeys, aFlags); +} + +bool RemoteTextureHostWrapper::SupportsExternalCompositing( + WebRenderBackend aBackend) { + if (!mRemoteTextureForDisplayList) { + return false; + } + return mRemoteTextureForDisplayList->SupportsExternalCompositing(aBackend); +} + +void RemoteTextureHostWrapper::UnbindTextureSource() {} + +void RemoteTextureHostWrapper::NotifyNotUsed() { + if (mRemoteTextureForDisplayList) { + // Release mRemoteTextureForDisplayList. + RemoteTextureMap::Get()->ReleaseRemoteTextureHostForDisplayList(this); + } + MOZ_ASSERT(!mRemoteTextureForDisplayList); + + RemoteTextureMap::Get()->UnregisterRemoteTextureHostWrapper( + mTextureId, mOwnerId, mForPid); +} + +TextureHostType RemoteTextureHostWrapper::GetTextureHostType() { + if (!mRemoteTextureForDisplayList) { + return TextureHostType::Unknown; + } + return mRemoteTextureForDisplayList->GetTextureHostType(); +} + +bool RemoteTextureHostWrapper::CheckIsReadyForRendering() { + if (!mRemoteTextureForDisplayList) { + // mRemoteTextureForDisplayList might be updated. + RemoteTextureMap::Get()->GetRemoteTextureForDisplayList(this); + MOZ_ASSERT(mRemoteTextureForDisplayList); + } + return !!mRemoteTextureForDisplayList; +} + +void RemoteTextureHostWrapper::ApplyTextureFlagsToRemoteTexture() { + if (!mRemoteTextureForDisplayList) { + return; + } + mRemoteTextureForDisplayList->SetFlags(mFlags | + TextureFlags::DEALLOCATE_CLIENT); +} + +TextureHost* RemoteTextureHostWrapper::GetRemoteTextureHostForDisplayList( + const MonitorAutoLock& aProofOfLock) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + return mRemoteTextureForDisplayList; +} + +void RemoteTextureHostWrapper::SetRemoteTextureHostForDisplayList( + const MonitorAutoLock& aProofOfLock, TextureHost* aTextureHost, + bool aIsSyncMode) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + mRemoteTextureForDisplayList = aTextureHost; + mIsSyncMode = aIsSyncMode; +} + +void RemoteTextureHostWrapper::ClearRemoteTextureHostForDisplayList( + const MonitorAutoLock& aProofOfLoc) { + MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); + mRemoteTextureForDisplayList = nullptr; +} + +bool RemoteTextureHostWrapper::IsWrappingSurfaceTextureHost() { + if (!mRemoteTextureForDisplayList) { + return false; + } + return mRemoteTextureForDisplayList->IsWrappingSurfaceTextureHost(); +} + +} // namespace mozilla::layers diff --git a/gfx/layers/composite/RemoteTextureHostWrapper.h b/gfx/layers/composite/RemoteTextureHostWrapper.h new file mode 100644 index 0000000000..8d4555567f --- /dev/null +++ b/gfx/layers/composite/RemoteTextureHostWrapper.h @@ -0,0 +1,120 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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_RemoteTextureHostWrapper_H +#define MOZILLA_GFX_RemoteTextureHostWrapper_H + +#include "mozilla/layers/TextureHost.h" +#include "mozilla/Monitor.h" + +namespace mozilla::layers { + +// This class wraps TextureHost of remote texture. +// In sync mode, mRemoteTextureForDisplayList holds TextureHost of mTextureId. +// In async mode, it could be previous remote texture's TextureHost that is +// compatible to the mTextureId's TextureHost. +class RemoteTextureHostWrapper : public TextureHost { + public: + static RefPtr<TextureHost> Create(const RemoteTextureId aTextureId, + const RemoteTextureOwnerId aOwnerId, + const base::ProcessId aForPid, + const gfx::IntSize aSize, + const TextureFlags aFlags); + + void DeallocateDeviceData() override {} + + gfx::SurfaceFormat GetFormat() const override; + + already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override { + return nullptr; + } + + gfx::YUVColorSpace GetYUVColorSpace() const override; + gfx::ColorDepth GetColorDepth() const override; + gfx::ColorRange GetColorRange() const override; + + gfx::IntSize GetSize() const override; + + bool IsValid() override; + +#ifdef MOZ_LAYERS_HAVE_LOG + const char* Name() override { return "RemoteTextureHostWrapper"; } +#endif + + void CreateRenderTexture( + const wr::ExternalImageId& aExternalImageId) override; + + void MaybeDestroyRenderTexture() override; + + uint32_t NumSubTextures() override; + + void PushResourceUpdates(wr::TransactionBuilder& aResources, + ResourceUpdateOp aOp, + const Range<wr::ImageKey>& aImageKeys, + const wr::ExternalImageId& aExtID) override; + + void PushDisplayItems(wr::DisplayListBuilder& aBuilder, + const wr::LayoutRect& aBounds, + const wr::LayoutRect& aClip, wr::ImageRendering aFilter, + const Range<wr::ImageKey>& aImageKeys, + PushDisplayItemFlagSet aFlags) override; + + bool SupportsExternalCompositing(WebRenderBackend aBackend) override; + + void UnbindTextureSource() override; + + void NotifyNotUsed() override; + + RemoteTextureHostWrapper* AsRemoteTextureHostWrapper() override { + return this; + } + + TextureHostType GetTextureHostType() override; + + bool IsWrappingSurfaceTextureHost() override; + + bool CheckIsReadyForRendering(); + + void ApplyTextureFlagsToRemoteTexture(); + + const RemoteTextureId mTextureId; + const RemoteTextureOwnerId mOwnerId; + const base::ProcessId mForPid; + const gfx::IntSize mSize; + + protected: + RemoteTextureHostWrapper(const RemoteTextureId aTextureId, + const RemoteTextureOwnerId aOwnerId, + const base::ProcessId aForPid, + const gfx::IntSize aSize, const TextureFlags aFlags); + virtual ~RemoteTextureHostWrapper(); + + // Called only by RemoteTextureMap + TextureHost* GetRemoteTextureHostForDisplayList( + const MonitorAutoLock& aProofOfLock); + // Called only by RemoteTextureMap + void SetRemoteTextureHostForDisplayList(const MonitorAutoLock& aProofOfLock, + TextureHost* aTextureHost, + bool aIsSyncMode); + void ClearRemoteTextureHostForDisplayList( + const MonitorAutoLock& aProofOfLock); + + // Updated by RemoteTextureMap + // + // Hold compositable ref of remote texture's TextureHost that is used for + // building WebRender display list. In sync mode, it is TextureHost of + // mTextureId. In async mode, it could be previous TextureHost that is + // compatible to the mTextureId's TextureHost. + CompositableTextureHostRef mRemoteTextureForDisplayList; + + bool mIsSyncMode = true; + + friend class RemoteTextureMap; +}; + +} // namespace mozilla::layers + +#endif // MOZILLA_GFX_RemoteTextureHostWrapper_H diff --git a/gfx/layers/composite/TextureHost.cpp b/gfx/layers/composite/TextureHost.cpp new file mode 100644 index 0000000000..d6952bb113 --- /dev/null +++ b/gfx/layers/composite/TextureHost.cpp @@ -0,0 +1,840 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "TextureHost.h" + +#include "CompositableHost.h" // for CompositableHost +#include "mozilla/gfx/2D.h" // for DataSourceSurface, Factory +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/ipc/Shmem.h" // for Shmem +#include "mozilla/layers/AsyncImagePipelineManager.h" +#include "mozilla/layers/BufferTexture.h" +#include "mozilla/layers/CompositableTransactionParent.h" // for CompositableParentManager +#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/Compositor.h" // for Compositor +#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator +#include "mozilla/layers/ImageBridgeParent.h" // for ImageBridgeParent +#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc +#include "mozilla/layers/RemoteTextureMap.h" +#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL +#include "mozilla/layers/ImageDataSerializer.h" +#include "mozilla/layers/TextureClient.h" +#include "mozilla/layers/GPUVideoTextureHost.h" +#include "mozilla/layers/WebRenderTextureHost.h" +#include "mozilla/StaticPrefs_layers.h" +#include "mozilla/StaticPrefs_gfx.h" +#include "mozilla/webrender/RenderBufferTextureHost.h" +#include "mozilla/webrender/RenderExternalTextureHost.h" +#include "mozilla/webrender/RenderThread.h" +#include "mozilla/webrender/WebRenderAPI.h" +#include "nsAString.h" +#include "mozilla/RefPtr.h" // for nsRefPtr +#include "nsPrintfCString.h" // for nsPrintfCString +#include "mozilla/layers/PTextureParent.h" +#include "mozilla/Unused.h" +#include <limits> +#include "../opengl/CompositorOGL.h" + +#include "gfxUtils.h" +#include "IPDLActor.h" + +#ifdef XP_MACOSX +# include "../opengl/MacIOSurfaceTextureHostOGL.h" +#endif + +#ifdef XP_WIN +# include "../d3d11/CompositorD3D11.h" +# include "mozilla/layers/TextureD3D11.h" +# ifdef MOZ_WMF_MEDIA_ENGINE +# include "mozilla/layers/DcompSurfaceImage.h" +# endif +#endif + +#if 0 +# define RECYCLE_LOG(...) printf_stderr(__VA_ARGS__) +#else +# define RECYCLE_LOG(...) \ + do { \ + } while (0) +#endif + +namespace mozilla { +namespace layers { + +/** + * TextureParent is the host-side IPDL glue between TextureClient and + * TextureHost. It is an IPDL actor just like LayerParent, CompositableParent, + * etc. + */ +class TextureParent : public ParentActor<PTextureParent> { + public: + TextureParent(HostIPCAllocator* aAllocator, uint64_t aSerial, + const wr::MaybeExternalImageId& aExternalImageId); + + virtual ~TextureParent(); + + bool Init(const SurfaceDescriptor& aSharedData, + ReadLockDescriptor&& aReadLock, const LayersBackend& aLayersBackend, + const TextureFlags& aFlags); + + void NotifyNotUsed(uint64_t aTransactionId); + + mozilla::ipc::IPCResult RecvRecycleTexture( + const TextureFlags& aTextureFlags) final; + + TextureHost* GetTextureHost() { return mTextureHost; } + + void Destroy() override; + + uint64_t GetSerial() const { return mSerial; } + + HostIPCAllocator* mSurfaceAllocator; + RefPtr<TextureHost> mTextureHost; + // mSerial is unique in TextureClient's process. + const uint64_t mSerial; + wr::MaybeExternalImageId mExternalImageId; +}; + +static bool WrapWithWebRenderTextureHost(ISurfaceAllocator* aDeallocator, + LayersBackend aBackend, + TextureFlags aFlags) { + if (!aDeallocator) { + return false; + } + if ((aFlags & TextureFlags::SNAPSHOT) || + (!aDeallocator->UsesImageBridge() && + !aDeallocator->AsCompositorBridgeParentBase())) { + return false; + } + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +PTextureParent* TextureHost::CreateIPDLActor( + HostIPCAllocator* aAllocator, const SurfaceDescriptor& aSharedData, + ReadLockDescriptor&& aReadLock, LayersBackend aLayersBackend, + TextureFlags aFlags, uint64_t aSerial, + const wr::MaybeExternalImageId& aExternalImageId) { + TextureParent* actor = + new TextureParent(aAllocator, aSerial, aExternalImageId); + if (!actor->Init(aSharedData, std::move(aReadLock), aLayersBackend, aFlags)) { + actor->ActorDestroy(ipc::IProtocol::ActorDestroyReason::FailedConstructor); + delete actor; + return nullptr; + } + return actor; +} + +// static +bool TextureHost::DestroyIPDLActor(PTextureParent* actor) { + delete actor; + return true; +} + +// static +bool TextureHost::SendDeleteIPDLActor(PTextureParent* actor) { + return PTextureParent::Send__delete__(actor); +} + +// static +TextureHost* TextureHost::AsTextureHost(PTextureParent* actor) { + if (!actor) { + return nullptr; + } + return static_cast<TextureParent*>(actor)->mTextureHost; +} + +// static +uint64_t TextureHost::GetTextureSerial(PTextureParent* actor) { + if (!actor) { + return UINT64_MAX; + } + return static_cast<TextureParent*>(actor)->mSerial; +} + +PTextureParent* TextureHost::GetIPDLActor() { return mActor; } + +void TextureHost::SetLastFwdTransactionId(uint64_t aTransactionId) { + MOZ_ASSERT(mFwdTransactionId <= aTransactionId); + mFwdTransactionId = aTransactionId; +} + +already_AddRefed<TextureHost> CreateDummyBufferTextureHost( + mozilla::layers::LayersBackend aBackend, + mozilla::layers::TextureFlags aFlags) { + // Ensure that the host will delete the memory. + aFlags &= ~TextureFlags::DEALLOCATE_CLIENT; + aFlags |= TextureFlags::DUMMY_TEXTURE; + UniquePtr<TextureData> textureData(BufferTextureData::Create( + gfx::IntSize(1, 1), gfx::SurfaceFormat::B8G8R8A8, gfx::BackendType::SKIA, + aBackend, aFlags, TextureAllocationFlags::ALLOC_DEFAULT, nullptr)); + SurfaceDescriptor surfDesc; + textureData->Serialize(surfDesc); + const SurfaceDescriptorBuffer& bufferDesc = + surfDesc.get_SurfaceDescriptorBuffer(); + const MemoryOrShmem& data = bufferDesc.data(); + RefPtr<TextureHost> host = + new MemoryTextureHost(reinterpret_cast<uint8_t*>(data.get_uintptr_t()), + bufferDesc.desc(), aFlags); + return host.forget(); +} + +already_AddRefed<TextureHost> TextureHost::Create( + const SurfaceDescriptor& aDesc, ReadLockDescriptor&& aReadLock, + ISurfaceAllocator* aDeallocator, LayersBackend aBackend, + TextureFlags aFlags, wr::MaybeExternalImageId& aExternalImageId) { + RefPtr<TextureHost> result; + + switch (aDesc.type()) { + case SurfaceDescriptor::TSurfaceDescriptorBuffer: + case SurfaceDescriptor::TSurfaceDescriptorGPUVideo: + result = CreateBackendIndependentTextureHost(aDesc, aDeallocator, + aBackend, aFlags); + break; + + case SurfaceDescriptor::TEGLImageDescriptor: + case SurfaceDescriptor::TSurfaceTextureDescriptor: + case SurfaceDescriptor::TSurfaceDescriptorAndroidHardwareBuffer: + case SurfaceDescriptor::TSurfaceDescriptorSharedGLTexture: + case SurfaceDescriptor::TSurfaceDescriptorDMABuf: + result = CreateTextureHostOGL(aDesc, aDeallocator, aBackend, aFlags); + break; + + case SurfaceDescriptor::TSurfaceDescriptorMacIOSurface: + result = CreateTextureHostOGL(aDesc, aDeallocator, aBackend, aFlags); + break; + +#ifdef XP_WIN + case SurfaceDescriptor::TSurfaceDescriptorD3D10: + case SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr: + result = CreateTextureHostD3D11(aDesc, aDeallocator, aBackend, aFlags); + break; +# ifdef MOZ_WMF_MEDIA_ENGINE + case SurfaceDescriptor::TSurfaceDescriptorDcompSurface: + result = + CreateTextureHostDcompSurface(aDesc, aDeallocator, aBackend, aFlags); + break; +# endif +#endif + case SurfaceDescriptor::TSurfaceDescriptorRecorded: { + const SurfaceDescriptorRecorded& desc = + aDesc.get_SurfaceDescriptorRecorded(); + UniquePtr<SurfaceDescriptor> realDesc = + aDeallocator + ? aDeallocator->AsCompositorBridgeParentBase() + ->LookupSurfaceDescriptorForClientTexture(desc.textureId()) + : nullptr; + if (!realDesc) { + gfxCriticalNote << "Failed to get descriptor for recorded texture."; + // Create a dummy to prevent any crashes due to missing IPDL actors. + result = CreateDummyBufferTextureHost(aBackend, aFlags); + break; + } + + result = + TextureHost::Create(*realDesc, std::move(aReadLock), aDeallocator, + aBackend, aFlags, aExternalImageId); + return result.forget(); + } + default: + MOZ_CRASH("GFX: Unsupported Surface type host"); + } + + if (!result) { + gfxCriticalNote << "TextureHost creation failure type=" << aDesc.type(); + } + + if (result && WrapWithWebRenderTextureHost(aDeallocator, aBackend, aFlags)) { + MOZ_ASSERT(aExternalImageId.isSome()); + result = new WebRenderTextureHost(aFlags, result, aExternalImageId.ref()); + } + + if (result) { + result->DeserializeReadLock(std::move(aReadLock), aDeallocator); + } + + return result.forget(); +} + +already_AddRefed<TextureHost> CreateBackendIndependentTextureHost( + const SurfaceDescriptor& aDesc, ISurfaceAllocator* aDeallocator, + LayersBackend aBackend, TextureFlags aFlags) { + RefPtr<TextureHost> result; + switch (aDesc.type()) { + case SurfaceDescriptor::TSurfaceDescriptorBuffer: { + const SurfaceDescriptorBuffer& bufferDesc = + aDesc.get_SurfaceDescriptorBuffer(); + const MemoryOrShmem& data = bufferDesc.data(); + switch (data.type()) { + case MemoryOrShmem::TShmem: { + const ipc::Shmem& shmem = data.get_Shmem(); + const BufferDescriptor& desc = bufferDesc.desc(); + if (!shmem.IsReadable()) { + // We failed to map the shmem so we can't verify its size. This + // should not be a fatal error, so just create the texture with + // nothing backing it. + result = new ShmemTextureHost(shmem, desc, aDeallocator, aFlags); + break; + } + + size_t bufSize = shmem.Size<char>(); + size_t reqSize = SIZE_MAX; + switch (desc.type()) { + case BufferDescriptor::TYCbCrDescriptor: { + const YCbCrDescriptor& ycbcr = desc.get_YCbCrDescriptor(); + reqSize = ImageDataSerializer::ComputeYCbCrBufferSize( + ycbcr.ySize(), ycbcr.yStride(), ycbcr.cbCrSize(), + ycbcr.cbCrStride(), ycbcr.yOffset(), ycbcr.cbOffset(), + ycbcr.crOffset()); + break; + } + case BufferDescriptor::TRGBDescriptor: { + const RGBDescriptor& rgb = desc.get_RGBDescriptor(); + reqSize = ImageDataSerializer::ComputeRGBBufferSize(rgb.size(), + rgb.format()); + break; + } + default: + gfxCriticalError() + << "Bad buffer host descriptor " << (int)desc.type(); + MOZ_CRASH("GFX: Bad descriptor"); + } + + if (reqSize == 0 || bufSize < reqSize) { + NS_ERROR( + "A client process gave a shmem too small to fit for its " + "descriptor!"); + return nullptr; + } + + result = new ShmemTextureHost(shmem, desc, aDeallocator, aFlags); + break; + } + case MemoryOrShmem::Tuintptr_t: { + if (aDeallocator && !aDeallocator->IsSameProcess()) { + NS_ERROR( + "A client process is trying to peek at our address space using " + "a MemoryTexture!"); + return nullptr; + } + + result = new MemoryTextureHost( + reinterpret_cast<uint8_t*>(data.get_uintptr_t()), + bufferDesc.desc(), aFlags); + break; + } + default: + gfxCriticalError() + << "Failed texture host for backend " << (int)data.type(); + MOZ_CRASH("GFX: No texture host for backend"); + } + break; + } + case SurfaceDescriptor::TSurfaceDescriptorGPUVideo: { + MOZ_ASSERT(aDesc.get_SurfaceDescriptorGPUVideo().type() == + SurfaceDescriptorGPUVideo::TSurfaceDescriptorRemoteDecoder); + result = GPUVideoTextureHost::CreateFromDescriptor( + aFlags, aDesc.get_SurfaceDescriptorGPUVideo()); + break; + } + default: { + NS_WARNING("No backend independent TextureHost for this descriptor type"); + } + } + return result.forget(); +} + +TextureHost::TextureHost(TextureHostType aType, TextureFlags aFlags) + : AtomicRefCountedWithFinalize("TextureHost"), + mTextureHostType(aType), + mActor(nullptr), + mFlags(aFlags), + mCompositableCount(0), + mFwdTransactionId(0), + mReadLocked(false) {} + +TextureHost::~TextureHost() { + if (mReadLocked) { + // If we still have a ReadLock, unlock it. At this point we don't care about + // the texture client being written into on the other side since it should + // be destroyed by now. But we will hit assertions if we don't ReadUnlock + // before destroying the lock itself. + ReadUnlock(); + } + if (mDestroyedCallback) { + mDestroyedCallback(); + } +} + +void TextureHost::Finalize() { + if (!(GetFlags() & TextureFlags::BORROWED_EXTERNAL_ID)) { + MaybeDestroyRenderTexture(); + } + + if (!(GetFlags() & TextureFlags::DEALLOCATE_CLIENT)) { + DeallocateSharedData(); + DeallocateDeviceData(); + } +} + +void TextureHost::UnbindTextureSource() { + if (mReadLocked) { + ReadUnlock(); + } +} + +void TextureHost::RecycleTexture(TextureFlags aFlags) { + MOZ_ASSERT(GetFlags() & TextureFlags::RECYCLE); + MOZ_ASSERT(aFlags & TextureFlags::RECYCLE); + mFlags = aFlags; +} + +void TextureHost::PrepareForUse() { + if ((mFlags & TextureFlags::REMOTE_TEXTURE) && AsSurfaceTextureHost()) { + MOZ_ASSERT(mExternalImageId.isSome()); + // Call PrepareForUse on render thread. + // See RenderAndroidSurfaceTextureHostOGL::PrepareForUse. + wr::RenderThread::Get()->PrepareForUse(*mExternalImageId); + } +} + +void TextureHost::NotifyNotUsed() { + if (!mActor) { + if ((mFlags & TextureFlags::REMOTE_TEXTURE) && AsSurfaceTextureHost()) { + MOZ_ASSERT(mExternalImageId.isSome()); + wr::RenderThread::Get()->NotifyNotUsed(*mExternalImageId); + } + return; + } + + // Do not need to call NotifyNotUsed() if TextureHost does not have + // TextureFlags::RECYCLE flag nor TextureFlags::WAIT_HOST_USAGE_END flag. + if (!(GetFlags() & TextureFlags::RECYCLE) && + !(GetFlags() & TextureFlags::WAIT_HOST_USAGE_END)) { + return; + } + + static_cast<TextureParent*>(mActor)->NotifyNotUsed(mFwdTransactionId); +} + +void TextureHost::CallNotifyNotUsed() { + if (!mActor) { + return; + } + static_cast<TextureParent*>(mActor)->NotifyNotUsed(mFwdTransactionId); +} + +void TextureHost::MaybeDestroyRenderTexture() { + if (mExternalImageId.isNothing()) { + // RenderTextureHost was not created + return; + } + // When TextureHost created RenderTextureHost, delete it here. + TextureHost::DestroyRenderTexture(mExternalImageId.ref()); +} + +void TextureHost::DestroyRenderTexture( + const wr::ExternalImageId& aExternalImageId) { + wr::RenderThread::Get()->UnregisterExternalImage(aExternalImageId); +} + +void TextureHost::EnsureRenderTexture( + const wr::MaybeExternalImageId& aExternalImageId) { + if (aExternalImageId.isNothing()) { + // TextureHost is wrapped by GPUVideoTextureHost. + if (mExternalImageId.isSome()) { + // RenderTextureHost was already created. + return; + } + mExternalImageId = + Some(AsyncImagePipelineManager::GetNextExternalImageId()); + } else { + // TextureHost is wrapped by WebRenderTextureHost. + if (aExternalImageId == mExternalImageId) { + // The texture has already been created. + return; + } + MOZ_ASSERT(mExternalImageId.isNothing()); + mExternalImageId = aExternalImageId; + } + CreateRenderTexture(mExternalImageId.ref()); +} + +TextureSource::TextureSource() : mCompositableCount(0) {} + +TextureSource::~TextureSource() = default; +BufferTextureHost::BufferTextureHost(const BufferDescriptor& aDesc, + TextureFlags aFlags) + : TextureHost(TextureHostType::Buffer, aFlags), mLocked(false) { + mDescriptor = aDesc; + switch (mDescriptor.type()) { + case BufferDescriptor::TYCbCrDescriptor: { + const YCbCrDescriptor& ycbcr = mDescriptor.get_YCbCrDescriptor(); + mSize = ycbcr.display().Size(); + mFormat = gfx::SurfaceFormat::YUV; + break; + } + case BufferDescriptor::TRGBDescriptor: { + const RGBDescriptor& rgb = mDescriptor.get_RGBDescriptor(); + mSize = rgb.size(); + mFormat = rgb.format(); + break; + } + default: + gfxCriticalError() << "Bad buffer host descriptor " + << (int)mDescriptor.type(); + MOZ_CRASH("GFX: Bad descriptor"); + } + +#ifdef XP_MACOSX + const int kMinSize = 1024; + const int kMaxSize = 4096; + mUseExternalTextures = + kMaxSize >= mSize.width && mSize.width >= kMinSize && + kMaxSize >= mSize.height && mSize.height >= kMinSize && + StaticPrefs::gfx_webrender_enable_client_storage_AtStartup(); +#else + mUseExternalTextures = false; +#endif +} + +BufferTextureHost::~BufferTextureHost() = default; + +void BufferTextureHost::DeallocateDeviceData() {} + +void BufferTextureHost::CreateRenderTexture( + const wr::ExternalImageId& aExternalImageId) { + RefPtr<wr::RenderTextureHost> texture; + + if (UseExternalTextures()) { + texture = + new wr::RenderExternalTextureHost(GetBuffer(), GetBufferDescriptor()); + } else { + texture = + new wr::RenderBufferTextureHost(GetBuffer(), GetBufferDescriptor()); + } + + wr::RenderThread::Get()->RegisterExternalImage(aExternalImageId, + texture.forget()); +} + +uint32_t BufferTextureHost::NumSubTextures() { + if (GetFormat() == gfx::SurfaceFormat::YUV) { + return 3; + } + + return 1; +} + +void BufferTextureHost::PushResourceUpdates( + wr::TransactionBuilder& aResources, ResourceUpdateOp aOp, + const Range<wr::ImageKey>& aImageKeys, const wr::ExternalImageId& aExtID) { + auto method = aOp == TextureHost::ADD_IMAGE + ? &wr::TransactionBuilder::AddExternalImage + : &wr::TransactionBuilder::UpdateExternalImage; + + // Use native textures if our backend requires it, or if our backend doesn't + // forbid it and we want to use them. + NativeTexturePolicy policy = + BackendNativeTexturePolicy(aResources.GetBackendType(), GetSize()); + bool useNativeTexture = + (policy == REQUIRE) || (policy != FORBID && UseExternalTextures()); + auto imageType = useNativeTexture ? wr::ExternalImageType::TextureHandle( + wr::ImageBufferKind::TextureRect) + : wr::ExternalImageType::Buffer(); + + if (GetFormat() != gfx::SurfaceFormat::YUV) { + MOZ_ASSERT(aImageKeys.length() == 1); + + wr::ImageDescriptor descriptor( + GetSize(), + ImageDataSerializer::ComputeRGBStride(GetFormat(), GetSize().width), + GetFormat()); + (aResources.*method)(aImageKeys[0], descriptor, aExtID, imageType, 0); + } else { + MOZ_ASSERT(aImageKeys.length() == 3); + + const layers::YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor(); + gfx::IntSize ySize = desc.display().Size(); + gfx::IntSize cbcrSize = ImageDataSerializer::GetCroppedCbCrSize(desc); + wr::ImageDescriptor yDescriptor( + ySize, desc.yStride(), SurfaceFormatForColorDepth(desc.colorDepth())); + wr::ImageDescriptor cbcrDescriptor( + cbcrSize, desc.cbCrStride(), + SurfaceFormatForColorDepth(desc.colorDepth())); + (aResources.*method)(aImageKeys[0], yDescriptor, aExtID, imageType, 0); + (aResources.*method)(aImageKeys[1], cbcrDescriptor, aExtID, imageType, 1); + (aResources.*method)(aImageKeys[2], cbcrDescriptor, aExtID, imageType, 2); + } +} + +void BufferTextureHost::PushDisplayItems(wr::DisplayListBuilder& aBuilder, + const wr::LayoutRect& aBounds, + const wr::LayoutRect& aClip, + wr::ImageRendering aFilter, + const Range<wr::ImageKey>& aImageKeys, + PushDisplayItemFlagSet aFlags) { + // SWGL should always try to bypass shaders and composite directly. + bool preferCompositorSurface = + aFlags.contains(PushDisplayItemFlag::PREFER_COMPOSITOR_SURFACE); + bool useExternalSurface = + aFlags.contains(PushDisplayItemFlag::SUPPORTS_EXTERNAL_BUFFER_TEXTURES); + if (GetFormat() != gfx::SurfaceFormat::YUV) { + MOZ_ASSERT(aImageKeys.length() == 1); + aBuilder.PushImage(aBounds, aClip, true, false, aFilter, aImageKeys[0], + !(mFlags & TextureFlags::NON_PREMULTIPLIED), + wr::ColorF{1.0f, 1.0f, 1.0f, 1.0f}, + preferCompositorSurface, useExternalSurface); + } else { + MOZ_ASSERT(aImageKeys.length() == 3); + const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor(); + aBuilder.PushYCbCrPlanarImage( + aBounds, aClip, true, aImageKeys[0], aImageKeys[1], aImageKeys[2], + wr::ToWrColorDepth(desc.colorDepth()), + wr::ToWrYuvColorSpace(desc.yUVColorSpace()), + wr::ToWrColorRange(desc.colorRange()), aFilter, preferCompositorSurface, + useExternalSurface); + } +} + +void TextureHost::DeserializeReadLock(ReadLockDescriptor&& aDesc, + ISurfaceAllocator* aAllocator) { + if (mReadLock) { + return; + } + + mReadLock = TextureReadLock::Deserialize(std::move(aDesc), aAllocator); +} + +void TextureHost::SetReadLocked() { + if (!mReadLock) { + return; + } + // If mReadLocked is true it means we haven't read unlocked yet and the + // content side should not have been able to write into this texture and read + // lock again! + MOZ_ASSERT(!mReadLocked); + mReadLocked = true; +} + +void TextureHost::ReadUnlock() { + if (mReadLock && mReadLocked) { + mReadLock->ReadUnlock(); + mReadLocked = false; + } +} + +bool TextureHost::NeedsYFlip() const { + return bool(mFlags & TextureFlags::ORIGIN_BOTTOM_LEFT); +} + +void BufferTextureHost::UnbindTextureSource() { + // This texture is not used by any layer anymore. + // If the texture has an intermediate buffer we don't care either because + // texture uploads are also performed synchronously for BufferTextureHost. + ReadUnlock(); +} + +gfx::SurfaceFormat BufferTextureHost::GetFormat() const { return mFormat; } + +gfx::YUVColorSpace BufferTextureHost::GetYUVColorSpace() const { + if (mFormat == gfx::SurfaceFormat::YUV) { + const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor(); + return desc.yUVColorSpace(); + } + return gfx::YUVColorSpace::Identity; +} + +gfx::ColorDepth BufferTextureHost::GetColorDepth() const { + if (mFormat == gfx::SurfaceFormat::YUV) { + const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor(); + return desc.colorDepth(); + } + return gfx::ColorDepth::COLOR_8; +} + +gfx::ColorRange BufferTextureHost::GetColorRange() const { + if (mFormat == gfx::SurfaceFormat::YUV) { + const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor(); + return desc.colorRange(); + } + return TextureHost::GetColorRange(); +} + +already_AddRefed<gfx::DataSourceSurface> BufferTextureHost::GetAsSurface() { + RefPtr<gfx::DataSourceSurface> result; + if (mFormat == gfx::SurfaceFormat::UNKNOWN) { + NS_WARNING("BufferTextureHost: unsupported format!"); + return nullptr; + } else if (mFormat == gfx::SurfaceFormat::YUV) { + result = ImageDataSerializer::DataSourceSurfaceFromYCbCrDescriptor( + GetBuffer(), mDescriptor.get_YCbCrDescriptor()); + if (NS_WARN_IF(!result)) { + return nullptr; + } + } else { + result = gfx::Factory::CreateWrappingDataSourceSurface( + GetBuffer(), + ImageDataSerializer::GetRGBStride(mDescriptor.get_RGBDescriptor()), + mSize, mFormat); + } + return result.forget(); +} + +ShmemTextureHost::ShmemTextureHost(const ipc::Shmem& aShmem, + const BufferDescriptor& aDesc, + ISurfaceAllocator* aDeallocator, + TextureFlags aFlags) + : BufferTextureHost(aDesc, aFlags), mDeallocator(aDeallocator) { + if (aShmem.IsReadable()) { + mShmem = MakeUnique<ipc::Shmem>(aShmem); + } else { + // This can happen if we failed to map the shmem on this process, perhaps + // because it was big and we didn't have enough contiguous address space + // available, even though we did on the child process. + // As a result this texture will be in an invalid state and Lock will + // always fail. + + gfxCriticalNote << "Failed to create a valid ShmemTextureHost"; + } + + MOZ_COUNT_CTOR(ShmemTextureHost); +} + +ShmemTextureHost::~ShmemTextureHost() { + MOZ_ASSERT(!mShmem || (mFlags & TextureFlags::DEALLOCATE_CLIENT), + "Leaking our buffer"); + DeallocateDeviceData(); + MOZ_COUNT_DTOR(ShmemTextureHost); +} + +void ShmemTextureHost::DeallocateSharedData() { + if (mShmem) { + MOZ_ASSERT(mDeallocator, + "Shared memory would leak without a ISurfaceAllocator"); + mDeallocator->AsShmemAllocator()->DeallocShmem(*mShmem); + mShmem = nullptr; + } +} + +void ShmemTextureHost::ForgetSharedData() { + if (mShmem) { + mShmem = nullptr; + } +} + +void ShmemTextureHost::OnShutdown() { mShmem = nullptr; } + +uint8_t* ShmemTextureHost::GetBuffer() { + return mShmem ? mShmem->get<uint8_t>() : nullptr; +} + +size_t ShmemTextureHost::GetBufferSize() { + return mShmem ? mShmem->Size<uint8_t>() : 0; +} + +MemoryTextureHost::MemoryTextureHost(uint8_t* aBuffer, + const BufferDescriptor& aDesc, + TextureFlags aFlags) + : BufferTextureHost(aDesc, aFlags), mBuffer(aBuffer) { + MOZ_COUNT_CTOR(MemoryTextureHost); +} + +MemoryTextureHost::~MemoryTextureHost() { + MOZ_ASSERT(!mBuffer || (mFlags & TextureFlags::DEALLOCATE_CLIENT), + "Leaking our buffer"); + DeallocateDeviceData(); + MOZ_COUNT_DTOR(MemoryTextureHost); +} + +void MemoryTextureHost::DeallocateSharedData() { + if (mBuffer) { + GfxMemoryImageReporter::WillFree(mBuffer); + } + delete[] mBuffer; + mBuffer = nullptr; +} + +void MemoryTextureHost::ForgetSharedData() { mBuffer = nullptr; } + +uint8_t* MemoryTextureHost::GetBuffer() { return mBuffer; } + +size_t MemoryTextureHost::GetBufferSize() { + // MemoryTextureHost just trusts that the buffer size is large enough to read + // anything we need to. That's because MemoryTextureHost has to trust the + // buffer pointer anyway, so the security model here is just that + // MemoryTexture's are restricted to same-process clients. + return std::numeric_limits<size_t>::max(); +} + +TextureParent::TextureParent(HostIPCAllocator* aSurfaceAllocator, + uint64_t aSerial, + const wr::MaybeExternalImageId& aExternalImageId) + : mSurfaceAllocator(aSurfaceAllocator), + mSerial(aSerial), + mExternalImageId(aExternalImageId) { + MOZ_COUNT_CTOR(TextureParent); +} + +TextureParent::~TextureParent() { MOZ_COUNT_DTOR(TextureParent); } + +void TextureParent::NotifyNotUsed(uint64_t aTransactionId) { + if (!mTextureHost) { + return; + } + mSurfaceAllocator->NotifyNotUsed(this, aTransactionId); +} + +bool TextureParent::Init(const SurfaceDescriptor& aSharedData, + ReadLockDescriptor&& aReadLock, + const LayersBackend& aBackend, + const TextureFlags& aFlags) { + mTextureHost = + TextureHost::Create(aSharedData, std::move(aReadLock), mSurfaceAllocator, + aBackend, aFlags, mExternalImageId); + if (mTextureHost) { + mTextureHost->mActor = this; + } + + return !!mTextureHost; +} + +void TextureParent::Destroy() { + if (!mTextureHost) { + return; + } + + if (mTextureHost->mReadLocked) { + // ReadUnlock here to make sure the ReadLock's shmem does not outlive the + // protocol that created it. + mTextureHost->ReadUnlock(); + } + + if (mTextureHost->GetFlags() & TextureFlags::DEALLOCATE_CLIENT) { + mTextureHost->ForgetSharedData(); + } + + mTextureHost->mActor = nullptr; + mTextureHost = nullptr; +} + +void TextureHost::ReceivedDestroy(PTextureParent* aActor) { + static_cast<TextureParent*>(aActor)->RecvDestroy(); +} + +mozilla::ipc::IPCResult TextureParent::RecvRecycleTexture( + const TextureFlags& aTextureFlags) { + if (!mTextureHost) { + return IPC_OK(); + } + mTextureHost->RecycleTexture(aTextureFlags); + return IPC_OK(); +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/composite/TextureHost.h b/gfx/layers/composite/TextureHost.h new file mode 100644 index 0000000000..f7fccfb735 --- /dev/null +++ b/gfx/layers/composite/TextureHost.h @@ -0,0 +1,1009 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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_TEXTUREHOST_H +#define MOZILLA_GFX_TEXTUREHOST_H + +#include <stddef.h> // for size_t +#include <stdint.h> // for uint64_t, uint32_t, uint8_t +#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc +#include "mozilla/Attributes.h" // for override +#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed, etc +#include "mozilla/gfx/Logging.h" +#include "mozilla/gfx/Matrix.h" +#include "mozilla/gfx/Point.h" // for IntSize, IntPoint +#include "mozilla/gfx/Rect.h" +#include "mozilla/gfx/Types.h" // for SurfaceFormat, etc +#include "mozilla/ipc/FileDescriptor.h" +#include "mozilla/layers/CompositorTypes.h" // for TextureFlags, etc +#include "mozilla/layers/LayersTypes.h" // for LayerRenderState, etc +#include "mozilla/layers/LayersSurfaces.h" +#include "mozilla/layers/TextureSourceProvider.h" +#include "mozilla/mozalloc.h" // for operator delete +#include "mozilla/Range.h" +#include "mozilla/UniquePtr.h" // for UniquePtr +#include "mozilla/webrender/WebRenderTypes.h" +#include "nsCOMPtr.h" // for already_AddRefed +#include "nsDebug.h" // for NS_WARNING +#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc +#include "nsRect.h" +#include "nsRegion.h" // for nsIntRegion +#include "nsTraceRefcnt.h" // for MOZ_COUNT_CTOR, etc +#include "nscore.h" // for nsACString +#include "mozilla/layers/AtomicRefCountedWithFinalize.h" + +class MacIOSurface; +namespace mozilla { +namespace gfx { +class DataSourceSurface; +} + +namespace ipc { +class Shmem; +} // namespace ipc + +namespace wr { +class DisplayListBuilder; +class TransactionBuilder; +} // namespace wr + +namespace layers { + +class AndroidHardwareBuffer; +class AndroidHardwareBufferTextureHost; +class BufferDescriptor; +class BufferTextureHost; +class Compositor; +class CompositableParentManager; +class ReadLockDescriptor; +class CompositorBridgeParent; +class SurfaceDescriptor; +class HostIPCAllocator; +class ISurfaceAllocator; +class MacIOSurfaceTextureHostOGL; +class SurfaceTextureHost; +class TextureHostOGL; +class TextureReadLock; +class TextureSourceOGL; +class TextureSourceD3D11; +class DataTextureSource; +class PTextureParent; +class RemoteTextureHostWrapper; +class TextureParent; +class WebRenderTextureHost; +class WrappingTextureSourceYCbCrBasic; + +/** + * A view on a TextureHost where the texture is internally represented as tiles + * (contrast with a tiled buffer, where each texture is a tile). For iteration + * by the texture's buffer host. This is only useful when the underlying surface + * is too big to fit in one device texture, which forces us to split it in + * smaller parts. Tiled Compositable is a different thing. + */ +class BigImageIterator { + public: + virtual void BeginBigImageIteration() = 0; + virtual void EndBigImageIteration(){}; + virtual gfx::IntRect GetTileRect() = 0; + virtual size_t GetTileCount() = 0; + virtual bool NextTile() = 0; +}; + +/** + * TextureSource is the interface for texture objects that can be composited + * by a given compositor backend. Since the drawing APIs are different + * between backends, the TextureSource interface is split into different + * interfaces (TextureSourceOGL, etc.), and TextureSource mostly provide + * access to these interfaces. + * + * This class is used on the compositor side. + */ +class TextureSource : public RefCounted<TextureSource> { + public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(TextureSource) + + TextureSource(); + + virtual ~TextureSource(); + + virtual const char* Name() const = 0; + + /** + * Should be overridden in order to deallocate the data that is associated + * with the rendering backend, such as GL textures. + */ + virtual void DeallocateDeviceData() {} + + /** + * Return the size of the texture in texels. + * If this is a tile iterator, GetSize must return the size of the current + * tile. + */ + virtual gfx::IntSize GetSize() const = 0; + + /** + * Return the pixel format of this texture + */ + virtual gfx::SurfaceFormat GetFormat() const { + return gfx::SurfaceFormat::UNKNOWN; + } + + /** + * Cast to a TextureSource for for each backend.. + */ + virtual TextureSourceOGL* AsSourceOGL() { + gfxCriticalNote << "Failed to cast " << Name() + << " into a TextureSourceOGL"; + return nullptr; + } + virtual TextureSourceD3D11* AsSourceD3D11() { return nullptr; } + /** + * Cast to a DataTextureSurce. + */ + virtual DataTextureSource* AsDataTextureSource() { return nullptr; } + + /** + * Overload this if the TextureSource supports big textures that don't fit in + * one device texture and must be tiled internally. + */ + virtual BigImageIterator* AsBigImageIterator() { return nullptr; } + + virtual void Unbind() {} + + void SetNextSibling(TextureSource* aTexture) { mNextSibling = aTexture; } + + TextureSource* GetNextSibling() const { return mNextSibling; } + + /** + * In some rare cases we currently need to consider a group of textures as one + * TextureSource, that can be split in sub-TextureSources. + */ + TextureSource* GetSubSource(int index) { + switch (index) { + case 0: + return this; + case 1: + return GetNextSibling(); + case 2: + return GetNextSibling() ? GetNextSibling()->GetNextSibling() : nullptr; + } + return nullptr; + } + + void AddCompositableRef() { ++mCompositableCount; } + + void ReleaseCompositableRef() { + --mCompositableCount; + MOZ_ASSERT(mCompositableCount >= 0); + } + + // When iterating as a BigImage, this creates temporary TextureSources + // wrapping individual tiles. + virtual RefPtr<TextureSource> ExtractCurrentTile() { + NS_WARNING("Implementation does not expose tile sources"); + return nullptr; + } + + int NumCompositableRefs() const { return mCompositableCount; } + + // The direct-map cpu buffer should be alive when gpu uses it. And it + // should not be updated while gpu reads it. This Sync() function + // implements this synchronized behavior by allowing us to check if + // the GPU is done with the texture, and block on it if aBlocking is + // true. + virtual bool Sync(bool aBlocking) { return true; } + + protected: + RefPtr<TextureSource> mNextSibling; + int mCompositableCount; +}; + +/// Equivalent of a RefPtr<TextureSource>, that calls AddCompositableRef and +/// ReleaseCompositableRef in addition to the usual AddRef and Release. +/// +/// The semantoics of these CompositableTextureRefs are important because they +/// are used both as a synchronization/safety mechanism, and as an optimization +/// mechanism. They are also tricky and subtle because we use them in a very +/// implicit way (assigning to a CompositableTextureRef is less visible than +/// explicitly calling a method or whatnot). +/// It is Therefore important to be careful about the way we use this tool. +/// +/// CompositableTextureRef is a mechanism that lets us count how many +/// compositables are using a given texture (for TextureSource and TextureHost). +/// We use it to run specific code when a texture is not used anymore, and also +/// we trigger fast paths on some operations when we can see that the texture's +/// CompositableTextureRef counter is equal to 1 (the texture is not shared +/// between compositables). +/// This means that it is important to observe the following rules: +/// * CompositableHosts that receive UseTexture and similar messages *must* +/// store all of the TextureHosts they receive in CompositableTextureRef slots +/// for as long as they may be using them. +/// * CompositableHosts must store each texture in a *single* +/// CompositableTextureRef slot to ensure that the counter properly reflects how +/// many compositables are using the texture. If a compositable needs to hold +/// two references to a given texture (for example to have a pointer to the +/// current texture in a list of textures that may be used), it can hold its +/// extra references with RefPtr or whichever pointer type makes sense. +template <typename T> +class CompositableTextureRef { + public: + CompositableTextureRef() = default; + + explicit CompositableTextureRef(const CompositableTextureRef& aOther) { + *this = aOther; + } + + explicit CompositableTextureRef(T* aOther) { *this = aOther; } + + ~CompositableTextureRef() { + if (mRef) { + mRef->ReleaseCompositableRef(); + } + } + + CompositableTextureRef& operator=(const CompositableTextureRef& aOther) { + if (aOther.get()) { + aOther->AddCompositableRef(); + } + if (mRef) { + mRef->ReleaseCompositableRef(); + } + mRef = aOther.get(); + return *this; + } + + CompositableTextureRef& operator=(T* aOther) { + if (aOther) { + aOther->AddCompositableRef(); + } + if (mRef) { + mRef->ReleaseCompositableRef(); + } + mRef = aOther; + return *this; + } + + T* get() const { return mRef; } + operator T*() const { return mRef; } + T* operator->() const { return mRef; } + T& operator*() const { return *mRef; } + + private: + RefPtr<T> mRef; +}; + +typedef CompositableTextureRef<TextureSource> CompositableTextureSourceRef; +typedef CompositableTextureRef<TextureHost> CompositableTextureHostRef; + +/** + * Interface for TextureSources that can be updated from a DataSourceSurface. + * + * All backend should implement at least one DataTextureSource. + */ +class DataTextureSource : public TextureSource { + public: + DataTextureSource() : mOwner(0), mUpdateSerial(0) {} + + const char* Name() const override { return "DataTextureSource"; } + + DataTextureSource* AsDataTextureSource() override { return this; } + + /** + * Upload a (portion of) surface to the TextureSource. + * + * The DataTextureSource doesn't own aSurface, although it owns and manage + * the device texture it uploads to internally. + */ + virtual bool Update(gfx::DataSourceSurface* aSurface, + nsIntRegion* aDestRegion = nullptr, + gfx::IntPoint* aSrcOffset = nullptr, + gfx::IntPoint* aDstOffset = nullptr) = 0; + + /** + * A facility to avoid reuploading when it is not necessary. + * The caller of Update can use GetUpdateSerial to see if the number has + * changed since last update, and call SetUpdateSerial after each successful + * update. The caller is responsible for managing the update serial except + * when the texture data is deallocated in which case the TextureSource should + * always reset the update serial to zero. + */ + uint32_t GetUpdateSerial() const { return mUpdateSerial; } + void SetUpdateSerial(uint32_t aValue) { mUpdateSerial = aValue; } + + // By default at least set the update serial to zero. + // overloaded versions should do that too. + void DeallocateDeviceData() override { SetUpdateSerial(0); } + +#ifdef DEBUG + /** + * Provide read access to the data as a DataSourceSurface. + * + * This is expected to be very slow and should be used for mostly debugging. + * XXX - implement everywhere and make it pure virtual. + */ + virtual already_AddRefed<gfx::DataSourceSurface> ReadBack() { + return nullptr; + }; +#endif + + void SetOwner(TextureHost* aOwner) { + auto newOwner = (uintptr_t)aOwner; + if (newOwner != mOwner) { + mOwner = newOwner; + SetUpdateSerial(0); + } + } + + bool IsOwnedBy(TextureHost* aOwner) const { + return mOwner == (uintptr_t)aOwner; + } + + bool HasOwner() const { return !IsOwnedBy(nullptr); } + + private: + // We store mOwner as an integer rather than as a pointer to make it clear + // it is not intended to be dereferenced. + uintptr_t mOwner; + uint32_t mUpdateSerial; +}; + +enum class TextureHostType : int8_t { + Unknown = 0, + Buffer, + DXGI, + DXGIYCbCr, + DcompSurface, + DMABUF, + MacIOSurface, + AndroidSurfaceTexture, + AndroidHardwareBuffer, + EGLImage, + GLTexture, + Last +}; + +/** + * TextureHost is a thin abstraction over texture data that need to be shared + * between the content process and the compositor process. It is the + * compositor-side half of a TextureClient/TextureHost pair. A corresponding + * TextureClient lives on the content-side. + * + * TextureHost only knows how to deserialize or synchronize generic image data + * (SurfaceDescriptor) and provide access to one or more TextureSource objects + * (these provide the necessary APIs for compositor backends to composite the + * image). + * + * A TextureHost implementation corresponds to one SurfaceDescriptor type, as + * opposed to TextureSource that corresponds to device textures. + * This means that for YCbCr planes, even though they are represented as + * 3 textures internally (3 TextureSources), we use 1 TextureHost and not 3, + * because the 3 planes are stored in the same buffer of shared memory, before + * they are uploaded separately. + * + * There is always one and only one TextureHost per TextureClient, and the + * TextureClient/Host pair only owns one buffer of image data through its + * lifetime. This means that the lifetime of the underlying shared data + * matches the lifetime of the TextureClient/Host pair. It also means + * TextureClient/Host do not implement double buffering, which is the + * reponsibility of the compositable (which would use two Texture pairs). + * + * The Lock/Unlock mecanism here mirrors Lock/Unlock in TextureClient. + * + */ +class TextureHost : public AtomicRefCountedWithFinalize<TextureHost> { + /** + * Called once, just before the destructor. + * + * Here goes the shut-down code that uses virtual methods. + * Must only be called by Release(). + */ + void Finalize(); + + friend class AtomicRefCountedWithFinalize<TextureHost>; + + public: + TextureHost(TextureHostType aType, TextureFlags aFlags); + + protected: + virtual ~TextureHost(); + + public: + /** + * Factory method. + */ + static already_AddRefed<TextureHost> Create( + const SurfaceDescriptor& aDesc, ReadLockDescriptor&& aReadLock, + ISurfaceAllocator* aDeallocator, LayersBackend aBackend, + TextureFlags aFlags, wr::MaybeExternalImageId& aExternalImageId); + + /** + * Lock the texture host for compositing without using compositor. + */ + virtual bool LockWithoutCompositor() { return true; } + /** + * Similar to Unlock(), but it should be called with LockWithoutCompositor(). + */ + virtual void UnlockWithoutCompositor() {} + + /** + * Note that the texture host format can be different from its corresponding + * texture source's. For example a ShmemTextureHost can have the ycbcr + * format and produce 3 "alpha" textures sources. + */ + virtual gfx::SurfaceFormat GetFormat() const = 0; + /** + * Return the format used for reading the texture. + * Apple's YCBCR_422 is R8G8B8X8. + */ + virtual gfx::SurfaceFormat GetReadFormat() const { return GetFormat(); } + + virtual gfx::YUVColorSpace GetYUVColorSpace() const { + return gfx::YUVColorSpace::Identity; + } + + /** + * Return the color depth of the image. Used with YUV textures. + */ + virtual gfx::ColorDepth GetColorDepth() const { + return gfx::ColorDepth::COLOR_8; + } + + /** + * Return true if using full range values (0-255 if 8 bits YUV). Used with YUV + * textures. + */ + virtual gfx::ColorRange GetColorRange() const { + return gfx::ColorRange::LIMITED; + } + + /** + * Called when another TextureHost will take over. + */ + virtual void UnbindTextureSource(); + + virtual bool IsValid() { return true; } + + /** + * Should be overridden in order to deallocate the data that is associated + * with the rendering backend, such as GL textures. + */ + virtual void DeallocateDeviceData() {} + + /** + * Should be overridden in order to deallocate the data that is shared with + * the content side, such as shared memory. + */ + virtual void DeallocateSharedData() {} + + /** + * Should be overridden in order to force the TextureHost to drop all + * references to it's shared data. + * + * This is important to ensure the correctness of the deallocation protocol. + */ + virtual void ForgetSharedData() {} + + virtual gfx::IntSize GetSize() const = 0; + + /** + * Should be overridden if TextureHost supports crop rect. + */ + virtual void SetCropRect(nsIntRect aCropRect) {} + + /** + * Debug facility. + * XXX - cool kids use Moz2D. See bug 882113. + */ + virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() = 0; + + /** + * XXX - Flags should only be set at creation time, this will be removed. + */ + void SetFlags(TextureFlags aFlags) { mFlags = aFlags; } + + /** + * XXX - Flags should only be set at creation time, this will be removed. + */ + void AddFlag(TextureFlags aFlag) { mFlags |= aFlag; } + + TextureFlags GetFlags() { return mFlags; } + + wr::MaybeExternalImageId GetMaybeExternalImageId() const { + return mExternalImageId; + } + + /** + * Allocate and deallocate a TextureParent actor. + * + * TextureParent< is an implementation detail of TextureHost that is not + * exposed to the rest of the code base. CreateIPDLActor and DestroyIPDLActor + * are for use with the managing IPDL protocols only (so that they can + * implement AllocPTextureParent and DeallocPTextureParent). + */ + static PTextureParent* CreateIPDLActor( + HostIPCAllocator* aAllocator, const SurfaceDescriptor& aSharedData, + ReadLockDescriptor&& aDescriptor, LayersBackend aLayersBackend, + TextureFlags aFlags, uint64_t aSerial, + const wr::MaybeExternalImageId& aExternalImageId); + static bool DestroyIPDLActor(PTextureParent* actor); + + /** + * Destroy the TextureChild/Parent pair. + */ + static bool SendDeleteIPDLActor(PTextureParent* actor); + + static void ReceivedDestroy(PTextureParent* actor); + + /** + * Get the TextureHost corresponding to the actor passed in parameter. + */ + static TextureHost* AsTextureHost(PTextureParent* actor); + + static uint64_t GetTextureSerial(PTextureParent* actor); + + /** + * Return a pointer to the IPDLActor. + * + * This is to be used with IPDL messages only. Do not store the returned + * pointer. + */ + PTextureParent* GetIPDLActor(); + + // If a texture host holds a reference to shmem, it should override this + // method to forget about the shmem _without_ releasing it. + virtual void OnShutdown() {} + + // Forget buffer actor. Used only for hacky fix for bug 966446. + virtual void ForgetBufferActor() {} + + virtual const char* Name() { return "TextureHost"; } + + /** + * Returns true if the TextureHost can be released before the rendering is + * completed, otherwise returns false. + */ + virtual bool NeedsDeferredDeletion() const { return true; } + + void AddCompositableRef() { + ++mCompositableCount; + if (mCompositableCount == 1) { + PrepareForUse(); + } + } + + void ReleaseCompositableRef() { + --mCompositableCount; + MOZ_ASSERT(mCompositableCount >= 0); + if (mCompositableCount == 0) { + UnbindTextureSource(); + // Send mFwdTransactionId to client side if necessary. + NotifyNotUsed(); + } + } + + int NumCompositableRefs() const { return mCompositableCount; } + + void SetLastFwdTransactionId(uint64_t aTransactionId); + + void DeserializeReadLock(ReadLockDescriptor&& aDesc, + ISurfaceAllocator* aAllocator); + void SetReadLocked(); + + TextureReadLock* GetReadLock() { return mReadLock; } + + virtual BufferTextureHost* AsBufferTextureHost() { return nullptr; } + virtual MacIOSurfaceTextureHostOGL* AsMacIOSurfaceTextureHost() { + return nullptr; + } + virtual WebRenderTextureHost* AsWebRenderTextureHost() { return nullptr; } + virtual SurfaceTextureHost* AsSurfaceTextureHost() { return nullptr; } + virtual AndroidHardwareBufferTextureHost* + AsAndroidHardwareBufferTextureHost() { + return nullptr; + } + virtual RemoteTextureHostWrapper* AsRemoteTextureHostWrapper() { + return nullptr; + } + + virtual bool IsWrappingBufferTextureHost() { return false; } + virtual bool IsWrappingSurfaceTextureHost() { return false; } + + // Create the corresponding RenderTextureHost type of this texture, and + // register the RenderTextureHost into render thread. + virtual void CreateRenderTexture( + const wr::ExternalImageId& aExternalImageId) { + MOZ_RELEASE_ASSERT( + false, + "No CreateRenderTexture() implementation for this TextureHost type."); + } + + void EnsureRenderTexture(const wr::MaybeExternalImageId& aExternalImageId); + + // Destroy RenderTextureHost when it was created by the TextureHost. + // It is called in TextureHost::Finalize(). + virtual void MaybeDestroyRenderTexture(); + + static void DestroyRenderTexture(const wr::ExternalImageId& aExternalImageId); + + /// Returns the number of actual textures that will be used to render this. + /// For example in a lot of YUV cases it will be 3 + virtual uint32_t NumSubTextures() { return 1; } + + enum ResourceUpdateOp { + ADD_IMAGE, + UPDATE_IMAGE, + }; + + // Add all necessary TextureHost informations to the resource update queue. + virtual void PushResourceUpdates(wr::TransactionBuilder& aResources, + ResourceUpdateOp aOp, + const Range<wr::ImageKey>& aImageKeys, + const wr::ExternalImageId& aExtID) { + MOZ_ASSERT_UNREACHABLE("Unimplemented"); + } + + enum class PushDisplayItemFlag { + // Passed if the caller wants these display items to be promoted + // to compositor surfaces if possible. + PREFER_COMPOSITOR_SURFACE, + + // Passed in the RenderCompositor supports BufferTextureHosts + // being used directly as external compositor surfaces. + SUPPORTS_EXTERNAL_BUFFER_TEXTURES, + }; + using PushDisplayItemFlagSet = EnumSet<PushDisplayItemFlag>; + + // Put all necessary WR commands into DisplayListBuilder for this textureHost + // rendering. + virtual void PushDisplayItems(wr::DisplayListBuilder& aBuilder, + const wr::LayoutRect& aBounds, + const wr::LayoutRect& aClip, + wr::ImageRendering aFilter, + const Range<wr::ImageKey>& aKeys, + PushDisplayItemFlagSet aFlags) { + MOZ_ASSERT_UNREACHABLE( + "No PushDisplayItems() implementation for this TextureHost type."); + } + + /** + * Some API's can use the cross-process IOSurface directly, such as OpenVR + */ + virtual MacIOSurface* GetMacIOSurface() { return nullptr; } + + virtual bool NeedsYFlip() const; + + virtual void SetAcquireFence(mozilla::ipc::FileDescriptor&& aFenceFd) {} + + virtual void SetReleaseFence(mozilla::ipc::FileDescriptor&& aFenceFd) {} + + virtual mozilla::ipc::FileDescriptor GetAndResetReleaseFence() { + return mozilla::ipc::FileDescriptor(); + } + + virtual AndroidHardwareBuffer* GetAndroidHardwareBuffer() const { + return nullptr; + } + + virtual bool SupportsExternalCompositing(WebRenderBackend aBackend) { + return false; + } + + virtual TextureHostType GetTextureHostType() { return mTextureHostType; } + + // Our WebRender backend may impose restrictions on whether textures are + // prepared as native textures or not, or it may have no restriction at + // all. This enumerates those possibilities. + enum NativeTexturePolicy { + REQUIRE, + FORBID, + DONT_CARE, + }; + + static NativeTexturePolicy BackendNativeTexturePolicy( + layers::WebRenderBackend aBackend, gfx::IntSize aSize) { + static const int32_t SWGL_DIMENSION_MAX = 1 << 15; + if (aBackend == WebRenderBackend::SOFTWARE) { + return (aSize.width <= SWGL_DIMENSION_MAX && + aSize.height <= SWGL_DIMENSION_MAX) + ? REQUIRE + : FORBID; + } + return DONT_CARE; + } + + void SetDestroyedCallback(std::function<void()>&& aDestroyedCallback) { + MOZ_ASSERT(!mDestroyedCallback); + mDestroyedCallback = std::move(aDestroyedCallback); + } + + protected: + virtual void ReadUnlock(); + + void RecycleTexture(TextureFlags aFlags); + + /** + * Called when mCompositableCount becomes from 0 to 1. + */ + virtual void PrepareForUse(); + + /** + * Called when mCompositableCount becomes 0. + */ + virtual void NotifyNotUsed(); + + // for Compositor. + void CallNotifyNotUsed(); + + TextureHostType mTextureHostType; + PTextureParent* mActor; + RefPtr<TextureReadLock> mReadLock; + TextureFlags mFlags; + int mCompositableCount; + uint64_t mFwdTransactionId; + bool mReadLocked; + wr::MaybeExternalImageId mExternalImageId; + + std::function<void()> mDestroyedCallback; + + friend class Compositor; + friend class RemoteTextureHostWrapper; + friend class TextureParent; + friend class TextureSourceProvider; + friend class GPUVideoTextureHost; + friend class WebRenderTextureHost; +}; + +/** + * TextureHost that wraps a random access buffer such as a Shmem or some raw + * memory. + * + * This TextureHost is backend-independent and the backend-specific bits are + * in the TextureSource. + * This class must be inherited to implement GetBuffer and DeallocSharedData + * (see ShmemTextureHost and MemoryTextureHost) + * + * Uploads happen when Lock is called. + * + * BufferTextureHost supports YCbCr and flavours of RGBA images (RGBX, A, etc.). + */ +class BufferTextureHost : public TextureHost { + public: + BufferTextureHost(const BufferDescriptor& aDescriptor, TextureFlags aFlags); + + virtual ~BufferTextureHost(); + + virtual uint8_t* GetBuffer() = 0; + + virtual size_t GetBufferSize() = 0; + + void UnbindTextureSource() override; + + void DeallocateDeviceData() override; + + /** + * Return the format that is exposed to the compositor when calling + * BindTextureSource. + * + * If the shared format is YCbCr and the compositor does not support it, + * GetFormat will be RGB32 (even though mFormat is SurfaceFormat::YUV). + */ + gfx::SurfaceFormat GetFormat() const override; + + gfx::YUVColorSpace GetYUVColorSpace() const override; + + gfx::ColorDepth GetColorDepth() const override; + + gfx::ColorRange GetColorRange() const override; + + gfx::IntSize GetSize() const override { return mSize; } + + already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override; + + bool NeedsDeferredDeletion() const override { + return TextureHost::NeedsDeferredDeletion() || UseExternalTextures(); + } + + BufferTextureHost* AsBufferTextureHost() override { return this; } + + bool IsWrappingBufferTextureHost() override { return true; } + + const BufferDescriptor& GetBufferDescriptor() const { return mDescriptor; } + + void CreateRenderTexture( + const wr::ExternalImageId& aExternalImageId) override; + + uint32_t NumSubTextures() override; + + void PushResourceUpdates(wr::TransactionBuilder& aResources, + ResourceUpdateOp aOp, + const Range<wr::ImageKey>& aImageKeys, + const wr::ExternalImageId& aExtID) override; + + void PushDisplayItems(wr::DisplayListBuilder& aBuilder, + const wr::LayoutRect& aBounds, + const wr::LayoutRect& aClip, wr::ImageRendering aFilter, + const Range<wr::ImageKey>& aImageKeys, + PushDisplayItemFlagSet aFlags) override; + + void DisableExternalTextures() { mUseExternalTextures = false; } + + protected: + bool UseExternalTextures() const { return mUseExternalTextures; } + + BufferDescriptor mDescriptor; + RefPtr<Compositor> mCompositor; + gfx::IntSize mSize; + gfx::SurfaceFormat mFormat; + bool mLocked; + bool mUseExternalTextures; + + class DataTextureSourceYCbCrBasic; +}; + +/** + * TextureHost that wraps shared memory. + * the corresponding texture on the client side is ShmemTextureClient. + * This TextureHost is backend-independent. + */ +class ShmemTextureHost : public BufferTextureHost { + public: + ShmemTextureHost(const mozilla::ipc::Shmem& aShmem, + const BufferDescriptor& aDesc, + ISurfaceAllocator* aDeallocator, TextureFlags aFlags); + + protected: + ~ShmemTextureHost(); + + public: + void DeallocateSharedData() override; + + void ForgetSharedData() override; + + uint8_t* GetBuffer() override; + + size_t GetBufferSize() override; + + const char* Name() override { return "ShmemTextureHost"; } + + void OnShutdown() override; + + protected: + UniquePtr<mozilla::ipc::Shmem> mShmem; + RefPtr<ISurfaceAllocator> mDeallocator; +}; + +/** + * TextureHost that wraps raw memory. + * The corresponding texture on the client side is MemoryTextureClient. + * Can obviously not be used in a cross process setup. + * This TextureHost is backend-independent. + */ +class MemoryTextureHost : public BufferTextureHost { + public: + MemoryTextureHost(uint8_t* aBuffer, const BufferDescriptor& aDesc, + TextureFlags aFlags); + + protected: + ~MemoryTextureHost(); + + public: + void DeallocateSharedData() override; + + void ForgetSharedData() override; + + uint8_t* GetBuffer() override; + + size_t GetBufferSize() override; + + const char* Name() override { return "MemoryTextureHost"; } + + protected: + uint8_t* mBuffer; +}; + +class MOZ_STACK_CLASS AutoLockTextureHostWithoutCompositor { + public: + explicit AutoLockTextureHostWithoutCompositor(TextureHost* aTexture) + : mTexture(aTexture) { + mLocked = mTexture ? mTexture->LockWithoutCompositor() : false; + } + + ~AutoLockTextureHostWithoutCompositor() { + if (mTexture && mLocked) { + mTexture->UnlockWithoutCompositor(); + } + } + + bool Failed() { return mTexture && !mLocked; } + + private: + RefPtr<TextureHost> mTexture; + bool mLocked; +}; + +/** + * This can be used as an offscreen rendering target by the compositor, and + * subsequently can be used as a source by the compositor. + */ +class CompositingRenderTarget : public TextureSource { + public: + explicit CompositingRenderTarget(const gfx::IntPoint& aOrigin) + : mClearOnBind(false), + mOrigin(aOrigin), + mZNear(0), + mZFar(0), + mHasComplexProjection(false), + mEnableDepthBuffer(false) {} + virtual ~CompositingRenderTarget() = default; + + const char* Name() const override { return "CompositingRenderTarget"; } + +#ifdef MOZ_DUMP_PAINTING + virtual already_AddRefed<gfx::DataSourceSurface> Dump( + Compositor* aCompositor) { + return nullptr; + } +#endif + + /** + * Perform a clear when recycling a non opaque surface. + * The clear is deferred to when the render target is bound. + */ + void ClearOnBind() { mClearOnBind = true; } + + const gfx::IntPoint& GetOrigin() const { return mOrigin; } + gfx::IntRect GetRect() { return gfx::IntRect(GetOrigin(), GetSize()); } + + /** + * If a Projection matrix is set, then it is used for rendering to + * this render target instead of generating one. If no explicit + * projection is set, Compositors are expected to generate an + * orthogonal maaping that maps 0..1 to the full size of the render + * target. + */ + bool HasComplexProjection() const { return mHasComplexProjection; } + void ClearProjection() { mHasComplexProjection = false; } + void SetProjection(const gfx::Matrix4x4& aNewMatrix, bool aEnableDepthBuffer, + float aZNear, float aZFar) { + mProjectionMatrix = aNewMatrix; + mEnableDepthBuffer = aEnableDepthBuffer; + mZNear = aZNear; + mZFar = aZFar; + mHasComplexProjection = true; + } + void GetProjection(gfx::Matrix4x4& aMatrix, bool& aEnableDepth, float& aZNear, + float& aZFar) { + MOZ_ASSERT(mHasComplexProjection); + aMatrix = mProjectionMatrix; + aEnableDepth = mEnableDepthBuffer; + aZNear = mZNear; + aZFar = mZFar; + } + + protected: + bool mClearOnBind; + + private: + gfx::IntPoint mOrigin; + + gfx::Matrix4x4 mProjectionMatrix; + float mZNear, mZFar; + bool mHasComplexProjection; + bool mEnableDepthBuffer; +}; + +/** + * Creates a TextureHost that can be used with any of the existing backends + * Not all SurfaceDescriptor types are supported + */ +already_AddRefed<TextureHost> CreateBackendIndependentTextureHost( + const SurfaceDescriptor& aDesc, ISurfaceAllocator* aDeallocator, + LayersBackend aBackend, TextureFlags aFlags); + +} // namespace layers +} // namespace mozilla + +#endif |