summaryrefslogtreecommitdiffstats
path: root/gfx/webrender_bindings
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
commit9e3c08db40b8916968b9f30096c7be3f00ce9647 (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /gfx/webrender_bindings
parentInitial commit. (diff)
downloadthunderbird-upstream.tar.xz
thunderbird-upstream.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/webrender_bindings')
-rw-r--r--gfx/webrender_bindings/Cargo.toml40
-rw-r--r--gfx/webrender_bindings/DCLayerTree.cpp1922
-rw-r--r--gfx/webrender_bindings/DCLayerTree.h461
-rw-r--r--gfx/webrender_bindings/Moz2DImageRenderer.cpp478
-rw-r--r--gfx/webrender_bindings/README.webrender23
-rw-r--r--gfx/webrender_bindings/RenderAndroidHardwareBufferTextureHost.cpp246
-rw-r--r--gfx/webrender_bindings/RenderAndroidHardwareBufferTextureHost.h73
-rw-r--r--gfx/webrender_bindings/RenderAndroidSurfaceTextureHost.cpp325
-rw-r--r--gfx/webrender_bindings/RenderAndroidSurfaceTextureHost.h96
-rw-r--r--gfx/webrender_bindings/RenderBufferTextureHost.cpp254
-rw-r--r--gfx/webrender_bindings/RenderBufferTextureHost.h81
-rw-r--r--gfx/webrender_bindings/RenderCompositor.cpp286
-rw-r--r--gfx/webrender_bindings/RenderCompositor.h222
-rw-r--r--gfx/webrender_bindings/RenderCompositorANGLE.cpp1059
-rw-r--r--gfx/webrender_bindings/RenderCompositorANGLE.h161
-rw-r--r--gfx/webrender_bindings/RenderCompositorD3D11SWGL.cpp484
-rw-r--r--gfx/webrender_bindings/RenderCompositorD3D11SWGL.h118
-rw-r--r--gfx/webrender_bindings/RenderCompositorEGL.cpp328
-rw-r--r--gfx/webrender_bindings/RenderCompositorEGL.h75
-rw-r--r--gfx/webrender_bindings/RenderCompositorLayersSWGL.cpp482
-rw-r--r--gfx/webrender_bindings/RenderCompositorLayersSWGL.h195
-rw-r--r--gfx/webrender_bindings/RenderCompositorNative.cpp672
-rw-r--r--gfx/webrender_bindings/RenderCompositorNative.h233
-rw-r--r--gfx/webrender_bindings/RenderCompositorOGL.cpp126
-rw-r--r--gfx/webrender_bindings/RenderCompositorOGL.h49
-rw-r--r--gfx/webrender_bindings/RenderCompositorOGLSWGL.cpp515
-rw-r--r--gfx/webrender_bindings/RenderCompositorOGLSWGL.h104
-rw-r--r--gfx/webrender_bindings/RenderCompositorRecordedFrame.h49
-rw-r--r--gfx/webrender_bindings/RenderCompositorSWGL.cpp315
-rw-r--r--gfx/webrender_bindings/RenderCompositorSWGL.h90
-rw-r--r--gfx/webrender_bindings/RenderD3D11TextureHost.cpp705
-rw-r--r--gfx/webrender_bindings/RenderD3D11TextureHost.h213
-rw-r--r--gfx/webrender_bindings/RenderDMABUFTextureHost.cpp67
-rw-r--r--gfx/webrender_bindings/RenderDMABUFTextureHost.h46
-rw-r--r--gfx/webrender_bindings/RenderDcompSurfaceTextureHost.cpp50
-rw-r--r--gfx/webrender_bindings/RenderDcompSurfaceTextureHost.h70
-rw-r--r--gfx/webrender_bindings/RenderEGLImageTextureHost.cpp96
-rw-r--r--gfx/webrender_bindings/RenderEGLImageTextureHost.h46
-rw-r--r--gfx/webrender_bindings/RenderExternalTextureHost.cpp265
-rw-r--r--gfx/webrender_bindings/RenderExternalTextureHost.h81
-rw-r--r--gfx/webrender_bindings/RenderMacIOSurfaceTextureHost.cpp157
-rw-r--r--gfx/webrender_bindings/RenderMacIOSurfaceTextureHost.h61
-rw-r--r--gfx/webrender_bindings/RenderSharedSurfaceTextureHost.cpp78
-rw-r--r--gfx/webrender_bindings/RenderSharedSurfaceTextureHost.h57
-rw-r--r--gfx/webrender_bindings/RenderTextureHost.cpp58
-rw-r--r--gfx/webrender_bindings/RenderTextureHost.h135
-rw-r--r--gfx/webrender_bindings/RenderTextureHostSWGL.cpp231
-rw-r--r--gfx/webrender_bindings/RenderTextureHostSWGL.h80
-rw-r--r--gfx/webrender_bindings/RenderTextureHostWrapper.cpp262
-rw-r--r--gfx/webrender_bindings/RenderTextureHostWrapper.h88
-rw-r--r--gfx/webrender_bindings/RenderThread.cpp1648
-rw-r--r--gfx/webrender_bindings/RenderThread.h489
-rw-r--r--gfx/webrender_bindings/RendererOGL.cpp430
-rw-r--r--gfx/webrender_bindings/RendererOGL.h176
-rw-r--r--gfx/webrender_bindings/RendererScreenshotGrabber.cpp104
-rw-r--r--gfx/webrender_bindings/RendererScreenshotGrabber.h101
-rw-r--r--gfx/webrender_bindings/WebRenderAPI.cpp1796
-rw-r--r--gfx/webrender_bindings/WebRenderAPI.h913
-rw-r--r--gfx/webrender_bindings/WebRenderTypes.cpp110
-rw-r--r--gfx/webrender_bindings/WebRenderTypes.h925
-rw-r--r--gfx/webrender_bindings/cbindgen.toml55
-rw-r--r--gfx/webrender_bindings/moz.build131
-rw-r--r--gfx/webrender_bindings/rustfmt.toml16
-rw-r--r--gfx/webrender_bindings/src/bindings.rs4015
-rw-r--r--gfx/webrender_bindings/src/lib.rs43
-rw-r--r--gfx/webrender_bindings/src/moz2d_renderer.rs871
-rw-r--r--gfx/webrender_bindings/src/program_cache.rs355
-rw-r--r--gfx/webrender_bindings/src/swgl_bindings.rs101
-rw-r--r--gfx/webrender_bindings/webrender_ffi.h123
69 files changed, 24810 insertions, 0 deletions
diff --git a/gfx/webrender_bindings/Cargo.toml b/gfx/webrender_bindings/Cargo.toml
new file mode 100644
index 0000000000..69a7eef1c0
--- /dev/null
+++ b/gfx/webrender_bindings/Cargo.toml
@@ -0,0 +1,40 @@
+[package]
+name = "webrender_bindings"
+version = "0.1.0"
+authors = ["The Mozilla Project Developers"]
+license = "MPL-2.0"
+
+[dependencies]
+dirs = "4"
+rayon = "1"
+num_cpus = "1.7.0"
+tracy-rs = "0.1"
+euclid = { version = "0.22.5", features = ["serde"] }
+app_units = "0.7"
+gleam = "0.15"
+log = "0.4"
+nsstring = { path = "../../xpcom/rust/nsstring" }
+bincode = "1.0"
+uuid = { version = "1.0", features = ["v4"] }
+fxhash = "0.2.1"
+thin-vec = { version = "0.2.1", features = ["gecko-ffi"] }
+swgl = { path = "../wr/swgl" }
+wr_malloc_size_of = { path = "../wr/wr_malloc_size_of" }
+gecko-profiler = { path = "../../tools/profiler/rust-api" }
+remove_dir_all = "0.5.3"
+
+[dependencies.webrender]
+path = "../wr/webrender"
+version = "0.62.0"
+default-features = false
+features = ["capture", "serialize_program", "gecko", "sw_compositor"]
+
+[target.'cfg(target_os = "windows")'.dependencies]
+dwrote = "0.11"
+winapi = "0.3"
+
+[target.'cfg(target_os = "macos")'.dependencies]
+core-foundation = "0.9"
+core-graphics = "0.22"
+foreign-types = "0.3.0"
+objc = "0.2"
diff --git a/gfx/webrender_bindings/DCLayerTree.cpp b/gfx/webrender_bindings/DCLayerTree.cpp
new file mode 100644
index 0000000000..0ededcb042
--- /dev/null
+++ b/gfx/webrender_bindings/DCLayerTree.cpp
@@ -0,0 +1,1922 @@
+/* -*- 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 "DCLayerTree.h"
+
+// -
+
+#include "mozilla/gfx/AllOfDcomp.h"
+#include <d3d11.h>
+#include <d3d11_1.h>
+#include <dxgi1_2.h>
+
+// -
+
+#include "gfxWindowsPlatform.h"
+#include "GLContext.h"
+#include "GLContextEGL.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/GPUParent.h"
+#include "mozilla/gfx/Matrix.h"
+#include "mozilla/StaticPrefs_gfx.h"
+#include "mozilla/webrender/RenderD3D11TextureHost.h"
+#include "mozilla/webrender/RenderDcompSurfaceTextureHost.h"
+#include "mozilla/webrender/RenderTextureHost.h"
+#include "mozilla/webrender/RenderThread.h"
+#include "mozilla/WindowsVersion.h"
+#include "mozilla/Telemetry.h"
+#include "nsPrintfCString.h"
+#include "WinUtils.h"
+
+// -
+
+#if defined(__MINGW32__) // 64 defines both 32 and 64
+// We need to fake some things, while we wait on updates to mingw's dcomp.h
+// header. Just enough that we can successfully fail to work there.
+# define MOZ_MINGW_DCOMP_H_INCOMPLETE
+struct IDCompositionColorMatrixEffect : public IDCompositionFilterEffect {};
+struct IDCompositionTableTransferEffect : public IDCompositionFilterEffect {};
+#endif // defined(__MINGW32__)
+
+namespace mozilla {
+namespace wr {
+
+extern LazyLogModule gRenderThreadLog;
+#define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
+
+#define LOG_H(msg, ...) \
+ MOZ_LOG(gDcompSurface, LogLevel::Debug, \
+ ("DCSurfaceHandle=%p, " msg, this, ##__VA_ARGS__))
+
+UniquePtr<GpuOverlayInfo> DCLayerTree::sGpuOverlayInfo;
+
+/* static */
+UniquePtr<DCLayerTree> DCLayerTree::Create(gl::GLContext* aGL,
+ EGLConfig aEGLConfig,
+ ID3D11Device* aDevice,
+ ID3D11DeviceContext* aCtx,
+ HWND aHwnd, nsACString& aError) {
+ RefPtr<IDCompositionDevice2> dCompDevice =
+ gfx::DeviceManagerDx::Get()->GetDirectCompositionDevice();
+ if (!dCompDevice) {
+ aError.Assign("DCLayerTree(no device)"_ns);
+ return nullptr;
+ }
+
+ auto layerTree =
+ MakeUnique<DCLayerTree>(aGL, aEGLConfig, aDevice, aCtx, dCompDevice);
+ if (!layerTree->Initialize(aHwnd, aError)) {
+ return nullptr;
+ }
+
+ return layerTree;
+}
+
+void DCLayerTree::Shutdown() { DCLayerTree::sGpuOverlayInfo = nullptr; }
+
+DCLayerTree::DCLayerTree(gl::GLContext* aGL, EGLConfig aEGLConfig,
+ ID3D11Device* aDevice, ID3D11DeviceContext* aCtx,
+ IDCompositionDevice2* aCompositionDevice)
+ : mGL(aGL),
+ mEGLConfig(aEGLConfig),
+ mDevice(aDevice),
+ mCtx(aCtx),
+ mCompositionDevice(aCompositionDevice),
+ mDebugCounter(false),
+ mDebugVisualRedrawRegions(false),
+ mEGLImage(EGL_NO_IMAGE),
+ mColorRBO(0),
+ mPendingCommit(false) {
+ LOG("DCLayerTree::DCLayerTree()");
+}
+
+DCLayerTree::~DCLayerTree() {
+ LOG("DCLayerTree::~DCLayerTree()");
+
+ ReleaseNativeCompositorResources();
+}
+
+void DCLayerTree::ReleaseNativeCompositorResources() {
+ const auto gl = GetGLContext();
+
+ DestroyEGLSurface();
+
+ // Delete any cached FBO objects
+ for (auto it = mFrameBuffers.begin(); it != mFrameBuffers.end(); ++it) {
+ gl->fDeleteRenderbuffers(1, &it->depthRboId);
+ gl->fDeleteFramebuffers(1, &it->fboId);
+ }
+}
+
+bool DCLayerTree::Initialize(HWND aHwnd, nsACString& aError) {
+ HRESULT hr;
+
+ RefPtr<IDCompositionDesktopDevice> desktopDevice;
+ hr = mCompositionDevice->QueryInterface(
+ (IDCompositionDesktopDevice**)getter_AddRefs(desktopDevice));
+ if (FAILED(hr)) {
+ aError.Assign(nsPrintfCString(
+ "DCLayerTree(get IDCompositionDesktopDevice failed %lx)", hr));
+ return false;
+ }
+
+ hr = desktopDevice->CreateTargetForHwnd(aHwnd, TRUE,
+ getter_AddRefs(mCompositionTarget));
+ if (FAILED(hr)) {
+ aError.Assign(nsPrintfCString(
+ "DCLayerTree(create DCompositionTarget failed %lx)", hr));
+ return false;
+ }
+
+ hr = mCompositionDevice->CreateVisual(getter_AddRefs(mRootVisual));
+ if (FAILED(hr)) {
+ aError.Assign(nsPrintfCString(
+ "DCLayerTree(create root DCompositionVisual failed %lx)", hr));
+ return false;
+ }
+
+ hr =
+ mCompositionDevice->CreateVisual(getter_AddRefs(mDefaultSwapChainVisual));
+ if (FAILED(hr)) {
+ aError.Assign(nsPrintfCString(
+ "DCLayerTree(create swap chain DCompositionVisual failed %lx)", hr));
+ return false;
+ }
+
+ if (gfx::gfxVars::UseWebRenderDCompVideoOverlayWin()) {
+ if (!InitializeVideoOverlaySupport()) {
+ RenderThread::Get()->HandleWebRenderError(WebRenderError::VIDEO_OVERLAY);
+ }
+ }
+ if (!sGpuOverlayInfo) {
+ // Set default if sGpuOverlayInfo was not set.
+ sGpuOverlayInfo = MakeUnique<GpuOverlayInfo>();
+ }
+
+ // Initialize SwapChainInfo
+ SupportsSwapChainTearing();
+
+ mCompositionTarget->SetRoot(mRootVisual);
+ // Set interporation mode to nearest, to ensure 1:1 sampling.
+ // By default, a visual inherits the interpolation mode of the parent visual.
+ // If no visuals set the interpolation mode, the default for the entire visual
+ // tree is nearest neighbor interpolation.
+ mRootVisual->SetBitmapInterpolationMode(
+ DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
+ return true;
+}
+
+bool FlagsSupportsOverlays(UINT flags) {
+ return (flags & (DXGI_OVERLAY_SUPPORT_FLAG_DIRECT |
+ DXGI_OVERLAY_SUPPORT_FLAG_SCALING));
+}
+
+// A warpper of IDXGIOutput4::CheckOverlayColorSpaceSupport()
+bool CheckOverlayColorSpaceSupport(DXGI_FORMAT aDxgiFormat,
+ DXGI_COLOR_SPACE_TYPE aDxgiColorSpace,
+ RefPtr<IDXGIOutput> aOutput,
+ RefPtr<ID3D11Device> aD3d11Device) {
+ UINT colorSpaceSupportFlags = 0;
+ RefPtr<IDXGIOutput4> output4;
+
+ if (FAILED(aOutput->QueryInterface(__uuidof(IDXGIOutput4),
+ getter_AddRefs(output4)))) {
+ return false;
+ }
+
+ if (FAILED(output4->CheckOverlayColorSpaceSupport(
+ aDxgiFormat, aDxgiColorSpace, aD3d11Device,
+ &colorSpaceSupportFlags))) {
+ return false;
+ }
+
+ return (colorSpaceSupportFlags &
+ DXGI_OVERLAY_COLOR_SPACE_SUPPORT_FLAG_PRESENT);
+}
+
+bool DCLayerTree::InitializeVideoOverlaySupport() {
+ MOZ_ASSERT(IsWin10AnniversaryUpdateOrLater());
+
+ HRESULT hr;
+
+ hr = mDevice->QueryInterface(
+ (ID3D11VideoDevice**)getter_AddRefs(mVideoDevice));
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Failed to get D3D11VideoDevice: " << gfx::hexa(hr);
+ return false;
+ }
+
+ hr =
+ mCtx->QueryInterface((ID3D11VideoContext**)getter_AddRefs(mVideoContext));
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Failed to get D3D11VideoContext: " << gfx::hexa(hr);
+ return false;
+ }
+
+ if (sGpuOverlayInfo) {
+ return true;
+ }
+
+ UniquePtr<GpuOverlayInfo> info = MakeUnique<GpuOverlayInfo>();
+
+ RefPtr<IDXGIDevice> dxgiDevice;
+ RefPtr<IDXGIAdapter> adapter;
+ mDevice->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
+ dxgiDevice->GetAdapter(getter_AddRefs(adapter));
+
+ unsigned int i = 0;
+ while (true) {
+ RefPtr<IDXGIOutput> output;
+ if (FAILED(adapter->EnumOutputs(i++, getter_AddRefs(output)))) {
+ break;
+ }
+ RefPtr<IDXGIOutput3> output3;
+ if (FAILED(output->QueryInterface(__uuidof(IDXGIOutput3),
+ getter_AddRefs(output3)))) {
+ break;
+ }
+
+ output3->CheckOverlaySupport(DXGI_FORMAT_NV12, mDevice,
+ &info->mNv12OverlaySupportFlags);
+ output3->CheckOverlaySupport(DXGI_FORMAT_YUY2, mDevice,
+ &info->mYuy2OverlaySupportFlags);
+ output3->CheckOverlaySupport(DXGI_FORMAT_B8G8R8A8_UNORM, mDevice,
+ &info->mBgra8OverlaySupportFlags);
+ output3->CheckOverlaySupport(DXGI_FORMAT_R10G10B10A2_UNORM, mDevice,
+ &info->mRgb10a2OverlaySupportFlags);
+
+ if (FlagsSupportsOverlays(info->mNv12OverlaySupportFlags)) {
+ // NV12 format is preferred if it's supported.
+ info->mOverlayFormatUsed = DXGI_FORMAT_NV12;
+ info->mSupportsHardwareOverlays = true;
+ }
+
+ if (!info->mSupportsHardwareOverlays &&
+ FlagsSupportsOverlays(info->mYuy2OverlaySupportFlags)) {
+ // If NV12 isn't supported, fallback to YUY2 if it's supported.
+ info->mOverlayFormatUsed = DXGI_FORMAT_YUY2;
+ info->mSupportsHardwareOverlays = true;
+ }
+
+ // RGB10A2 overlay is used for displaying HDR content. In Intel's
+ // platform, RGB10A2 overlay is enabled only when
+ // DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 is supported.
+ if (FlagsSupportsOverlays(info->mRgb10a2OverlaySupportFlags)) {
+ if (!CheckOverlayColorSpaceSupport(
+ DXGI_FORMAT_R10G10B10A2_UNORM,
+ DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020, output, mDevice))
+ info->mRgb10a2OverlaySupportFlags = 0;
+ }
+
+ // Early out after the first output that reports overlay support. All
+ // outputs are expected to report the same overlay support according to
+ // Microsoft's WDDM documentation:
+ // https://docs.microsoft.com/en-us/windows-hardware/drivers/display/multiplane-overlay-hardware-requirements
+ if (info->mSupportsHardwareOverlays) {
+ break;
+ }
+ }
+
+ if (!StaticPrefs::gfx_webrender_dcomp_video_yuv_overlay_win_AtStartup()) {
+ info->mOverlayFormatUsed = DXGI_FORMAT_B8G8R8A8_UNORM;
+ info->mSupportsHardwareOverlays = false;
+ }
+
+ info->mSupportsOverlays = info->mSupportsHardwareOverlays;
+
+ sGpuOverlayInfo = std::move(info);
+
+ if (auto* gpuParent = gfx::GPUParent::GetSingleton()) {
+ gpuParent->NotifyOverlayInfo(GetOverlayInfo());
+ }
+
+ return true;
+}
+
+DCSurface* DCLayerTree::GetSurface(wr::NativeSurfaceId aId) const {
+ auto surface_it = mDCSurfaces.find(aId);
+ MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end());
+ return surface_it->second.get();
+}
+
+void DCLayerTree::SetDefaultSwapChain(IDXGISwapChain1* aSwapChain) {
+ LOG("DCLayerTree::SetDefaultSwapChain()");
+
+ mRootVisual->AddVisual(mDefaultSwapChainVisual, TRUE, nullptr);
+ mDefaultSwapChainVisual->SetContent(aSwapChain);
+ // Default SwapChain's visual does not need linear interporation.
+ mDefaultSwapChainVisual->SetBitmapInterpolationMode(
+ DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
+ mPendingCommit = true;
+}
+
+void DCLayerTree::MaybeUpdateDebug() {
+ bool updated = false;
+ updated |= MaybeUpdateDebugCounter();
+ updated |= MaybeUpdateDebugVisualRedrawRegions();
+ if (updated) {
+ mPendingCommit = true;
+ }
+}
+
+void DCLayerTree::MaybeCommit() {
+ if (!mPendingCommit) {
+ return;
+ }
+ mCompositionDevice->Commit();
+}
+
+void DCLayerTree::WaitForCommitCompletion() {
+ mCompositionDevice->WaitForCommitCompletion();
+}
+
+void DCLayerTree::DisableNativeCompositor() {
+ MOZ_ASSERT(mCurrentSurface.isNothing());
+ MOZ_ASSERT(mCurrentLayers.empty());
+
+ ReleaseNativeCompositorResources();
+ mPrevLayers.clear();
+ mRootVisual->RemoveAllVisuals();
+}
+
+bool DCLayerTree::MaybeUpdateDebugCounter() {
+ bool debugCounter = StaticPrefs::gfx_webrender_debug_dcomp_counter();
+ if (mDebugCounter == debugCounter) {
+ return false;
+ }
+
+ RefPtr<IDCompositionDeviceDebug> debugDevice;
+ HRESULT hr = mCompositionDevice->QueryInterface(
+ (IDCompositionDeviceDebug**)getter_AddRefs(debugDevice));
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ if (debugCounter) {
+ debugDevice->EnableDebugCounters();
+ } else {
+ debugDevice->DisableDebugCounters();
+ }
+
+ mDebugCounter = debugCounter;
+ return true;
+}
+
+bool DCLayerTree::MaybeUpdateDebugVisualRedrawRegions() {
+ bool debugVisualRedrawRegions =
+ StaticPrefs::gfx_webrender_debug_dcomp_redraw_regions();
+ if (mDebugVisualRedrawRegions == debugVisualRedrawRegions) {
+ return false;
+ }
+
+ RefPtr<IDCompositionVisualDebug> visualDebug;
+ HRESULT hr = mRootVisual->QueryInterface(
+ (IDCompositionVisualDebug**)getter_AddRefs(visualDebug));
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ if (debugVisualRedrawRegions) {
+ visualDebug->EnableRedrawRegions();
+ } else {
+ visualDebug->DisableRedrawRegions();
+ }
+
+ mDebugVisualRedrawRegions = debugVisualRedrawRegions;
+ return true;
+}
+
+void DCLayerTree::CompositorBeginFrame() { mCurrentFrame++; }
+
+void DCLayerTree::CompositorEndFrame() {
+ auto start = TimeStamp::Now();
+ // Check if the visual tree of surfaces is the same as last frame.
+ bool same = mPrevLayers == mCurrentLayers;
+
+ if (!same) {
+ // If not, we need to rebuild the visual tree. Note that addition or
+ // removal of tiles no longer needs to rebuild the main visual tree
+ // here, since they are added as children of the surface visual.
+ mRootVisual->RemoveAllVisuals();
+ }
+
+ for (auto it = mCurrentLayers.begin(); it != mCurrentLayers.end(); ++it) {
+ auto surface_it = mDCSurfaces.find(*it);
+ MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end());
+ const auto surface = surface_it->second.get();
+ // Ensure surface is trimmed to updated tile valid rects
+ surface->UpdateAllocatedRect();
+ if (!same) {
+ // Add surfaces in z-order they were added to the scene.
+ const auto visual = surface->GetVisual();
+ mRootVisual->AddVisual(visual, false, nullptr);
+ }
+ }
+
+ mPrevLayers.swap(mCurrentLayers);
+ mCurrentLayers.clear();
+
+ mCompositionDevice->Commit();
+
+ auto end = TimeStamp::Now();
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::COMPOSITE_SWAP_TIME,
+ (end - start).ToMilliseconds() * 10.);
+
+ // Remove any framebuffers that haven't been
+ // used in the last 60 frames.
+ //
+ // This should use nsTArray::RemoveElementsBy once
+ // CachedFrameBuffer is able to properly destroy
+ // itself in the destructor.
+ const auto gl = GetGLContext();
+ for (uint32_t i = 0, len = mFrameBuffers.Length(); i < len; ++i) {
+ auto& fb = mFrameBuffers[i];
+ if ((mCurrentFrame - fb.lastFrameUsed) > 60) {
+ gl->fDeleteRenderbuffers(1, &fb.depthRboId);
+ gl->fDeleteFramebuffers(1, &fb.fboId);
+ mFrameBuffers.UnorderedRemoveElementAt(i);
+ --i; // Examine the element again, if necessary.
+ --len;
+ }
+ }
+}
+
+void DCLayerTree::Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset,
+ uint32_t* aFboId, wr::DeviceIntRect aDirtyRect,
+ wr::DeviceIntRect aValidRect) {
+ auto surface = GetSurface(aId.surface_id);
+ auto tile = surface->GetTile(aId.x, aId.y);
+ wr::DeviceIntPoint targetOffset{0, 0};
+
+ // If tile owns an IDCompositionSurface we use it, otherwise we're using an
+ // IDCompositionVirtualSurface owned by the DCSurface.
+ RefPtr<IDCompositionSurface> compositionSurface;
+ if (surface->mIsVirtualSurface) {
+ gfx::IntRect validRect(aValidRect.min.x, aValidRect.min.y,
+ aValidRect.width(), aValidRect.height());
+ if (!tile->mValidRect.IsEqualEdges(validRect)) {
+ tile->mValidRect = validRect;
+ surface->DirtyAllocatedRect();
+ }
+ wr::DeviceIntSize tileSize = surface->GetTileSize();
+ compositionSurface = surface->GetCompositionSurface();
+ wr::DeviceIntPoint virtualOffset = surface->GetVirtualOffset();
+ targetOffset.x = virtualOffset.x + tileSize.width * aId.x;
+ targetOffset.y = virtualOffset.y + tileSize.height * aId.y;
+ } else {
+ compositionSurface = tile->Bind(aValidRect);
+ }
+
+ if (tile->mNeedsFullDraw) {
+ // dcomp requires that the first BeginDraw on a non-virtual surface is the
+ // full size of the pixel buffer.
+ auto tileSize = surface->GetTileSize();
+ aDirtyRect.min.x = 0;
+ aDirtyRect.min.y = 0;
+ aDirtyRect.max.x = tileSize.width;
+ aDirtyRect.max.y = tileSize.height;
+ tile->mNeedsFullDraw = false;
+ }
+
+ *aFboId = CreateEGLSurfaceForCompositionSurface(
+ aDirtyRect, aOffset, compositionSurface, targetOffset);
+ mCurrentSurface = Some(compositionSurface);
+}
+
+void DCLayerTree::Unbind() {
+ if (mCurrentSurface.isNothing()) {
+ return;
+ }
+
+ RefPtr<IDCompositionSurface> surface = mCurrentSurface.ref();
+ surface->EndDraw();
+
+ DestroyEGLSurface();
+ mCurrentSurface = Nothing();
+}
+
+void DCLayerTree::CreateSurface(wr::NativeSurfaceId aId,
+ wr::DeviceIntPoint aVirtualOffset,
+ wr::DeviceIntSize aTileSize, bool aIsOpaque) {
+ auto it = mDCSurfaces.find(aId);
+ MOZ_RELEASE_ASSERT(it == mDCSurfaces.end());
+ if (it != mDCSurfaces.end()) {
+ // DCSurface already exists.
+ return;
+ }
+
+ // Tile size needs to be positive.
+ if (aTileSize.width <= 0 || aTileSize.height <= 0) {
+ gfxCriticalNote << "TileSize is not positive aId: " << wr::AsUint64(aId)
+ << " aTileSize(" << aTileSize.width << ","
+ << aTileSize.height << ")";
+ }
+
+ bool isVirtualSurface =
+ StaticPrefs::gfx_webrender_dcomp_use_virtual_surfaces_AtStartup();
+ auto surface = MakeUnique<DCSurface>(aTileSize, aVirtualOffset,
+ isVirtualSurface, aIsOpaque, this);
+ if (!surface->Initialize()) {
+ gfxCriticalNote << "Failed to initialize DCSurface: " << wr::AsUint64(aId);
+ return;
+ }
+
+ mDCSurfaces[aId] = std::move(surface);
+}
+
+void DCLayerTree::CreateExternalSurface(wr::NativeSurfaceId aId,
+ bool aIsOpaque) {
+ auto it = mDCSurfaces.find(aId);
+ MOZ_RELEASE_ASSERT(it == mDCSurfaces.end());
+
+ auto surface = MakeUnique<DCExternalSurfaceWrapper>(aIsOpaque, this);
+ if (!surface->Initialize()) {
+ gfxCriticalNote << "Failed to initialize DCExternalSurfaceWrapper: "
+ << wr::AsUint64(aId);
+ return;
+ }
+
+ mDCSurfaces[aId] = std::move(surface);
+}
+
+void DCLayerTree::DestroySurface(NativeSurfaceId aId) {
+ auto surface_it = mDCSurfaces.find(aId);
+ MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end());
+ auto surface = surface_it->second.get();
+
+ mRootVisual->RemoveVisual(surface->GetVisual());
+ mDCSurfaces.erase(surface_it);
+}
+
+void DCLayerTree::CreateTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY) {
+ auto surface = GetSurface(aId);
+ surface->CreateTile(aX, aY);
+}
+
+void DCLayerTree::DestroyTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY) {
+ auto surface = GetSurface(aId);
+ surface->DestroyTile(aX, aY);
+}
+
+void DCLayerTree::AttachExternalImage(wr::NativeSurfaceId aId,
+ wr::ExternalImageId aExternalImage) {
+ auto surface_it = mDCSurfaces.find(aId);
+ MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end());
+ surface_it->second->AttachExternalImage(aExternalImage);
+}
+
+void DCExternalSurfaceWrapper::AttachExternalImage(
+ wr::ExternalImageId aExternalImage) {
+ if (auto* surface = EnsureSurfaceForExternalImage(aExternalImage)) {
+ surface->AttachExternalImage(aExternalImage);
+ }
+}
+
+template <class ToT>
+struct QI {
+ template <class FromT>
+ [[nodiscard]] static inline RefPtr<ToT> From(FromT* const from) {
+ RefPtr<ToT> to;
+ (void)from->QueryInterface(static_cast<ToT**>(getter_AddRefs(to)));
+ return to;
+ }
+};
+
+DCSurface* DCExternalSurfaceWrapper::EnsureSurfaceForExternalImage(
+ wr::ExternalImageId aExternalImage) {
+ if (mSurface) {
+ return mSurface.get();
+ }
+
+ // Create a new surface based on the texture type.
+ RenderTextureHost* texture =
+ RenderThread::Get()->GetRenderTexture(aExternalImage);
+ if (texture && texture->AsRenderDXGITextureHost()) {
+ mSurface.reset(new DCSurfaceVideo(mIsOpaque, mDCLayerTree));
+ if (!mSurface->Initialize()) {
+ gfxCriticalNote << "Failed to initialize DCSurfaceVideo: "
+ << wr::AsUint64(aExternalImage);
+ mSurface = nullptr;
+ }
+ } else if (texture && texture->AsRenderDcompSurfaceTextureHost()) {
+ mSurface.reset(new DCSurfaceHandle(mIsOpaque, mDCLayerTree));
+ if (!mSurface->Initialize()) {
+ gfxCriticalNote << "Failed to initialize DCSurfaceHandle: "
+ << wr::AsUint64(aExternalImage);
+ mSurface = nullptr;
+ }
+ }
+ if (!mSurface) {
+ gfxCriticalNote << "Failed to create a surface for external image: "
+ << gfx::hexa(texture);
+ return nullptr;
+ }
+
+ // Add surface's visual which will contain video data to our root visual.
+ const auto surfaceVisual = mSurface->GetVisual();
+ mVisual->AddVisual(surfaceVisual, true, nullptr);
+
+ // -
+ // Apply color management.
+
+ [&]() {
+ const auto cmsMode = GfxColorManagementMode();
+ if (cmsMode == CMSMode::Off) return;
+
+ const auto dcomp = mDCLayerTree->GetCompositionDevice();
+ const auto dcomp3 = QI<IDCompositionDevice3>::From(dcomp);
+ if (!dcomp3) {
+ NS_WARNING(
+ "No IDCompositionDevice3, cannot use dcomp for color management.");
+ return;
+ }
+
+ // -
+
+ const auto cspace = [&]() {
+ const auto rangedCspace = texture->GetYUVColorSpace();
+ const auto info = FromYUVRangedColorSpace(rangedCspace);
+ auto ret = ToColorSpace2(info.space);
+ if (ret == gfx::ColorSpace2::Display && cmsMode == CMSMode::All) {
+ ret = gfx::ColorSpace2::SRGB;
+ }
+ return ret;
+ }();
+
+ const bool rec709GammaAsSrgb =
+ StaticPrefs::gfx_color_management_rec709_gamma_as_srgb();
+ const bool rec2020GammaAsRec709 =
+ StaticPrefs::gfx_color_management_rec2020_gamma_as_rec709();
+
+ auto cspaceDesc = color::ColorspaceDesc{};
+ switch (cspace) {
+ case gfx::ColorSpace2::Display:
+ return; // No color management needed!
+ case gfx::ColorSpace2::SRGB:
+ cspaceDesc.chrom = color::Chromaticities::Srgb();
+ cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb();
+ break;
+
+ case gfx::ColorSpace2::DISPLAY_P3:
+ cspaceDesc.chrom = color::Chromaticities::DisplayP3();
+ cspaceDesc.tf = color::PiecewiseGammaDesc::DisplayP3();
+ break;
+
+ case gfx::ColorSpace2::BT601_525:
+ cspaceDesc.chrom = color::Chromaticities::Rec601_525_Ntsc();
+ if (rec709GammaAsSrgb) {
+ cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb();
+ } else {
+ cspaceDesc.tf = color::PiecewiseGammaDesc::Rec709();
+ }
+ break;
+
+ case gfx::ColorSpace2::BT709:
+ cspaceDesc.chrom = color::Chromaticities::Rec709();
+ if (rec709GammaAsSrgb) {
+ cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb();
+ } else {
+ cspaceDesc.tf = color::PiecewiseGammaDesc::Rec709();
+ }
+ break;
+
+ case gfx::ColorSpace2::BT2020:
+ cspaceDesc.chrom = color::Chromaticities::Rec2020();
+ if (rec2020GammaAsRec709 && rec709GammaAsSrgb) {
+ cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb();
+ } else if (rec2020GammaAsRec709) {
+ cspaceDesc.tf = color::PiecewiseGammaDesc::Rec709();
+ } else {
+ // Just Rec709 with slightly more precision.
+ cspaceDesc.tf = color::PiecewiseGammaDesc::Rec2020_12bit();
+ }
+ break;
+ }
+
+ const auto cprofileIn = color::ColorProfileDesc::From(cspaceDesc);
+ auto cprofileOut = mDCLayerTree->OutputColorProfile();
+ bool pretendSrgb = true;
+ if (pretendSrgb) {
+ cprofileOut = color::ColorProfileDesc::From({
+ color::Chromaticities::Srgb(),
+ color::PiecewiseGammaDesc::Srgb(),
+ });
+ }
+ const auto conversion = color::ColorProfileConversionDesc::From({
+ .src = cprofileIn,
+ .dst = cprofileOut,
+ });
+
+ // -
+
+ auto chain = ColorManagementChain::From(*dcomp3, conversion);
+ mCManageChain = Some(chain);
+
+ surfaceVisual->SetEffect(mCManageChain->last.get());
+ }();
+
+ return mSurface.get();
+}
+
+void DCExternalSurfaceWrapper::PresentExternalSurface(gfx::Matrix& aTransform) {
+ MOZ_ASSERT(mSurface);
+ if (auto* surface = mSurface->AsDCSurfaceVideo()) {
+ if (surface->CalculateSwapChainSize(aTransform)) {
+ surface->PresentVideo();
+ }
+ } else if (auto* surface = mSurface->AsDCSurfaceHandle()) {
+ surface->PresentSurfaceHandle();
+ }
+}
+
+template <typename T>
+static inline D2D1_RECT_F D2DRect(const T& aRect) {
+ return D2D1::RectF(aRect.X(), aRect.Y(), aRect.XMost(), aRect.YMost());
+}
+
+static inline D2D1_MATRIX_3X2_F D2DMatrix(const gfx::Matrix& aTransform) {
+ return D2D1::Matrix3x2F(aTransform._11, aTransform._12, aTransform._21,
+ aTransform._22, aTransform._31, aTransform._32);
+}
+
+void DCLayerTree::AddSurface(wr::NativeSurfaceId aId,
+ const wr::CompositorSurfaceTransform& aTransform,
+ wr::DeviceIntRect aClipRect,
+ wr::ImageRendering aImageRendering) {
+ auto it = mDCSurfaces.find(aId);
+ MOZ_RELEASE_ASSERT(it != mDCSurfaces.end());
+ const auto surface = it->second.get();
+ const auto visual = surface->GetVisual();
+
+ wr::DeviceIntPoint virtualOffset = surface->GetVirtualOffset();
+
+ float sx = aTransform.scale.x;
+ float sy = aTransform.scale.y;
+ float tx = aTransform.offset.x;
+ float ty = aTransform.offset.y;
+ gfx::Matrix transform(sx, 0.0, 0.0, sy, tx, ty);
+
+ surface->PresentExternalSurface(transform);
+
+ transform.PreTranslate(-virtualOffset.x, -virtualOffset.y);
+
+ // The DirectComposition API applies clipping *before* any
+ // transforms/offset, whereas we want the clip applied after. Right now, we
+ // only support rectilinear transforms, and then we transform our clip into
+ // pre-transform coordinate space for it to be applied there.
+ // DirectComposition does have an option for pre-transform clipping, if you
+ // create an explicit IDCompositionEffectGroup object and set a 3D transform
+ // on that. I suspect that will perform worse though, so we should only do
+ // that for complex transforms (which are never provided right now).
+ MOZ_ASSERT(transform.IsRectilinear());
+ gfx::Rect clip = transform.Inverse().TransformBounds(gfx::Rect(
+ aClipRect.min.x, aClipRect.min.y, aClipRect.width(), aClipRect.height()));
+ // Set the clip rect - converting from world space to the pre-offset space
+ // that DC requires for rectangle clips.
+ visual->SetClip(D2DRect(clip));
+
+ // TODO: The input matrix is a 4x4, but we only support a 3x2 at
+ // the D3D API level (unless we QI to IDCompositionVisual3, which might
+ // not be available?).
+ // Should we assert here, or restrict at the WR API level.
+ visual->SetTransform(D2DMatrix(transform));
+
+ if (aImageRendering == wr::ImageRendering::Auto) {
+ visual->SetBitmapInterpolationMode(
+ DCOMPOSITION_BITMAP_INTERPOLATION_MODE_LINEAR);
+ } else {
+ visual->SetBitmapInterpolationMode(
+ DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
+ }
+
+ mCurrentLayers.push_back(aId);
+}
+
+GLuint DCLayerTree::GetOrCreateFbo(int aWidth, int aHeight) {
+ const auto gl = GetGLContext();
+ GLuint fboId = 0;
+
+ // Check if we have a cached FBO with matching dimensions
+ for (auto it = mFrameBuffers.begin(); it != mFrameBuffers.end(); ++it) {
+ if (it->width == aWidth && it->height == aHeight) {
+ fboId = it->fboId;
+ it->lastFrameUsed = mCurrentFrame;
+ break;
+ }
+ }
+
+ // If not, create a new FBO with attached depth buffer
+ if (fboId == 0) {
+ // Create the depth buffer
+ GLuint depthRboId;
+ gl->fGenRenderbuffers(1, &depthRboId);
+ gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, depthRboId);
+ gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, LOCAL_GL_DEPTH_COMPONENT24,
+ aWidth, aHeight);
+
+ // Create the framebuffer and attach the depth buffer to it
+ gl->fGenFramebuffers(1, &fboId);
+ gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, fboId);
+ gl->fFramebufferRenderbuffer(LOCAL_GL_DRAW_FRAMEBUFFER,
+ LOCAL_GL_DEPTH_ATTACHMENT,
+ LOCAL_GL_RENDERBUFFER, depthRboId);
+
+ // Store this in the cache for future calls.
+ // TODO(gw): Maybe we should periodically scan this list and remove old
+ // entries that
+ // haven't been used for some time?
+ DCLayerTree::CachedFrameBuffer frame_buffer_info;
+ frame_buffer_info.width = aWidth;
+ frame_buffer_info.height = aHeight;
+ frame_buffer_info.fboId = fboId;
+ frame_buffer_info.depthRboId = depthRboId;
+ frame_buffer_info.lastFrameUsed = mCurrentFrame;
+ mFrameBuffers.AppendElement(frame_buffer_info);
+ }
+
+ return fboId;
+}
+
+bool DCLayerTree::EnsureVideoProcessor(const gfx::IntSize& aInputSize,
+ const gfx::IntSize& aOutputSize) {
+ HRESULT hr;
+
+ if (!mVideoDevice || !mVideoContext) {
+ return false;
+ }
+
+ if (mVideoProcessor && (aInputSize <= mVideoInputSize) &&
+ (aOutputSize <= mVideoOutputSize)) {
+ return true;
+ }
+
+ mVideoProcessor = nullptr;
+ mVideoProcessorEnumerator = nullptr;
+
+ D3D11_VIDEO_PROCESSOR_CONTENT_DESC desc = {};
+ desc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
+ desc.InputFrameRate.Numerator = 60;
+ desc.InputFrameRate.Denominator = 1;
+ desc.InputWidth = aInputSize.width;
+ desc.InputHeight = aInputSize.height;
+ desc.OutputFrameRate.Numerator = 60;
+ desc.OutputFrameRate.Denominator = 1;
+ desc.OutputWidth = aOutputSize.width;
+ desc.OutputHeight = aOutputSize.height;
+ desc.Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL;
+
+ hr = mVideoDevice->CreateVideoProcessorEnumerator(
+ &desc, getter_AddRefs(mVideoProcessorEnumerator));
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Failed to create VideoProcessorEnumerator: "
+ << gfx::hexa(hr);
+ return false;
+ }
+
+ hr = mVideoDevice->CreateVideoProcessor(mVideoProcessorEnumerator, 0,
+ getter_AddRefs(mVideoProcessor));
+ if (FAILED(hr)) {
+ mVideoProcessor = nullptr;
+ mVideoProcessorEnumerator = nullptr;
+ gfxCriticalNote << "Failed to create VideoProcessor: " << gfx::hexa(hr);
+ return false;
+ }
+
+ // Reduce power cosumption
+ // By default, the driver might perform certain processing tasks
+ // automatically
+ mVideoContext->VideoProcessorSetStreamAutoProcessingMode(mVideoProcessor, 0,
+ FALSE);
+
+ mVideoInputSize = aInputSize;
+ mVideoOutputSize = aOutputSize;
+
+ return true;
+}
+
+bool DCLayerTree::SupportsHardwareOverlays() {
+ return sGpuOverlayInfo->mSupportsHardwareOverlays;
+}
+
+bool DCLayerTree::SupportsSwapChainTearing() {
+ RefPtr<ID3D11Device> device = mDevice;
+ static const bool supported = [device] {
+ RefPtr<IDXGIDevice> dxgiDevice;
+ RefPtr<IDXGIAdapter> adapter;
+ device->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
+ dxgiDevice->GetAdapter(getter_AddRefs(adapter));
+
+ RefPtr<IDXGIFactory5> dxgiFactory;
+ HRESULT hr = adapter->GetParent(
+ IID_PPV_ARGS((IDXGIFactory5**)getter_AddRefs(dxgiFactory)));
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ BOOL presentAllowTearing = FALSE;
+ hr = dxgiFactory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING,
+ &presentAllowTearing,
+ sizeof(presentAllowTearing));
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ if (auto* gpuParent = gfx::GPUParent::GetSingleton()) {
+ gpuParent->NotifySwapChainInfo(
+ layers::SwapChainInfo(!!presentAllowTearing));
+ } else if (XRE_IsParentProcess()) {
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+ }
+ return !!presentAllowTearing;
+ }();
+ return supported;
+}
+
+DXGI_FORMAT DCLayerTree::GetOverlayFormatForSDR() {
+ return sGpuOverlayInfo->mOverlayFormatUsed;
+}
+
+static layers::OverlaySupportType FlagsToOverlaySupportType(
+ UINT aFlags, bool aSoftwareOverlaySupported) {
+ if (aFlags & DXGI_OVERLAY_SUPPORT_FLAG_SCALING) {
+ return layers::OverlaySupportType::Scaling;
+ }
+ if (aFlags & DXGI_OVERLAY_SUPPORT_FLAG_DIRECT) {
+ return layers::OverlaySupportType::Direct;
+ }
+ if (aSoftwareOverlaySupported) {
+ return layers::OverlaySupportType::Software;
+ }
+ return layers::OverlaySupportType::None;
+}
+
+layers::OverlayInfo DCLayerTree::GetOverlayInfo() {
+ layers::OverlayInfo info;
+
+ info.mSupportsOverlays = sGpuOverlayInfo->mSupportsHardwareOverlays;
+ info.mNv12Overlay =
+ FlagsToOverlaySupportType(sGpuOverlayInfo->mNv12OverlaySupportFlags,
+ /* aSoftwareOverlaySupported */ false);
+ info.mYuy2Overlay =
+ FlagsToOverlaySupportType(sGpuOverlayInfo->mYuy2OverlaySupportFlags,
+ /* aSoftwareOverlaySupported */ false);
+ info.mBgra8Overlay =
+ FlagsToOverlaySupportType(sGpuOverlayInfo->mBgra8OverlaySupportFlags,
+ /* aSoftwareOverlaySupported */ true);
+ info.mRgb10a2Overlay =
+ FlagsToOverlaySupportType(sGpuOverlayInfo->mRgb10a2OverlaySupportFlags,
+ /* aSoftwareOverlaySupported */ false);
+
+ return info;
+}
+
+DCSurface::DCSurface(wr::DeviceIntSize aTileSize,
+ wr::DeviceIntPoint aVirtualOffset, bool aIsVirtualSurface,
+ bool aIsOpaque, DCLayerTree* aDCLayerTree)
+ : mIsVirtualSurface(aIsVirtualSurface),
+ mDCLayerTree(aDCLayerTree),
+ mTileSize(aTileSize),
+ mIsOpaque(aIsOpaque),
+ mAllocatedRectDirty(true),
+ mVirtualOffset(aVirtualOffset) {}
+
+DCSurface::~DCSurface() {}
+
+bool DCSurface::Initialize() {
+ // Create a visual for tiles to attach to, whether virtual or not.
+ HRESULT hr;
+ const auto dCompDevice = mDCLayerTree->GetCompositionDevice();
+ hr = dCompDevice->CreateVisual(getter_AddRefs(mVisual));
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Failed to create DCompositionVisual: " << gfx::hexa(hr);
+ return false;
+ }
+
+ // If virtual surface is enabled, create and attach to visual, in this case
+ // the tiles won't own visuals or surfaces.
+ if (mIsVirtualSurface) {
+ DXGI_ALPHA_MODE alpha_mode =
+ mIsOpaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED;
+
+ hr = dCompDevice->CreateVirtualSurface(
+ VIRTUAL_SURFACE_SIZE, VIRTUAL_SURFACE_SIZE, DXGI_FORMAT_R8G8B8A8_UNORM,
+ alpha_mode, getter_AddRefs(mVirtualSurface));
+ MOZ_ASSERT(SUCCEEDED(hr));
+
+ // Bind the surface memory to this visual
+ hr = mVisual->SetContent(mVirtualSurface);
+ MOZ_ASSERT(SUCCEEDED(hr));
+ }
+
+ return true;
+}
+
+void DCSurface::CreateTile(int32_t aX, int32_t aY) {
+ TileKey key(aX, aY);
+ MOZ_RELEASE_ASSERT(mDCTiles.find(key) == mDCTiles.end());
+
+ auto tile = MakeUnique<DCTile>(mDCLayerTree);
+ if (!tile->Initialize(aX, aY, mTileSize, mIsVirtualSurface, mIsOpaque,
+ mVisual)) {
+ gfxCriticalNote << "Failed to initialize DCTile: " << aX << aY;
+ return;
+ }
+
+ if (mIsVirtualSurface) {
+ mAllocatedRectDirty = true;
+ } else {
+ mVisual->AddVisual(tile->GetVisual(), false, nullptr);
+ }
+
+ mDCTiles[key] = std::move(tile);
+}
+
+void DCSurface::DestroyTile(int32_t aX, int32_t aY) {
+ TileKey key(aX, aY);
+ if (mIsVirtualSurface) {
+ mAllocatedRectDirty = true;
+ } else {
+ auto tile = GetTile(aX, aY);
+ mVisual->RemoveVisual(tile->GetVisual());
+ }
+ mDCTiles.erase(key);
+}
+
+void DCSurface::DirtyAllocatedRect() { mAllocatedRectDirty = true; }
+
+void DCSurface::UpdateAllocatedRect() {
+ if (mAllocatedRectDirty) {
+ if (mVirtualSurface) {
+ // The virtual surface may have holes in it (for example, an empty tile
+ // that has no primitives). Instead of trimming to a single bounding
+ // rect, supply the rect of each valid tile to handle this case.
+ std::vector<RECT> validRects;
+
+ for (auto it = mDCTiles.begin(); it != mDCTiles.end(); ++it) {
+ auto tile = GetTile(it->first.mX, it->first.mY);
+ RECT rect;
+
+ rect.left = (LONG)(mVirtualOffset.x + it->first.mX * mTileSize.width +
+ tile->mValidRect.x);
+ rect.top = (LONG)(mVirtualOffset.y + it->first.mY * mTileSize.height +
+ tile->mValidRect.y);
+ rect.right = rect.left + tile->mValidRect.width;
+ rect.bottom = rect.top + tile->mValidRect.height;
+
+ validRects.push_back(rect);
+ }
+
+ mVirtualSurface->Trim(validRects.data(), validRects.size());
+ }
+ // When not using a virtual surface, we still want to reset this
+ mAllocatedRectDirty = false;
+ }
+}
+
+DCTile* DCSurface::GetTile(int32_t aX, int32_t aY) const {
+ TileKey key(aX, aY);
+ auto tile_it = mDCTiles.find(key);
+ MOZ_RELEASE_ASSERT(tile_it != mDCTiles.end());
+ return tile_it->second.get();
+}
+
+DCSurfaceVideo::DCSurfaceVideo(bool aIsOpaque, DCLayerTree* aDCLayerTree)
+ : DCSurface(wr::DeviceIntSize{}, wr::DeviceIntPoint{}, false, aIsOpaque,
+ aDCLayerTree) {}
+
+DCSurfaceVideo::~DCSurfaceVideo() {
+ ReleaseDecodeSwapChainResources();
+ MOZ_ASSERT(!mSwapChainSurfaceHandle);
+}
+
+bool IsYUVSwapChainFormat(DXGI_FORMAT aFormat) {
+ if (aFormat == DXGI_FORMAT_NV12 || aFormat == DXGI_FORMAT_YUY2) {
+ return true;
+ }
+ return false;
+}
+
+void DCSurfaceVideo::AttachExternalImage(wr::ExternalImageId aExternalImage) {
+ RenderTextureHost* texture =
+ RenderThread::Get()->GetRenderTexture(aExternalImage);
+ MOZ_RELEASE_ASSERT(texture);
+
+ if (mPrevTexture == texture) {
+ return;
+ }
+
+ // XXX if software decoded video frame format is nv12, it could be used as
+ // video overlay.
+ if (!texture || !texture->AsRenderDXGITextureHost() ||
+ texture->GetFormat() != gfx::SurfaceFormat::NV12) {
+ gfxCriticalNote << "Unsupported RenderTexture for overlay: "
+ << gfx::hexa(texture);
+ return;
+ }
+
+ mRenderTextureHost = texture;
+}
+
+bool DCSurfaceVideo::CalculateSwapChainSize(gfx::Matrix& aTransform) {
+ if (!mRenderTextureHost) {
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+ return false;
+ }
+
+ mVideoSize = mRenderTextureHost->AsRenderDXGITextureHost()->GetSize(0);
+
+ // When RenderTextureHost, swapChainSize or VideoSwapChain are updated,
+ // DCSurfaceVideo::PresentVideo() needs to be called.
+ bool needsToPresent = mPrevTexture != mRenderTextureHost;
+ gfx::IntSize swapChainSize = mVideoSize;
+ gfx::Matrix transform = aTransform;
+
+ // When video is rendered to axis aligned integer rectangle, video scaling
+ // could be done by VideoProcessor
+ bool scaleVideoAtVideoProcessor = false;
+ if (StaticPrefs::gfx_webrender_dcomp_video_vp_scaling_win_AtStartup() &&
+ aTransform.PreservesAxisAlignedRectangles()) {
+ gfx::Size scaledSize = gfx::Size(mVideoSize) * aTransform.ScaleFactors();
+ gfx::IntSize size(int32_t(std::round(scaledSize.width)),
+ int32_t(std::round(scaledSize.height)));
+ if (gfx::FuzzyEqual(scaledSize.width, size.width, 0.1f) &&
+ gfx::FuzzyEqual(scaledSize.height, size.height, 0.1f)) {
+ scaleVideoAtVideoProcessor = true;
+ swapChainSize = size;
+ }
+ }
+
+ if (scaleVideoAtVideoProcessor) {
+ // 4:2:2 subsampled formats like YUY2 must have an even width, and 4:2:0
+ // subsampled formats like NV12 must have an even width and height.
+ if (swapChainSize.width % 2 == 1) {
+ swapChainSize.width += 1;
+ }
+ if (swapChainSize.height % 2 == 1) {
+ swapChainSize.height += 1;
+ }
+ transform = gfx::Matrix::Translation(aTransform.GetTranslation());
+ }
+
+ if (!mVideoSwapChain || mSwapChainSize != swapChainSize) {
+ needsToPresent = true;
+ ReleaseDecodeSwapChainResources();
+ // Update mSwapChainSize before creating SwapChain
+ mSwapChainSize = swapChainSize;
+
+ auto swapChainFormat = GetSwapChainFormat();
+ bool useYUVSwapChain = IsYUVSwapChainFormat(swapChainFormat);
+ if (useYUVSwapChain) {
+ // Tries to create YUV SwapChain
+ CreateVideoSwapChain();
+ if (!mVideoSwapChain) {
+ mFailedYuvSwapChain = true;
+ ReleaseDecodeSwapChainResources();
+
+ gfxCriticalNote << "Fallback to RGB SwapChain";
+ }
+ }
+ // Tries to create RGB SwapChain
+ if (!mVideoSwapChain) {
+ CreateVideoSwapChain();
+ }
+ }
+
+ aTransform = transform;
+
+ return needsToPresent;
+}
+
+void DCSurfaceVideo::PresentVideo() {
+ if (!mRenderTextureHost) {
+ return;
+ }
+
+ if (!mVideoSwapChain) {
+ gfxCriticalNote << "Failed to create VideoSwapChain";
+ RenderThread::Get()->NotifyWebRenderError(
+ wr::WebRenderError::VIDEO_OVERLAY);
+ return;
+ }
+
+ mVisual->SetContent(mVideoSwapChain);
+
+ if (!CallVideoProcessorBlt()) {
+ auto swapChainFormat = GetSwapChainFormat();
+ bool useYUVSwapChain = IsYUVSwapChainFormat(swapChainFormat);
+ if (useYUVSwapChain) {
+ mFailedYuvSwapChain = true;
+ ReleaseDecodeSwapChainResources();
+ return;
+ }
+ RenderThread::Get()->NotifyWebRenderError(
+ wr::WebRenderError::VIDEO_OVERLAY);
+ return;
+ }
+
+ auto start = TimeStamp::Now();
+ HRESULT hr = mVideoSwapChain->Present(0, 0);
+ auto end = TimeStamp::Now();
+
+ if (FAILED(hr) && hr != DXGI_STATUS_OCCLUDED) {
+ gfxCriticalNoteOnce << "video Present failed: " << gfx::hexa(hr);
+ }
+
+ mPrevTexture = mRenderTextureHost;
+
+ // Disable video overlay if mVideoSwapChain->Present() is too slow. It drops
+ // fps.
+
+ if (!StaticPrefs::gfx_webrender_dcomp_video_check_slow_present()) {
+ return;
+ }
+
+ const auto maxWaitDurationMs = 2.0;
+ const auto maxSlowPresentCount = 5;
+ const auto duration = (end - start).ToMilliseconds();
+
+ if (duration > maxWaitDurationMs) {
+ mSlowPresentCount++;
+ } else {
+ mSlowPresentCount = 0;
+ }
+
+ if (mSlowPresentCount > maxSlowPresentCount) {
+ gfxCriticalNoteOnce << "Video swapchain present is slow";
+ RenderThread::Get()->HandleWebRenderError(WebRenderError::VIDEO_OVERLAY);
+ }
+}
+
+DXGI_FORMAT DCSurfaceVideo::GetSwapChainFormat() {
+ if (mFailedYuvSwapChain || !mDCLayerTree->SupportsHardwareOverlays()) {
+ return DXGI_FORMAT_B8G8R8A8_UNORM;
+ }
+ return mDCLayerTree->GetOverlayFormatForSDR();
+}
+
+bool DCSurfaceVideo::CreateVideoSwapChain() {
+ MOZ_ASSERT(mRenderTextureHost);
+
+ const auto device = mDCLayerTree->GetDevice();
+
+ RefPtr<IDXGIDevice> dxgiDevice;
+ device->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
+
+ RefPtr<IDXGIFactoryMedia> dxgiFactoryMedia;
+ {
+ RefPtr<IDXGIAdapter> adapter;
+ dxgiDevice->GetAdapter(getter_AddRefs(adapter));
+ adapter->GetParent(
+ IID_PPV_ARGS((IDXGIFactoryMedia**)getter_AddRefs(dxgiFactoryMedia)));
+ }
+
+ mSwapChainSurfaceHandle = gfx::DeviceManagerDx::CreateDCompSurfaceHandle();
+ if (!mSwapChainSurfaceHandle) {
+ gfxCriticalNote << "Failed to create DCompSurfaceHandle";
+ return false;
+ }
+
+ auto swapChainFormat = GetSwapChainFormat();
+
+ DXGI_SWAP_CHAIN_DESC1 desc = {};
+ desc.Width = mSwapChainSize.width;
+ desc.Height = mSwapChainSize.height;
+ desc.Format = swapChainFormat;
+ desc.Stereo = FALSE;
+ desc.SampleDesc.Count = 1;
+ desc.BufferCount = 2;
+ desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ desc.Scaling = DXGI_SCALING_STRETCH;
+ desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
+ desc.Flags = DXGI_SWAP_CHAIN_FLAG_FULLSCREEN_VIDEO;
+ if (IsYUVSwapChainFormat(swapChainFormat)) {
+ desc.Flags |= DXGI_SWAP_CHAIN_FLAG_YUV_VIDEO;
+ }
+ desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
+
+ HRESULT hr;
+ hr = dxgiFactoryMedia->CreateSwapChainForCompositionSurfaceHandle(
+ device, mSwapChainSurfaceHandle, &desc, nullptr,
+ getter_AddRefs(mVideoSwapChain));
+
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Failed to create video SwapChain: " << gfx::hexa(hr)
+ << " " << mSwapChainSize;
+ return false;
+ }
+
+ mSwapChainFormat = swapChainFormat;
+ return true;
+}
+
+// TODO: Replace with YUVRangedColorSpace
+static Maybe<DXGI_COLOR_SPACE_TYPE> GetSourceDXGIColorSpace(
+ const gfx::YUVColorSpace aYUVColorSpace,
+ const gfx::ColorRange aColorRange) {
+ if (aYUVColorSpace == gfx::YUVColorSpace::BT601) {
+ if (aColorRange == gfx::ColorRange::FULL) {
+ return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601);
+ } else {
+ return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601);
+ }
+ } else if (aYUVColorSpace == gfx::YUVColorSpace::BT709) {
+ if (aColorRange == gfx::ColorRange::FULL) {
+ return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709);
+ } else {
+ return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709);
+ }
+ } else if (aYUVColorSpace == gfx::YUVColorSpace::BT2020) {
+ if (aColorRange == gfx::ColorRange::FULL) {
+ // XXX Add SMPTEST2084 handling. HDR content is not handled yet by
+ // video overlay.
+ return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020);
+ } else {
+ return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020);
+ }
+ }
+
+ return Nothing();
+}
+
+static Maybe<DXGI_COLOR_SPACE_TYPE> GetSourceDXGIColorSpace(
+ const gfx::YUVRangedColorSpace aYUVColorSpace) {
+ const auto info = FromYUVRangedColorSpace(aYUVColorSpace);
+ return GetSourceDXGIColorSpace(info.space, info.range);
+}
+
+static void SetNvidiaVideoSuperRes(ID3D11VideoContext* videoContext,
+ ID3D11VideoProcessor* videoProcessor,
+ bool enabled) {
+ LOG("SetNvidiaVideoSuperRes() enabled=%d", enabled);
+
+ // Undocumented NVIDIA driver constants
+ constexpr GUID nvGUID = {0xD43CE1B3,
+ 0x1F4B,
+ 0x48AC,
+ {0xBA, 0xEE, 0xC3, 0xC2, 0x53, 0x75, 0xE6, 0xF7}};
+
+ constexpr UINT nvExtensionVersion = 0x1;
+ constexpr UINT nvExtensionMethodSuperResolution = 0x2;
+ struct {
+ UINT version;
+ UINT method;
+ UINT enable;
+ } streamExtensionInfo = {nvExtensionVersion, nvExtensionMethodSuperResolution,
+ enabled ? 0 : 1u};
+
+ HRESULT hr;
+ hr = videoContext->VideoProcessorSetStreamExtension(
+ videoProcessor, 0, &nvGUID, sizeof(streamExtensionInfo),
+ &streamExtensionInfo);
+
+ // Ignore errors as could be unsupported
+ if (FAILED(hr)) {
+ LOG("SetNvidiaVideoSuperRes() error: %lx", hr);
+ return;
+ }
+}
+
+static UINT GetVendorId(ID3D11VideoDevice* const videoDevice) {
+ RefPtr<IDXGIDevice> dxgiDevice;
+ RefPtr<IDXGIAdapter> adapter;
+ videoDevice->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
+ dxgiDevice->GetAdapter(getter_AddRefs(adapter));
+
+ DXGI_ADAPTER_DESC adapterDesc;
+ adapter->GetDesc(&adapterDesc);
+
+ return adapterDesc.VendorId;
+}
+
+bool DCSurfaceVideo::CallVideoProcessorBlt() {
+ MOZ_ASSERT(mRenderTextureHost);
+
+ HRESULT hr;
+ const auto videoDevice = mDCLayerTree->GetVideoDevice();
+ const auto videoContext = mDCLayerTree->GetVideoContext();
+ const auto texture = mRenderTextureHost->AsRenderDXGITextureHost();
+
+ Maybe<DXGI_COLOR_SPACE_TYPE> sourceColorSpace =
+ GetSourceDXGIColorSpace(texture->GetYUVColorSpace());
+ if (sourceColorSpace.isNothing()) {
+ gfxCriticalNote << "Unsupported color space";
+ return false;
+ }
+
+ RefPtr<ID3D11Texture2D> texture2D = texture->GetD3D11Texture2DWithGL();
+ if (!texture2D) {
+ gfxCriticalNote << "Failed to get D3D11Texture2D";
+ return false;
+ }
+
+ if (!mVideoSwapChain) {
+ return false;
+ }
+
+ if (!mDCLayerTree->EnsureVideoProcessor(mVideoSize, mSwapChainSize)) {
+ gfxCriticalNote << "EnsureVideoProcessor Failed";
+ return false;
+ }
+
+ RefPtr<IDXGISwapChain3> swapChain3;
+ mVideoSwapChain->QueryInterface(
+ (IDXGISwapChain3**)getter_AddRefs(swapChain3));
+ if (!swapChain3) {
+ gfxCriticalNote << "Failed to get IDXGISwapChain3";
+ return false;
+ }
+
+ RefPtr<ID3D11VideoContext1> videoContext1;
+ videoContext->QueryInterface(
+ (ID3D11VideoContext1**)getter_AddRefs(videoContext1));
+ if (!videoContext1) {
+ gfxCriticalNote << "Failed to get ID3D11VideoContext1";
+ return false;
+ }
+
+ const auto videoProcessor = mDCLayerTree->GetVideoProcessor();
+ const auto videoProcessorEnumerator =
+ mDCLayerTree->GetVideoProcessorEnumerator();
+
+ DXGI_COLOR_SPACE_TYPE inputColorSpace = sourceColorSpace.ref();
+ videoContext1->VideoProcessorSetStreamColorSpace1(videoProcessor, 0,
+ inputColorSpace);
+
+ DXGI_COLOR_SPACE_TYPE outputColorSpace =
+ IsYUVSwapChainFormat(mSwapChainFormat)
+ ? inputColorSpace
+ : DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
+ hr = swapChain3->SetColorSpace1(outputColorSpace);
+ if (FAILED(hr)) {
+ gfxCriticalNoteOnce << "SetColorSpace1 failed: " << gfx::hexa(hr);
+ RenderThread::Get()->NotifyWebRenderError(
+ wr::WebRenderError::VIDEO_OVERLAY);
+ return false;
+ }
+ videoContext1->VideoProcessorSetOutputColorSpace1(videoProcessor,
+ outputColorSpace);
+
+ D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inputDesc = {};
+ inputDesc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D;
+ inputDesc.Texture2D.ArraySlice = texture->ArrayIndex();
+
+ RefPtr<ID3D11VideoProcessorInputView> inputView;
+ hr = videoDevice->CreateVideoProcessorInputView(
+ texture2D, videoProcessorEnumerator, &inputDesc,
+ getter_AddRefs(inputView));
+ if (FAILED(hr)) {
+ gfxCriticalNote << "ID3D11VideoProcessorInputView creation failed: "
+ << gfx::hexa(hr);
+ return false;
+ }
+
+ D3D11_VIDEO_PROCESSOR_STREAM stream = {};
+ stream.Enable = true;
+ stream.OutputIndex = 0;
+ stream.InputFrameOrField = 0;
+ stream.PastFrames = 0;
+ stream.FutureFrames = 0;
+ stream.pInputSurface = inputView.get();
+
+ RECT destRect;
+ destRect.left = 0;
+ destRect.top = 0;
+ destRect.right = mSwapChainSize.width;
+ destRect.bottom = mSwapChainSize.height;
+
+ videoContext->VideoProcessorSetOutputTargetRect(videoProcessor, TRUE,
+ &destRect);
+ videoContext->VideoProcessorSetStreamDestRect(videoProcessor, 0, TRUE,
+ &destRect);
+ RECT sourceRect;
+ sourceRect.left = 0;
+ sourceRect.top = 0;
+ sourceRect.right = mVideoSize.width;
+ sourceRect.bottom = mVideoSize.height;
+ videoContext->VideoProcessorSetStreamSourceRect(videoProcessor, 0, TRUE,
+ &sourceRect);
+
+ if (!mOutputView) {
+ RefPtr<ID3D11Texture2D> backBuf;
+ mVideoSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D),
+ (void**)getter_AddRefs(backBuf));
+
+ D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outputDesc = {};
+ outputDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D;
+ outputDesc.Texture2D.MipSlice = 0;
+
+ hr = videoDevice->CreateVideoProcessorOutputView(
+ backBuf, videoProcessorEnumerator, &outputDesc,
+ getter_AddRefs(mOutputView));
+ if (FAILED(hr)) {
+ gfxCriticalNote << "ID3D11VideoProcessorOutputView creation failed: "
+ << gfx::hexa(hr);
+ return false;
+ }
+ }
+
+ const UINT vendorId = GetVendorId(videoDevice);
+ if (vendorId == 0x10DE &&
+ StaticPrefs::gfx_webrender_super_resolution_nvidia_AtStartup()) {
+ SetNvidiaVideoSuperRes(videoContext, videoProcessor, true);
+ }
+
+ hr = videoContext->VideoProcessorBlt(videoProcessor, mOutputView, 0, 1,
+ &stream);
+ if (FAILED(hr)) {
+ gfxCriticalNote << "VideoProcessorBlt failed: " << gfx::hexa(hr);
+ return false;
+ }
+
+ return true;
+}
+
+void DCSurfaceVideo::ReleaseDecodeSwapChainResources() {
+ mOutputView = nullptr;
+ mVideoSwapChain = nullptr;
+ mDecodeSwapChain = nullptr;
+ mDecodeResource = nullptr;
+ if (mSwapChainSurfaceHandle) {
+ ::CloseHandle(mSwapChainSurfaceHandle);
+ mSwapChainSurfaceHandle = 0;
+ }
+}
+
+DCSurfaceHandle::DCSurfaceHandle(bool aIsOpaque, DCLayerTree* aDCLayerTree)
+ : DCSurface(wr::DeviceIntSize{}, wr::DeviceIntPoint{}, false, aIsOpaque,
+ aDCLayerTree) {}
+
+void DCSurfaceHandle::AttachExternalImage(wr::ExternalImageId aExternalImage) {
+ RenderTextureHost* texture =
+ RenderThread::Get()->GetRenderTexture(aExternalImage);
+ RenderDcompSurfaceTextureHost* renderTexture =
+ texture ? texture->AsRenderDcompSurfaceTextureHost() : nullptr;
+ if (!renderTexture) {
+ gfxCriticalNote << "Unsupported RenderTexture for DCSurfaceHandle: "
+ << gfx::hexa(texture);
+ return;
+ }
+
+ const auto handle = renderTexture->GetDcompSurfaceHandle();
+ if (GetSurfaceHandle() == handle) {
+ return;
+ }
+
+ LOG_H("AttachExternalImage, ext-image=%" PRIu64 ", texture=%p, handle=%p",
+ wr::AsUint64(aExternalImage), renderTexture, handle);
+ mDcompTextureHost = renderTexture;
+}
+
+HANDLE DCSurfaceHandle::GetSurfaceHandle() const {
+ if (mDcompTextureHost) {
+ return mDcompTextureHost->GetDcompSurfaceHandle();
+ }
+ return nullptr;
+}
+
+IDCompositionSurface* DCSurfaceHandle::EnsureSurface() {
+ if (auto* surface = mDcompTextureHost->GetSurface()) {
+ return surface;
+ }
+
+ // Texture host hasn't created the surface yet, ask it to create a new one.
+ RefPtr<IDCompositionDevice> device;
+ HRESULT hr = mDCLayerTree->GetCompositionDevice()->QueryInterface(
+ (IDCompositionDevice**)getter_AddRefs(device));
+ if (FAILED(hr)) {
+ gfxCriticalNote
+ << "Failed to convert IDCompositionDevice2 to IDCompositionDevice: "
+ << gfx::hexa(hr);
+ return nullptr;
+ }
+
+ return mDcompTextureHost->CreateSurfaceFromDevice(device);
+}
+
+void DCSurfaceHandle::PresentSurfaceHandle() {
+ LOG_H("PresentSurfaceHandle");
+ if (IDCompositionSurface* surface = EnsureSurface()) {
+ LOG_H("Set surface %p to visual", surface);
+ mVisual->SetContent(surface);
+ } else {
+ mVisual->SetContent(nullptr);
+ }
+}
+
+DCTile::DCTile(DCLayerTree* aDCLayerTree) : mDCLayerTree(aDCLayerTree) {}
+
+DCTile::~DCTile() {}
+
+bool DCTile::Initialize(int aX, int aY, wr::DeviceIntSize aSize,
+ bool aIsVirtualSurface, bool aIsOpaque,
+ RefPtr<IDCompositionVisual2> mSurfaceVisual) {
+ if (aSize.width <= 0 || aSize.height <= 0) {
+ return false;
+ }
+
+ mSize = aSize;
+ mIsOpaque = aIsOpaque;
+ mIsVirtualSurface = aIsVirtualSurface;
+ mNeedsFullDraw = !aIsVirtualSurface;
+
+ if (aIsVirtualSurface) {
+ // Initially, the entire tile is considered valid, unless it is set by
+ // the SetTileProperties method.
+ mValidRect.x = 0;
+ mValidRect.y = 0;
+ mValidRect.width = aSize.width;
+ mValidRect.height = aSize.height;
+ } else {
+ HRESULT hr;
+ const auto dCompDevice = mDCLayerTree->GetCompositionDevice();
+ // Create the visual and put it in the tree under the surface visual
+ hr = dCompDevice->CreateVisual(getter_AddRefs(mVisual));
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Failed to CreateVisual for DCTile: " << gfx::hexa(hr);
+ return false;
+ }
+ mSurfaceVisual->AddVisual(mVisual, false, nullptr);
+ // Position the tile relative to the surface visual
+ mVisual->SetOffsetX(aX * aSize.width);
+ mVisual->SetOffsetY(aY * aSize.height);
+ // Clip the visual so it doesn't show anything until we update it
+ D2D_RECT_F clip = {0, 0, 0, 0};
+ mVisual->SetClip(clip);
+ // Create the underlying pixel buffer.
+ mCompositionSurface = CreateCompositionSurface(aSize, aIsOpaque);
+ if (!mCompositionSurface) {
+ return false;
+ }
+ hr = mVisual->SetContent(mCompositionSurface);
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Failed to SetContent for DCTile: " << gfx::hexa(hr);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+RefPtr<IDCompositionSurface> DCTile::CreateCompositionSurface(
+ wr::DeviceIntSize aSize, bool aIsOpaque) {
+ HRESULT hr;
+ const auto dCompDevice = mDCLayerTree->GetCompositionDevice();
+ const auto alphaMode =
+ aIsOpaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED;
+ RefPtr<IDCompositionSurface> compositionSurface;
+
+ hr = dCompDevice->CreateSurface(aSize.width, aSize.height,
+ DXGI_FORMAT_R8G8B8A8_UNORM, alphaMode,
+ getter_AddRefs(compositionSurface));
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Failed to CreateSurface for DCTile: " << gfx::hexa(hr);
+ return nullptr;
+ }
+ return compositionSurface;
+}
+
+RefPtr<IDCompositionSurface> DCTile::Bind(wr::DeviceIntRect aValidRect) {
+ if (mVisual != nullptr) {
+ // Tile owns a visual, set the size of the visual to match the portion we
+ // want to be visible.
+ D2D_RECT_F clip_rect;
+ clip_rect.left = aValidRect.min.x;
+ clip_rect.top = aValidRect.min.y;
+ clip_rect.right = aValidRect.max.x;
+ clip_rect.bottom = aValidRect.max.y;
+ mVisual->SetClip(clip_rect);
+ }
+ return mCompositionSurface;
+}
+
+GLuint DCLayerTree::CreateEGLSurfaceForCompositionSurface(
+ wr::DeviceIntRect aDirtyRect, wr::DeviceIntPoint* aOffset,
+ RefPtr<IDCompositionSurface> aCompositionSurface,
+ wr::DeviceIntPoint aSurfaceOffset) {
+ MOZ_ASSERT(aCompositionSurface.get());
+
+ HRESULT hr;
+ const auto gl = GetGLContext();
+ RefPtr<ID3D11Texture2D> backBuf;
+ POINT offset;
+
+ RECT update_rect;
+ update_rect.left = aSurfaceOffset.x + aDirtyRect.min.x;
+ update_rect.top = aSurfaceOffset.y + aDirtyRect.min.y;
+ update_rect.right = aSurfaceOffset.x + aDirtyRect.max.x;
+ update_rect.bottom = aSurfaceOffset.y + aDirtyRect.max.y;
+ hr = aCompositionSurface->BeginDraw(&update_rect, __uuidof(ID3D11Texture2D),
+ (void**)getter_AddRefs(backBuf), &offset);
+
+ if (FAILED(hr)) {
+ LayoutDeviceIntRect rect = widget::WinUtils::ToIntRect(update_rect);
+
+ gfxCriticalNote << "DCompositionSurface::BeginDraw failed: "
+ << gfx::hexa(hr) << " " << rect;
+ RenderThread::Get()->HandleWebRenderError(WebRenderError::BEGIN_DRAW);
+ return false;
+ }
+
+ // DC includes the origin of the dirty / update rect in the draw offset,
+ // undo that here since WR expects it to be an absolute offset.
+ offset.x -= aDirtyRect.min.x;
+ offset.y -= aDirtyRect.min.y;
+
+ D3D11_TEXTURE2D_DESC desc;
+ backBuf->GetDesc(&desc);
+
+ const auto& gle = gl::GLContextEGL::Cast(gl);
+ const auto& egl = gle->mEgl;
+
+ const auto buffer = reinterpret_cast<EGLClientBuffer>(backBuf.get());
+
+ // Construct an EGLImage wrapper around the D3D texture for ANGLE.
+ const EGLint attribs[] = {LOCAL_EGL_NONE};
+ mEGLImage = egl->fCreateImage(EGL_NO_CONTEXT, LOCAL_EGL_D3D11_TEXTURE_ANGLE,
+ buffer, attribs);
+
+ // Get the current FBO and RBO id, so we can restore them later
+ GLint currentFboId, currentRboId;
+ gl->fGetIntegerv(LOCAL_GL_DRAW_FRAMEBUFFER_BINDING, &currentFboId);
+ gl->fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, &currentRboId);
+
+ // Create a render buffer object that is backed by the EGL image.
+ gl->fGenRenderbuffers(1, &mColorRBO);
+ gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mColorRBO);
+ gl->fEGLImageTargetRenderbufferStorage(LOCAL_GL_RENDERBUFFER, mEGLImage);
+
+ // Get or create an FBO for the specified dimensions
+ GLuint fboId = GetOrCreateFbo(desc.Width, desc.Height);
+
+ // Attach the new renderbuffer to the FBO
+ gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, fboId);
+ gl->fFramebufferRenderbuffer(LOCAL_GL_DRAW_FRAMEBUFFER,
+ LOCAL_GL_COLOR_ATTACHMENT0,
+ LOCAL_GL_RENDERBUFFER, mColorRBO);
+
+ // Restore previous FBO and RBO bindings
+ gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, currentFboId);
+ gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, currentRboId);
+
+ aOffset->x = offset.x;
+ aOffset->y = offset.y;
+
+ return fboId;
+}
+
+void DCLayerTree::DestroyEGLSurface() {
+ const auto gl = GetGLContext();
+
+ if (mColorRBO) {
+ gl->fDeleteRenderbuffers(1, &mColorRBO);
+ mColorRBO = 0;
+ }
+
+ if (mEGLImage) {
+ const auto& gle = gl::GLContextEGL::Cast(gl);
+ const auto& egl = gle->mEgl;
+ egl->fDestroyImage(mEGLImage);
+ mEGLImage = EGL_NO_IMAGE;
+ }
+}
+
+// -
+
+color::ColorProfileDesc DCLayerTree::QueryOutputColorProfile() {
+ // GPU process can't simply init gfxPlatform, (and we don't need most of it)
+ // but we do need gfxPlatform::GetCMSOutputProfile().
+ // So we steal what we need through the window:
+ const auto outputProfileData =
+ gfxWindowsPlatform::GetPlatformCMSOutputProfileData_Impl();
+
+ const auto qcmsProfile = qcms_profile_from_memory(
+ outputProfileData.Elements(), outputProfileData.Length());
+ const auto release = MakeScopeExit([&]() {
+ if (qcmsProfile) {
+ qcms_profile_release(qcmsProfile);
+ }
+ });
+
+ const bool print = gfxEnv::MOZ_GL_SPEW();
+
+ const auto ret = [&]() {
+ if (qcmsProfile) {
+ return color::ColorProfileDesc::From(*qcmsProfile);
+ }
+ if (print) {
+ printf_stderr(
+ "Missing or failed to load display color profile, defaulting to "
+ "sRGB.\n");
+ }
+ const auto MISSING_PROFILE_DEFAULT_SPACE = color::ColorspaceDesc{
+ color::Chromaticities::Srgb(),
+ color::PiecewiseGammaDesc::Srgb(),
+ };
+ return color::ColorProfileDesc::From(MISSING_PROFILE_DEFAULT_SPACE);
+ }();
+
+ if (print) {
+ const auto gammaGuess = color::GuessGamma(ret.linearFromTf.r);
+ printf_stderr(
+ "Display profile:\n"
+ " Approx Gamma: %f\n"
+ " XYZ-D65 Red : %f, %f, %f\n"
+ " XYZ-D65 Green: %f, %f, %f\n"
+ " XYZ-D65 Blue : %f, %f, %f\n",
+ gammaGuess, ret.xyzd65FromLinearRgb.at(0, 0),
+ ret.xyzd65FromLinearRgb.at(0, 1), ret.xyzd65FromLinearRgb.at(0, 2),
+
+ ret.xyzd65FromLinearRgb.at(1, 0), ret.xyzd65FromLinearRgb.at(1, 1),
+ ret.xyzd65FromLinearRgb.at(1, 2),
+
+ ret.xyzd65FromLinearRgb.at(2, 0), ret.xyzd65FromLinearRgb.at(2, 1),
+ ret.xyzd65FromLinearRgb.at(2, 2));
+ }
+
+ return ret;
+}
+
+inline D2D1_MATRIX_5X4_F to_D2D1_MATRIX_5X4_F(const color::mat4& m) {
+ return D2D1_MATRIX_5X4_F{{{
+ m.rows[0][0],
+ m.rows[1][0],
+ m.rows[2][0],
+ m.rows[3][0],
+ m.rows[0][1],
+ m.rows[1][1],
+ m.rows[2][1],
+ m.rows[3][1],
+ m.rows[0][2],
+ m.rows[1][2],
+ m.rows[2][2],
+ m.rows[3][2],
+ m.rows[0][3],
+ m.rows[1][3],
+ m.rows[2][3],
+ m.rows[3][3],
+ 0,
+ 0,
+ 0,
+ 0,
+ }}};
+}
+
+ColorManagementChain ColorManagementChain::From(
+ IDCompositionDevice3& dcomp,
+ const color::ColorProfileConversionDesc& conv) {
+ auto ret = ColorManagementChain{};
+
+#if !defined(MOZ_MINGW_DCOMP_H_INCOMPLETE)
+
+ const auto Append = [&](const RefPtr<IDCompositionFilterEffect>& afterLast) {
+ if (ret.last) {
+ afterLast->SetInput(0, ret.last, 0);
+ }
+ ret.last = afterLast;
+ };
+
+ const auto MaybeAppendColorMatrix = [&](const color::mat4& m) {
+ RefPtr<IDCompositionColorMatrixEffect> e;
+ if (approx(m, color::mat4::Identity())) return e;
+ dcomp.CreateColorMatrixEffect(getter_AddRefs(e));
+ MOZ_ASSERT(e);
+ if (!e) return e;
+ e->SetMatrix(to_D2D1_MATRIX_5X4_F(m));
+ Append(e);
+ return e;
+ };
+ const auto MaybeAppendTableTransfer = [&](const color::RgbTransferTables& t) {
+ RefPtr<IDCompositionTableTransferEffect> e;
+ if (!t.r.size() && !t.g.size() && !t.b.size()) return e;
+ dcomp.CreateTableTransferEffect(getter_AddRefs(e));
+ MOZ_ASSERT(e);
+ if (!e) return e;
+ e->SetRedTable(t.r.data(), t.r.size());
+ e->SetGreenTable(t.g.data(), t.g.size());
+ e->SetBlueTable(t.b.data(), t.b.size());
+ Append(e);
+ return e;
+ };
+
+ ret.srcRgbFromSrcYuv = MaybeAppendColorMatrix(conv.srcRgbFromSrcYuv);
+ ret.srcLinearFromSrcTf = MaybeAppendTableTransfer(conv.srcLinearFromSrcTf);
+ ret.dstLinearFromSrcLinear =
+ MaybeAppendColorMatrix(color::mat4(conv.dstLinearFromSrcLinear));
+ ret.dstTfFromDstLinear = MaybeAppendTableTransfer(conv.dstTfFromDstLinear);
+
+#endif // !defined(MOZ_MINGW_DCOMP_H_INCOMPLETE)
+
+ return ret;
+}
+
+ColorManagementChain::~ColorManagementChain() = default;
+
+} // namespace wr
+} // namespace mozilla
+
+#undef LOG_H
diff --git a/gfx/webrender_bindings/DCLayerTree.h b/gfx/webrender_bindings/DCLayerTree.h
new file mode 100644
index 0000000000..2dd3a061ac
--- /dev/null
+++ b/gfx/webrender_bindings/DCLayerTree.h
@@ -0,0 +1,461 @@
+/* -*- 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_DCLAYER_TREE_H
+#define MOZILLA_GFX_DCLAYER_TREE_H
+
+#include <dxgiformat.h>
+#include <unordered_map>
+#include <vector>
+#include <windows.h>
+
+#include "Colorspaces.h"
+#include "GLTypes.h"
+#include "mozilla/HashFunctions.h"
+#include "mozilla/layers/OverlayInfo.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/webrender/WebRenderTypes.h"
+
+struct ID3D11Device;
+struct ID3D11DeviceContext;
+struct ID3D11VideoDevice;
+struct ID3D11VideoContext;
+struct ID3D11VideoProcessor;
+struct ID3D11VideoProcessorEnumerator;
+struct ID3D11VideoProcessorOutputView;
+struct IDCompositionColorMatrixEffect;
+struct IDCompositionFilterEffect;
+struct IDCompositionTableTransferEffect;
+struct IDCompositionDevice2;
+struct IDCompositionDevice3;
+struct IDCompositionSurface;
+struct IDCompositionTarget;
+struct IDCompositionVisual2;
+struct IDXGIDecodeSwapChain;
+struct IDXGIResource;
+struct IDXGISwapChain1;
+struct IDCompositionVirtualSurface;
+
+namespace mozilla {
+
+namespace gl {
+class GLContext;
+}
+
+namespace wr {
+
+// The size of the virtual surface. This is large enough such that we
+// will never render a surface larger than this.
+#define VIRTUAL_SURFACE_SIZE (1024 * 1024)
+
+class DCTile;
+class DCSurface;
+class DCSurfaceVideo;
+class DCSurfaceHandle;
+class RenderTextureHost;
+class RenderDcompSurfaceTextureHost;
+
+struct GpuOverlayInfo {
+ bool mSupportsOverlays = false;
+ bool mSupportsHardwareOverlays = false;
+ DXGI_FORMAT mOverlayFormatUsed = DXGI_FORMAT_B8G8R8A8_UNORM;
+ DXGI_FORMAT mOverlayFormatUsedHdr = DXGI_FORMAT_R10G10B10A2_UNORM;
+ UINT mNv12OverlaySupportFlags = 0;
+ UINT mYuy2OverlaySupportFlags = 0;
+ UINT mBgra8OverlaySupportFlags = 0;
+ UINT mRgb10a2OverlaySupportFlags = 0;
+};
+
+// -
+
+struct ColorManagementChain {
+ RefPtr<IDCompositionColorMatrixEffect> srcRgbFromSrcYuv;
+ RefPtr<IDCompositionTableTransferEffect> srcLinearFromSrcTf;
+ RefPtr<IDCompositionColorMatrixEffect> dstLinearFromSrcLinear;
+ RefPtr<IDCompositionTableTransferEffect> dstTfFromDstLinear;
+ RefPtr<IDCompositionFilterEffect> last;
+
+ static ColorManagementChain From(IDCompositionDevice3& dcomp,
+ const color::ColorProfileConversionDesc&);
+
+ ~ColorManagementChain();
+};
+
+// -
+
+/**
+ * DCLayerTree manages direct composition layers.
+ * It does not manage gecko's layers::Layer.
+ */
+class DCLayerTree {
+ public:
+ static UniquePtr<DCLayerTree> Create(gl::GLContext* aGL, EGLConfig aEGLConfig,
+ ID3D11Device* aDevice,
+ ID3D11DeviceContext* aCtx, HWND aHwnd,
+ nsACString& aError);
+
+ static void Shutdown();
+
+ explicit DCLayerTree(gl::GLContext* aGL, EGLConfig aEGLConfig,
+ ID3D11Device* aDevice, ID3D11DeviceContext* aCtx,
+ IDCompositionDevice2* aCompositionDevice);
+ ~DCLayerTree();
+
+ void SetDefaultSwapChain(IDXGISwapChain1* aSwapChain);
+ void MaybeUpdateDebug();
+ void MaybeCommit();
+ void WaitForCommitCompletion();
+ void DisableNativeCompositor();
+
+ // Interface for wr::Compositor
+ void CompositorBeginFrame();
+ void CompositorEndFrame();
+ void Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset, uint32_t* aFboId,
+ wr::DeviceIntRect aDirtyRect, wr::DeviceIntRect aValidRect);
+ void Unbind();
+ void CreateSurface(wr::NativeSurfaceId aId, wr::DeviceIntPoint aVirtualOffset,
+ wr::DeviceIntSize aTileSize, bool aIsOpaque);
+ void CreateExternalSurface(wr::NativeSurfaceId aId, bool aIsOpaque);
+ void DestroySurface(NativeSurfaceId aId);
+ void CreateTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY);
+ void DestroyTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY);
+ void AttachExternalImage(wr::NativeSurfaceId aId,
+ wr::ExternalImageId aExternalImage);
+ void AddSurface(wr::NativeSurfaceId aId,
+ const wr::CompositorSurfaceTransform& aTransform,
+ wr::DeviceIntRect aClipRect,
+ wr::ImageRendering aImageRendering);
+
+ gl::GLContext* GetGLContext() const { return mGL; }
+ EGLConfig GetEGLConfig() const { return mEGLConfig; }
+ ID3D11Device* GetDevice() const { return mDevice; }
+ IDCompositionDevice2* GetCompositionDevice() const {
+ return mCompositionDevice;
+ }
+ ID3D11VideoDevice* GetVideoDevice() const { return mVideoDevice; }
+ ID3D11VideoContext* GetVideoContext() const { return mVideoContext; }
+ ID3D11VideoProcessor* GetVideoProcessor() const { return mVideoProcessor; }
+ ID3D11VideoProcessorEnumerator* GetVideoProcessorEnumerator() const {
+ return mVideoProcessorEnumerator;
+ }
+ bool EnsureVideoProcessor(const gfx::IntSize& aInputSize,
+ const gfx::IntSize& aOutputSize);
+
+ DCSurface* GetSurface(wr::NativeSurfaceId aId) const;
+
+ // Get or create an FBO with depth buffer suitable for specified dimensions
+ GLuint GetOrCreateFbo(int aWidth, int aHeight);
+
+ bool SupportsHardwareOverlays();
+ DXGI_FORMAT GetOverlayFormatForSDR();
+
+ bool SupportsSwapChainTearing();
+
+ protected:
+ bool Initialize(HWND aHwnd, nsACString& aError);
+ bool InitializeVideoOverlaySupport();
+ bool MaybeUpdateDebugCounter();
+ bool MaybeUpdateDebugVisualRedrawRegions();
+ void DestroyEGLSurface();
+ GLuint CreateEGLSurfaceForCompositionSurface(
+ wr::DeviceIntRect aDirtyRect, wr::DeviceIntPoint* aOffset,
+ RefPtr<IDCompositionSurface> aCompositionSurface,
+ wr::DeviceIntPoint aSurfaceOffset);
+ void ReleaseNativeCompositorResources();
+ layers::OverlayInfo GetOverlayInfo();
+
+ RefPtr<gl::GLContext> mGL;
+ EGLConfig mEGLConfig;
+
+ RefPtr<ID3D11Device> mDevice;
+ RefPtr<ID3D11DeviceContext> mCtx;
+
+ RefPtr<IDCompositionDevice2> mCompositionDevice;
+ RefPtr<IDCompositionTarget> mCompositionTarget;
+ RefPtr<IDCompositionVisual2> mRootVisual;
+ RefPtr<IDCompositionVisual2> mDefaultSwapChainVisual;
+
+ RefPtr<ID3D11VideoDevice> mVideoDevice;
+ RefPtr<ID3D11VideoContext> mVideoContext;
+ RefPtr<ID3D11VideoProcessor> mVideoProcessor;
+ RefPtr<ID3D11VideoProcessorEnumerator> mVideoProcessorEnumerator;
+ gfx::IntSize mVideoInputSize;
+ gfx::IntSize mVideoOutputSize;
+
+ bool mDebugCounter;
+ bool mDebugVisualRedrawRegions;
+
+ Maybe<RefPtr<IDCompositionSurface>> mCurrentSurface;
+
+ // The EGL image that is bound to the D3D texture provided by
+ // DirectComposition.
+ EGLImage mEGLImage;
+
+ // The GL render buffer ID that maps the EGLImage to an RBO for attaching to
+ // an FBO.
+ GLuint mColorRBO;
+
+ struct SurfaceIdHashFn {
+ std::size_t operator()(const wr::NativeSurfaceId& aId) const {
+ return HashGeneric(wr::AsUint64(aId));
+ }
+ };
+
+ std::unordered_map<wr::NativeSurfaceId, UniquePtr<DCSurface>, SurfaceIdHashFn>
+ mDCSurfaces;
+
+ // A list of layer IDs as they are added to the visual tree this frame.
+ std::vector<wr::NativeSurfaceId> mCurrentLayers;
+
+ // The previous frame's list of layer IDs in visual order.
+ std::vector<wr::NativeSurfaceId> mPrevLayers;
+
+ // Information about a cached FBO that is retained between frames.
+ struct CachedFrameBuffer {
+ int width;
+ int height;
+ GLuint fboId;
+ GLuint depthRboId;
+ int lastFrameUsed;
+ };
+
+ // A cache of FBOs, containing a depth buffer allocated to a specific size.
+ // TODO(gw): Might be faster as a hashmap? The length is typically much less
+ // than 10.
+ nsTArray<CachedFrameBuffer> mFrameBuffers;
+ int mCurrentFrame = 0;
+
+ bool mPendingCommit;
+
+ static color::ColorProfileDesc QueryOutputColorProfile();
+
+ mutable Maybe<color::ColorProfileDesc> mOutputColorProfile;
+
+ public:
+ const color::ColorProfileDesc& OutputColorProfile() const {
+ if (!mOutputColorProfile) {
+ mOutputColorProfile = Some(QueryOutputColorProfile());
+ }
+ return *mOutputColorProfile;
+ }
+
+ protected:
+ static UniquePtr<GpuOverlayInfo> sGpuOverlayInfo;
+};
+
+/**
+ Represents a single picture cache slice. Each surface contains some
+ number of tiles. An implementation may choose to allocate individual
+ tiles to render in to (as the current impl does), or allocate a large
+ single virtual surface to draw into (e.g. the DirectComposition virtual
+ surface API in future).
+ */
+class DCSurface {
+ public:
+ const bool mIsVirtualSurface;
+
+ explicit DCSurface(wr::DeviceIntSize aTileSize,
+ wr::DeviceIntPoint aVirtualOffset, bool aIsVirtualSurface,
+ bool aIsOpaque, DCLayerTree* aDCLayerTree);
+ virtual ~DCSurface();
+
+ bool Initialize();
+ void CreateTile(int32_t aX, int32_t aY);
+ void DestroyTile(int32_t aX, int32_t aY);
+
+ IDCompositionVisual2* GetVisual() const { return mVisual; }
+ DCTile* GetTile(int32_t aX, int32_t aY) const;
+
+ struct TileKey {
+ TileKey(int32_t aX, int32_t aY) : mX(aX), mY(aY) {}
+
+ int32_t mX;
+ int32_t mY;
+ };
+
+ wr::DeviceIntSize GetTileSize() const { return mTileSize; }
+ wr::DeviceIntPoint GetVirtualOffset() const { return mVirtualOffset; }
+
+ IDCompositionVirtualSurface* GetCompositionSurface() const {
+ return mVirtualSurface;
+ }
+
+ void UpdateAllocatedRect();
+ void DirtyAllocatedRect();
+
+ // Implement these if the inherited surface supports attaching external image.
+ virtual void AttachExternalImage(wr::ExternalImageId aExternalImage) {
+ MOZ_RELEASE_ASSERT(true, "Not support attaching external image");
+ }
+ virtual void PresentExternalSurface(gfx::Matrix& aTransform) {
+ MOZ_RELEASE_ASSERT(true, "Not support presenting external surface");
+ }
+
+ virtual DCSurfaceVideo* AsDCSurfaceVideo() { return nullptr; }
+ virtual DCSurfaceHandle* AsDCSurfaceHandle() { return nullptr; }
+
+ protected:
+ DCLayerTree* mDCLayerTree;
+
+ struct TileKeyHashFn {
+ std::size_t operator()(const TileKey& aId) const {
+ return HashGeneric(aId.mX, aId.mY);
+ }
+ };
+
+ // The visual for this surface. No content is attached to here, but tiles
+ // that belong to this surface are added as children. In this way, we can
+ // set the clip and scroll offset once, on this visual, to affect all
+ // children.
+ //
+ // However when using a virtual surface, it is directly attached to this
+ // visual and the tiles do not own visuals.
+ //
+ // Whether mIsVirtualSurface is enabled is decided at DCSurface creation
+ // time based on the pref gfx.webrender.dcomp-use-virtual-surfaces
+ RefPtr<IDCompositionVisual2> mVisual;
+
+ wr::DeviceIntSize mTileSize;
+ bool mIsOpaque;
+ bool mAllocatedRectDirty;
+ std::unordered_map<TileKey, UniquePtr<DCTile>, TileKeyHashFn> mDCTiles;
+ wr::DeviceIntPoint mVirtualOffset;
+ RefPtr<IDCompositionVirtualSurface> mVirtualSurface;
+};
+
+/**
+ * A wrapper surface which can contain either a DCVideo or a DCSurfaceHandle.
+ */
+class DCExternalSurfaceWrapper : public DCSurface {
+ public:
+ DCExternalSurfaceWrapper(bool aIsOpaque, DCLayerTree* aDCLayerTree)
+ : DCSurface(wr::DeviceIntSize{}, wr::DeviceIntPoint{},
+ false /* virtual surface */, false /* opaque */,
+ aDCLayerTree),
+ mIsOpaque(aIsOpaque) {}
+ ~DCExternalSurfaceWrapper() = default;
+
+ void AttachExternalImage(wr::ExternalImageId aExternalImage) override;
+
+ void PresentExternalSurface(gfx::Matrix& aTransform) override;
+
+ DCSurfaceVideo* AsDCSurfaceVideo() override {
+ return mSurface ? mSurface->AsDCSurfaceVideo() : nullptr;
+ }
+
+ DCSurfaceHandle* AsDCSurfaceHandle() override {
+ return mSurface ? mSurface->AsDCSurfaceHandle() : nullptr;
+ }
+
+ private:
+ DCSurface* EnsureSurfaceForExternalImage(wr::ExternalImageId aExternalImage);
+
+ UniquePtr<DCSurface> mSurface;
+ const bool mIsOpaque;
+ Maybe<ColorManagementChain> mCManageChain;
+};
+
+class DCSurfaceVideo : public DCSurface {
+ public:
+ DCSurfaceVideo(bool aIsOpaque, DCLayerTree* aDCLayerTree);
+
+ void AttachExternalImage(wr::ExternalImageId aExternalImage) override;
+ bool CalculateSwapChainSize(gfx::Matrix& aTransform);
+ void PresentVideo();
+
+ DCSurfaceVideo* AsDCSurfaceVideo() override { return this; }
+
+ protected:
+ virtual ~DCSurfaceVideo();
+
+ DXGI_FORMAT GetSwapChainFormat();
+ bool CreateVideoSwapChain();
+ bool CallVideoProcessorBlt();
+ void ReleaseDecodeSwapChainResources();
+
+ RefPtr<ID3D11VideoProcessorOutputView> mOutputView;
+ RefPtr<IDXGIResource> mDecodeResource;
+ RefPtr<IDXGISwapChain1> mVideoSwapChain;
+ RefPtr<IDXGIDecodeSwapChain> mDecodeSwapChain;
+ HANDLE mSwapChainSurfaceHandle = 0;
+ gfx::IntSize mVideoSize;
+ gfx::IntSize mSwapChainSize;
+ DXGI_FORMAT mSwapChainFormat = DXGI_FORMAT_B8G8R8A8_UNORM;
+ bool mFailedYuvSwapChain = false;
+ RefPtr<RenderTextureHost> mRenderTextureHost;
+ RefPtr<RenderTextureHost> mPrevTexture;
+ int mSlowPresentCount = 0;
+};
+
+/**
+ * A DC surface contains a IDCompositionSurface that is directly constructed by
+ * a handle. This is used by the Media Foundataion media engine, which would
+ * store the decoded video content in the surface.
+ */
+class DCSurfaceHandle : public DCSurface {
+ public:
+ DCSurfaceHandle(bool aIsOpaque, DCLayerTree* aDCLayerTree);
+ ~DCSurfaceHandle() = default;
+
+ void AttachExternalImage(wr::ExternalImageId aExternalImage) override;
+ void PresentSurfaceHandle();
+
+ DCSurfaceHandle* AsDCSurfaceHandle() override { return this; }
+
+ protected:
+ HANDLE GetSurfaceHandle() const;
+ IDCompositionSurface* EnsureSurface();
+
+ RefPtr<RenderDcompSurfaceTextureHost> mDcompTextureHost;
+};
+
+class DCTile {
+ public:
+ gfx::IntRect mValidRect;
+
+ DCLayerTree* mDCLayerTree;
+ // Indicates that when the first BeginDraw occurs on the surface it must be
+ // full size - required by dcomp on non-virtual surfaces.
+ bool mNeedsFullDraw;
+
+ explicit DCTile(DCLayerTree* aDCLayerTree);
+ ~DCTile();
+ bool Initialize(int aX, int aY, wr::DeviceIntSize aSize,
+ bool aIsVirtualSurface, bool aIsOpaque,
+ RefPtr<IDCompositionVisual2> mSurfaceVisual);
+ RefPtr<IDCompositionSurface> Bind(wr::DeviceIntRect aValidRect);
+ IDCompositionVisual2* GetVisual() { return mVisual; }
+
+ protected:
+ // Size in pixels of this tile, some may be unused. Set by Initialize.
+ wr::DeviceIntSize mSize;
+ // Whether the tile is composited as opaque (ignores alpha) or transparent.
+ // Set by Initialize.
+ bool mIsOpaque;
+ // Some code paths differ based on whether parent surface is virtual.
+ bool mIsVirtualSurface;
+ // Visual that displays the composition surface, or NULL if the tile belongs
+ // to a virtual surface.
+ RefPtr<IDCompositionVisual2> mVisual;
+ // Surface for the visual, or NULL if the tile has not had its first Bind or
+ // belongs to a virtual surface.
+ RefPtr<IDCompositionSurface> mCompositionSurface;
+
+ RefPtr<IDCompositionSurface> CreateCompositionSurface(wr::DeviceIntSize aSize,
+ bool aIsOpaque);
+};
+
+static inline bool operator==(const DCSurface::TileKey& a0,
+ const DCSurface::TileKey& a1) {
+ return a0.mX == a1.mX && a0.mY == a1.mY;
+}
+
+} // namespace wr
+} // namespace mozilla
+
+#endif
diff --git a/gfx/webrender_bindings/Moz2DImageRenderer.cpp b/gfx/webrender_bindings/Moz2DImageRenderer.cpp
new file mode 100644
index 0000000000..500e7bf1c3
--- /dev/null
+++ b/gfx/webrender_bindings/Moz2DImageRenderer.cpp
@@ -0,0 +1,478 @@
+/* -*- 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 "mozilla/StaticPrefs_gfx.h"
+#include "gfxUtils.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/Range.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/RectAbsolute.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/gfx/RecordedEvent.h"
+#include "mozilla/layers/WebRenderDrawEventRecorder.h"
+#include "WebRenderTypes.h"
+#include "webrender_ffi.h"
+#include "GeckoProfiler.h"
+
+#include <unordered_map>
+
+#ifdef XP_DARWIN
+# include "mozilla/gfx/UnscaledFontMac.h"
+#elif defined(XP_WIN)
+# include "mozilla/gfx/UnscaledFontDWrite.h"
+#else
+# include "mozilla/gfx/UnscaledFontFreeType.h"
+#endif
+
+namespace std {
+template <>
+struct hash<mozilla::wr::FontKey> {
+ size_t operator()(const mozilla::wr::FontKey& key) const {
+ return hash<size_t>()(mozilla::wr::AsUint64(key));
+ }
+};
+
+template <>
+struct hash<mozilla::wr::FontInstanceKey> {
+ size_t operator()(const mozilla::wr::FontInstanceKey& key) const {
+ return hash<size_t>()(mozilla::wr::AsUint64(key));
+ }
+};
+}; // namespace std
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace wr {
+
+struct FontTemplate {
+ const uint8_t* mData;
+ size_t mSize;
+ uint32_t mIndex;
+ const VecU8* mVec;
+ RefPtr<UnscaledFont> mUnscaledFont;
+
+ FontTemplate() : mData(nullptr), mSize(0), mIndex(0), mVec(nullptr) {}
+
+ ~FontTemplate() {
+ if (mVec) {
+ wr_dec_ref_arc(mVec);
+ }
+ }
+};
+
+struct FontInstanceData {
+ WrFontKey mFontKey;
+ float mSize;
+ Maybe<FontInstanceOptions> mOptions;
+ Maybe<FontInstancePlatformOptions> mPlatformOptions;
+ UniquePtr<gfx::FontVariation[]> mVariations;
+ size_t mNumVariations;
+ RefPtr<ScaledFont> mScaledFont;
+
+ FontInstanceData() : mSize(0), mNumVariations(0) {}
+};
+
+StaticMutex sFontDataTableLock;
+std::unordered_map<WrFontKey, FontTemplate> sFontDataTable;
+std::unordered_map<WrFontInstanceKey, FontInstanceData> sBlobFontTable;
+
+// Fixed-size ring buffer logging font deletion events to aid debugging.
+static struct FontDeleteLog {
+ static const size_t MAX_ENTRIES = 256;
+
+ uint64_t mEntries[MAX_ENTRIES] = {0};
+ size_t mNextEntry = 0;
+
+ void AddEntry(uint64_t aEntry) {
+ mEntries[mNextEntry] = aEntry;
+ mNextEntry = (mNextEntry + 1) % MAX_ENTRIES;
+ }
+
+ void Add(WrFontKey aKey) { AddEntry(AsUint64(aKey)); }
+
+ // Store namespace clears as font id 0, since this will never be allocated.
+ void Add(WrIdNamespace aNamespace) {
+ AddEntry(AsUint64(WrFontKey{aNamespace, 0}));
+ }
+
+ void AddAll() { AddEntry(~0); }
+
+ // Find a matching entry in the log, searching backwards starting at the
+ // newest entry and finishing with the oldest entry. Returns a brief
+ // description of why the font was deleted, if known.
+ const char* Find(WrFontKey aKey) {
+ uint64_t keyEntry = AsUint64(aKey);
+ uint64_t namespaceEntry = AsUint64(WrFontKey{aKey.mNamespace, 0});
+ size_t offset = mNextEntry;
+ do {
+ offset = (offset + MAX_ENTRIES - 1) % MAX_ENTRIES;
+ if (mEntries[offset] == keyEntry) {
+ return "deleted font";
+ } else if (mEntries[offset] == namespaceEntry) {
+ return "cleared namespace";
+ } else if (mEntries[offset] == (uint64_t)~0) {
+ return "cleared all";
+ }
+ } while (offset != mNextEntry);
+ return "unknown font";
+ }
+} sFontDeleteLog;
+
+void ClearAllBlobImageResources() {
+ StaticMutexAutoLock lock(sFontDataTableLock);
+ sFontDeleteLog.AddAll();
+ sBlobFontTable.clear();
+ sFontDataTable.clear();
+}
+
+extern "C" {
+void ClearBlobImageResources(WrIdNamespace aNamespace) {
+ StaticMutexAutoLock lock(sFontDataTableLock);
+ sFontDeleteLog.Add(aNamespace);
+ for (auto i = sBlobFontTable.begin(); i != sBlobFontTable.end();) {
+ if (i->first.mNamespace == aNamespace) {
+ i = sBlobFontTable.erase(i);
+ } else {
+ i++;
+ }
+ }
+ for (auto i = sFontDataTable.begin(); i != sFontDataTable.end();) {
+ if (i->first.mNamespace == aNamespace) {
+ i = sFontDataTable.erase(i);
+ } else {
+ i++;
+ }
+ }
+}
+
+bool HasFontData(WrFontKey aKey) {
+ StaticMutexAutoLock lock(sFontDataTableLock);
+ return sFontDataTable.find(aKey) != sFontDataTable.end();
+}
+
+void AddFontData(WrFontKey aKey, const uint8_t* aData, size_t aSize,
+ uint32_t aIndex, const ArcVecU8* aVec) {
+ StaticMutexAutoLock lock(sFontDataTableLock);
+ auto i = sFontDataTable.find(aKey);
+ if (i == sFontDataTable.end()) {
+ FontTemplate& font = sFontDataTable[aKey];
+ font.mData = aData;
+ font.mSize = aSize;
+ font.mIndex = aIndex;
+ font.mVec = wr_add_ref_arc(aVec);
+ }
+}
+
+void AddNativeFontHandle(WrFontKey aKey, void* aHandle, uint32_t aIndex) {
+ StaticMutexAutoLock lock(sFontDataTableLock);
+ auto i = sFontDataTable.find(aKey);
+ if (i == sFontDataTable.end()) {
+ FontTemplate& font = sFontDataTable[aKey];
+#ifdef XP_DARWIN
+ font.mUnscaledFont =
+ new UnscaledFontMac(reinterpret_cast<CGFontRef>(aHandle), false);
+#elif defined(XP_WIN)
+ font.mUnscaledFont = new UnscaledFontDWrite(
+ reinterpret_cast<IDWriteFontFace*>(aHandle), nullptr);
+#elif defined(ANDROID)
+ font.mUnscaledFont = new UnscaledFontFreeType(
+ reinterpret_cast<const char*>(aHandle), aIndex);
+#else
+ font.mUnscaledFont = new UnscaledFontFontconfig(
+ reinterpret_cast<const char*>(aHandle), aIndex);
+#endif
+ }
+}
+
+void DeleteFontData(WrFontKey aKey) {
+ StaticMutexAutoLock lock(sFontDataTableLock);
+ sFontDeleteLog.Add(aKey);
+ auto i = sFontDataTable.find(aKey);
+ if (i != sFontDataTable.end()) {
+ sFontDataTable.erase(i);
+ }
+}
+
+void AddBlobFont(WrFontInstanceKey aInstanceKey, WrFontKey aFontKey,
+ float aSize, const FontInstanceOptions* aOptions,
+ const FontInstancePlatformOptions* aPlatformOptions,
+ const FontVariation* aVariations, size_t aNumVariations) {
+ StaticMutexAutoLock lock(sFontDataTableLock);
+ auto i = sBlobFontTable.find(aInstanceKey);
+ if (i == sBlobFontTable.end()) {
+ FontInstanceData& font = sBlobFontTable[aInstanceKey];
+ font.mFontKey = aFontKey;
+ font.mSize = aSize;
+ if (aOptions) {
+ font.mOptions = Some(*aOptions);
+ }
+ if (aPlatformOptions) {
+ font.mPlatformOptions = Some(*aPlatformOptions);
+ }
+ if (aNumVariations) {
+ font.mNumVariations = aNumVariations;
+ font.mVariations.reset(new gfx::FontVariation[aNumVariations]);
+ PodCopy(font.mVariations.get(),
+ reinterpret_cast<const gfx::FontVariation*>(aVariations),
+ aNumVariations);
+ }
+ }
+}
+
+void DeleteBlobFont(WrFontInstanceKey aKey) {
+ StaticMutexAutoLock lock(sFontDataTableLock);
+ auto i = sBlobFontTable.find(aKey);
+ if (i != sBlobFontTable.end()) {
+ sBlobFontTable.erase(i);
+ }
+}
+
+} // extern
+
+static RefPtr<UnscaledFont> GetUnscaledFont(Translator* aTranslator,
+ WrFontKey aKey) {
+ auto i = sFontDataTable.find(aKey);
+ if (i == sFontDataTable.end()) {
+ gfxDevCrash(LogReason::UnscaledFontNotFound)
+ << "Failed to get UnscaledFont entry for FontKey " << aKey.mHandle
+ << " because " << sFontDeleteLog.Find(aKey);
+ return nullptr;
+ }
+ FontTemplate& data = i->second;
+ if (data.mUnscaledFont) {
+ return data.mUnscaledFont;
+ }
+ MOZ_ASSERT(data.mData);
+ FontType type =
+#ifdef XP_DARWIN
+ FontType::MAC;
+#elif defined(XP_WIN)
+ FontType::DWRITE;
+#elif defined(ANDROID)
+ FontType::FREETYPE;
+#else
+ FontType::FONTCONFIG;
+#endif
+ // makes a copy of the data
+ RefPtr<NativeFontResource> fontResource = Factory::CreateNativeFontResource(
+ (uint8_t*)data.mData, data.mSize, type, aTranslator->GetFontContext());
+ RefPtr<UnscaledFont> unscaledFont;
+ if (!fontResource) {
+ gfxDevCrash(LogReason::NativeFontResourceNotFound)
+ << "Failed to create NativeFontResource for FontKey " << aKey.mHandle;
+ } else {
+ // Instance data is only needed for GDI fonts which webrender does not
+ // support.
+ unscaledFont = fontResource->CreateUnscaledFont(data.mIndex, nullptr, 0);
+ if (!unscaledFont) {
+ gfxDevCrash(LogReason::UnscaledFontNotFound)
+ << "Failed to create UnscaledFont for FontKey " << aKey.mHandle;
+ }
+ }
+ data.mUnscaledFont = unscaledFont;
+ return unscaledFont;
+}
+
+static RefPtr<ScaledFont> GetScaledFont(Translator* aTranslator,
+ WrFontInstanceKey aKey) {
+ StaticMutexAutoLock lock(sFontDataTableLock);
+ auto i = sBlobFontTable.find(aKey);
+ if (i == sBlobFontTable.end()) {
+ gfxDevCrash(LogReason::ScaledFontNotFound)
+ << "Failed to get ScaledFont entry for FontInstanceKey "
+ << aKey.mHandle;
+ return nullptr;
+ }
+ FontInstanceData& data = i->second;
+ if (data.mScaledFont) {
+ return data.mScaledFont;
+ }
+ RefPtr<UnscaledFont> unscaled = GetUnscaledFont(aTranslator, data.mFontKey);
+ if (!unscaled) {
+ return nullptr;
+ }
+ RefPtr<ScaledFont> scaled = unscaled->CreateScaledFontFromWRFont(
+ data.mSize, data.mOptions.ptrOr(nullptr),
+ data.mPlatformOptions.ptrOr(nullptr), data.mVariations.get(),
+ data.mNumVariations);
+ if (!scaled) {
+ gfxDevCrash(LogReason::ScaledFontNotFound)
+ << "Failed to create ScaledFont for FontKey " << aKey.mHandle;
+ }
+ data.mScaledFont = scaled;
+ return data.mScaledFont;
+}
+
+template <typename T>
+T ConvertFromBytes(const uint8_t* bytes) {
+ T t;
+ memcpy(&t, bytes, sizeof(T));
+ return t;
+}
+
+struct Reader {
+ const uint8_t* buf;
+ size_t len;
+ size_t pos;
+
+ Reader(const uint8_t* buf, size_t len) : buf(buf), len(len), pos(0) {}
+
+ template <typename T>
+ T Read() {
+ MOZ_RELEASE_ASSERT(pos + sizeof(T) <= len);
+ T ret = ConvertFromBytes<T>(buf + pos);
+ pos += sizeof(T);
+ return ret;
+ }
+
+ size_t ReadSize() { return Read<size_t>(); }
+ int ReadInt() { return Read<int>(); }
+
+ IntRectAbsolute ReadBounds() { return Read<IntRectAbsolute>(); }
+
+ layers::BlobFont ReadBlobFont() { return Read<layers::BlobFont>(); }
+};
+
+static bool Moz2DRenderCallback(const Range<const uint8_t> aBlob,
+ gfx::SurfaceFormat aFormat,
+ const mozilla::wr::DeviceIntRect* aVisibleRect,
+ const mozilla::wr::LayoutIntRect* aRenderRect,
+ const uint16_t aTileSize,
+ const mozilla::wr::TileOffset* aTileOffset,
+ const mozilla::wr::LayoutIntRect* aDirtyRect,
+ Range<uint8_t> aOutput) {
+ IntSize size(aRenderRect->width(), aRenderRect->height());
+ AUTO_PROFILER_TRACING_MARKER("WebRender", "RasterizeSingleBlob", GRAPHICS);
+ MOZ_RELEASE_ASSERT(size.width > 0 && size.height > 0);
+ if (size.width <= 0 || size.height <= 0) {
+ return false;
+ }
+
+ auto stride = size.width * gfx::BytesPerPixel(aFormat);
+
+ if (aOutput.length() < static_cast<size_t>(size.height * stride)) {
+ return false;
+ }
+
+ // In bindings.rs we allocate a buffer filled with opaque white.
+ bool uninitialized = false;
+
+ RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateDrawTargetForData(
+ gfx::BackendType::SKIA, aOutput.begin().get(), size, stride, aFormat,
+ uninitialized);
+
+ if (!dt) {
+ return false;
+ }
+
+ // We try hard to not have empty blobs but we can end up with
+ // them because of CompositorHitTestInfo and merging.
+ size_t footerSize = sizeof(size_t);
+ MOZ_RELEASE_ASSERT(aBlob.length() >= footerSize);
+ size_t indexOffset = ConvertFromBytes<size_t>(aBlob.end().get() - footerSize);
+
+ // aRenderRect is the part of the blob that we are currently rendering
+ // (for example a tile) in the same coordinate space as aVisibleRect.
+ IntPoint origin = gfx::IntPoint(aRenderRect->min.x, aRenderRect->min.y);
+
+ MOZ_RELEASE_ASSERT(indexOffset <= aBlob.length() - footerSize);
+ Reader reader(aBlob.begin().get() + indexOffset,
+ aBlob.length() - footerSize - indexOffset);
+
+ dt = gfx::Factory::CreateOffsetDrawTarget(dt, origin);
+
+ auto bounds = gfx::IntRect(origin, size);
+
+ if (aDirtyRect) {
+ gfx::Rect dirty(aDirtyRect->min.x, aDirtyRect->min.y, aDirtyRect->width(),
+ aDirtyRect->height());
+ dt->PushClipRect(dirty);
+ bounds =
+ bounds.Intersect(IntRect(aDirtyRect->min.x, aDirtyRect->min.y,
+ aDirtyRect->width(), aDirtyRect->height()));
+ }
+
+ bool ret = true;
+ size_t offset = 0;
+ auto absBounds = IntRectAbsolute::FromRect(bounds);
+ while (reader.pos < reader.len) {
+ size_t end = reader.ReadSize();
+ size_t extra_end = reader.ReadSize();
+ MOZ_RELEASE_ASSERT(extra_end >= end);
+ MOZ_RELEASE_ASSERT(extra_end < aBlob.length());
+
+ auto combinedBounds = absBounds.Intersect(reader.ReadBounds());
+ if (combinedBounds.IsEmpty()) {
+ offset = extra_end;
+ continue;
+ }
+
+ layers::WebRenderTranslator translator(dt);
+ Reader fontReader(aBlob.begin().get() + end, extra_end - end);
+ size_t count = fontReader.ReadSize();
+ for (size_t i = 0; i < count; i++) {
+ layers::BlobFont blobFont = fontReader.ReadBlobFont();
+ RefPtr<ScaledFont> scaledFont =
+ GetScaledFont(&translator, blobFont.mFontInstanceKey);
+ translator.AddScaledFont(blobFont.mScaledFontPtr, scaledFont);
+ }
+
+ Range<const uint8_t> blob(aBlob.begin() + offset, aBlob.begin() + end);
+ ret =
+ translator.TranslateRecording((char*)blob.begin().get(), blob.length());
+ if (!ret) {
+ gfxCriticalNote << "Replay failure: " << translator.GetError();
+ MOZ_RELEASE_ASSERT(false);
+ }
+ offset = extra_end;
+ }
+
+ if (StaticPrefs::gfx_webrender_debug_blob_paint_flashing()) {
+ dt->SetTransform(gfx::Matrix());
+ float r = float(rand()) / float(RAND_MAX);
+ float g = float(rand()) / float(RAND_MAX);
+ float b = float(rand()) / float(RAND_MAX);
+ dt->FillRect(gfx::Rect(origin.x, origin.y, size.width, size.height),
+ gfx::ColorPattern(gfx::DeviceColor(r, g, b, 0.5)));
+ }
+
+ if (aDirtyRect) {
+ dt->PopClip();
+ }
+
+#if 0
+ static int i = 0;
+ char filename[40];
+ sprintf(filename, "out%d.png", i++);
+ gfxUtils::WriteAsPNG(dt, filename);
+#endif
+
+ return ret;
+}
+
+} // namespace wr
+} // namespace mozilla
+
+extern "C" {
+
+bool wr_moz2d_render_cb(const mozilla::wr::ByteSlice blob,
+ mozilla::wr::ImageFormat aFormat,
+ const mozilla::wr::LayoutIntRect* aRenderRect,
+ const mozilla::wr::DeviceIntRect* aVisibleRect,
+ const uint16_t aTileSize,
+ const mozilla::wr::TileOffset* aTileOffset,
+ const mozilla::wr::LayoutIntRect* aDirtyRect,
+ mozilla::wr::MutByteSlice output) {
+ return mozilla::wr::Moz2DRenderCallback(
+ mozilla::wr::ByteSliceToRange(blob),
+ mozilla::wr::ImageFormatToSurfaceFormat(aFormat), aVisibleRect,
+ aRenderRect, aTileSize, aTileOffset, aDirtyRect,
+ mozilla::wr::MutByteSliceToRange(output));
+}
+
+} // extern
diff --git a/gfx/webrender_bindings/README.webrender b/gfx/webrender_bindings/README.webrender
new file mode 100644
index 0000000000..a42c33092d
--- /dev/null
+++ b/gfx/webrender_bindings/README.webrender
@@ -0,0 +1,23 @@
+To build and run WebRender in Gecko:
+1. Install Rust if you don't have it already
+ If you are doing gecko builds already, you should already have Rust as it is a build requirement.
+ If not, you can install it using |mach bootstrap| (recommended) or from https://www.rust-lang.org/
+ Note: If installing manually, use the stable 64-bit release - on Windows make sure to use the MSVC ABI installer.
+ Ensure that rustc and cargo are in your $PATH (adding $HOME/.cargo/bin/ should be sufficient)
+2. Build using |mach build|.
+ You don't need anything special in your mozconfig for local builds; webrender will be built by default.
+3. Run with |MOZ_WEBRENDER=1| in your environment. e.g. |MOZ_WEBRENDER=1 ./mach run|.
+ Alternatively, you can set the gfx.webrender.enabled pref to true (browser restart required).
+ Note that on Linux, acceleration is disabled by default and it needs to be enabled for WebRender to work.
+ On Linux you can enable acceleration by putting |MOZ_ACCELERATED=1| in your environment, or setting layers.acceleration.force-enabled to true in about:config.
+4. Verify WebRender is enabled. You can do this by going to about:support and checking the "Compositing" line in the Graphics section. It should say "WebRender".
+ There should also be a WebRender section under "Decision Log" in about:support, which will provide some more detail on what caused it to be enabled/disabled.
+
+When making changes:
+ - Make the changes you want.
+ - Run |mach build| or |mach build binaries| as desired.
+
+For a debug webrender build:
+ Use a debug mozconfig (ac_add_options --enable-debug)
+ You can also use an opt build but make webrender less optimized by putting opt-level=0 in the [profile.release] section of your toolkit/library/rust/Cargo.toml file
+ See also https://groups.google.com/forum/#!topic/mozilla.dev.servo/MbeMcqqO1fs
diff --git a/gfx/webrender_bindings/RenderAndroidHardwareBufferTextureHost.cpp b/gfx/webrender_bindings/RenderAndroidHardwareBufferTextureHost.cpp
new file mode 100644
index 0000000000..7b4db85f4b
--- /dev/null
+++ b/gfx/webrender_bindings/RenderAndroidHardwareBufferTextureHost.cpp
@@ -0,0 +1,246 @@
+/* -*- 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 "RenderAndroidHardwareBufferTextureHost.h"
+
+#include "mozilla/layers/AndroidHardwareBuffer.h"
+#include "mozilla/webrender/RenderThread.h"
+#include "GLContextEGL.h"
+#include "GLLibraryEGL.h"
+#include "GLReadTexImageHelper.h"
+#include "OGLShaderConfig.h"
+
+namespace mozilla {
+namespace wr {
+
+RenderAndroidHardwareBufferTextureHost::RenderAndroidHardwareBufferTextureHost(
+ layers::AndroidHardwareBuffer* aAndroidHardwareBuffer)
+ : mAndroidHardwareBuffer(aAndroidHardwareBuffer),
+ mEGLImage(EGL_NO_IMAGE),
+ mTextureHandle(0) {
+ MOZ_ASSERT(mAndroidHardwareBuffer);
+ MOZ_COUNT_CTOR_INHERITED(RenderAndroidHardwareBufferTextureHost,
+ RenderTextureHost);
+}
+
+RenderAndroidHardwareBufferTextureHost::
+ ~RenderAndroidHardwareBufferTextureHost() {
+ MOZ_COUNT_DTOR_INHERITED(RenderAndroidHardwareBufferTextureHost,
+ RenderTextureHost);
+ DeleteTextureHandle();
+ DestroyEGLImage();
+}
+
+gfx::IntSize RenderAndroidHardwareBufferTextureHost::GetSize() const {
+ if (mAndroidHardwareBuffer) {
+ return mAndroidHardwareBuffer->mSize;
+ }
+ return gfx::IntSize();
+}
+
+bool RenderAndroidHardwareBufferTextureHost::EnsureLockable() {
+ if (!mAndroidHardwareBuffer) {
+ return false;
+ }
+
+ auto fenceFd = mAndroidHardwareBuffer->GetAndResetAcquireFence();
+ if (fenceFd.IsValid()) {
+ const auto& gle = gl::GLContextEGL::Cast(mGL);
+ const auto& egl = gle->mEgl;
+
+ auto rawFD = fenceFd.TakePlatformHandle();
+ const EGLint attribs[] = {LOCAL_EGL_SYNC_NATIVE_FENCE_FD_ANDROID,
+ rawFD.get(), LOCAL_EGL_NONE};
+
+ EGLSync sync =
+ egl->fCreateSync(LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
+ if (sync) {
+ // Release fd here, since it is owned by EGLSync
+ Unused << rawFD.release();
+
+ if (egl->IsExtensionSupported(gl::EGLExtension::KHR_wait_sync)) {
+ egl->fWaitSync(sync, 0);
+ } else {
+ egl->fClientWaitSync(sync, 0, LOCAL_EGL_FOREVER);
+ }
+ egl->fDestroySync(sync);
+ } else {
+ gfxCriticalNote << "Failed to create EGLSync from acquire fence fd";
+ }
+ }
+
+ if (mTextureHandle) {
+ return true;
+ }
+
+ if (!mEGLImage) {
+ // XXX add crop handling for video
+ // Should only happen the first time.
+ const auto& gle = gl::GLContextEGL::Cast(mGL);
+ const auto& egl = gle->mEgl;
+
+ const EGLint attrs[] = {
+ LOCAL_EGL_IMAGE_PRESERVED,
+ LOCAL_EGL_TRUE,
+ LOCAL_EGL_NONE,
+ LOCAL_EGL_NONE,
+ };
+
+ EGLClientBuffer clientBuffer = egl->mLib->fGetNativeClientBufferANDROID(
+ mAndroidHardwareBuffer->GetNativeBuffer());
+ mEGLImage = egl->fCreateImage(
+ EGL_NO_CONTEXT, LOCAL_EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs);
+ }
+ MOZ_ASSERT(mEGLImage);
+
+ mGL->fGenTextures(1, &mTextureHandle);
+ mGL->fBindTexture(LOCAL_GL_TEXTURE_EXTERNAL, mTextureHandle);
+ mGL->fTexParameteri(LOCAL_GL_TEXTURE_EXTERNAL, LOCAL_GL_TEXTURE_WRAP_T,
+ LOCAL_GL_CLAMP_TO_EDGE);
+ mGL->fTexParameteri(LOCAL_GL_TEXTURE_EXTERNAL, LOCAL_GL_TEXTURE_WRAP_S,
+ LOCAL_GL_CLAMP_TO_EDGE);
+ mGL->fEGLImageTargetTexture2D(LOCAL_GL_TEXTURE_EXTERNAL, mEGLImage);
+
+ ActivateBindAndTexParameteri(mGL, LOCAL_GL_TEXTURE0,
+ LOCAL_GL_TEXTURE_EXTERNAL_OES, mTextureHandle);
+ return true;
+}
+
+wr::WrExternalImage RenderAndroidHardwareBufferTextureHost::Lock(
+ uint8_t aChannelIndex, gl::GLContext* aGL) {
+ MOZ_ASSERT(aChannelIndex == 0);
+
+ if (mGL.get() != aGL) {
+ if (mGL) {
+ // This should not happen.
+ MOZ_ASSERT_UNREACHABLE("Unexpected GL context");
+ return InvalidToWrExternalImage();
+ }
+ mGL = aGL;
+ }
+
+ if (!mGL || !mGL->MakeCurrent()) {
+ return InvalidToWrExternalImage();
+ }
+
+ if (!EnsureLockable()) {
+ return InvalidToWrExternalImage();
+ }
+
+ const auto uvs = GetUvCoords(GetSize());
+ return NativeTextureToWrExternalImage(
+ mTextureHandle, uvs.first.x, uvs.first.y, uvs.second.x, uvs.second.y);
+}
+
+void RenderAndroidHardwareBufferTextureHost::Unlock() {}
+
+size_t RenderAndroidHardwareBufferTextureHost::Bytes() {
+ return GetSize().width * GetSize().height *
+ BytesPerPixel(mAndroidHardwareBuffer->mFormat);
+}
+
+void RenderAndroidHardwareBufferTextureHost::DeleteTextureHandle() {
+ if (!mTextureHandle) {
+ return;
+ }
+ MOZ_ASSERT(mGL);
+ mGL->fDeleteTextures(1, &mTextureHandle);
+ mTextureHandle = 0;
+}
+
+void RenderAndroidHardwareBufferTextureHost::DestroyEGLImage() {
+ if (!mEGLImage) {
+ return;
+ }
+ MOZ_ASSERT(mGL);
+ const auto& gle = gl::GLContextEGL::Cast(mGL);
+ const auto& egl = gle->mEgl;
+ egl->fDestroyImage(mEGLImage);
+ mEGLImage = EGL_NO_IMAGE;
+}
+
+gfx::SurfaceFormat RenderAndroidHardwareBufferTextureHost::GetFormat() const {
+ MOZ_ASSERT(mAndroidHardwareBuffer->mFormat == gfx::SurfaceFormat::R8G8B8A8 ||
+ mAndroidHardwareBuffer->mFormat == gfx::SurfaceFormat::R8G8B8X8);
+
+ if (mAndroidHardwareBuffer->mFormat == gfx::SurfaceFormat::R8G8B8A8) {
+ return gfx::SurfaceFormat::B8G8R8A8;
+ }
+
+ if (mAndroidHardwareBuffer->mFormat == gfx::SurfaceFormat::R8G8B8X8) {
+ return gfx::SurfaceFormat::B8G8R8X8;
+ }
+
+ gfxCriticalNoteOnce
+ << "Unexpected color format of RenderAndroidSurfaceTextureHost";
+
+ return gfx::SurfaceFormat::UNKNOWN;
+}
+
+already_AddRefed<DataSourceSurface>
+RenderAndroidHardwareBufferTextureHost::ReadTexImage() {
+ if (!mGL) {
+ mGL = RenderThread::Get()->SingletonGL();
+ if (!mGL) {
+ return nullptr;
+ }
+ }
+
+ if (!EnsureLockable()) {
+ return nullptr;
+ }
+
+ /* Allocate resulting image surface */
+ int32_t stride = GetSize().width * BytesPerPixel(GetFormat());
+ RefPtr<DataSourceSurface> surf = Factory::CreateDataSourceSurfaceWithStride(
+ GetSize(), GetFormat(), stride);
+ if (!surf) {
+ return nullptr;
+ }
+
+ layers::ShaderConfigOGL config = layers::ShaderConfigFromTargetAndFormat(
+ LOCAL_GL_TEXTURE_EXTERNAL, mAndroidHardwareBuffer->mFormat);
+ int shaderConfig = config.mFeatures;
+
+ bool ret = mGL->ReadTexImageHelper()->ReadTexImage(
+ surf, mTextureHandle, LOCAL_GL_TEXTURE_EXTERNAL, GetSize(), shaderConfig,
+ /* aYInvert */ false);
+ if (!ret) {
+ return nullptr;
+ }
+
+ return surf.forget();
+}
+
+bool RenderAndroidHardwareBufferTextureHost::MapPlane(
+ RenderCompositor* aCompositor, uint8_t aChannelIndex,
+ PlaneInfo& aPlaneInfo) {
+ RefPtr<gfx::DataSourceSurface> readback = ReadTexImage();
+ if (!readback) {
+ return false;
+ }
+
+ DataSourceSurface::MappedSurface map;
+ if (!readback->Map(DataSourceSurface::MapType::READ, &map)) {
+ return false;
+ }
+
+ mReadback = readback;
+ aPlaneInfo.mSize = GetSize();
+ aPlaneInfo.mStride = map.mStride;
+ aPlaneInfo.mData = map.mData;
+ return true;
+}
+
+void RenderAndroidHardwareBufferTextureHost::UnmapPlanes() {
+ if (mReadback) {
+ mReadback->Unmap();
+ mReadback = nullptr;
+ }
+}
+
+} // namespace wr
+} // namespace mozilla
diff --git a/gfx/webrender_bindings/RenderAndroidHardwareBufferTextureHost.h b/gfx/webrender_bindings/RenderAndroidHardwareBufferTextureHost.h
new file mode 100644
index 0000000000..cd79c791ed
--- /dev/null
+++ b/gfx/webrender_bindings/RenderAndroidHardwareBufferTextureHost.h
@@ -0,0 +1,73 @@
+/* -*- 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_RenderAndroidHardwareBufferTextureHost_H
+#define MOZILLA_GFX_RenderAndroidHardwareBufferTextureHost_H
+
+#include "GLContextTypes.h"
+#include "GLTypes.h"
+#include "RenderTextureHostSWGL.h"
+
+namespace mozilla {
+
+namespace layers {
+class AndroidHardwareBuffer;
+}
+
+namespace wr {
+
+class RenderAndroidHardwareBufferTextureHost final
+ : public RenderTextureHostSWGL {
+ public:
+ explicit RenderAndroidHardwareBufferTextureHost(
+ layers::AndroidHardwareBuffer* aAndroidHardwareBuffer);
+
+ wr::WrExternalImage Lock(uint8_t aChannelIndex, gl::GLContext* aGL) override;
+ void Unlock() override;
+
+ size_t Bytes() override;
+
+ RenderAndroidHardwareBufferTextureHost*
+ AsRenderAndroidHardwareBufferTextureHost() override {
+ return this;
+ }
+
+ // RenderTextureHostSWGL
+ gfx::SurfaceFormat GetFormat() const override;
+ gfx::ColorDepth GetColorDepth() const override {
+ return gfx::ColorDepth::COLOR_8;
+ }
+ size_t GetPlaneCount() const override { return 1; }
+ bool MapPlane(RenderCompositor* aCompositor, uint8_t aChannelIndex,
+ PlaneInfo& aPlaneInfo) override;
+ void UnmapPlanes() override;
+
+ layers::AndroidHardwareBuffer* GetAndroidHardwareBuffer() {
+ return mAndroidHardwareBuffer;
+ }
+
+ gfx::IntSize GetSize() const;
+
+ private:
+ virtual ~RenderAndroidHardwareBufferTextureHost();
+ bool EnsureLockable();
+ void DestroyEGLImage();
+ void DeleteTextureHandle();
+ already_AddRefed<gfx::DataSourceSurface> ReadTexImage();
+
+ const RefPtr<layers::AndroidHardwareBuffer> mAndroidHardwareBuffer;
+
+ RefPtr<gl::GLContext> mGL;
+ EGLImage mEGLImage;
+ GLuint mTextureHandle;
+
+ RefPtr<gfx::DataSourceSurface> mReadback;
+};
+
+} // namespace wr
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_RenderAndroidHardwareBufferTextureHost_H
diff --git a/gfx/webrender_bindings/RenderAndroidSurfaceTextureHost.cpp b/gfx/webrender_bindings/RenderAndroidSurfaceTextureHost.cpp
new file mode 100644
index 0000000000..c41e244e06
--- /dev/null
+++ b/gfx/webrender_bindings/RenderAndroidSurfaceTextureHost.cpp
@@ -0,0 +1,325 @@
+/* -*- 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 "RenderAndroidSurfaceTextureHost.h"
+
+#include "GLReadTexImageHelper.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/webrender/RenderThread.h"
+#include "GLContext.h"
+#include "AndroidSurfaceTexture.h"
+
+namespace mozilla {
+namespace wr {
+
+RenderAndroidSurfaceTextureHost::RenderAndroidSurfaceTextureHost(
+ const java::GeckoSurfaceTexture::GlobalRef& aSurfTex, gfx::IntSize aSize,
+ gfx::SurfaceFormat aFormat, bool aContinuousUpdate,
+ Maybe<gfx::Matrix4x4> aTransformOverride, bool aIsRemoteTexture)
+ : mSurfTex(aSurfTex),
+ mSize(aSize),
+ mFormat(aFormat),
+ mContinuousUpdate(aContinuousUpdate),
+ mTransformOverride(aTransformOverride),
+ mPrepareStatus(STATUS_NONE),
+ mAttachedToGLContext(false),
+ mIsRemoteTexture(aIsRemoteTexture) {
+ MOZ_COUNT_CTOR_INHERITED(RenderAndroidSurfaceTextureHost, RenderTextureHost);
+
+ if (mSurfTex) {
+ mSurfTex->IncrementUse();
+ }
+}
+
+RenderAndroidSurfaceTextureHost::~RenderAndroidSurfaceTextureHost() {
+ MOZ_ASSERT(RenderThread::IsInRenderThread());
+ MOZ_COUNT_DTOR_INHERITED(RenderAndroidSurfaceTextureHost, RenderTextureHost);
+ // The SurfaceTexture gets destroyed when its use count reaches zero.
+ if (mSurfTex) {
+ mSurfTex->DecrementUse();
+ }
+}
+
+wr::WrExternalImage RenderAndroidSurfaceTextureHost::Lock(uint8_t aChannelIndex,
+ gl::GLContext* aGL) {
+ MOZ_ASSERT(aChannelIndex == 0);
+ MOZ_ASSERT((mPrepareStatus == STATUS_PREPARED) ||
+ (!mSurfTex->IsSingleBuffer() &&
+ mPrepareStatus == STATUS_UPDATE_TEX_IMAGE_NEEDED) ||
+ mIsRemoteTexture);
+
+ if (mIsRemoteTexture) {
+ EnsureAttachedToGLContext();
+ }
+
+ if (mGL.get() != aGL) {
+ // This should not happen. On android, SingletonGL is used.
+ MOZ_ASSERT_UNREACHABLE("Unexpected GL context");
+ return InvalidToWrExternalImage();
+ }
+
+ if (!mSurfTex || !mGL || !mGL->MakeCurrent()) {
+ return InvalidToWrExternalImage();
+ }
+
+ MOZ_ASSERT(mAttachedToGLContext);
+ if (!mAttachedToGLContext) {
+ return InvalidToWrExternalImage();
+ }
+
+ UpdateTexImageIfNecessary();
+
+ const auto uvs = GetUvCoords(mSize);
+ return NativeTextureToWrExternalImage(mSurfTex->GetTexName(), uvs.first.x,
+ uvs.first.y, uvs.second.x,
+ uvs.second.y);
+}
+
+void RenderAndroidSurfaceTextureHost::Unlock() {}
+
+bool RenderAndroidSurfaceTextureHost::EnsureAttachedToGLContext() {
+ // During handling WebRenderError, GeckoSurfaceTexture should not be attached
+ // to GLContext.
+ if (RenderThread::Get()->IsHandlingWebRenderError()) {
+ return false;
+ }
+
+ if (mAttachedToGLContext) {
+ return true;
+ }
+
+ if (!mGL) {
+ mGL = RenderThread::Get()->SingletonGL();
+ }
+
+ if (!mSurfTex || !mGL || !mGL->MakeCurrent()) {
+ return false;
+ }
+
+ if (!mSurfTex->IsAttachedToGLContext((int64_t)mGL.get())) {
+ GLuint texName;
+ mGL->fGenTextures(1, &texName);
+ ActivateBindAndTexParameteri(mGL, LOCAL_GL_TEXTURE0,
+ LOCAL_GL_TEXTURE_EXTERNAL_OES, texName);
+
+ if (NS_FAILED(mSurfTex->AttachToGLContext((int64_t)mGL.get(), texName))) {
+ MOZ_ASSERT(0);
+ mGL->fDeleteTextures(1, &texName);
+ return false;
+ }
+ }
+
+ mAttachedToGLContext = true;
+ return true;
+}
+
+void RenderAndroidSurfaceTextureHost::PrepareForUse() {
+ // When SurfaceTexture is single buffer mode, UpdateTexImage needs to be
+ // called only once for each publish. If UpdateTexImage is called more
+ // than once, it causes hang on puglish side. And UpdateTexImage needs to
+ // be called on render thread, since the SurfaceTexture is consumed on render
+ // thread.
+ MOZ_ASSERT(RenderThread::IsInRenderThread());
+ MOZ_ASSERT(mPrepareStatus == STATUS_NONE);
+
+ if (mContinuousUpdate || !mSurfTex) {
+ return;
+ }
+
+ mPrepareStatus = STATUS_MIGHT_BE_USED_BY_WR;
+
+ if (mSurfTex->IsSingleBuffer()) {
+ EnsureAttachedToGLContext();
+ // When SurfaceTexture is single buffer mode, it is OK to call
+ // UpdateTexImage() here.
+ mSurfTex->UpdateTexImage();
+ mPrepareStatus = STATUS_PREPARED;
+ }
+}
+
+void RenderAndroidSurfaceTextureHost::NotifyForUse() {
+ MOZ_ASSERT(RenderThread::IsInRenderThread());
+
+ if (mPrepareStatus == STATUS_MIGHT_BE_USED_BY_WR) {
+ // This happens when SurfaceTexture of video is rendered on WebRender.
+ // There is a case that SurfaceTexture is not rendered on WebRender, instead
+ // it is rendered to WebGL and the SurfaceTexture should not be attached to
+ // gl context of WebRender. It is ugly. But it is same as Compositor
+ // rendering.
+ MOZ_ASSERT(!mSurfTex->IsSingleBuffer());
+ if (!EnsureAttachedToGLContext()) {
+ return;
+ }
+ mPrepareStatus = STATUS_UPDATE_TEX_IMAGE_NEEDED;
+ }
+}
+
+void RenderAndroidSurfaceTextureHost::NotifyNotUsed() {
+ MOZ_ASSERT(RenderThread::IsInRenderThread());
+
+ if (!mSurfTex) {
+ MOZ_ASSERT(mPrepareStatus == STATUS_NONE);
+ return;
+ }
+
+ if (mIsRemoteTexture) {
+ UpdateTexImageIfNecessary();
+ }
+
+ if (mSurfTex->IsSingleBuffer()) {
+ MOZ_ASSERT(mPrepareStatus == STATUS_PREPARED);
+ MOZ_ASSERT(mAttachedToGLContext);
+ // Release SurfaceTexture's buffer to client side.
+ mGL->MakeCurrent();
+ mSurfTex->ReleaseTexImage();
+ } else if (mPrepareStatus == STATUS_UPDATE_TEX_IMAGE_NEEDED) {
+ MOZ_ASSERT(mAttachedToGLContext);
+ // This could happen when video frame was skipped. UpdateTexImage() neeeds
+ // to be called for adjusting SurfaceTexture's buffer status.
+ mSurfTex->UpdateTexImage();
+ }
+
+ mPrepareStatus = STATUS_NONE;
+}
+
+void RenderAndroidSurfaceTextureHost::UpdateTexImageIfNecessary() {
+ if (mIsRemoteTexture) {
+ EnsureAttachedToGLContext();
+ if (mPrepareStatus == STATUS_NONE) {
+ PrepareForUse();
+ }
+ if (mPrepareStatus == STATUS_MIGHT_BE_USED_BY_WR) {
+ NotifyForUse();
+ }
+ }
+
+ if (mContinuousUpdate) {
+ MOZ_ASSERT(!mSurfTex->IsSingleBuffer());
+ mSurfTex->UpdateTexImage();
+ } else if (mPrepareStatus == STATUS_UPDATE_TEX_IMAGE_NEEDED) {
+ MOZ_ASSERT(!mSurfTex->IsSingleBuffer());
+ // When SurfaceTexture is not single buffer mode, call UpdateTexImage() once
+ // just before rendering. During playing video, one SurfaceTexture is used
+ // for all RenderAndroidSurfaceTextureHosts of video.
+ mSurfTex->UpdateTexImage();
+ mPrepareStatus = STATUS_PREPARED;
+ }
+}
+
+gfx::SurfaceFormat RenderAndroidSurfaceTextureHost::GetFormat() const {
+ MOZ_ASSERT(mFormat == gfx::SurfaceFormat::R8G8B8A8 ||
+ mFormat == gfx::SurfaceFormat::R8G8B8X8);
+
+ if (mFormat == gfx::SurfaceFormat::R8G8B8A8) {
+ return gfx::SurfaceFormat::B8G8R8A8;
+ }
+
+ if (mFormat == gfx::SurfaceFormat::R8G8B8X8) {
+ return gfx::SurfaceFormat::B8G8R8X8;
+ }
+
+ gfxCriticalNoteOnce
+ << "Unexpected color format of RenderAndroidSurfaceTextureHost";
+
+ return gfx::SurfaceFormat::UNKNOWN;
+}
+
+already_AddRefed<DataSourceSurface>
+RenderAndroidSurfaceTextureHost::ReadTexImage() {
+ if (!mGL) {
+ mGL = RenderThread::Get()->SingletonGL();
+ if (!mGL) {
+ return nullptr;
+ }
+ }
+
+ /* Allocate resulting image surface */
+ int32_t stride = mSize.width * BytesPerPixel(GetFormat());
+ RefPtr<DataSourceSurface> surf =
+ Factory::CreateDataSourceSurfaceWithStride(mSize, GetFormat(), stride);
+ if (!surf) {
+ return nullptr;
+ }
+
+ layers::ShaderConfigOGL config = layers::ShaderConfigFromTargetAndFormat(
+ LOCAL_GL_TEXTURE_EXTERNAL, mFormat);
+ int shaderConfig = config.mFeatures;
+
+ bool ret = mGL->ReadTexImageHelper()->ReadTexImage(
+ surf, mSurfTex->GetTexName(), LOCAL_GL_TEXTURE_EXTERNAL, mSize,
+ shaderConfig, /* aYInvert */ false);
+ if (!ret) {
+ return nullptr;
+ }
+
+ return surf.forget();
+}
+
+bool RenderAndroidSurfaceTextureHost::MapPlane(RenderCompositor* aCompositor,
+ uint8_t aChannelIndex,
+ PlaneInfo& aPlaneInfo) {
+ UpdateTexImageIfNecessary();
+
+ RefPtr<gfx::DataSourceSurface> readback = ReadTexImage();
+ if (!readback) {
+ return false;
+ }
+
+ DataSourceSurface::MappedSurface map;
+ if (!readback->Map(DataSourceSurface::MapType::READ, &map)) {
+ return false;
+ }
+
+ mReadback = readback;
+ aPlaneInfo.mSize = mSize;
+ aPlaneInfo.mStride = map.mStride;
+ aPlaneInfo.mData = map.mData;
+ return true;
+}
+
+void RenderAndroidSurfaceTextureHost::UnmapPlanes() {
+ if (mReadback) {
+ mReadback->Unmap();
+ mReadback = nullptr;
+ }
+}
+
+std::pair<gfx::Point, gfx::Point> RenderAndroidSurfaceTextureHost::GetUvCoords(
+ gfx::IntSize aTextureSize) const {
+ gfx::Matrix4x4 transform;
+
+ // GetTransformMatrix() returns the transform set by the producer side of the
+ // SurfaceTexture that must be applied to texture coordinates when
+ // sampling. In some cases we may have set an override value, such as in
+ // AndroidNativeWindowTextureData where we own the producer side, or for
+ // MediaCodec output on devices where where we know the value is incorrect.
+ if (mTransformOverride) {
+ transform = *mTransformOverride;
+ } else if (mSurfTex) {
+ const auto& surf = java::sdk::SurfaceTexture::LocalRef(
+ java::sdk::SurfaceTexture::Ref::From(mSurfTex));
+ gl::AndroidSurfaceTexture::GetTransformMatrix(surf, &transform);
+ }
+
+ // We expect this transform to always be rectilinear, usually just a
+ // y-flip and sometimes an x and y scale. This allows this function
+ // to simply transform and return 2 points here instead of 4.
+ MOZ_ASSERT(transform.IsRectilinear(),
+ "Unexpected non-rectilinear transform returned from "
+ "SurfaceTexture.GetTransformMatrix()");
+
+ transform.PostScale(aTextureSize.width, aTextureSize.height, 0.0);
+
+ gfx::Point uv0 = gfx::Point(0.0, 0.0);
+ gfx::Point uv1 = gfx::Point(1.0, 1.0);
+ uv0 = transform.TransformPoint(uv0);
+ uv1 = transform.TransformPoint(uv1);
+
+ return std::make_pair(uv0, uv1);
+}
+
+} // namespace wr
+} // namespace mozilla
diff --git a/gfx/webrender_bindings/RenderAndroidSurfaceTextureHost.h b/gfx/webrender_bindings/RenderAndroidSurfaceTextureHost.h
new file mode 100644
index 0000000000..1a6238a72f
--- /dev/null
+++ b/gfx/webrender_bindings/RenderAndroidSurfaceTextureHost.h
@@ -0,0 +1,96 @@
+/* -*- 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_RENDERANDROIDSURFACETEXTUREHOST_H
+#define MOZILLA_GFX_RENDERANDROIDSURFACETEXTUREHOST_H
+
+#include "mozilla/java/GeckoSurfaceTextureWrappers.h"
+#include "mozilla/layers/TextureHostOGL.h"
+#include "RenderTextureHostSWGL.h"
+
+namespace mozilla {
+
+namespace gfx {
+class DataSourceSurface;
+}
+
+namespace wr {
+
+class RenderAndroidSurfaceTextureHost final : public RenderTextureHostSWGL {
+ public:
+ explicit RenderAndroidSurfaceTextureHost(
+ const java::GeckoSurfaceTexture::GlobalRef& aSurfTex, gfx::IntSize aSize,
+ gfx::SurfaceFormat aFormat, bool aContinuousUpdate,
+ Maybe<gfx::Matrix4x4> aTransformOverride, bool aIsRemoteTexture);
+
+ wr::WrExternalImage Lock(uint8_t aChannelIndex, gl::GLContext* aGL) override;
+ void Unlock() override;
+
+ size_t Bytes() override {
+ return mSize.width * mSize.height * BytesPerPixel(mFormat);
+ }
+
+ void PrepareForUse() override;
+ void NotifyForUse() override;
+ void NotifyNotUsed() override;
+
+ // RenderTextureHostSWGL
+ gfx::SurfaceFormat GetFormat() const override;
+ gfx::ColorDepth GetColorDepth() const override {
+ return gfx::ColorDepth::COLOR_8;
+ }
+ size_t GetPlaneCount() const override { return 1; }
+ bool MapPlane(RenderCompositor* aCompositor, uint8_t aChannelIndex,
+ PlaneInfo& aPlaneInfo) override;
+ void UnmapPlanes() override;
+
+ RenderAndroidSurfaceTextureHost* AsRenderAndroidSurfaceTextureHost()
+ override {
+ return this;
+ }
+
+ void UpdateTexImageIfNecessary();
+
+ mozilla::java::GeckoSurfaceTexture::GlobalRef mSurfTex;
+ const gfx::IntSize mSize;
+ const gfx::SurfaceFormat mFormat;
+ // mContinuousUpdate was used for rendering video in the past.
+ // It is not used on current gecko.
+ const bool mContinuousUpdate;
+ const Maybe<gfx::Matrix4x4> mTransformOverride;
+
+ private:
+ virtual ~RenderAndroidSurfaceTextureHost();
+ bool EnsureAttachedToGLContext();
+
+ already_AddRefed<gfx::DataSourceSurface> ReadTexImage();
+
+ // Returns the UV coordinates to be used when sampling the texture, taking in
+ // to account the SurfaceTexture's transform if applicable.
+ std::pair<gfx::Point, gfx::Point> GetUvCoords(
+ gfx::IntSize aTextureSize) const override;
+
+ enum PrepareStatus {
+ STATUS_NONE,
+ STATUS_MIGHT_BE_USED_BY_WR,
+ STATUS_UPDATE_TEX_IMAGE_NEEDED,
+ STATUS_PREPARED
+ };
+
+ PrepareStatus mPrepareStatus;
+ bool mAttachedToGLContext;
+
+ RefPtr<gl::GLContext> mGL;
+
+ RefPtr<gfx::DataSourceSurface> mReadback;
+
+ bool mIsRemoteTexture;
+};
+
+} // namespace wr
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_RENDERANDROIDSURFACETEXTUREHOST_H
diff --git a/gfx/webrender_bindings/RenderBufferTextureHost.cpp b/gfx/webrender_bindings/RenderBufferTextureHost.cpp
new file mode 100644
index 0000000000..920556c7ef
--- /dev/null
+++ b/gfx/webrender_bindings/RenderBufferTextureHost.cpp
@@ -0,0 +1,254 @@
+/* -*- 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 "RenderBufferTextureHost.h"
+
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+
+namespace mozilla {
+namespace wr {
+
+RenderBufferTextureHost::RenderBufferTextureHost(
+ uint8_t* aBuffer, const layers::BufferDescriptor& aDescriptor)
+ : mBuffer(aBuffer),
+ mDescriptor(aDescriptor),
+ mMap(),
+ mYMap(),
+ mCbMap(),
+ mCrMap(),
+ mLocked(false) {
+ MOZ_COUNT_CTOR_INHERITED(RenderBufferTextureHost, RenderTextureHost);
+
+ switch (mDescriptor.type()) {
+ case layers::BufferDescriptor::TYCbCrDescriptor: {
+ const layers::YCbCrDescriptor& ycbcr = mDescriptor.get_YCbCrDescriptor();
+ mSize = ycbcr.display().Size();
+ mFormat = gfx::SurfaceFormat::YUV;
+ break;
+ }
+ case layers::BufferDescriptor::TRGBDescriptor: {
+ const layers::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");
+ }
+}
+
+RenderBufferTextureHost::~RenderBufferTextureHost() {
+ MOZ_COUNT_DTOR_INHERITED(RenderBufferTextureHost, RenderTextureHost);
+}
+
+wr::WrExternalImage RenderBufferTextureHost::Lock(uint8_t aChannelIndex,
+ gl::GLContext* aGL) {
+ if (!mLocked) {
+ if (!GetBuffer()) {
+ if (!mDestroyed) {
+ // We hit some problems to get the shmem.
+ gfxCriticalNote << "GetBuffer Failed";
+ }
+ return InvalidToWrExternalImage();
+ }
+ if (mFormat != gfx::SurfaceFormat::YUV) {
+ mSurface = gfx::Factory::CreateWrappingDataSourceSurface(
+ GetBuffer(),
+ layers::ImageDataSerializer::GetRGBStride(
+ mDescriptor.get_RGBDescriptor()),
+ mSize, mFormat);
+ if (NS_WARN_IF(!mSurface)) {
+ gfxCriticalNote << "DataSourceSurface is null";
+ return InvalidToWrExternalImage();
+ }
+ if (NS_WARN_IF(
+ !mSurface->Map(gfx::DataSourceSurface::MapType::READ, &mMap))) {
+ mSurface = nullptr;
+ gfxCriticalNote << "Failed to map Surface";
+ return InvalidToWrExternalImage();
+ }
+ } else {
+ const layers::YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
+ auto cbcrSize = layers::ImageDataSerializer::GetCroppedCbCrSize(desc);
+
+ mYSurface = gfx::Factory::CreateWrappingDataSourceSurface(
+ layers::ImageDataSerializer::GetYChannel(GetBuffer(), desc),
+ desc.yStride(), desc.display().Size(), gfx::SurfaceFormat::A8);
+ mCbSurface = gfx::Factory::CreateWrappingDataSourceSurface(
+ layers::ImageDataSerializer::GetCbChannel(GetBuffer(), desc),
+ desc.cbCrStride(), cbcrSize, gfx::SurfaceFormat::A8);
+ mCrSurface = gfx::Factory::CreateWrappingDataSourceSurface(
+ layers::ImageDataSerializer::GetCrChannel(GetBuffer(), desc),
+ desc.cbCrStride(), cbcrSize, gfx::SurfaceFormat::A8);
+ if (NS_WARN_IF(!mYSurface || !mCbSurface || !mCrSurface)) {
+ mYSurface = mCbSurface = mCrSurface = nullptr;
+ gfxCriticalNote << "YCbCr Surface is null";
+ return InvalidToWrExternalImage();
+ }
+ if (NS_WARN_IF(
+ !mYSurface->Map(gfx::DataSourceSurface::MapType::READ, &mYMap) ||
+ !mCbSurface->Map(gfx::DataSourceSurface::MapType::READ,
+ &mCbMap) ||
+ !mCrSurface->Map(gfx::DataSourceSurface::MapType::READ,
+ &mCrMap))) {
+ mYSurface = mCbSurface = mCrSurface = nullptr;
+ gfxCriticalNote << "Failed to map YCbCr Surface";
+ return InvalidToWrExternalImage();
+ }
+ }
+ mLocked = true;
+ }
+
+ RenderBufferData data = GetBufferDataForRender(aChannelIndex);
+ return RawDataToWrExternalImage(data.mData, data.mBufferSize);
+}
+
+void RenderBufferTextureHost::Unlock() {
+ if (mLocked) {
+ if (mSurface) {
+ mSurface->Unmap();
+ mSurface = nullptr;
+ } else if (mYSurface) {
+ mYSurface->Unmap();
+ mCbSurface->Unmap();
+ mCrSurface->Unmap();
+ mYSurface = mCbSurface = mCrSurface = nullptr;
+ }
+ mLocked = false;
+ }
+}
+
+RenderBufferTextureHost::RenderBufferData
+RenderBufferTextureHost::GetBufferDataForRender(uint8_t aChannelIndex) {
+ MOZ_ASSERT(mFormat != gfx::SurfaceFormat::YUV || aChannelIndex < 3);
+ MOZ_ASSERT(mFormat == gfx::SurfaceFormat::YUV || aChannelIndex < 1);
+ MOZ_ASSERT(mLocked);
+
+ if (mFormat != gfx::SurfaceFormat::YUV) {
+ MOZ_ASSERT(mSurface);
+
+ return RenderBufferData(mMap.mData,
+ mMap.mStride * mSurface->GetSize().height);
+ } else {
+ MOZ_ASSERT(mYSurface && mCbSurface && mCrSurface);
+
+ switch (aChannelIndex) {
+ case 0:
+ return RenderBufferData(mYMap.mData,
+ mYMap.mStride * mYSurface->GetSize().height);
+ break;
+ case 1:
+ return RenderBufferData(mCbMap.mData,
+ mCbMap.mStride * mCbSurface->GetSize().height);
+ break;
+ case 2:
+ return RenderBufferData(mCrMap.mData,
+ mCrMap.mStride * mCrSurface->GetSize().height);
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+ return RenderBufferData(nullptr, 0);
+ }
+ }
+}
+
+size_t RenderBufferTextureHost::GetPlaneCount() const {
+ switch (mDescriptor.type()) {
+ case layers::BufferDescriptor::TYCbCrDescriptor:
+ return 3;
+ default:
+ return 1;
+ }
+}
+
+gfx::SurfaceFormat RenderBufferTextureHost::GetFormat() const {
+ switch (mDescriptor.type()) {
+ case layers::BufferDescriptor::TYCbCrDescriptor:
+ return gfx::SurfaceFormat::YUV;
+ default:
+ return mDescriptor.get_RGBDescriptor().format();
+ }
+}
+
+gfx::ColorDepth RenderBufferTextureHost::GetColorDepth() const {
+ switch (mDescriptor.type()) {
+ case layers::BufferDescriptor::TYCbCrDescriptor:
+ return mDescriptor.get_YCbCrDescriptor().colorDepth();
+ default:
+ return gfx::ColorDepth::COLOR_8;
+ }
+}
+
+gfx::YUVRangedColorSpace RenderBufferTextureHost::GetYUVColorSpace() const {
+ switch (mDescriptor.type()) {
+ case layers::BufferDescriptor::TYCbCrDescriptor:
+ return gfx::GetYUVRangedColorSpace(mDescriptor.get_YCbCrDescriptor());
+ default:
+ return gfx::YUVRangedColorSpace::Default;
+ }
+}
+
+bool RenderBufferTextureHost::MapPlane(RenderCompositor* aCompositor,
+ uint8_t aChannelIndex,
+ PlaneInfo& aPlaneInfo) {
+ if (!mBuffer) {
+ if (!mDestroyed) {
+ // We hit some problems to get the shmem.
+ gfxCriticalNote << "GetBuffer Failed";
+ }
+ return false;
+ }
+
+ switch (mDescriptor.type()) {
+ case layers::BufferDescriptor::TYCbCrDescriptor: {
+ const layers::YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
+ switch (aChannelIndex) {
+ case 0:
+ aPlaneInfo.mData =
+ layers::ImageDataSerializer::GetYChannel(mBuffer, desc);
+ aPlaneInfo.mStride = desc.yStride();
+ aPlaneInfo.mSize = desc.display().Size();
+ break;
+ case 1:
+ aPlaneInfo.mData =
+ layers::ImageDataSerializer::GetCbChannel(mBuffer, desc);
+ aPlaneInfo.mStride = desc.cbCrStride();
+ aPlaneInfo.mSize =
+ layers::ImageDataSerializer::GetCroppedCbCrSize(desc);
+ break;
+ case 2:
+ aPlaneInfo.mData =
+ layers::ImageDataSerializer::GetCrChannel(mBuffer, desc);
+ aPlaneInfo.mStride = desc.cbCrStride();
+ aPlaneInfo.mSize =
+ layers::ImageDataSerializer::GetCroppedCbCrSize(desc);
+ break;
+ }
+ break;
+ }
+ default: {
+ const layers::RGBDescriptor& desc = mDescriptor.get_RGBDescriptor();
+ aPlaneInfo.mData = mBuffer;
+ aPlaneInfo.mStride = layers::ImageDataSerializer::GetRGBStride(desc);
+ aPlaneInfo.mSize = desc.size();
+ break;
+ }
+ }
+ return true;
+}
+
+void RenderBufferTextureHost::UnmapPlanes() {}
+
+void RenderBufferTextureHost::Destroy() {
+ mBuffer = nullptr;
+ mDestroyed = true;
+}
+
+} // namespace wr
+} // namespace mozilla
diff --git a/gfx/webrender_bindings/RenderBufferTextureHost.h b/gfx/webrender_bindings/RenderBufferTextureHost.h
new file mode 100644
index 0000000000..0fd2ef26b7
--- /dev/null
+++ b/gfx/webrender_bindings/RenderBufferTextureHost.h
@@ -0,0 +1,81 @@
+/* -*- 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_RENDERBUFFERTEXTUREHOST_H
+#define MOZILLA_GFX_RENDERBUFFERTEXTUREHOST_H
+
+#include "RenderTextureHostSWGL.h"
+
+namespace mozilla {
+namespace wr {
+
+class RenderBufferTextureHost final : public RenderTextureHostSWGL {
+ public:
+ RenderBufferTextureHost(uint8_t* aBuffer,
+ const layers::BufferDescriptor& aDescriptor);
+
+ // RenderTextureHost
+ wr::WrExternalImage Lock(uint8_t aChannelIndex, gl::GLContext* aGL) override;
+ void Unlock() override;
+
+ size_t Bytes() override {
+ return mSize.width * mSize.height * BytesPerPixel(mFormat);
+ }
+ class RenderBufferData {
+ public:
+ RenderBufferData(uint8_t* aData, size_t aBufferSize)
+ : mData(aData), mBufferSize(aBufferSize) {}
+ const uint8_t* mData;
+ size_t mBufferSize;
+ };
+
+ RenderBufferData GetBufferDataForRender(uint8_t aChannelIndex);
+
+ // RenderTextureHostSWGL
+ size_t GetPlaneCount() const override;
+
+ gfx::SurfaceFormat GetFormat() const override;
+
+ gfx::ColorDepth GetColorDepth() const override;
+
+ gfx::YUVRangedColorSpace GetYUVColorSpace() const override;
+
+ bool MapPlane(RenderCompositor* aCompositor, uint8_t aChannelIndex,
+ PlaneInfo& aPlaneInfo) override;
+
+ void UnmapPlanes() override;
+
+ void Destroy() override;
+
+ private:
+ virtual ~RenderBufferTextureHost();
+
+ uint8_t* GetBuffer() const { return mBuffer; }
+
+ uint8_t* mBuffer;
+ layers::BufferDescriptor mDescriptor;
+ gfx::IntSize mSize;
+ gfx::SurfaceFormat mFormat;
+
+ RefPtr<gfx::DataSourceSurface> mSurface;
+ gfx::DataSourceSurface::MappedSurface mMap;
+
+ RefPtr<gfx::DataSourceSurface> mYSurface;
+ RefPtr<gfx::DataSourceSurface> mCbSurface;
+ RefPtr<gfx::DataSourceSurface> mCrSurface;
+ gfx::DataSourceSurface::MappedSurface mYMap;
+ gfx::DataSourceSurface::MappedSurface mCbMap;
+ gfx::DataSourceSurface::MappedSurface mCrMap;
+
+ bool mLocked;
+
+ bool mDestroyed = false;
+};
+
+} // namespace wr
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_RENDERBUFFERTEXTUREHOST_H
diff --git a/gfx/webrender_bindings/RenderCompositor.cpp b/gfx/webrender_bindings/RenderCompositor.cpp
new file mode 100644
index 0000000000..f4211328f9
--- /dev/null
+++ b/gfx/webrender_bindings/RenderCompositor.cpp
@@ -0,0 +1,286 @@
+/* -*- 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 "RenderCompositor.h"
+
+#include "gfxConfig.h"
+#include "gfxPlatform.h"
+#include "GLContext.h"
+#include "mozilla/StaticPrefs_gfx.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/layers/SyncObject.h"
+#include "mozilla/webrender/RenderCompositorLayersSWGL.h"
+#include "mozilla/webrender/RenderCompositorOGL.h"
+#include "mozilla/webrender/RenderCompositorSWGL.h"
+#include "mozilla/widget/CompositorWidget.h"
+
+#ifdef XP_WIN
+# include "mozilla/webrender/RenderCompositorANGLE.h"
+# include "mozilla/widget/WinCompositorWidget.h"
+#endif
+
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WAYLAND) || defined(MOZ_X11)
+# include "mozilla/webrender/RenderCompositorEGL.h"
+#endif
+
+#ifdef MOZ_WAYLAND
+# include "mozilla/webrender/RenderCompositorNative.h"
+#endif
+
+#ifdef XP_MACOSX
+# include "mozilla/webrender/RenderCompositorNative.h"
+#endif
+
+namespace mozilla::wr {
+
+void wr_compositor_add_surface(void* aCompositor, wr::NativeSurfaceId aId,
+ const wr::CompositorSurfaceTransform* aTransform,
+ wr::DeviceIntRect aClipRect,
+ wr::ImageRendering aImageRendering) {
+ RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor);
+ compositor->AddSurface(aId, *aTransform, aClipRect, aImageRendering);
+}
+
+void wr_compositor_begin_frame(void* aCompositor) {
+ RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor);
+ compositor->CompositorBeginFrame();
+}
+
+void wr_compositor_bind(void* aCompositor, wr::NativeTileId aId,
+ wr::DeviceIntPoint* aOffset, uint32_t* aFboId,
+ wr::DeviceIntRect aDirtyRect,
+ wr::DeviceIntRect aValidRect) {
+ RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor);
+ compositor->Bind(aId, aOffset, aFboId, aDirtyRect, aValidRect);
+}
+
+void wr_compositor_create_surface(void* aCompositor, wr::NativeSurfaceId aId,
+ wr::DeviceIntPoint aVirtualOffset,
+ wr::DeviceIntSize aTileSize, bool aIsOpaque) {
+ RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor);
+ compositor->CreateSurface(aId, aVirtualOffset, aTileSize, aIsOpaque);
+}
+
+void wr_compositor_create_external_surface(void* aCompositor,
+ wr::NativeSurfaceId aId,
+ bool aIsOpaque) {
+ RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor);
+ compositor->CreateExternalSurface(aId, aIsOpaque);
+}
+
+void wr_compositor_create_backdrop_surface(void* aCompositor,
+ wr::NativeSurfaceId aId,
+ wr::ColorF aColor) {
+ RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor);
+ compositor->CreateBackdropSurface(aId, aColor);
+}
+
+void wr_compositor_create_tile(void* aCompositor, wr::NativeSurfaceId aId,
+ int32_t aX, int32_t aY) {
+ RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor);
+ compositor->CreateTile(aId, aX, aY);
+}
+
+void wr_compositor_destroy_tile(void* aCompositor, wr::NativeSurfaceId aId,
+ int32_t aX, int32_t aY) {
+ RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor);
+ compositor->DestroyTile(aId, aX, aY);
+}
+
+void wr_compositor_destroy_surface(void* aCompositor, NativeSurfaceId aId) {
+ RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor);
+ compositor->DestroySurface(aId);
+}
+
+void wr_compositor_attach_external_image(void* aCompositor,
+ wr::NativeSurfaceId aId,
+ wr::ExternalImageId aExternalImage) {
+ RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor);
+ compositor->AttachExternalImage(aId, aExternalImage);
+}
+
+void wr_compositor_start_compositing(void* aCompositor, wr::ColorF aClearColor,
+ const wr::DeviceIntRect* aDirtyRects,
+ size_t aNumDirtyRects,
+ const wr::DeviceIntRect* aOpaqueRects,
+ size_t aNumOpaqueRects) {
+ RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor);
+ compositor->StartCompositing(aClearColor, aDirtyRects, aNumDirtyRects,
+ aOpaqueRects, aNumOpaqueRects);
+}
+
+void wr_compositor_end_frame(void* aCompositor) {
+ RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor);
+ compositor->CompositorEndFrame();
+}
+
+void wr_compositor_enable_native_compositor(void* aCompositor, bool aEnable) {
+ RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor);
+ compositor->EnableNativeCompositor(aEnable);
+}
+
+void wr_compositor_get_capabilities(void* aCompositor,
+ CompositorCapabilities* aCaps) {
+ RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor);
+ compositor->GetCompositorCapabilities(aCaps);
+}
+
+void wr_compositor_get_window_visibility(void* aCompositor,
+ WindowVisibility* aVisibility) {
+ RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor);
+ compositor->GetWindowVisibility(aVisibility);
+}
+
+void wr_compositor_unbind(void* aCompositor) {
+ RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor);
+ compositor->Unbind();
+}
+
+void wr_compositor_deinit(void* aCompositor) {
+ RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor);
+ compositor->DeInit();
+}
+
+void wr_compositor_map_tile(void* aCompositor, wr::NativeTileId aId,
+ wr::DeviceIntRect aDirtyRect,
+ wr::DeviceIntRect aValidRect, void** aData,
+ int32_t* aStride) {
+ RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor);
+ compositor->MapTile(aId, aDirtyRect, aValidRect, aData, aStride);
+}
+
+void wr_compositor_unmap_tile(void* aCompositor) {
+ RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor);
+ compositor->UnmapTile();
+}
+
+void wr_partial_present_compositor_set_buffer_damage_region(
+ void* aCompositor, const wr::DeviceIntRect* aRects, size_t aNRects) {
+ RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor);
+ compositor->SetBufferDamageRegion(aRects, aNRects);
+}
+
+/* static */
+UniquePtr<RenderCompositor> RenderCompositor::Create(
+ const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError) {
+ if (aWidget->GetCompositorOptions().UseSoftwareWebRender()) {
+#ifdef XP_MACOSX
+ // Mac uses NativeLayerCA
+ if (!gfxPlatform::IsHeadless()) {
+ return RenderCompositorNativeSWGL::Create(aWidget, aError);
+ }
+#elif defined(MOZ_WAYLAND)
+ if (gfx::gfxVars::UseWebRenderCompositor()) {
+ return RenderCompositorNativeSWGL::Create(aWidget, aError);
+ }
+#endif
+ UniquePtr<RenderCompositor> comp =
+ RenderCompositorLayersSWGL::Create(aWidget, aError);
+ if (comp) {
+ return comp;
+ }
+#if defined(MOZ_WIDGET_ANDROID)
+ // On Android, we do not want to fallback from RenderCompositorOGLSWGL to
+ // RenderCompositorSWGL.
+ if (aWidget->GetCompositorOptions().AllowSoftwareWebRenderOGL()) {
+ return nullptr;
+ }
+#endif
+ return RenderCompositorSWGL::Create(aWidget, aError);
+ }
+
+#ifdef XP_WIN
+ if (gfx::gfxVars::UseWebRenderANGLE()) {
+ return RenderCompositorANGLE::Create(aWidget, aError);
+ }
+#endif
+
+#if defined(MOZ_WAYLAND)
+ if (gfx::gfxVars::UseWebRenderCompositor()) {
+ return RenderCompositorNativeOGL::Create(aWidget, aError);
+ }
+#endif
+
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WAYLAND) || defined(MOZ_X11)
+ UniquePtr<RenderCompositor> eglCompositor =
+ RenderCompositorEGL::Create(aWidget, aError);
+ if (eglCompositor) {
+ return eglCompositor;
+ }
+#endif
+
+#if defined(MOZ_WIDGET_ANDROID)
+ // RenderCompositorOGL is not used on android
+ return nullptr;
+#elif defined(XP_MACOSX)
+ // Mac uses NativeLayerCA
+ return RenderCompositorNativeOGL::Create(aWidget, aError);
+#else
+ return RenderCompositorOGL::Create(aWidget, aError);
+#endif
+}
+
+RenderCompositor::RenderCompositor(
+ const RefPtr<widget::CompositorWidget>& aWidget)
+ : mWidget(aWidget) {}
+
+RenderCompositor::~RenderCompositor() = default;
+
+bool RenderCompositor::MakeCurrent() { return gl()->MakeCurrent(); }
+
+void RenderCompositor::GetCompositorCapabilities(
+ CompositorCapabilities* aCaps) {
+ if (StaticPrefs::gfx_webrender_compositor_max_update_rects_AtStartup() > 0) {
+ aCaps->max_update_rects = 1;
+ } else {
+ aCaps->max_update_rects = 0;
+ }
+}
+
+void RenderCompositor::GetWindowVisibility(WindowVisibility* aVisibility) {
+#ifdef XP_WIN
+ auto* widget = mWidget->AsWindows();
+ if (!widget) {
+ return;
+ }
+ aVisibility->size_mode = ToWrWindowSizeMode(widget->GetWindowSizeMode());
+ aVisibility->is_fully_occluded = widget->GetWindowIsFullyOccluded();
+#endif
+}
+
+GLenum RenderCompositor::IsContextLost(bool aForce) {
+ auto* glc = gl();
+ // GetGraphicsResetStatus may trigger an implicit MakeCurrent if robustness
+ // is not supported, so unless we are forcing, pass on the check.
+ if (!glc || (!aForce && !glc->IsSupported(gl::GLFeature::robustness))) {
+ return LOCAL_GL_NO_ERROR;
+ }
+ auto resetStatus = glc->fGetGraphicsResetStatus();
+ switch (resetStatus) {
+ case LOCAL_GL_NO_ERROR:
+ break;
+ case LOCAL_GL_INNOCENT_CONTEXT_RESET_ARB:
+ NS_WARNING("Device reset due to system / different context");
+ break;
+ case LOCAL_GL_PURGED_CONTEXT_RESET_NV:
+ NS_WARNING("Device reset due to NV video memory purged");
+ break;
+ case LOCAL_GL_GUILTY_CONTEXT_RESET_ARB:
+ gfxCriticalError() << "Device reset due to WR context";
+ break;
+ case LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB:
+ gfxCriticalNote << "Device reset may be due to WR context";
+ break;
+ default:
+ gfxCriticalError() << "Device reset with WR context unexpected status: "
+ << gfx::hexa(resetStatus);
+ break;
+ }
+ return resetStatus;
+}
+
+} // namespace mozilla::wr
diff --git a/gfx/webrender_bindings/RenderCompositor.h b/gfx/webrender_bindings/RenderCompositor.h
new file mode 100644
index 0000000000..89b06395c1
--- /dev/null
+++ b/gfx/webrender_bindings/RenderCompositor.h
@@ -0,0 +1,222 @@
+/* -*- 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_RENDERCOMPOSITOR_H
+#define MOZILLA_GFX_RENDERCOMPOSITOR_H
+
+#include "mozilla/ipc/FileDescriptor.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/webrender/WebRenderTypes.h"
+#include "Units.h"
+
+#include "GLTypes.h"
+
+namespace mozilla {
+
+namespace gl {
+class GLContext;
+}
+
+namespace layers {
+class CompositionRecorder;
+class SyncObjectHost;
+} // namespace layers
+
+namespace widget {
+class CompositorWidget;
+}
+
+namespace wr {
+
+class RenderCompositorLayersSWGL;
+class RenderCompositorD3D11SWGL;
+
+class RenderCompositor {
+ public:
+ static UniquePtr<RenderCompositor> Create(
+ const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError);
+
+ RenderCompositor(const RefPtr<widget::CompositorWidget>& aWidget);
+ virtual ~RenderCompositor();
+
+ virtual bool BeginFrame() = 0;
+
+ // Optional handler in case the frame was aborted allowing the compositor
+ // to clean up relevant resources if required.
+ virtual void CancelFrame() {}
+
+ // Called to notify the RenderCompositor that all of the commands for a frame
+ // have been pushed to the queue.
+ // @return a RenderedFrameId for the frame
+ virtual RenderedFrameId EndFrame(
+ const nsTArray<DeviceIntRect>& aDirtyRects) = 0;
+ // Returns false when waiting gpu tasks is failed.
+ // It might happen when rendering context is lost.
+ virtual bool WaitForGPU() { return true; }
+
+ // Check for and return the last completed frame.
+ // @return the last (highest) completed RenderedFrameId
+ virtual RenderedFrameId GetLastCompletedFrameId() {
+ return mLatestRenderFrameId.Prev();
+ }
+
+ // Update FrameId when WR rendering does not happen.
+ virtual RenderedFrameId UpdateFrameId() { return GetNextRenderFrameId(); }
+
+ virtual void Pause() = 0;
+ virtual bool Resume() = 0;
+ // Called when WR rendering is skipped
+ virtual void Update() {}
+
+ virtual gl::GLContext* gl() const { return nullptr; }
+ virtual void* swgl() const { return nullptr; }
+
+ virtual bool MakeCurrent();
+
+ virtual bool UseANGLE() const { return false; }
+
+ virtual bool UseDComp() const { return false; }
+
+ virtual bool UseTripleBuffering() const { return false; }
+
+ virtual layers::WebRenderBackend BackendType() const {
+ return layers::WebRenderBackend::HARDWARE;
+ }
+ virtual layers::WebRenderCompositor CompositorType() const {
+ return layers::WebRenderCompositor::DRAW;
+ }
+
+ virtual RenderCompositorD3D11SWGL* AsRenderCompositorD3D11SWGL() {
+ return nullptr;
+ }
+
+ virtual RenderCompositorLayersSWGL* AsRenderCompositorLayersSWGL() {
+ return nullptr;
+ }
+
+ // True if AttachExternalImage supports being used with an external
+ // image that maps to a RenderBufferTextureHost
+ virtual bool SupportsExternalBufferTextures() const { return false; }
+
+ virtual LayoutDeviceIntSize GetBufferSize() = 0;
+
+ widget::CompositorWidget* GetWidget() const { return mWidget; }
+
+ layers::SyncObjectHost* GetSyncObject() const { return mSyncObject.get(); }
+
+ virtual GLenum IsContextLost(bool aForce);
+
+ virtual bool SupportAsyncScreenshot() { return true; }
+
+ virtual bool ShouldUseNativeCompositor() { return false; }
+
+ // Interface for wr::Compositor
+ virtual void CompositorBeginFrame() {}
+ virtual void CompositorEndFrame() {}
+ virtual void Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset,
+ uint32_t* aFboId, wr::DeviceIntRect aDirtyRect,
+ wr::DeviceIntRect aValidRect) {}
+ virtual void Unbind() {}
+ virtual bool MapTile(wr::NativeTileId aId, wr::DeviceIntRect aDirtyRect,
+ wr::DeviceIntRect aValidRect, void** aData,
+ int32_t* aStride) {
+ return false;
+ }
+ virtual void UnmapTile() {}
+ virtual void CreateSurface(wr::NativeSurfaceId aId,
+ wr::DeviceIntPoint aVirtualOffset,
+ wr::DeviceIntSize aTileSize, bool aIsOpaque) {}
+ virtual void CreateExternalSurface(wr::NativeSurfaceId aId, bool aIsOpaque) {}
+ virtual void CreateBackdropSurface(wr::NativeSurfaceId aId,
+ wr::ColorF aColor) {}
+ virtual void DestroySurface(NativeSurfaceId aId) {}
+ virtual void CreateTile(wr::NativeSurfaceId, int32_t aX, int32_t aY) {}
+ virtual void DestroyTile(wr::NativeSurfaceId, int32_t aX, int32_t aY) {}
+ virtual void AttachExternalImage(wr::NativeSurfaceId aId,
+ wr::ExternalImageId aExternalImage) {}
+ virtual void AddSurface(wr::NativeSurfaceId aId,
+ const wr::CompositorSurfaceTransform& aTransform,
+ wr::DeviceIntRect aClipRect,
+ wr::ImageRendering aImageRendering) {}
+ // Called in the middle of a frame after all surfaces have been added but
+ // before tiles are updated to signal that early compositing can start
+ virtual void StartCompositing(wr::ColorF aClearColor,
+ const wr::DeviceIntRect* aDirtyRects,
+ size_t aNumDirtyRects,
+ const wr::DeviceIntRect* aOpaqueRects,
+ size_t aNumOpaqueRects) {}
+ virtual void EnableNativeCompositor(bool aEnable) {}
+ virtual void DeInit() {}
+ // Overrides any of the default compositor capabilities for behavior this
+ // compositor might require.
+ virtual void GetCompositorCapabilities(CompositorCapabilities* aCaps);
+
+ virtual void GetWindowVisibility(WindowVisibility* aVisibility);
+
+ // Interface for partial present
+ virtual bool UsePartialPresent() { return false; }
+ virtual bool RequestFullRender() { return false; }
+ virtual uint32_t GetMaxPartialPresentRects() { return 0; }
+ virtual bool ShouldDrawPreviousPartialPresentRegions() { return false; }
+ // Returns the age of the current backbuffer., This should be used, if
+ // ShouldDrawPreviousPartialPresentRegions() returns true, to determine the
+ // region which must be rendered in addition to the current frame's dirty
+ // rect.
+ virtual size_t GetBufferAge() const { return 0; }
+ // Allows webrender to specify the total region that will be rendered to this
+ // frame, ie the frame's dirty region and some previous frames' dirty regions,
+ // if applicable (calculated using the buffer age). Must be called before
+ // anything has been rendered to the main framebuffer.
+ virtual void SetBufferDamageRegion(const wr::DeviceIntRect* aRects,
+ size_t aNumRects) {}
+
+ // Whether the surface origin is top-left.
+ virtual bool SurfaceOriginIsTopLeft() { return false; }
+
+ // Does readback if wr_renderer_readback() could not get correct WR rendered
+ // result. It could happen when WebRender renders to multiple overlay layers.
+ virtual bool MaybeReadback(const gfx::IntSize& aReadbackSize,
+ const wr::ImageFormat& aReadbackFormat,
+ const Range<uint8_t>& aReadbackBuffer,
+ bool* aNeedsYFlip) {
+ return false;
+ }
+ virtual void MaybeRequestAllowFrameRecording(bool aWillRecord) {}
+ virtual bool MaybeRecordFrame(layers::CompositionRecorder& aRecorder) {
+ return false;
+ }
+ virtual bool MaybeGrabScreenshot(const gfx::IntSize& aWindowSize) {
+ return false;
+ }
+ virtual bool MaybeProcessScreenshotQueue() { return false; }
+
+ // Returns FileDescriptor of release fence.
+ // Release fence is a fence that is used for waiting until usage/composite of
+ // AHardwareBuffer is ended. The fence is delivered to client side via
+ // ImageBridge. It is used only on android.
+ virtual ipc::FileDescriptor GetAndResetReleaseFence() {
+ return ipc::FileDescriptor();
+ }
+
+ virtual bool IsPaused() { return false; }
+
+ protected:
+ // We default this to 2, so that mLatestRenderFrameId.Prev() is always valid.
+ RenderedFrameId mLatestRenderFrameId = RenderedFrameId{2};
+ RenderedFrameId GetNextRenderFrameId() {
+ mLatestRenderFrameId = mLatestRenderFrameId.Next();
+ return mLatestRenderFrameId;
+ }
+
+ RefPtr<widget::CompositorWidget> mWidget;
+ RefPtr<layers::SyncObjectHost> mSyncObject;
+};
+
+} // namespace wr
+} // namespace mozilla
+
+#endif
diff --git a/gfx/webrender_bindings/RenderCompositorANGLE.cpp b/gfx/webrender_bindings/RenderCompositorANGLE.cpp
new file mode 100644
index 0000000000..2ea2bf1124
--- /dev/null
+++ b/gfx/webrender_bindings/RenderCompositorANGLE.cpp
@@ -0,0 +1,1059 @@
+/* -*- 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 "RenderCompositorANGLE.h"
+
+#include "GLContext.h"
+#include "GLContextEGL.h"
+#include "GLContextProvider.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/gfx/StackArray.h"
+#include "mozilla/layers/HelpersD3D11.h"
+#include "mozilla/layers/SyncObject.h"
+#include "mozilla/ProfilerMarkers.h"
+#include "mozilla/StaticPrefs_gfx.h"
+#include "mozilla/webrender/DCLayerTree.h"
+#include "mozilla/webrender/RenderThread.h"
+#include "mozilla/widget/CompositorWidget.h"
+#include "mozilla/widget/WinCompositorWidget.h"
+#include "mozilla/WindowsVersion.h"
+#include "mozilla/Telemetry.h"
+#include "nsPrintfCString.h"
+#include "FxROutputHandler.h"
+
+#undef NTDDI_VERSION
+#define NTDDI_VERSION NTDDI_WIN8
+
+#include <d3d11.h>
+#include <dcomp.h>
+#include <dxgi1_2.h>
+
+// Flag for PrintWindow() that is defined in Winuser.h. It is defined since
+// Windows 8.1. This allows PrintWindow to capture window content that is
+// rendered with DirectComposition.
+#undef PW_RENDERFULLCONTENT
+#define PW_RENDERFULLCONTENT 0x00000002
+
+namespace mozilla {
+namespace wr {
+
+extern LazyLogModule gRenderThreadLog;
+#define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
+
+/* static */
+UniquePtr<RenderCompositor> RenderCompositorANGLE::Create(
+ const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError) {
+ RefPtr<gl::GLContext> gl = RenderThread::Get()->SingletonGL(aError);
+ if (!gl) {
+ if (aError.IsEmpty()) {
+ aError.Assign("RcANGLE(no shared GL)"_ns);
+ } else {
+ aError.Append("(Create)"_ns);
+ }
+ return nullptr;
+ }
+
+ UniquePtr<RenderCompositorANGLE> compositor =
+ MakeUnique<RenderCompositorANGLE>(aWidget, std::move(gl));
+ if (!compositor->Initialize(aError)) {
+ return nullptr;
+ }
+ return compositor;
+}
+
+RenderCompositorANGLE::RenderCompositorANGLE(
+ const RefPtr<widget::CompositorWidget>& aWidget,
+ RefPtr<gl::GLContext>&& aGL)
+ : RenderCompositor(aWidget),
+ mGL(aGL),
+ mEGLConfig(nullptr),
+ mEGLSurface(nullptr),
+ mUseTripleBuffering(false),
+ mUseAlpha(false),
+ mUseNativeCompositor(true),
+ mUsePartialPresent(false),
+ mFullRender(false),
+ mDisablingNativeCompositor(false) {
+ MOZ_ASSERT(mGL);
+ LOG("RenderCompositorANGLE::RenderCompositorANGLE()");
+}
+
+RenderCompositorANGLE::~RenderCompositorANGLE() {
+ LOG("RenderCompositorANGLE::~RenderCompositorANGLE()");
+
+ DestroyEGLSurface();
+ MOZ_ASSERT(!mEGLSurface);
+}
+
+ID3D11Device* RenderCompositorANGLE::GetDeviceOfEGLDisplay(nsACString& aError) {
+ const auto& gle = gl::GLContextEGL::Cast(mGL);
+ const auto& egl = gle->mEgl;
+ MOZ_ASSERT(egl);
+ if (!egl ||
+ !egl->mLib->IsExtensionSupported(gl::EGLLibExtension::EXT_device_query)) {
+ aError.Assign("RcANGLE(no EXT_device_query support)"_ns);
+ return nullptr;
+ }
+
+ // Fetch the D3D11 device.
+ EGLDeviceEXT eglDevice = nullptr;
+ egl->fQueryDisplayAttribEXT(LOCAL_EGL_DEVICE_EXT, (EGLAttrib*)&eglDevice);
+ MOZ_ASSERT(eglDevice);
+ ID3D11Device* device = nullptr;
+ egl->mLib->fQueryDeviceAttribEXT(eglDevice, LOCAL_EGL_D3D11_DEVICE_ANGLE,
+ (EGLAttrib*)&device);
+ if (!device) {
+ aError.Assign("RcANGLE(get D3D11Device from EGLDisplay failed)"_ns);
+ return nullptr;
+ }
+ return device;
+}
+
+bool RenderCompositorANGLE::Initialize(nsACString& aError) {
+ // TODO(aosmond): This causes us to lose WebRender because it is unable to
+ // distinguish why we failed and retry once the reset is complete. This does
+ // appear to happen in the wild, so we really should try to do something
+ // differently here.
+ if (RenderThread::Get()->IsHandlingDeviceReset()) {
+ aError.Assign("RcANGLE(waiting device reset)"_ns);
+ return false;
+ }
+
+ // Force enable alpha channel to make sure ANGLE use correct framebuffer
+ // formart
+ const auto& gle = gl::GLContextEGL::Cast(mGL);
+ const auto& egl = gle->mEgl;
+ if (!gl::CreateConfig(*egl, &mEGLConfig, /* bpp */ 32,
+ /* enableDepthBuffer */ false, mGL->IsGLES())) {
+ aError.Assign("RcANGLE(create EGLConfig failed)"_ns);
+ return false;
+ }
+ MOZ_ASSERT(mEGLConfig);
+
+ mDevice = GetDeviceOfEGLDisplay(aError);
+
+ if (!mDevice) {
+ return false;
+ }
+
+ mDevice->GetImmediateContext(getter_AddRefs(mCtx));
+ if (!mCtx) {
+ aError.Assign("RcANGLE(get immediate context failed)"_ns);
+ return false;
+ }
+
+ // Disable native compositor when fast snapshot is needed.
+ // Taking snapshot of native compositor is very slow on Windows.
+ if (mWidget->GetCompositorOptions().NeedFastSnaphot()) {
+ mUseNativeCompositor = false;
+ }
+
+ // Create DCLayerTree when DirectComposition is used.
+ if (gfx::gfxVars::UseWebRenderDCompWin()) {
+ HWND compositorHwnd = GetCompositorHwnd();
+ if (compositorHwnd) {
+ mDCLayerTree = DCLayerTree::Create(mGL, mEGLConfig, mDevice, mCtx,
+ compositorHwnd, aError);
+ if (!mDCLayerTree) {
+ return false;
+ }
+ } else {
+ aError.Assign("RcANGLE(no compositor window)"_ns);
+ return false;
+ }
+ }
+
+ // Create SwapChain when compositor is not used
+ if (!UseCompositor()) {
+ if (!CreateSwapChain(aError)) {
+ // SwapChain creation failed.
+ return false;
+ }
+ }
+
+ mSyncObject = layers::SyncObjectHost::CreateSyncObjectHost(mDevice);
+ if (!mSyncObject->Init()) {
+ // Some errors occur. Clear the mSyncObject here.
+ // Then, there will be no texture synchronization.
+ aError.Assign("RcANGLE(create SyncObject failed)"_ns);
+ return false;
+ }
+
+ InitializeUsePartialPresent();
+
+ return true;
+}
+
+HWND RenderCompositorANGLE::GetCompositorHwnd() {
+ HWND hwnd = 0;
+
+ if (XRE_IsGPUProcess()) {
+ hwnd = mWidget->AsWindows()->GetCompositorHwnd();
+ } else if (
+ StaticPrefs::
+ gfx_webrender_enabled_no_gpu_process_with_angle_win_AtStartup()) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ // When GPU process does not exist, we do not need to use compositor window.
+ hwnd = mWidget->AsWindows()->GetHwnd();
+ }
+
+ return hwnd;
+}
+
+bool RenderCompositorANGLE::CreateSwapChain(nsACString& aError) {
+ MOZ_ASSERT(!UseCompositor());
+
+ HWND hwnd = mWidget->AsWindows()->GetHwnd();
+
+ RefPtr<IDXGIDevice> dxgiDevice;
+ mDevice->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
+
+ RefPtr<IDXGIFactory> dxgiFactory;
+ {
+ RefPtr<IDXGIAdapter> adapter;
+ dxgiDevice->GetAdapter(getter_AddRefs(adapter));
+
+ adapter->GetParent(
+ IID_PPV_ARGS((IDXGIFactory**)getter_AddRefs(dxgiFactory)));
+ }
+
+ RefPtr<IDXGIFactory2> dxgiFactory2;
+ HRESULT hr = dxgiFactory->QueryInterface(
+ (IDXGIFactory2**)getter_AddRefs(dxgiFactory2));
+ if (FAILED(hr)) {
+ dxgiFactory2 = nullptr;
+ }
+
+ CreateSwapChainForDCompIfPossible(dxgiFactory2);
+ if (gfx::gfxVars::UseWebRenderDCompWin() && !mSwapChain) {
+ MOZ_ASSERT(GetCompositorHwnd());
+ aError.Assign("RcANGLE(create swapchain for dcomp failed)"_ns);
+ return false;
+ }
+
+ if (!mSwapChain && dxgiFactory2) {
+ RefPtr<IDXGISwapChain1> swapChain1;
+ bool useTripleBuffering = false;
+
+ DXGI_SWAP_CHAIN_DESC1 desc{};
+ desc.Width = 0;
+ desc.Height = 0;
+ desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
+ desc.SampleDesc.Count = 1;
+ desc.SampleDesc.Quality = 0;
+ desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+
+ bool useFlipSequential = gfx::gfxVars::UseWebRenderFlipSequentialWin();
+ if (useFlipSequential && !mWidget->AsWindows()->GetCompositorHwnd()) {
+ useFlipSequential = false;
+ gfxCriticalNoteOnce << "FLIP_SEQUENTIAL needs CompositorHwnd. Fallback";
+ }
+
+ if (useFlipSequential) {
+ useTripleBuffering = gfx::gfxVars::UseWebRenderTripleBufferingWin();
+ if (useTripleBuffering) {
+ desc.BufferCount = 3;
+ } else {
+ desc.BufferCount = 2;
+ }
+ desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
+ desc.Scaling = DXGI_SCALING_NONE;
+ } else {
+ desc.BufferCount = 1;
+ desc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL;
+ desc.Scaling = DXGI_SCALING_STRETCH;
+ }
+ desc.Flags = 0;
+
+ hr = dxgiFactory2->CreateSwapChainForHwnd(
+ mDevice, hwnd, &desc, nullptr, nullptr, getter_AddRefs(swapChain1));
+ if (SUCCEEDED(hr) && swapChain1) {
+ DXGI_RGBA color = {1.0f, 1.0f, 1.0f, 1.0f};
+ swapChain1->SetBackgroundColor(&color);
+ mSwapChain = swapChain1;
+ mSwapChain1 = swapChain1;
+ mUseTripleBuffering = useTripleBuffering;
+ } else if (useFlipSequential) {
+ gfxCriticalNoteOnce << "FLIP_SEQUENTIAL is not supported. Fallback";
+ }
+ }
+
+ if (!mSwapChain) {
+ if (mWidget->AsWindows()->GetCompositorHwnd()) {
+ // Destroy compositor window.
+ mWidget->AsWindows()->DestroyCompositorWindow();
+ hwnd = mWidget->AsWindows()->GetHwnd();
+ }
+
+ DXGI_SWAP_CHAIN_DESC swapDesc{};
+ swapDesc.BufferDesc.Width = 0;
+ swapDesc.BufferDesc.Height = 0;
+ swapDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
+ swapDesc.BufferDesc.RefreshRate.Numerator = 60;
+ swapDesc.BufferDesc.RefreshRate.Denominator = 1;
+ swapDesc.SampleDesc.Count = 1;
+ swapDesc.SampleDesc.Quality = 0;
+ swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ swapDesc.BufferCount = 1;
+ swapDesc.OutputWindow = hwnd;
+ swapDesc.Windowed = TRUE;
+ swapDesc.Flags = 0;
+ swapDesc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL;
+
+ HRESULT hr = dxgiFactory->CreateSwapChain(dxgiDevice, &swapDesc,
+ getter_AddRefs(mSwapChain));
+ if (FAILED(hr)) {
+ aError.Assign(
+ nsPrintfCString("RcANGLE(swap chain create failed %lx)", hr));
+ return false;
+ }
+
+ RefPtr<IDXGISwapChain1> swapChain1;
+ hr = mSwapChain->QueryInterface(
+ (IDXGISwapChain1**)getter_AddRefs(swapChain1));
+ if (SUCCEEDED(hr)) {
+ mSwapChain1 = swapChain1;
+ }
+ }
+
+ // We need this because we don't want DXGI to respond to Alt+Enter.
+ dxgiFactory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
+
+ if (!ResizeBufferIfNeeded()) {
+ aError.Assign("RcANGLE(resize buffer failed)"_ns);
+ return false;
+ }
+
+ return true;
+}
+
+void RenderCompositorANGLE::CreateSwapChainForDCompIfPossible(
+ IDXGIFactory2* aDXGIFactory2) {
+ if (!aDXGIFactory2 || !mDCLayerTree) {
+ return;
+ }
+
+ HWND hwnd = GetCompositorHwnd();
+ if (!hwnd) {
+ // When DirectComposition or DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL is used,
+ // compositor window needs to exist.
+ if (gfx::gfxVars::UseWebRenderDCompWin() ||
+ gfx::gfxVars::UseWebRenderFlipSequentialWin()) {
+ gfxCriticalNote << "Compositor window was not created";
+ }
+ return;
+ }
+
+ // When compositor is enabled, CompositionSurface is used for rendering.
+ // It does not support triple buffering.
+ bool useTripleBuffering =
+ gfx::gfxVars::UseWebRenderTripleBufferingWin() && !UseCompositor();
+ // Non Glass window is common since Windows 10.
+ bool useAlpha = false;
+ RefPtr<IDXGISwapChain1> swapChain1 =
+ CreateSwapChainForDComp(useTripleBuffering, useAlpha);
+ if (swapChain1) {
+ mSwapChain = swapChain1;
+ mSwapChain1 = swapChain1;
+ mUseTripleBuffering = useTripleBuffering;
+ mUseAlpha = useAlpha;
+ mDCLayerTree->SetDefaultSwapChain(swapChain1);
+ } else {
+ // Clear CLayerTree on falire
+ mDCLayerTree = nullptr;
+ }
+}
+
+RefPtr<IDXGISwapChain1> RenderCompositorANGLE::CreateSwapChainForDComp(
+ bool aUseTripleBuffering, bool aUseAlpha) {
+ HRESULT hr;
+ RefPtr<IDXGIDevice> dxgiDevice;
+ mDevice->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
+
+ RefPtr<IDXGIFactory> dxgiFactory;
+ {
+ RefPtr<IDXGIAdapter> adapter;
+ dxgiDevice->GetAdapter(getter_AddRefs(adapter));
+
+ adapter->GetParent(
+ IID_PPV_ARGS((IDXGIFactory**)getter_AddRefs(dxgiFactory)));
+ }
+
+ RefPtr<IDXGIFactory2> dxgiFactory2;
+ hr = dxgiFactory->QueryInterface(
+ (IDXGIFactory2**)getter_AddRefs(dxgiFactory2));
+ if (FAILED(hr)) {
+ return nullptr;
+ }
+
+ RefPtr<IDXGISwapChain1> swapChain1;
+ DXGI_SWAP_CHAIN_DESC1 desc{};
+ // DXGI does not like 0x0 swapchains. Swap chain creation failed when 0x0 was
+ // set.
+ desc.Width = 1;
+ desc.Height = 1;
+ desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
+ desc.SampleDesc.Count = 1;
+ desc.SampleDesc.Quality = 0;
+ desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ if (aUseTripleBuffering) {
+ desc.BufferCount = 3;
+ } else {
+ desc.BufferCount = 2;
+ }
+ // DXGI_SCALING_NONE caused swap chain creation failure.
+ desc.Scaling = DXGI_SCALING_STRETCH;
+ desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
+ if (aUseAlpha) {
+ // This could degrade performance. Use it only when it is necessary.
+ desc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
+ } else {
+ desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
+ }
+ desc.Flags = 0;
+
+ hr = dxgiFactory2->CreateSwapChainForComposition(mDevice, &desc, nullptr,
+ getter_AddRefs(swapChain1));
+ if (SUCCEEDED(hr) && swapChain1) {
+ DXGI_RGBA color = {1.0f, 1.0f, 1.0f, 1.0f};
+ swapChain1->SetBackgroundColor(&color);
+ return swapChain1;
+ }
+
+ return nullptr;
+}
+
+bool RenderCompositorANGLE::BeginFrame() {
+ mWidget->AsWindows()->UpdateCompositorWndSizeIfNecessary();
+
+ if (!UseCompositor()) {
+ if (mDCLayerTree) {
+ bool useAlpha = mWidget->AsWindows()->HasGlass();
+ // When Alpha usage is changed, SwapChain needs to be recreatd.
+ if (useAlpha != mUseAlpha) {
+ DestroyEGLSurface();
+ mBufferSize.reset();
+
+ RefPtr<IDXGISwapChain1> swapChain1 =
+ CreateSwapChainForDComp(mUseTripleBuffering, useAlpha);
+ if (swapChain1) {
+ mSwapChain = swapChain1;
+ mUseAlpha = useAlpha;
+ mDCLayerTree->SetDefaultSwapChain(swapChain1);
+ // When alpha is used, we want to disable partial present.
+ // See Bug 1595027.
+ if (useAlpha) {
+ mFullRender = true;
+ }
+ } else {
+ gfxCriticalNote << "Failed to re-create SwapChain";
+ RenderThread::Get()->HandleWebRenderError(
+ WebRenderError::NEW_SURFACE);
+ return false;
+ }
+ }
+ }
+
+ if (!ResizeBufferIfNeeded()) {
+ return false;
+ }
+ }
+
+ if (!MakeCurrent()) {
+ gfxCriticalNote << "Failed to make render context current, can't draw.";
+ return false;
+ }
+
+ if (RenderThread::Get()->SyncObjectNeeded() && mSyncObject) {
+ if (!mSyncObject->Synchronize(/* aFallible */ true)) {
+ // It's timeout or other error. Handle the device-reset here.
+ RenderThread::Get()->HandleDeviceReset(
+ "SyncObject", LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB);
+ return false;
+ }
+ }
+ return true;
+}
+
+RenderedFrameId RenderCompositorANGLE::EndFrame(
+ const nsTArray<DeviceIntRect>& aDirtyRects) {
+ RenderedFrameId frameId = GetNextRenderFrameId();
+ InsertGraphicsCommandsFinishedWaitQuery(frameId);
+
+ if (!UseCompositor()) {
+ auto start = TimeStamp::Now();
+ if (mWidget->AsWindows()->HasFxrOutputHandler()) {
+ // There is a Firefox Reality handler for this swapchain. Update this
+ // window's contents to the VR window.
+ FxROutputHandler* fxrHandler =
+ mWidget->AsWindows()->GetFxrOutputHandler();
+ if (fxrHandler->TryInitialize(mSwapChain, mDevice)) {
+ fxrHandler->UpdateOutput(mCtx);
+ }
+ }
+
+ const LayoutDeviceIntSize& bufferSize = mBufferSize.ref();
+
+ // During high contrast mode, alpha is used. In this case,
+ // IDXGISwapChain1::Present1 shows nothing with compositor window.
+ // In this case, we want to disable partial present by full render.
+ // See Bug 1595027
+ MOZ_ASSERT_IF(mUsePartialPresent && mUseAlpha, mFullRender);
+
+ if (mUsePartialPresent && !mUseAlpha && mSwapChain1) {
+ // Clear full render flag.
+ mFullRender = false;
+ // If there is no diry rect, we skip SwapChain present.
+ if (!aDirtyRects.IsEmpty()) {
+ int rectsCount = 0;
+ StackArray<RECT, 1> rects(aDirtyRects.Length());
+
+ for (size_t i = 0; i < aDirtyRects.Length(); ++i) {
+ const DeviceIntRect& rect = aDirtyRects[i];
+ // Clip rect to bufferSize
+ int left = std::max(0, std::min(rect.min.x, bufferSize.width));
+ int top = std::max(0, std::min(rect.min.y, bufferSize.height));
+ int right = std::max(0, std::min(rect.max.x, bufferSize.width));
+ int bottom = std::max(0, std::min(rect.max.y, bufferSize.height));
+
+ // When rect is not empty, the rect could be passed to Present1().
+ if (left < right && top < bottom) {
+ rects[rectsCount].left = left;
+ rects[rectsCount].top = top;
+ rects[rectsCount].right = right;
+ rects[rectsCount].bottom = bottom;
+ rectsCount++;
+ }
+ }
+
+ if (rectsCount > 0) {
+ DXGI_PRESENT_PARAMETERS params;
+ PodZero(&params);
+ params.DirtyRectsCount = rectsCount;
+ params.pDirtyRects = rects.data();
+
+ HRESULT hr;
+ hr = mSwapChain1->Present1(0, 0, &params);
+ if (FAILED(hr) && hr != DXGI_STATUS_OCCLUDED) {
+ gfxCriticalNote << "Present1 failed: " << gfx::hexa(hr);
+ mFullRender = true;
+ }
+ }
+ }
+ } else {
+ mSwapChain->Present(0, 0);
+ }
+ auto end = TimeStamp::Now();
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::COMPOSITE_SWAP_TIME,
+ (end - start).ToMilliseconds() * 10.);
+ }
+
+ if (mDisablingNativeCompositor) {
+ // During disabling native compositor, we need to wait all gpu tasks
+ // complete. Otherwise, rendering window could cause white flash.
+ WaitForPreviousGraphicsCommandsFinishedQuery(/* aWaitAll */ true);
+ mDisablingNativeCompositor = false;
+ }
+
+ if (mDCLayerTree) {
+ mDCLayerTree->MaybeUpdateDebug();
+ mDCLayerTree->MaybeCommit();
+ }
+
+ return frameId;
+}
+
+bool RenderCompositorANGLE::WaitForGPU() {
+ // Note: this waits on the query we inserted in the previous frame,
+ // not the one we just inserted now. Example:
+ // Insert query #1
+ // Present #1
+ // (first frame, no wait)
+ // Insert query #2
+ // Present #2
+ // Wait for query #1.
+ // Insert query #3
+ // Present #3
+ // Wait for query #2.
+ //
+ // This ensures we're done reading textures before swapping buffers.
+ if (!StaticPrefs::gfx_webrender_wait_gpu_finished_disabled_AtStartup()) {
+ return WaitForPreviousGraphicsCommandsFinishedQuery();
+ }
+ return true;
+}
+
+bool RenderCompositorANGLE::ResizeBufferIfNeeded() {
+ MOZ_ASSERT(mSwapChain);
+
+ LayoutDeviceIntSize size = mWidget->GetClientSize();
+
+ // DXGI does not like 0x0 swapchains. ResizeBuffers() failed when 0x0 was set
+ // when DComp is used.
+ size.width = std::max(size.width, 1);
+ size.height = std::max(size.height, 1);
+
+ if (mBufferSize.isSome() && mBufferSize.ref() == size) {
+ MOZ_ASSERT(mEGLSurface);
+ return true;
+ }
+
+ // Release EGLSurface of back buffer before calling ResizeBuffers().
+ DestroyEGLSurface();
+
+ mBufferSize = Some(size);
+
+ if (!CreateEGLSurface()) {
+ mBufferSize.reset();
+ return false;
+ }
+
+ if (mUsePartialPresent) {
+ mFullRender = true;
+ }
+ return true;
+}
+
+bool RenderCompositorANGLE::CreateEGLSurface() {
+ MOZ_ASSERT(mBufferSize.isSome());
+ MOZ_ASSERT(mEGLSurface == EGL_NO_SURFACE);
+
+ HRESULT hr;
+ RefPtr<ID3D11Texture2D> backBuf;
+
+ if (mBufferSize.isNothing()) {
+ gfxCriticalNote << "Buffer size is invalid";
+ return false;
+ }
+
+ const LayoutDeviceIntSize& size = mBufferSize.ref();
+
+ // Resize swap chain
+ DXGI_SWAP_CHAIN_DESC desc;
+ hr = mSwapChain->GetDesc(&desc);
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Failed to read swap chain description: "
+ << gfx::hexa(hr) << " Size : " << size;
+ return false;
+ }
+ hr = mSwapChain->ResizeBuffers(desc.BufferCount, size.width, size.height,
+ DXGI_FORMAT_B8G8R8A8_UNORM, 0);
+ if (FAILED(hr)) {
+ gfxCriticalNote << "Failed to resize swap chain buffers: " << gfx::hexa(hr)
+ << " Size : " << size;
+ return false;
+ }
+
+ hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D),
+ (void**)getter_AddRefs(backBuf));
+ if (hr == DXGI_ERROR_INVALID_CALL) {
+ // This happens on some GPUs/drivers when there's a TDR.
+ if (mDevice->GetDeviceRemovedReason() != S_OK) {
+ gfxCriticalError() << "GetBuffer returned invalid call: " << gfx::hexa(hr)
+ << " Size : " << size;
+ return false;
+ }
+ }
+
+ const EGLint pbuffer_attribs[]{LOCAL_EGL_WIDTH, size.width, LOCAL_EGL_HEIGHT,
+ size.height, LOCAL_EGL_NONE};
+
+ const auto buffer = reinterpret_cast<EGLClientBuffer>(backBuf.get());
+
+ const auto& gle = gl::GLContextEGL::Cast(mGL);
+ const auto& egl = gle->mEgl;
+ const EGLSurface surface = egl->fCreatePbufferFromClientBuffer(
+ LOCAL_EGL_D3D_TEXTURE_ANGLE, buffer, mEGLConfig, pbuffer_attribs);
+
+ if (!surface) {
+ EGLint err = egl->mLib->fGetError();
+ gfxCriticalError() << "Failed to create Pbuffer of back buffer error: "
+ << gfx::hexa(err) << " Size : " << size;
+ return false;
+ }
+
+ mEGLSurface = surface;
+
+ return true;
+}
+
+void RenderCompositorANGLE::DestroyEGLSurface() {
+ // Release EGLSurface of back buffer before calling ResizeBuffers().
+ if (mEGLSurface) {
+ const auto& gle = gl::GLContextEGL::Cast(gl());
+ const auto& egl = gle->mEgl;
+ gle->SetEGLSurfaceOverride(EGL_NO_SURFACE);
+ egl->fDestroySurface(mEGLSurface);
+ mEGLSurface = nullptr;
+ }
+}
+
+void RenderCompositorANGLE::Pause() {}
+
+bool RenderCompositorANGLE::Resume() { return true; }
+
+void RenderCompositorANGLE::Update() {
+ // Update compositor window's size if it exists.
+ // It needs to be called here, since OS might update compositor
+ // window's size at unexpected timing.
+ mWidget->AsWindows()->UpdateCompositorWndSizeIfNecessary();
+}
+
+bool RenderCompositorANGLE::MakeCurrent() {
+ gl::GLContextEGL::Cast(gl())->SetEGLSurfaceOverride(mEGLSurface);
+ return gl()->MakeCurrent();
+}
+
+LayoutDeviceIntSize RenderCompositorANGLE::GetBufferSize() {
+ if (!UseCompositor()) {
+ MOZ_ASSERT(mBufferSize.isSome());
+ if (mBufferSize.isNothing()) {
+ return LayoutDeviceIntSize();
+ }
+ return mBufferSize.ref();
+ } else {
+ auto size = mWidget->GetClientSize();
+ // This size is used for WR DEBUG_OVERLAY. Its DCTile does not like 0.
+ size.width = std::max(size.width, 1);
+ size.height = std::max(size.height, 1);
+ return size;
+ }
+}
+
+RefPtr<ID3D11Query> RenderCompositorANGLE::GetD3D11Query() {
+ RefPtr<ID3D11Query> query;
+
+ if (mRecycledQuery) {
+ query = mRecycledQuery.forget();
+ return query;
+ }
+
+ CD3D11_QUERY_DESC desc(D3D11_QUERY_EVENT);
+ HRESULT hr = mDevice->CreateQuery(&desc, getter_AddRefs(query));
+ if (FAILED(hr) || !query) {
+ gfxWarning() << "Could not create D3D11_QUERY_EVENT: " << gfx::hexa(hr);
+ return nullptr;
+ }
+ return query;
+}
+
+void RenderCompositorANGLE::InsertGraphicsCommandsFinishedWaitQuery(
+ RenderedFrameId aFrameId) {
+ RefPtr<ID3D11Query> query;
+ query = GetD3D11Query();
+ if (!query) {
+ return;
+ }
+
+ mCtx->End(query);
+ mCtx->Flush();
+ mWaitForPresentQueries.emplace(aFrameId, query);
+}
+
+bool RenderCompositorANGLE::WaitForPreviousGraphicsCommandsFinishedQuery(
+ bool aWaitAll) {
+ size_t waitLatency = mUseTripleBuffering ? 3 : 2;
+ if (aWaitAll) {
+ waitLatency = 1;
+ }
+
+ while (mWaitForPresentQueries.size() >= waitLatency) {
+ auto queryPair = mWaitForPresentQueries.front();
+ BOOL result;
+ bool ret =
+ layers::WaitForFrameGPUQuery(mDevice, mCtx, queryPair.second, &result);
+
+ if (!ret) {
+ mWaitForPresentQueries.pop();
+ return false;
+ }
+
+ // Recycle query for later use.
+ mRecycledQuery = queryPair.second;
+ mLastCompletedFrameId = queryPair.first;
+ mWaitForPresentQueries.pop();
+ }
+ return true;
+}
+
+RenderedFrameId RenderCompositorANGLE::GetLastCompletedFrameId() {
+ while (!mWaitForPresentQueries.empty()) {
+ auto queryPair = mWaitForPresentQueries.front();
+ if (mCtx->GetData(queryPair.second, nullptr, 0,
+ D3D11_ASYNC_GETDATA_DONOTFLUSH) != S_OK) {
+ break;
+ }
+
+ mRecycledQuery = queryPair.second;
+ mLastCompletedFrameId = queryPair.first;
+ mWaitForPresentQueries.pop();
+ }
+
+ nsPrintfCString marker("Pending frames %u",
+ (uint32_t)mWaitForPresentQueries.size());
+ PROFILER_MARKER_TEXT("GetLastCompletedFrameId", GRAPHICS, {}, marker);
+
+ return mLastCompletedFrameId;
+}
+
+RenderedFrameId RenderCompositorANGLE::UpdateFrameId() {
+ RenderedFrameId frameId = GetNextRenderFrameId();
+ InsertGraphicsCommandsFinishedWaitQuery(frameId);
+ return frameId;
+}
+
+GLenum RenderCompositorANGLE::IsContextLost(bool aForce) {
+ // glGetGraphicsResetStatus does not always work to detect timeout detection
+ // and recovery (TDR). On Windows, ANGLE itself is just relying upon the same
+ // API, so we should not need to check it separately.
+ auto reason = mDevice->GetDeviceRemovedReason();
+ switch (reason) {
+ case S_OK:
+ return LOCAL_GL_NO_ERROR;
+ case DXGI_ERROR_DEVICE_REMOVED:
+ case DXGI_ERROR_DRIVER_INTERNAL_ERROR:
+ NS_WARNING("Device reset due to system / different device");
+ return LOCAL_GL_INNOCENT_CONTEXT_RESET_ARB;
+ case DXGI_ERROR_DEVICE_HUNG:
+ case DXGI_ERROR_DEVICE_RESET:
+ case DXGI_ERROR_INVALID_CALL:
+ gfxCriticalError() << "Device reset due to WR device: "
+ << gfx::hexa(reason);
+ return LOCAL_GL_GUILTY_CONTEXT_RESET_ARB;
+ default:
+ gfxCriticalError() << "Device reset with WR device unexpected reason: "
+ << gfx::hexa(reason);
+ return LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB;
+ }
+}
+
+bool RenderCompositorANGLE::UseCompositor() {
+ if (!mUseNativeCompositor) {
+ return false;
+ }
+
+ if (!mDCLayerTree || !gfx::gfxVars::UseWebRenderCompositor()) {
+ return false;
+ }
+ return true;
+}
+
+bool RenderCompositorANGLE::SupportAsyncScreenshot() {
+ return !UseCompositor() && !mDisablingNativeCompositor;
+}
+
+bool RenderCompositorANGLE::ShouldUseNativeCompositor() {
+ return UseCompositor();
+}
+
+void RenderCompositorANGLE::CompositorBeginFrame() {
+ mDCLayerTree->CompositorBeginFrame();
+}
+
+void RenderCompositorANGLE::CompositorEndFrame() {
+ mDCLayerTree->CompositorEndFrame();
+}
+
+void RenderCompositorANGLE::Bind(wr::NativeTileId aId,
+ wr::DeviceIntPoint* aOffset, uint32_t* aFboId,
+ wr::DeviceIntRect aDirtyRect,
+ wr::DeviceIntRect aValidRect) {
+ mDCLayerTree->Bind(aId, aOffset, aFboId, aDirtyRect, aValidRect);
+}
+
+void RenderCompositorANGLE::Unbind() { mDCLayerTree->Unbind(); }
+
+void RenderCompositorANGLE::CreateSurface(wr::NativeSurfaceId aId,
+ wr::DeviceIntPoint aVirtualOffset,
+ wr::DeviceIntSize aTileSize,
+ bool aIsOpaque) {
+ mDCLayerTree->CreateSurface(aId, aVirtualOffset, aTileSize, aIsOpaque);
+}
+
+void RenderCompositorANGLE::CreateExternalSurface(wr::NativeSurfaceId aId,
+ bool aIsOpaque) {
+ mDCLayerTree->CreateExternalSurface(aId, aIsOpaque);
+}
+
+void RenderCompositorANGLE::DestroySurface(NativeSurfaceId aId) {
+ mDCLayerTree->DestroySurface(aId);
+}
+
+void RenderCompositorANGLE::CreateTile(wr::NativeSurfaceId aId, int aX,
+ int aY) {
+ mDCLayerTree->CreateTile(aId, aX, aY);
+}
+
+void RenderCompositorANGLE::DestroyTile(wr::NativeSurfaceId aId, int aX,
+ int aY) {
+ mDCLayerTree->DestroyTile(aId, aX, aY);
+}
+
+void RenderCompositorANGLE::AttachExternalImage(
+ wr::NativeSurfaceId aId, wr::ExternalImageId aExternalImage) {
+ mDCLayerTree->AttachExternalImage(aId, aExternalImage);
+}
+
+void RenderCompositorANGLE::AddSurface(
+ wr::NativeSurfaceId aId, const wr::CompositorSurfaceTransform& aTransform,
+ wr::DeviceIntRect aClipRect, wr::ImageRendering aImageRendering) {
+ mDCLayerTree->AddSurface(aId, aTransform, aClipRect, aImageRendering);
+}
+
+void RenderCompositorANGLE::GetCompositorCapabilities(
+ CompositorCapabilities* aCaps) {
+ RenderCompositor::GetCompositorCapabilities(aCaps);
+
+ if (StaticPrefs::gfx_webrender_dcomp_use_virtual_surfaces_AtStartup()) {
+ aCaps->virtual_surface_size = VIRTUAL_SURFACE_SIZE;
+ } else {
+ aCaps->virtual_surface_size = 0;
+ }
+ // DComp video overlay does not support negative scaling. See Bug 1831820
+ aCaps->supports_external_compositor_surface_negative_scaling = false;
+}
+
+void RenderCompositorANGLE::EnableNativeCompositor(bool aEnable) {
+ // XXX Re-enable native compositor is not handled yet.
+ MOZ_RELEASE_ASSERT(!mDisablingNativeCompositor);
+ MOZ_RELEASE_ASSERT(!aEnable);
+ LOG("RenderCompositorANGLE::EnableNativeCompositor() aEnable %d", aEnable);
+
+ if (!UseCompositor()) {
+ return;
+ }
+
+ mUseNativeCompositor = false;
+ mDCLayerTree->DisableNativeCompositor();
+
+ bool useAlpha = mWidget->AsWindows()->HasGlass();
+ DestroyEGLSurface();
+ mBufferSize.reset();
+
+ RefPtr<IDXGISwapChain1> swapChain1 =
+ CreateSwapChainForDComp(mUseTripleBuffering, useAlpha);
+ if (swapChain1) {
+ mSwapChain = swapChain1;
+ mUseAlpha = useAlpha;
+ mDCLayerTree->SetDefaultSwapChain(swapChain1);
+ // When alpha is used, we want to disable partial present.
+ // See Bug 1595027.
+ if (useAlpha) {
+ mFullRender = true;
+ }
+ ResizeBufferIfNeeded();
+ } else {
+ gfxCriticalNote << "Failed to re-create SwapChain";
+ RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE);
+ return;
+ }
+ mDisablingNativeCompositor = true;
+}
+
+void RenderCompositorANGLE::InitializeUsePartialPresent() {
+ // Even when mSwapChain1 is null, we could enable WR partial present, since
+ // when mSwapChain1 is null, SwapChain is blit model swap chain with one
+ // buffer.
+ if (UseCompositor() || mWidget->AsWindows()->HasFxrOutputHandler() ||
+ gfx::gfxVars::WebRenderMaxPartialPresentRects() <= 0) {
+ mUsePartialPresent = false;
+ } else {
+ mUsePartialPresent = true;
+ }
+}
+
+bool RenderCompositorANGLE::UsePartialPresent() { return mUsePartialPresent; }
+
+bool RenderCompositorANGLE::RequestFullRender() { return mFullRender; }
+
+uint32_t RenderCompositorANGLE::GetMaxPartialPresentRects() {
+ if (!mUsePartialPresent) {
+ return 0;
+ }
+ return gfx::gfxVars::WebRenderMaxPartialPresentRects();
+}
+
+bool RenderCompositorANGLE::MaybeReadback(
+ const gfx::IntSize& aReadbackSize, const wr::ImageFormat& aReadbackFormat,
+ const Range<uint8_t>& aReadbackBuffer, bool* aNeedsYFlip) {
+ MOZ_ASSERT(aReadbackFormat == wr::ImageFormat::BGRA8);
+
+ if (!UseCompositor()) {
+ return false;
+ }
+
+ auto start = TimeStamp::Now();
+
+ HDC nulldc = ::GetDC(NULL);
+ HDC dc = ::CreateCompatibleDC(nulldc);
+ ::ReleaseDC(nullptr, nulldc);
+ if (!dc) {
+ gfxCriticalError() << "CreateCompatibleDC failed";
+ return false;
+ }
+
+ BITMAPV4HEADER header;
+ memset(&header, 0, sizeof(BITMAPV4HEADER));
+ header.bV4Size = sizeof(BITMAPV4HEADER);
+ header.bV4Width = aReadbackSize.width;
+ header.bV4Height = -LONG(aReadbackSize.height); // top-to-buttom DIB
+ header.bV4Planes = 1;
+ header.bV4BitCount = 32;
+ header.bV4V4Compression = BI_BITFIELDS;
+ header.bV4RedMask = 0x00FF0000;
+ header.bV4GreenMask = 0x0000FF00;
+ header.bV4BlueMask = 0x000000FF;
+ header.bV4AlphaMask = 0xFF000000;
+
+ void* readbackBits = nullptr;
+ HBITMAP bitmap =
+ ::CreateDIBSection(dc, reinterpret_cast<BITMAPINFO*>(&header),
+ DIB_RGB_COLORS, &readbackBits, nullptr, 0);
+ if (!bitmap) {
+ ::DeleteDC(dc);
+ gfxCriticalError() << "CreateDIBSection failed";
+ return false;
+ }
+
+ ::SelectObject(dc, bitmap);
+
+ UINT flags = PW_CLIENTONLY | PW_RENDERFULLCONTENT;
+ HWND hwnd = mWidget->AsWindows()->GetHwnd();
+
+ mDCLayerTree->WaitForCommitCompletion();
+
+ BOOL result = ::PrintWindow(hwnd, dc, flags);
+ if (!result) {
+ ::DeleteObject(bitmap);
+ ::DeleteDC(dc);
+ gfxCriticalError() << "PrintWindow failed";
+ return false;
+ }
+
+ ::GdiFlush();
+
+ memcpy(&aReadbackBuffer[0], readbackBits, aReadbackBuffer.length());
+
+ ::DeleteObject(bitmap);
+ ::DeleteDC(dc);
+
+ uint32_t latencyMs = round((TimeStamp::Now() - start).ToMilliseconds());
+ if (latencyMs > 500) {
+ gfxCriticalNote << "Readback took too long: " << latencyMs << " ms";
+ }
+
+ if (aNeedsYFlip) {
+ *aNeedsYFlip = false;
+ }
+
+ return true;
+}
+
+} // namespace wr
+} // namespace mozilla
diff --git a/gfx/webrender_bindings/RenderCompositorANGLE.h b/gfx/webrender_bindings/RenderCompositorANGLE.h
new file mode 100644
index 0000000000..cb1203e3fe
--- /dev/null
+++ b/gfx/webrender_bindings/RenderCompositorANGLE.h
@@ -0,0 +1,161 @@
+/* -*- 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_RENDERCOMPOSITOR_ANGLE_H
+#define MOZILLA_GFX_RENDERCOMPOSITOR_ANGLE_H
+
+#include <queue>
+
+#include "GLTypes.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/webrender/RenderCompositor.h"
+#include "mozilla/webrender/RenderThread.h"
+
+struct ID3D11DeviceContext;
+struct ID3D11Device;
+struct ID3D11Query;
+struct IDXGIFactory2;
+struct IDXGISwapChain;
+struct IDXGISwapChain1;
+
+namespace mozilla {
+namespace gl {
+class GLLibraryEGL;
+} // namespace gl
+
+namespace wr {
+
+class DCLayerTree;
+
+class RenderCompositorANGLE : public RenderCompositor {
+ public:
+ static UniquePtr<RenderCompositor> Create(
+ const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError);
+
+ explicit RenderCompositorANGLE(
+ const RefPtr<widget::CompositorWidget>& aWidget,
+ RefPtr<gl::GLContext>&& aGL);
+ virtual ~RenderCompositorANGLE();
+ bool Initialize(nsACString& aError);
+
+ bool BeginFrame() override;
+ RenderedFrameId EndFrame(const nsTArray<DeviceIntRect>& aDirtyRects) final;
+ bool WaitForGPU() override;
+ RenderedFrameId GetLastCompletedFrameId() final;
+ RenderedFrameId UpdateFrameId() final;
+ void Pause() override;
+ bool Resume() override;
+ void Update() override;
+
+ gl::GLContext* gl() const override { return mGL; }
+
+ bool MakeCurrent() override;
+
+ bool UseANGLE() const override { return true; }
+
+ bool UseDComp() const override { return !!mDCLayerTree; }
+
+ bool UseTripleBuffering() const override { return mUseTripleBuffering; }
+
+ layers::WebRenderCompositor CompositorType() const override {
+ if (UseDComp()) {
+ return layers::WebRenderCompositor::DIRECT_COMPOSITION;
+ }
+ return layers::WebRenderCompositor::DRAW;
+ }
+
+ LayoutDeviceIntSize GetBufferSize() override;
+
+ GLenum IsContextLost(bool aForce) override;
+
+ bool SurfaceOriginIsTopLeft() override { return true; }
+
+ bool SupportAsyncScreenshot() override;
+
+ bool ShouldUseNativeCompositor() override;
+
+ // Interface for wr::Compositor
+ void CompositorBeginFrame() override;
+ void CompositorEndFrame() override;
+ void Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset, uint32_t* aFboId,
+ wr::DeviceIntRect aDirtyRect,
+ wr::DeviceIntRect aValidRect) override;
+ void Unbind() override;
+ void CreateSurface(wr::NativeSurfaceId aId, wr::DeviceIntPoint aVirtualOffset,
+ wr::DeviceIntSize aTileSize, bool aIsOpaque) override;
+ void CreateExternalSurface(wr::NativeSurfaceId aId, bool aIsOpaque) override;
+ void DestroySurface(NativeSurfaceId aId) override;
+ void CreateTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY) override;
+ void DestroyTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY) override;
+ void AttachExternalImage(wr::NativeSurfaceId aId,
+ wr::ExternalImageId aExternalImage) override;
+ void AddSurface(wr::NativeSurfaceId aId,
+ const wr::CompositorSurfaceTransform& aTransform,
+ wr::DeviceIntRect aClipRect,
+ wr::ImageRendering aImageRendering) override;
+ void EnableNativeCompositor(bool aEnable) override;
+ void GetCompositorCapabilities(CompositorCapabilities* aCaps) override;
+
+ // Interface for partial present
+ bool UsePartialPresent() override;
+ bool RequestFullRender() override;
+ uint32_t GetMaxPartialPresentRects() override;
+
+ bool MaybeReadback(const gfx::IntSize& aReadbackSize,
+ const wr::ImageFormat& aReadbackFormat,
+ const Range<uint8_t>& aReadbackBuffer,
+ bool* aNeedsYFlip) override;
+
+ protected:
+ bool UseCompositor();
+ void InitializeUsePartialPresent();
+ void InsertGraphicsCommandsFinishedWaitQuery(
+ RenderedFrameId aRenderedFrameId);
+ bool WaitForPreviousGraphicsCommandsFinishedQuery(bool aWaitAll = false);
+ bool ResizeBufferIfNeeded();
+ bool CreateEGLSurface();
+ void DestroyEGLSurface();
+ ID3D11Device* GetDeviceOfEGLDisplay(nsACString& aError);
+ bool CreateSwapChain(nsACString& aError);
+ void CreateSwapChainForDCompIfPossible(IDXGIFactory2* aDXGIFactory2);
+ RefPtr<IDXGISwapChain1> CreateSwapChainForDComp(bool aUseTripleBuffering,
+ bool aUseAlpha);
+ RefPtr<ID3D11Query> GetD3D11Query();
+ void ReleaseNativeCompositorResources();
+ HWND GetCompositorHwnd();
+
+ RefPtr<gl::GLContext> mGL;
+
+ EGLConfig mEGLConfig;
+ EGLSurface mEGLSurface;
+
+ bool mUseTripleBuffering;
+ bool mUseAlpha;
+
+ RefPtr<ID3D11Device> mDevice;
+ RefPtr<ID3D11DeviceContext> mCtx;
+ RefPtr<IDXGISwapChain> mSwapChain;
+ RefPtr<IDXGISwapChain1> mSwapChain1;
+
+ UniquePtr<DCLayerTree> mDCLayerTree;
+
+ std::queue<std::pair<RenderedFrameId, RefPtr<ID3D11Query>>>
+ mWaitForPresentQueries;
+ RefPtr<ID3D11Query> mRecycledQuery;
+ RenderedFrameId mLastCompletedFrameId;
+
+ Maybe<LayoutDeviceIntSize> mBufferSize;
+ bool mUseNativeCompositor;
+ bool mUsePartialPresent;
+ bool mFullRender;
+ // Used to know a timing of disabling native compositor.
+ bool mDisablingNativeCompositor;
+};
+
+} // namespace wr
+} // namespace mozilla
+
+#endif
diff --git a/gfx/webrender_bindings/RenderCompositorD3D11SWGL.cpp b/gfx/webrender_bindings/RenderCompositorD3D11SWGL.cpp
new file mode 100644
index 0000000000..1fdae182c2
--- /dev/null
+++ b/gfx/webrender_bindings/RenderCompositorD3D11SWGL.cpp
@@ -0,0 +1,484 @@
+/* -*- 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 "RenderCompositorD3D11SWGL.h"
+
+#include "gfxConfig.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/widget/CompositorWidget.h"
+#include "mozilla/layers/Effects.h"
+#include "mozilla/webrender/RenderD3D11TextureHost.h"
+#include "RenderCompositorRecordedFrame.h"
+#include "RenderThread.h"
+
+namespace mozilla {
+using namespace layers;
+
+namespace wr {
+
+extern LazyLogModule gRenderThreadLog;
+#define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
+
+RenderCompositorD3D11SWGL::UploadMode
+RenderCompositorD3D11SWGL::GetUploadMode() {
+ int mode = StaticPrefs::gfx_webrender_software_d3d11_upload_mode();
+ switch (mode) {
+ case 1:
+ return Upload_Immediate;
+ case 2:
+ return Upload_Staging;
+ case 3:
+ return Upload_StagingNoBlock;
+ case 4:
+ return Upload_StagingPooled;
+ default:
+ return Upload_Staging;
+ }
+}
+
+UniquePtr<RenderCompositor> RenderCompositorD3D11SWGL::Create(
+ const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError) {
+ if (!aWidget->GetCompositorOptions().AllowSoftwareWebRenderD3D11() ||
+ !gfx::gfxConfig::IsEnabled(gfx::Feature::D3D11_COMPOSITING)) {
+ return nullptr;
+ }
+
+ void* ctx = wr_swgl_create_context();
+ if (!ctx) {
+ gfxCriticalNote << "Failed SWGL context creation for WebRender";
+ return nullptr;
+ }
+
+ RefPtr<CompositorD3D11> compositor = MakeAndAddRef<CompositorD3D11>(aWidget);
+ nsCString log;
+ if (!compositor->Initialize(&log)) {
+ gfxCriticalNote << "Failed to initialize CompositorD3D11 for SWGL: "
+ << log.get();
+ return nullptr;
+ }
+ return MakeUnique<RenderCompositorD3D11SWGL>(compositor, aWidget, ctx);
+}
+
+RenderCompositorD3D11SWGL::RenderCompositorD3D11SWGL(
+ CompositorD3D11* aCompositor,
+ const RefPtr<widget::CompositorWidget>& aWidget, void* aContext)
+ : RenderCompositorLayersSWGL(aCompositor, aWidget, aContext) {
+ LOG("RenderCompositorD3D11SWGL::RenderCompositorD3D11SWGL()");
+
+ mSyncObject = GetCompositorD3D11()->GetSyncObject();
+}
+
+RenderCompositorD3D11SWGL::~RenderCompositorD3D11SWGL() {
+ LOG("RenderCompositorD3D11SWGL::~RenderCompositorD3D11SWGL()");
+}
+
+bool RenderCompositorD3D11SWGL::BeginFrame() {
+ if (!RenderCompositorLayersSWGL::BeginFrame()) {
+ return false;
+ }
+
+ mUploadMode = GetUploadMode();
+ return true;
+}
+
+void RenderCompositorD3D11SWGL::HandleExternalImage(
+ RenderTextureHost* aExternalImage, FrameSurface& aFrameSurface) {
+ // We need to hold the texture source separately from the effect,
+ // since the effect doesn't hold a strong reference.
+ RefPtr<DataTextureSourceD3D11> layer;
+ RefPtr<TexturedEffect> texturedEffect;
+ gfx::IntSize size;
+ if (auto* host = aExternalImage->AsRenderDXGITextureHost()) {
+ if (!host->EnsureD3D11Texture2D(GetDevice())) {
+ return;
+ }
+
+ layer = new DataTextureSourceD3D11(GetDevice(), host->GetFormat(),
+ host->GetD3D11Texture2D());
+ if (host->GetFormat() == gfx::SurfaceFormat::NV12 ||
+ host->GetFormat() == gfx::SurfaceFormat::P010 ||
+ host->GetFormat() == gfx::SurfaceFormat::P016) {
+ const auto yuv = FromYUVRangedColorSpace(host->GetYUVColorSpace());
+ texturedEffect =
+ new EffectNV12(layer, yuv.space, yuv.range, host->GetColorDepth(),
+ aFrameSurface.mFilter);
+ } else {
+ MOZ_ASSERT(host->GetFormat() == gfx::SurfaceFormat::B8G8R8X8 ||
+ host->GetFormat() == gfx::SurfaceFormat::B8G8R8A8);
+ texturedEffect = CreateTexturedEffect(host->GetFormat(), layer,
+ aFrameSurface.mFilter, true);
+ }
+ size = host->GetSize(0);
+ host->LockInternal();
+ } else if (auto* host = aExternalImage->AsRenderDXGIYCbCrTextureHost()) {
+ if (!host->EnsureD3D11Texture2D(GetDevice())) {
+ return;
+ }
+
+ layer = new DataTextureSourceD3D11(GetDevice(), gfx::SurfaceFormat::A8,
+ host->GetD3D11Texture2D(0));
+ RefPtr<DataTextureSourceD3D11> u = new DataTextureSourceD3D11(
+ GetDevice(), gfx::SurfaceFormat::A8, host->GetD3D11Texture2D(1));
+ layer->SetNextSibling(u);
+ RefPtr<DataTextureSourceD3D11> v = new DataTextureSourceD3D11(
+ GetDevice(), gfx::SurfaceFormat::A8, host->GetD3D11Texture2D(2));
+ u->SetNextSibling(v);
+
+ const auto yuv = FromYUVRangedColorSpace(host->GetYUVColorSpace());
+ texturedEffect =
+ new EffectYCbCr(layer, yuv.space, yuv.range, host->GetColorDepth(),
+ aFrameSurface.mFilter);
+ size = host->GetSize(0);
+ host->LockInternal();
+ }
+
+ gfx::Rect drawRect(0, 0, size.width, size.height);
+
+ EffectChain effect;
+ effect.mPrimaryEffect = texturedEffect;
+ mCompositor->DrawQuad(drawRect, aFrameSurface.mClipRect, effect, 1.0,
+ aFrameSurface.mTransform, drawRect);
+
+ if (auto* host = aExternalImage->AsRenderDXGITextureHost()) {
+ host->Unlock();
+ } else if (auto* host = aExternalImage->AsRenderDXGIYCbCrTextureHost()) {
+ host->Unlock();
+ }
+}
+
+void RenderCompositorD3D11SWGL::Pause() {}
+
+bool RenderCompositorD3D11SWGL::Resume() { return true; }
+
+GLenum RenderCompositorD3D11SWGL::IsContextLost(bool aForce) {
+ // CompositorD3D11 uses ID3D11Device for composite. The device status needs to
+ // be checked.
+ auto reason = GetDevice()->GetDeviceRemovedReason();
+ switch (reason) {
+ case S_OK:
+ return LOCAL_GL_NO_ERROR;
+ case DXGI_ERROR_DEVICE_REMOVED:
+ case DXGI_ERROR_DRIVER_INTERNAL_ERROR:
+ NS_WARNING("Device reset due to system / different device");
+ return LOCAL_GL_INNOCENT_CONTEXT_RESET_ARB;
+ case DXGI_ERROR_DEVICE_HUNG:
+ case DXGI_ERROR_DEVICE_RESET:
+ case DXGI_ERROR_INVALID_CALL:
+ gfxCriticalError() << "Device reset due to WR device: "
+ << gfx::hexa(reason);
+ return LOCAL_GL_GUILTY_CONTEXT_RESET_ARB;
+ default:
+ gfxCriticalError() << "Device reset with WR device unexpected reason: "
+ << gfx::hexa(reason);
+ return LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB;
+ }
+}
+
+UniquePtr<RenderCompositorLayersSWGL::Surface>
+RenderCompositorD3D11SWGL::DoCreateSurface(wr::DeviceIntSize aTileSize,
+ bool aIsOpaque) {
+ return MakeUnique<SurfaceD3D11SWGL>(aTileSize, aIsOpaque);
+}
+
+SurfaceD3D11SWGL::SurfaceD3D11SWGL(wr::DeviceIntSize aTileSize, bool aIsOpaque)
+ : Surface(aTileSize, aIsOpaque) {}
+
+RenderCompositorD3D11SWGL::TileD3D11::TileD3D11(
+ layers::DataTextureSourceD3D11* aTexture, ID3D11Texture2D* aStagingTexture,
+ gfx::DataSourceSurface* aDataSourceSurface, Surface* aOwner,
+ RenderCompositorD3D11SWGL* aRenderCompositor)
+ : Tile(),
+ mTexture(aTexture),
+ mStagingTexture(aStagingTexture),
+ mSurface(aDataSourceSurface),
+ mOwner(aOwner->AsSurfaceD3D11SWGL()),
+ mRenderCompositor(aRenderCompositor) {}
+
+bool RenderCompositorD3D11SWGL::TileD3D11::Map(wr::DeviceIntRect aDirtyRect,
+ wr::DeviceIntRect aValidRect,
+ void** aData, int32_t* aStride) {
+ const UploadMode uploadMode = mRenderCompositor->GetUploadMode();
+ const gfx::IntSize tileSize = mOwner->TileSize();
+
+ if (!IsValid()) {
+ return false;
+ }
+
+ // Check if this tile's upload method matches what we're using for this frame,
+ // and if not then reallocate to fix it. Do this before we copy the struct
+ // into mCurrentTile.
+ if (uploadMode == Upload_Immediate) {
+ if (mStagingTexture) {
+ MOZ_ASSERT(!mSurface);
+ mStagingTexture = nullptr;
+ mSurface = mRenderCompositor->CreateStagingSurface(tileSize);
+ }
+ } else {
+ if (mSurface) {
+ MOZ_ASSERT(!mStagingTexture);
+ mSurface = nullptr;
+ mStagingTexture = mRenderCompositor->CreateStagingTexture(tileSize);
+ }
+ }
+
+ mRenderCompositor->mCurrentStagingTexture = mStagingTexture;
+ mRenderCompositor->mCurrentStagingTextureIsTemp = false;
+
+ if (uploadMode == Upload_Immediate) {
+ gfx::DataSourceSurface::MappedSurface map;
+ if (!mSurface->Map(gfx::DataSourceSurface::READ_WRITE, &map)) {
+ return false;
+ }
+
+ *aData = map.mData + aValidRect.min.y * map.mStride + aValidRect.min.x * 4;
+ *aStride = map.mStride;
+ // Ensure our mapped data is accessible by writing to the beginning and end
+ // of the dirty region. See bug 171519
+ uint32_t* probeData = (uint32_t*)map.mData +
+ aDirtyRect.min.y * (map.mStride / 4) +
+ aDirtyRect.min.x;
+ *probeData = 0;
+ uint32_t* probeDataEnd = (uint32_t*)map.mData +
+ (aDirtyRect.max.y - 1) * (map.mStride / 4) +
+ (aDirtyRect.max.x - 1);
+ *probeDataEnd = 0;
+
+ mValidRect = gfx::Rect(aValidRect.min.x, aValidRect.min.y,
+ aValidRect.width(), aValidRect.height());
+ return true;
+ }
+
+ if (!mRenderCompositor->mCurrentStagingTexture) {
+ return false;
+ }
+
+ RefPtr<ID3D11DeviceContext> context;
+ mRenderCompositor->GetDevice()->GetImmediateContext(getter_AddRefs(context));
+
+ D3D11_MAPPED_SUBRESOURCE mappedSubresource;
+
+ bool shouldBlock = uploadMode == Upload_Staging;
+
+ HRESULT hr = context->Map(
+ mRenderCompositor->mCurrentStagingTexture, 0, D3D11_MAP_READ_WRITE,
+ shouldBlock ? 0 : D3D11_MAP_FLAG_DO_NOT_WAIT, &mappedSubresource);
+ if (hr == DXGI_ERROR_WAS_STILL_DRAWING) {
+ // mCurrentTile is a copy of the real tile data, so we can just replace the
+ // staging one with a temporary for this draw. The staging texture for the
+ // real tile remains untouched, so we'll go back to using that for future
+ // frames and discard the new one. In the future we could improve this by
+ // having a pool of shared staging textures for all the tiles.
+
+ // Mark the tile as having a temporary staging texture.
+ mRenderCompositor->mCurrentStagingTextureIsTemp = true;
+
+ // Try grabbing a texture from the staging pool and see if we can use that.
+ if (uploadMode == Upload_StagingPooled && mOwner->mStagingPool.Length()) {
+ mRenderCompositor->mCurrentStagingTexture =
+ mOwner->mStagingPool.ElementAt(0);
+ mOwner->mStagingPool.RemoveElementAt(0);
+ hr = context->Map(mRenderCompositor->mCurrentStagingTexture, 0,
+ D3D11_MAP_READ_WRITE, D3D11_MAP_FLAG_DO_NOT_WAIT,
+ &mappedSubresource);
+
+ // If that failed, put it back into the pool (but at the end).
+ if (hr == DXGI_ERROR_WAS_STILL_DRAWING) {
+ mOwner->mStagingPool.AppendElement(
+ mRenderCompositor->mCurrentStagingTexture);
+ }
+ }
+
+ // No staging textures, or we tried one and it was busy. Allocate a brand
+ // new one instead.
+ if (hr == DXGI_ERROR_WAS_STILL_DRAWING) {
+ mRenderCompositor->mCurrentStagingTexture =
+ mRenderCompositor->CreateStagingTexture(tileSize);
+ if (!mRenderCompositor->mCurrentStagingTexture) {
+ return false;
+ }
+ hr = context->Map(mRenderCompositor->mCurrentStagingTexture, 0,
+ D3D11_MAP_READ_WRITE, 0, &mappedSubresource);
+ }
+ }
+ if (!SUCCEEDED(hr)) {
+ gfxCriticalError() << "Failed to map tile: " << gfx::hexa(hr);
+ // This is only expected to fail if we hit a device reset.
+ MOZ_RELEASE_ASSERT(
+ mRenderCompositor->GetDevice()->GetDeviceRemovedReason() != S_OK);
+ return false;
+ }
+
+ // aData is expected to contain a pointer to the first pixel within the valid
+ // rect, so take the mapped resource's data (which covers the full tile size)
+ // and offset it by the top/left of the valid rect.
+ *aData = (uint8_t*)mappedSubresource.pData +
+ aValidRect.min.y * mappedSubresource.RowPitch + aValidRect.min.x * 4;
+ *aStride = mappedSubresource.RowPitch;
+
+ // Ensure our mapped data is accessible by writing to the beginning and end
+ // of the dirty region. See bug 171519
+ uint32_t* probeData = (uint32_t*)mappedSubresource.pData +
+ aDirtyRect.min.y * (mappedSubresource.RowPitch / 4) +
+ aDirtyRect.min.x;
+ *probeData = 0;
+ uint32_t* probeDataEnd =
+ (uint32_t*)mappedSubresource.pData +
+ (aDirtyRect.max.y - 1) * (mappedSubresource.RowPitch / 4) +
+ (aDirtyRect.max.x - 1);
+ *probeDataEnd = 0;
+
+ // Store the new valid rect, so that we can composite only those pixels
+ mValidRect = gfx::Rect(aValidRect.min.x, aValidRect.min.y, aValidRect.width(),
+ aValidRect.height());
+
+ return true;
+}
+
+void RenderCompositorD3D11SWGL::TileD3D11::Unmap(
+ const gfx::IntRect& aDirtyRect) {
+ const UploadMode uploadMode = mRenderCompositor->GetUploadMode();
+
+ if (!IsValid()) {
+ return;
+ }
+
+ if (mSurface) {
+ MOZ_ASSERT(uploadMode == Upload_Immediate);
+ mSurface->Unmap();
+ nsIntRegion dirty(aDirtyRect);
+ // This uses UpdateSubresource, which blocks, so is likely implemented as a
+ // memcpy into driver owned memory, followed by an async upload. The staging
+ // method should avoid this extra copy, and is likely to be faster usually.
+ // We could possible do this call on a background thread so that sw-wr can
+ // start drawing the next tile while the memcpy is in progress.
+ mTexture->Update(mSurface, &dirty);
+ return;
+ }
+
+ if (!mRenderCompositor->mCurrentStagingTexture) {
+ return;
+ }
+
+ RefPtr<ID3D11DeviceContext> context;
+ mRenderCompositor->GetDevice()->GetImmediateContext(getter_AddRefs(context));
+
+ context->Unmap(mRenderCompositor->mCurrentStagingTexture, 0);
+
+ D3D11_BOX box;
+ box.front = 0;
+ box.back = 1;
+ box.left = aDirtyRect.X();
+ box.top = aDirtyRect.Y();
+ box.right = aDirtyRect.XMost();
+ box.bottom = aDirtyRect.YMost();
+
+ context->CopySubresourceRegion(
+ mTexture->GetD3D11Texture(), 0, aDirtyRect.x, aDirtyRect.y, 0,
+ mRenderCompositor->mCurrentStagingTexture, 0, &box);
+
+ // If we allocated a temp staging texture for this tile, and we're running
+ // in pooled mode, then consider adding it to the pool for later.
+ if (mRenderCompositor->mCurrentStagingTextureIsTemp &&
+ uploadMode == Upload_StagingPooled) {
+ static const uint32_t kMaxPoolSize = 5;
+ if (mOwner->mStagingPool.Length() < kMaxPoolSize) {
+ mOwner->mStagingPool.AppendElement(
+ mRenderCompositor->mCurrentStagingTexture);
+ }
+ }
+
+ mRenderCompositor->mCurrentStagingTexture = nullptr;
+ mRenderCompositor->mCurrentStagingTextureIsTemp = false;
+}
+
+bool RenderCompositorD3D11SWGL::TileD3D11::IsValid() { return !!mTexture; }
+
+already_AddRefed<ID3D11Texture2D>
+RenderCompositorD3D11SWGL::CreateStagingTexture(const gfx::IntSize aSize) {
+ CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, aSize.width,
+ aSize.height, 1, 1);
+
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE | D3D11_CPU_ACCESS_READ;
+ desc.Usage = D3D11_USAGE_STAGING;
+ desc.BindFlags = 0;
+
+ RefPtr<ID3D11Texture2D> cpuTexture;
+ DebugOnly<HRESULT> hr =
+ GetDevice()->CreateTexture2D(&desc, nullptr, getter_AddRefs(cpuTexture));
+ MOZ_ASSERT(SUCCEEDED(hr));
+ if (!cpuTexture) {
+ gfxCriticalNote << "Failed to create StagingTexture: " << aSize;
+ RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE);
+ }
+ return cpuTexture.forget();
+}
+
+already_AddRefed<gfx::DataSourceSurface>
+RenderCompositorD3D11SWGL::CreateStagingSurface(const gfx::IntSize aSize) {
+ return gfx::Factory::CreateDataSourceSurface(aSize,
+ gfx::SurfaceFormat::B8G8R8A8);
+}
+
+UniquePtr<RenderCompositorLayersSWGL::Tile>
+RenderCompositorD3D11SWGL::DoCreateTile(Surface* aSurface) {
+ MOZ_RELEASE_ASSERT(aSurface);
+
+ const auto tileSize = aSurface->TileSize();
+
+ if (mUploadMode == Upload_Immediate) {
+ RefPtr<DataTextureSourceD3D11> source =
+ new DataTextureSourceD3D11(gfx::SurfaceFormat::B8G8R8A8, mCompositor,
+ layers::TextureFlags::NO_FLAGS);
+ RefPtr<gfx::DataSourceSurface> surf = CreateStagingSurface(tileSize);
+ return MakeUnique<TileD3D11>(source, nullptr, surf, aSurface, this);
+ }
+
+ MOZ_ASSERT(mUploadMode == Upload_Staging ||
+ mUploadMode == Upload_StagingNoBlock ||
+ mUploadMode == Upload_StagingPooled);
+
+ CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, tileSize.width,
+ tileSize.height, 1, 1);
+
+ RefPtr<ID3D11Texture2D> texture;
+ DebugOnly<HRESULT> hr =
+ GetDevice()->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture));
+ MOZ_ASSERT(SUCCEEDED(hr));
+ if (!texture) {
+ gfxCriticalNote << "Failed to allocate Texture2D: " << aSurface->TileSize();
+ RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE);
+ return MakeUnique<TileD3D11>(nullptr, nullptr, nullptr, aSurface, this);
+ }
+
+ RefPtr<DataTextureSourceD3D11> source = new DataTextureSourceD3D11(
+ GetDevice(), gfx::SurfaceFormat::B8G8R8A8, texture);
+
+ RefPtr<ID3D11Texture2D> cpuTexture = CreateStagingTexture(tileSize);
+ return MakeUnique<TileD3D11>(source, cpuTexture, nullptr, aSurface, this);
+}
+
+bool RenderCompositorD3D11SWGL::MaybeReadback(
+ const gfx::IntSize& aReadbackSize, const wr::ImageFormat& aReadbackFormat,
+ const Range<uint8_t>& aReadbackBuffer, bool* aNeedsYFlip) {
+ MOZ_ASSERT(aReadbackFormat == wr::ImageFormat::BGRA8);
+
+ auto stride =
+ aReadbackSize.width * gfx::BytesPerPixel(gfx::SurfaceFormat::B8G8R8A8);
+ RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateDrawTargetForData(
+ gfx::BackendType::SKIA, &aReadbackBuffer[0], aReadbackSize, stride,
+ gfx::SurfaceFormat::B8G8R8A8, false);
+ if (!dt) {
+ return false;
+ }
+
+ GetCompositorD3D11()->Readback(dt);
+ return true;
+}
+
+} // namespace wr
+} // namespace mozilla
diff --git a/gfx/webrender_bindings/RenderCompositorD3D11SWGL.h b/gfx/webrender_bindings/RenderCompositorD3D11SWGL.h
new file mode 100644
index 0000000000..2365230f66
--- /dev/null
+++ b/gfx/webrender_bindings/RenderCompositorD3D11SWGL.h
@@ -0,0 +1,118 @@
+/* -*- 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_RENDERCOMPOSITOR_D3D11_H
+#define MOZILLA_GFX_RENDERCOMPOSITOR_D3D11_H
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/layers/ScreenshotGrabber.h"
+#include "mozilla/layers/TextureD3D11.h"
+#include "mozilla/layers/CompositorD3D11.h"
+#include "mozilla/webrender/RenderCompositorLayersSWGL.h"
+
+namespace mozilla {
+
+namespace wr {
+
+class SurfaceD3D11SWGL;
+
+class RenderCompositorD3D11SWGL : public RenderCompositorLayersSWGL {
+ public:
+ static UniquePtr<RenderCompositor> Create(
+ const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError);
+
+ RenderCompositorD3D11SWGL(layers::CompositorD3D11* aCompositor,
+ const RefPtr<widget::CompositorWidget>& aWidget,
+ void* aContext);
+ virtual ~RenderCompositorD3D11SWGL();
+
+ void Pause() override;
+ bool Resume() override;
+
+ GLenum IsContextLost(bool aForce) override;
+
+ layers::WebRenderCompositor CompositorType() const override {
+ return layers::WebRenderCompositor::D3D11;
+ }
+ RenderCompositorD3D11SWGL* AsRenderCompositorD3D11SWGL() override {
+ return this;
+ }
+
+ bool BeginFrame() override;
+
+ bool MaybeReadback(const gfx::IntSize& aReadbackSize,
+ const wr::ImageFormat& aReadbackFormat,
+ const Range<uint8_t>& aReadbackBuffer,
+ bool* aNeedsYFlip) override;
+
+ layers::CompositorD3D11* GetCompositorD3D11() {
+ return mCompositor->AsCompositorD3D11();
+ }
+
+ ID3D11Device* GetDevice() { return GetCompositorD3D11()->GetDevice(); }
+
+ private:
+ already_AddRefed<ID3D11Texture2D> CreateStagingTexture(
+ const gfx::IntSize aSize);
+ already_AddRefed<gfx::DataSourceSurface> CreateStagingSurface(
+ const gfx::IntSize aSize);
+
+ void HandleExternalImage(RenderTextureHost* aExternalImage,
+ FrameSurface& aFrameSurface) override;
+ UniquePtr<RenderCompositorLayersSWGL::Surface> DoCreateSurface(
+ wr::DeviceIntSize aTileSize, bool aIsOpaque) override;
+ UniquePtr<RenderCompositorLayersSWGL::Tile> DoCreateTile(
+ Surface* aSurface) override;
+
+ class TileD3D11 : public RenderCompositorLayersSWGL::Tile {
+ public:
+ TileD3D11(layers::DataTextureSourceD3D11* aTexture,
+ ID3D11Texture2D* aStagingTexture,
+ gfx::DataSourceSurface* aDataSourceSurface, Surface* aOwner,
+ RenderCompositorD3D11SWGL* aRenderCompositor);
+ virtual ~TileD3D11() {}
+
+ bool Map(wr::DeviceIntRect aDirtyRect, wr::DeviceIntRect aValidRect,
+ void** aData, int32_t* aStride) override;
+ void Unmap(const gfx::IntRect& aDirtyRect) override;
+ layers::DataTextureSource* GetTextureSource() override { return mTexture; }
+ bool IsValid() override;
+
+ private:
+ RefPtr<layers::DataTextureSourceD3D11> mTexture;
+ RefPtr<ID3D11Texture2D> mStagingTexture;
+ RefPtr<gfx::DataSourceSurface> mSurface;
+ SurfaceD3D11SWGL* mOwner;
+ RenderCompositorD3D11SWGL* mRenderCompositor;
+ };
+
+ enum UploadMode {
+ Upload_Immediate,
+ Upload_Staging,
+ Upload_StagingNoBlock,
+ Upload_StagingPooled
+ };
+ UploadMode GetUploadMode();
+ UploadMode mUploadMode = Upload_Staging;
+
+ RefPtr<ID3D11Texture2D> mCurrentStagingTexture;
+ bool mCurrentStagingTextureIsTemp = false;
+};
+
+class SurfaceD3D11SWGL : public RenderCompositorLayersSWGL::Surface {
+ public:
+ SurfaceD3D11SWGL(wr::DeviceIntSize aTileSize, bool aIsOpaque);
+ virtual ~SurfaceD3D11SWGL() {}
+
+ SurfaceD3D11SWGL* AsSurfaceD3D11SWGL() override { return this; }
+
+ nsTArray<RefPtr<ID3D11Texture2D>> mStagingPool;
+};
+
+} // namespace wr
+} // namespace mozilla
+
+#endif
diff --git a/gfx/webrender_bindings/RenderCompositorEGL.cpp b/gfx/webrender_bindings/RenderCompositorEGL.cpp
new file mode 100644
index 0000000000..21c2de1634
--- /dev/null
+++ b/gfx/webrender_bindings/RenderCompositorEGL.cpp
@@ -0,0 +1,328 @@
+/* -*- 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 "RenderCompositorEGL.h"
+
+#include "GLContext.h"
+#include "GLContextEGL.h"
+#include "GLContextProvider.h"
+#include "GLLibraryEGL.h"
+#include "mozilla/StaticPrefs_gfx.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/layers/BuildConstants.h"
+#include "mozilla/webrender/RenderThread.h"
+#include "mozilla/widget/CompositorWidget.h"
+
+#ifdef MOZ_WAYLAND
+# include "mozilla/WidgetUtilsGtk.h"
+# include "mozilla/widget/GtkCompositorWidget.h"
+#endif
+
+#ifdef MOZ_WIDGET_ANDROID
+# include "mozilla/java/GeckoSurfaceTextureWrappers.h"
+# include "mozilla/layers/AndroidHardwareBuffer.h"
+# include "mozilla/widget/AndroidCompositorWidget.h"
+# include <android/native_window.h>
+# include <android/native_window_jni.h>
+#endif
+
+namespace mozilla::wr {
+
+extern LazyLogModule gRenderThreadLog;
+#define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
+
+/* static */
+UniquePtr<RenderCompositor> RenderCompositorEGL::Create(
+ const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError) {
+ if ((kIsWayland || kIsX11) && !gfx::gfxVars::UseEGL()) {
+ return nullptr;
+ }
+ RefPtr<gl::GLContext> gl = RenderThread::Get()->SingletonGL(aError);
+ if (!gl) {
+ if (aError.IsEmpty()) {
+ aError.Assign("RcANGLE(no shared GL)"_ns);
+ } else {
+ aError.Append("(Create)"_ns);
+ }
+ return nullptr;
+ }
+ return MakeUnique<RenderCompositorEGL>(aWidget, std::move(gl));
+}
+
+EGLSurface RenderCompositorEGL::CreateEGLSurface() {
+ EGLSurface surface = EGL_NO_SURFACE;
+ surface = gl::GLContextEGL::CreateEGLSurfaceForCompositorWidget(
+ mWidget, gl::GLContextEGL::Cast(gl())->mSurfaceConfig);
+ if (surface == EGL_NO_SURFACE) {
+ const auto* renderThread = RenderThread::Get();
+ gfxCriticalNote << "Failed to create EGLSurface. "
+ << renderThread->RendererCount() << " renderers, "
+ << renderThread->ActiveRendererCount() << " active.";
+ }
+ return surface;
+}
+
+RenderCompositorEGL::RenderCompositorEGL(
+ const RefPtr<widget::CompositorWidget>& aWidget,
+ RefPtr<gl::GLContext>&& aGL)
+ : RenderCompositor(aWidget), mGL(aGL), mEGLSurface(EGL_NO_SURFACE) {
+ MOZ_ASSERT(mGL);
+ LOG("RenderCompositorEGL::RenderCompositorEGL()");
+}
+
+RenderCompositorEGL::~RenderCompositorEGL() {
+ LOG("RenderCompositorEGL::~RenderCompositorEGL()");
+#ifdef MOZ_WIDGET_ANDROID
+ java::GeckoSurfaceTexture::DestroyUnused((int64_t)gl());
+#endif
+ DestroyEGLSurface();
+}
+
+bool RenderCompositorEGL::BeginFrame() {
+ if ((kIsWayland || kIsX11) && mEGLSurface == EGL_NO_SURFACE) {
+ gfxCriticalNote
+ << "We don't have EGLSurface to draw into. Called too early?";
+ return false;
+ }
+#ifdef MOZ_WAYLAND
+ if (mWidget->AsGTK()) {
+ mWidget->AsGTK()->SetEGLNativeWindowSize(GetBufferSize());
+ }
+#endif
+ if (!MakeCurrent()) {
+ gfxCriticalNote << "Failed to make render context current, can't draw.";
+ return false;
+ }
+
+#ifdef MOZ_WIDGET_ANDROID
+ java::GeckoSurfaceTexture::DestroyUnused((int64_t)gl());
+ gl()->MakeCurrent(); // DestroyUnused can change the current context!
+#endif
+
+ return true;
+}
+
+RenderedFrameId RenderCompositorEGL::EndFrame(
+ const nsTArray<DeviceIntRect>& aDirtyRects) {
+#ifdef MOZ_WIDGET_ANDROID
+ const auto& gle = gl::GLContextEGL::Cast(gl());
+ const auto& egl = gle->mEgl;
+
+ EGLSync sync = nullptr;
+ if (layers::AndroidHardwareBufferApi::Get()) {
+ sync = egl->fCreateSync(LOCAL_EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
+ }
+ if (sync) {
+ int fenceFd = egl->fDupNativeFenceFDANDROID(sync);
+ if (fenceFd >= 0) {
+ mReleaseFenceFd = ipc::FileDescriptor(UniqueFileHandle(fenceFd));
+ }
+ egl->fDestroySync(sync);
+ sync = nullptr;
+ }
+#endif
+
+ RenderedFrameId frameId = GetNextRenderFrameId();
+#ifdef MOZ_WAYLAND
+ if (mWidget->IsHidden()) {
+ return frameId;
+ }
+#endif
+ if (mEGLSurface != EGL_NO_SURFACE && aDirtyRects.Length() > 0) {
+ gfx::IntRegion bufferInvalid;
+ const auto bufferSize = GetBufferSize();
+ for (const DeviceIntRect& rect : aDirtyRects) {
+ const auto left = std::max(0, std::min(bufferSize.width, rect.min.x));
+ const auto top = std::max(0, std::min(bufferSize.height, rect.min.y));
+
+ const auto right = std::min(bufferSize.width, std::max(0, rect.max.x));
+ const auto bottom = std::min(bufferSize.height, std::max(0, rect.max.y));
+
+ const auto width = right - left;
+ const auto height = bottom - top;
+
+ bufferInvalid.OrWith(
+ gfx::IntRect(left, (GetBufferSize().height - bottom), width, height));
+ }
+ gl()->SetDamage(bufferInvalid);
+ }
+ gl()->SwapBuffers();
+ return frameId;
+}
+
+void RenderCompositorEGL::Pause() { DestroyEGLSurface(); }
+
+bool RenderCompositorEGL::Resume() {
+ if (kIsAndroid) {
+ // Destroy EGLSurface if it exists.
+ DestroyEGLSurface();
+
+ auto size = GetBufferSize();
+ GLint maxTextureSize = 0;
+ gl()->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, (GLint*)&maxTextureSize);
+
+ // When window size is too big, hardware buffer allocation could fail.
+ if (maxTextureSize < size.width || maxTextureSize < size.height) {
+ gfxCriticalNote << "Too big ANativeWindow size(" << size.width << ", "
+ << size.height << ") MaxTextureSize " << maxTextureSize;
+ return false;
+ }
+
+ mEGLSurface = CreateEGLSurface();
+ if (mEGLSurface == EGL_NO_SURFACE) {
+ // Often when we fail to create an EGL surface it is because the Java
+ // Surface we have been provided is invalid. Therefore the on the first
+ // occurence we don't raise a WebRenderError and instead just return
+ // failure. This allows the widget a chance to request a new Java
+ // Surface. On subsequent failures, raising the WebRenderError will
+ // result in the compositor being recreated, falling back through
+ // webrender configurations, and eventually crashing if we still do not
+ // succeed.
+ if (!mHandlingNewSurfaceError) {
+ mHandlingNewSurfaceError = true;
+ } else {
+ RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE);
+ }
+ return false;
+ }
+ mHandlingNewSurfaceError = false;
+
+ gl::GLContextEGL::Cast(gl())->SetEGLSurfaceOverride(mEGLSurface);
+ } else if (kIsWayland || kIsX11) {
+ // Destroy EGLSurface if it exists and create a new one. We will set the
+ // swap interval after MakeCurrent() has been called.
+ DestroyEGLSurface();
+ mEGLSurface = CreateEGLSurface();
+ if (mEGLSurface != EGL_NO_SURFACE) {
+ // We have a new EGL surface, which on wayland needs to be configured for
+ // non-blocking buffer swaps. We need MakeCurrent() to set our current EGL
+ // context before we call eglSwapInterval, which is why we do it here
+ // rather than where the surface was created.
+ const auto& gle = gl::GLContextEGL::Cast(gl());
+ const auto& egl = gle->mEgl;
+ MakeCurrent();
+
+ const int interval = gfx::gfxVars::SwapIntervalEGL() ? 1 : 0;
+ egl->fSwapInterval(interval);
+ } else {
+ RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE);
+ return false;
+ }
+ }
+ return true;
+}
+
+bool RenderCompositorEGL::IsPaused() { return mEGLSurface == EGL_NO_SURFACE; }
+
+bool RenderCompositorEGL::MakeCurrent() {
+ const auto& gle = gl::GLContextEGL::Cast(gl());
+
+ gle->SetEGLSurfaceOverride(mEGLSurface);
+ bool ok = gl()->MakeCurrent();
+ if (!gl()->IsGLES() && ok && mEGLSurface != EGL_NO_SURFACE) {
+ // If we successfully made a surface current, set the draw buffer
+ // appropriately. It's not well-defined by the EGL spec whether
+ // eglMakeCurrent should do this automatically. See bug 1646135.
+ gl()->fDrawBuffer(gl()->IsDoubleBuffered() ? LOCAL_GL_BACK
+ : LOCAL_GL_FRONT);
+ }
+ return ok;
+}
+
+void RenderCompositorEGL::DestroyEGLSurface() {
+ const auto& gle = gl::GLContextEGL::Cast(gl());
+ const auto& egl = gle->mEgl;
+
+ // Release EGLSurface of back buffer before calling ResizeBuffers().
+ if (mEGLSurface) {
+ gle->SetEGLSurfaceOverride(EGL_NO_SURFACE);
+ if (!egl->fMakeCurrent(EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
+ const EGLint err = egl->mLib->fGetError();
+ gfxCriticalNote << "Error in eglMakeCurrent: " << gfx::hexa(err);
+ }
+ if (!egl->fDestroySurface(mEGLSurface)) {
+ const EGLint err = egl->mLib->fGetError();
+ gfxCriticalNote << "Error in eglDestroySurface: " << gfx::hexa(err);
+ }
+ mEGLSurface = nullptr;
+ }
+}
+
+ipc::FileDescriptor RenderCompositorEGL::GetAndResetReleaseFence() {
+#ifdef MOZ_WIDGET_ANDROID
+ MOZ_ASSERT(!layers::AndroidHardwareBufferApi::Get() ||
+ mReleaseFenceFd.IsValid());
+ return std::move(mReleaseFenceFd);
+#else
+ return ipc::FileDescriptor();
+#endif
+}
+
+LayoutDeviceIntSize RenderCompositorEGL::GetBufferSize() {
+ return mWidget->GetClientSize();
+}
+
+bool RenderCompositorEGL::UsePartialPresent() {
+ return gfx::gfxVars::WebRenderMaxPartialPresentRects() > 0;
+}
+
+bool RenderCompositorEGL::RequestFullRender() { return false; }
+
+uint32_t RenderCompositorEGL::GetMaxPartialPresentRects() {
+ return gfx::gfxVars::WebRenderMaxPartialPresentRects();
+}
+
+bool RenderCompositorEGL::ShouldDrawPreviousPartialPresentRegions() {
+ return true;
+}
+
+size_t RenderCompositorEGL::GetBufferAge() const {
+ if (!StaticPrefs::
+ gfx_webrender_allow_partial_present_buffer_age_AtStartup()) {
+ return 0;
+ }
+ return gl()->GetBufferAge();
+}
+
+void RenderCompositorEGL::SetBufferDamageRegion(const wr::DeviceIntRect* aRects,
+ size_t aNumRects) {
+ const auto& gle = gl::GLContextEGL::Cast(gl());
+ const auto& egl = gle->mEgl;
+ if (gle->HasKhrPartialUpdate() &&
+ StaticPrefs::gfx_webrender_allow_partial_present_buffer_age_AtStartup()) {
+ std::vector<EGLint> rects;
+ rects.reserve(4 * aNumRects);
+ const auto bufferSize = GetBufferSize();
+ for (size_t i = 0; i < aNumRects; i++) {
+ const auto left =
+ std::max(0, std::min(bufferSize.width, aRects[i].min.x));
+ const auto top =
+ std::max(0, std::min(bufferSize.height, aRects[i].min.y));
+
+ const auto right =
+ std::min(bufferSize.width, std::max(0, aRects[i].max.x));
+ const auto bottom =
+ std::min(bufferSize.height, std::max(0, aRects[i].max.y));
+
+ const auto width = right - left;
+ const auto height = bottom - top;
+
+ rects.push_back(left);
+ rects.push_back(bufferSize.height - bottom);
+ rects.push_back(width);
+ rects.push_back(height);
+ }
+ const auto ret =
+ egl->fSetDamageRegion(mEGLSurface, rects.data(), rects.size() / 4);
+ if (ret == LOCAL_EGL_FALSE) {
+ const auto err = egl->mLib->fGetError();
+ gfxCriticalError() << "Error in eglSetDamageRegion: " << gfx::hexa(err);
+ }
+ }
+}
+
+} // namespace mozilla::wr
diff --git a/gfx/webrender_bindings/RenderCompositorEGL.h b/gfx/webrender_bindings/RenderCompositorEGL.h
new file mode 100644
index 0000000000..8e851b2fc0
--- /dev/null
+++ b/gfx/webrender_bindings/RenderCompositorEGL.h
@@ -0,0 +1,75 @@
+/* -*- 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_RENDERCOMPOSITOR_EGL_H
+#define MOZILLA_GFX_RENDERCOMPOSITOR_EGL_H
+
+#include "GLTypes.h"
+#include "mozilla/webrender/RenderCompositor.h"
+
+namespace mozilla {
+
+namespace wr {
+
+class RenderCompositorEGL : public RenderCompositor {
+ public:
+ static UniquePtr<RenderCompositor> Create(
+ const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError);
+
+ explicit RenderCompositorEGL(const RefPtr<widget::CompositorWidget>& aWidget,
+ RefPtr<gl::GLContext>&& aGL);
+ virtual ~RenderCompositorEGL();
+
+ bool BeginFrame() override;
+ RenderedFrameId EndFrame(const nsTArray<DeviceIntRect>& aDirtyRects) final;
+ void Pause() override;
+ bool Resume() override;
+ bool IsPaused() override;
+
+ gl::GLContext* gl() const override { return mGL; }
+
+ bool MakeCurrent() override;
+
+ bool UseANGLE() const override { return false; }
+
+ LayoutDeviceIntSize GetBufferSize() override;
+
+ // Interface for partial present
+ bool UsePartialPresent() override;
+ bool RequestFullRender() override;
+ uint32_t GetMaxPartialPresentRects() override;
+ bool ShouldDrawPreviousPartialPresentRegions() override;
+ size_t GetBufferAge() const override;
+ void SetBufferDamageRegion(const wr::DeviceIntRect* aRects,
+ size_t aNumRects) override;
+
+ ipc::FileDescriptor GetAndResetReleaseFence() override;
+
+ protected:
+ EGLSurface CreateEGLSurface();
+
+ void DestroyEGLSurface();
+
+ RefPtr<gl::GLContext> mGL;
+
+ EGLSurface mEGLSurface;
+
+ // Whether we are in the process of handling a NEW_SURFACE error. On Android
+ // this is used to allow the widget an opportunity to recover from the first
+ // instance, before raising a WebRenderError on subsequent occurences.
+ bool mHandlingNewSurfaceError = false;
+
+ // FileDescriptor of release fence.
+ // Release fence is a fence that is used for waiting until usage/composite of
+ // AHardwareBuffer is ended. The fence is delivered to client side via
+ // ImageBridge. It is used only on android.
+ ipc::FileDescriptor mReleaseFenceFd;
+};
+
+} // namespace wr
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_RENDERCOMPOSITOR_EGL_H
diff --git a/gfx/webrender_bindings/RenderCompositorLayersSWGL.cpp b/gfx/webrender_bindings/RenderCompositorLayersSWGL.cpp
new file mode 100644
index 0000000000..1e4550e9e8
--- /dev/null
+++ b/gfx/webrender_bindings/RenderCompositorLayersSWGL.cpp
@@ -0,0 +1,482 @@
+/* -*- 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 "RenderCompositorLayersSWGL.h"
+
+#include "GLContext.h"
+#include "GLContextEGL.h"
+#include "mozilla/layers/BuildConstants.h"
+#include "mozilla/layers/Effects.h"
+#include "mozilla/layers/TextureHostOGL.h"
+#include "mozilla/widget/CompositorWidget.h"
+#include "RenderCompositorRecordedFrame.h"
+
+#if defined(XP_WIN)
+# include "mozilla/webrender/RenderCompositorD3D11SWGL.h"
+#else
+# include "mozilla/webrender/RenderCompositorOGLSWGL.h"
+#endif
+
+namespace mozilla {
+using namespace layers;
+using namespace gfx;
+
+namespace wr {
+
+UniquePtr<RenderCompositor> RenderCompositorLayersSWGL::Create(
+ const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError) {
+#ifdef XP_WIN
+ return RenderCompositorD3D11SWGL::Create(aWidget, aError);
+#else
+ return RenderCompositorOGLSWGL::Create(aWidget, aError);
+#endif
+}
+
+RenderCompositorLayersSWGL::RenderCompositorLayersSWGL(
+ Compositor* aCompositor, const RefPtr<widget::CompositorWidget>& aWidget,
+ void* aContext)
+ : RenderCompositor(aWidget),
+ mCompositor(aCompositor),
+ mContext(aContext),
+ mCurrentTileId(wr::NativeTileId()) {
+ MOZ_ASSERT(mCompositor);
+ MOZ_ASSERT(mContext);
+}
+
+RenderCompositorLayersSWGL::~RenderCompositorLayersSWGL() {
+ wr_swgl_destroy_context(mContext);
+}
+
+bool RenderCompositorLayersSWGL::MakeCurrent() {
+ wr_swgl_make_current(mContext);
+ return true;
+}
+
+bool RenderCompositorLayersSWGL::BeginFrame() {
+ MOZ_ASSERT(!mInFrame);
+ MakeCurrent();
+ mInFrame = true;
+ return true;
+}
+
+void RenderCompositorLayersSWGL::CancelFrame() {
+ MOZ_ASSERT(mInFrame);
+ mInFrame = false;
+ if (mCompositingStarted) {
+ mCompositor->CancelFrame();
+ mCompositingStarted = false;
+ }
+}
+
+void RenderCompositorLayersSWGL::StartCompositing(
+ wr::ColorF aClearColor, const wr::DeviceIntRect* aDirtyRects,
+ size_t aNumDirtyRects, const wr::DeviceIntRect* aOpaqueRects,
+ size_t aNumOpaqueRects) {
+ MOZ_RELEASE_ASSERT(!mCompositingStarted);
+
+ if (!mInFrame || aNumDirtyRects == 0) {
+ return;
+ }
+
+ gfx::IntRect bounds(gfx::IntPoint(0, 0), GetBufferSize().ToUnknownSize());
+ nsIntRegion dirty;
+
+ MOZ_RELEASE_ASSERT(aNumDirtyRects > 0);
+ for (size_t i = 0; i < aNumDirtyRects; i++) {
+ const auto& rect = aDirtyRects[i];
+ dirty.OrWith(
+ gfx::IntRect(rect.min.x, rect.min.y, rect.width(), rect.height()));
+ }
+ dirty.AndWith(bounds);
+
+ nsIntRegion opaque(bounds);
+ opaque.SubOut(mWidget->GetTransparentRegion().ToUnknownRegion());
+ for (size_t i = 0; i < aNumOpaqueRects; i++) {
+ const auto& rect = aOpaqueRects[i];
+ opaque.OrWith(
+ gfx::IntRect(rect.min.x, rect.min.y, rect.width(), rect.height()));
+ }
+
+ mCompositor->SetClearColor(gfx::DeviceColor(aClearColor.r, aClearColor.g,
+ aClearColor.b, aClearColor.a));
+
+ if (!mCompositor->BeginFrameForWindow(dirty, Nothing(), bounds, opaque)) {
+ return;
+ }
+ mCompositingStarted = true;
+}
+
+void RenderCompositorLayersSWGL::CompositorEndFrame() {
+ nsTArray<FrameSurface> frameSurfaces = std::move(mFrameSurfaces);
+
+ if (!mCompositingStarted) {
+ return;
+ }
+
+ for (auto& frameSurface : frameSurfaces) {
+ auto surfaceCursor = mSurfaces.find(frameSurface.mId);
+ MOZ_RELEASE_ASSERT(surfaceCursor != mSurfaces.end());
+ Surface* surface = surfaceCursor->second.get();
+
+ for (auto it = surface->mTiles.begin(); it != surface->mTiles.end(); ++it) {
+ if (!it->second->IsValid()) {
+ continue;
+ }
+
+ gfx::Point tileOffset(it->first.mX * surface->mTileSize.width,
+ it->first.mY * surface->mTileSize.height);
+ gfx::Rect drawRect = it->second->mValidRect + tileOffset;
+
+ RefPtr<TexturedEffect> texturedEffect =
+ new EffectRGB(it->second->GetTextureSource(),
+ /* aPremultiplied */ true, frameSurface.mFilter);
+ if (surface->mIsOpaque) {
+ texturedEffect->mPremultipliedCopy = true;
+ }
+
+ texturedEffect->mTextureCoords =
+ gfx::Rect(it->second->mValidRect.x / surface->mTileSize.width,
+ it->second->mValidRect.y / surface->mTileSize.height,
+ it->second->mValidRect.width / surface->mTileSize.width,
+ it->second->mValidRect.height / surface->mTileSize.height);
+
+ EffectChain effect;
+ effect.mPrimaryEffect = texturedEffect;
+ mCompositor->DrawQuad(drawRect, frameSurface.mClipRect, effect, 1.0,
+ frameSurface.mTransform, drawRect);
+ }
+
+ if (surface->mExternalImage) {
+ HandleExternalImage(surface->mExternalImage, frameSurface);
+ }
+ }
+}
+
+RenderedFrameId RenderCompositorLayersSWGL::EndFrame(
+ const nsTArray<DeviceIntRect>& aDirtyRects) {
+ MOZ_ASSERT(mInFrame);
+ mInFrame = false;
+ if (mCompositingStarted) {
+ mCompositor->EndFrame();
+ mCompositingStarted = false;
+ }
+ return GetNextRenderFrameId();
+}
+
+LayoutDeviceIntSize RenderCompositorLayersSWGL::GetBufferSize() {
+ return mWidget->GetClientSize();
+}
+
+void RenderCompositorLayersSWGL::Bind(wr::NativeTileId aId,
+ wr::DeviceIntPoint* aOffset,
+ uint32_t* aFboId,
+ wr::DeviceIntRect aDirtyRect,
+ wr::DeviceIntRect aValidRect) {
+ MOZ_RELEASE_ASSERT(false);
+}
+
+void RenderCompositorLayersSWGL::Unbind() { MOZ_RELEASE_ASSERT(false); }
+
+bool RenderCompositorLayersSWGL::MapTile(wr::NativeTileId aId,
+ wr::DeviceIntRect aDirtyRect,
+ wr::DeviceIntRect aValidRect,
+ void** aData, int32_t* aStride) {
+ auto surfaceCursor = mSurfaces.find(aId.surface_id);
+ MOZ_RELEASE_ASSERT(surfaceCursor != mSurfaces.end());
+ Surface* surface = surfaceCursor->second.get();
+
+ auto layerCursor = surface->mTiles.find(TileKey(aId.x, aId.y));
+ MOZ_RELEASE_ASSERT(layerCursor != surface->mTiles.end());
+
+ mCurrentTile = layerCursor->second.get();
+ mCurrentTileId = aId;
+ mCurrentTileDirty = gfx::IntRect(aDirtyRect.min.x, aDirtyRect.min.y,
+ aDirtyRect.width(), aDirtyRect.height());
+
+ if (!mCurrentTile->Map(aDirtyRect, aValidRect, aData, aStride)) {
+ gfxCriticalNote << "MapTile failed aValidRect: "
+ << gfx::Rect(aValidRect.min.x, aValidRect.min.y,
+ aValidRect.width(), aValidRect.height());
+ return false;
+ }
+
+ // Store the new valid rect, so that we can composite only those pixels
+ mCurrentTile->mValidRect = gfx::Rect(aValidRect.min.x, aValidRect.min.y,
+ aValidRect.width(), aValidRect.height());
+ return true;
+}
+
+void RenderCompositorLayersSWGL::UnmapTile() {
+ mCurrentTile->Unmap(mCurrentTileDirty);
+ mCurrentTile = nullptr;
+}
+
+void RenderCompositorLayersSWGL::CreateSurface(
+ wr::NativeSurfaceId aId, wr::DeviceIntPoint aVirtualOffset,
+ wr::DeviceIntSize aTileSize, bool aIsOpaque) {
+ MOZ_RELEASE_ASSERT(mSurfaces.find(aId) == mSurfaces.end());
+ auto surface = DoCreateSurface(aTileSize, aIsOpaque);
+ mSurfaces.insert({aId, std::move(surface)});
+}
+
+UniquePtr<RenderCompositorLayersSWGL::Surface>
+RenderCompositorLayersSWGL::DoCreateSurface(wr::DeviceIntSize aTileSize,
+ bool aIsOpaque) {
+ return MakeUnique<Surface>(aTileSize, aIsOpaque);
+}
+
+void RenderCompositorLayersSWGL::CreateExternalSurface(wr::NativeSurfaceId aId,
+ bool aIsOpaque) {
+ MOZ_RELEASE_ASSERT(mSurfaces.find(aId) == mSurfaces.end());
+ auto surface = MakeUnique<Surface>(wr::DeviceIntSize{}, aIsOpaque);
+ surface->mIsExternal = true;
+ mSurfaces.insert({aId, std::move(surface)});
+}
+
+void RenderCompositorLayersSWGL::DestroySurface(NativeSurfaceId aId) {
+ auto surfaceCursor = mSurfaces.find(aId);
+ MOZ_RELEASE_ASSERT(surfaceCursor != mSurfaces.end());
+ mSurfaces.erase(surfaceCursor);
+}
+
+void RenderCompositorLayersSWGL::CreateTile(wr::NativeSurfaceId aId, int32_t aX,
+ int32_t aY) {
+ auto surfaceCursor = mSurfaces.find(aId);
+ MOZ_RELEASE_ASSERT(surfaceCursor != mSurfaces.end());
+ Surface* surface = surfaceCursor->second.get();
+ MOZ_RELEASE_ASSERT(!surface->mIsExternal);
+
+ auto tile = DoCreateTile(surface);
+ surface->mTiles.insert({TileKey(aX, aY), std::move(tile)});
+}
+
+void RenderCompositorLayersSWGL::DestroyTile(wr::NativeSurfaceId aId,
+ int32_t aX, int32_t aY) {
+ auto surfaceCursor = mSurfaces.find(aId);
+ MOZ_RELEASE_ASSERT(surfaceCursor != mSurfaces.end());
+ Surface* surface = surfaceCursor->second.get();
+ MOZ_RELEASE_ASSERT(!surface->mIsExternal);
+
+ auto layerCursor = surface->mTiles.find(TileKey(aX, aY));
+ MOZ_RELEASE_ASSERT(layerCursor != surface->mTiles.end());
+ surface->mTiles.erase(layerCursor);
+}
+
+void RenderCompositorLayersSWGL::AttachExternalImage(
+ wr::NativeSurfaceId aId, wr::ExternalImageId aExternalImage) {
+ RenderTextureHost* image =
+ RenderThread::Get()->GetRenderTexture(aExternalImage);
+ MOZ_ASSERT(image);
+ if (!image) {
+ gfxCriticalNoteOnce
+ << "Failed to get RenderTextureHost for D3D11SWGL extId:"
+ << AsUint64(aExternalImage);
+ return;
+ }
+#if defined(XP_WIN)
+ MOZ_RELEASE_ASSERT(image->AsRenderDXGITextureHost() ||
+ image->AsRenderDXGIYCbCrTextureHost());
+#elif defined(ANDROID)
+ MOZ_RELEASE_ASSERT(image->AsRenderAndroidHardwareBufferTextureHost() ||
+ image->AsRenderAndroidSurfaceTextureHost() ||
+ image->IsWrappingAsyncRemoteTexture());
+#endif
+
+ auto surfaceCursor = mSurfaces.find(aId);
+ MOZ_RELEASE_ASSERT(surfaceCursor != mSurfaces.end());
+
+ Surface* surface = surfaceCursor->second.get();
+ surface->mExternalImage = image;
+ MOZ_RELEASE_ASSERT(surface->mTiles.empty());
+ MOZ_RELEASE_ASSERT(surface->mIsExternal);
+}
+
+// static
+gfx::SamplingFilter RenderCompositorLayersSWGL::ToSamplingFilter(
+ wr::ImageRendering aImageRendering) {
+ if (aImageRendering == wr::ImageRendering::Auto) {
+ return gfx::SamplingFilter::LINEAR;
+ }
+ return gfx::SamplingFilter::POINT;
+}
+
+void RenderCompositorLayersSWGL::AddSurface(
+ wr::NativeSurfaceId aId, const wr::CompositorSurfaceTransform& aTransform,
+ wr::DeviceIntRect aClipRect, wr::ImageRendering aImageRendering) {
+ float sx = aTransform.scale.x;
+ float sy = aTransform.scale.y;
+ float tx = aTransform.offset.x;
+ float ty = aTransform.offset.y;
+ gfx::Matrix4x4 transform(sx, 0.0, 0.0, 0.0, 0.0, sy, 0.0, 0.0, 0.0, 0.0, 1.0,
+ 0.0, tx, ty, 0.0, 1.0);
+ gfx::IntRect clipRect(aClipRect.min.x, aClipRect.min.y, aClipRect.width(),
+ aClipRect.height());
+
+ mFrameSurfaces.AppendElement(FrameSurface{aId, transform, clipRect,
+ ToSamplingFilter(aImageRendering)});
+}
+
+void RenderCompositorLayersSWGL::MaybeRequestAllowFrameRecording(
+ bool aWillRecord) {
+ mCompositor->RequestAllowFrameRecording(aWillRecord);
+}
+
+class WindowLMC : public profiler_screenshots::Window {
+ public:
+ explicit WindowLMC(Compositor* aCompositor) : mCompositor(aCompositor) {}
+
+ already_AddRefed<profiler_screenshots::RenderSource> GetWindowContents(
+ const gfx::IntSize& aWindowSize) override;
+ already_AddRefed<profiler_screenshots::DownscaleTarget> CreateDownscaleTarget(
+ const gfx::IntSize& aSize) override;
+ already_AddRefed<profiler_screenshots::AsyncReadbackBuffer>
+ CreateAsyncReadbackBuffer(const gfx::IntSize& aSize) override;
+
+ protected:
+ Compositor* mCompositor;
+};
+
+class RenderSourceLMC : public profiler_screenshots::RenderSource {
+ public:
+ explicit RenderSourceLMC(CompositingRenderTarget* aRT)
+ : RenderSource(aRT->GetSize()), mRT(aRT) {}
+
+ const auto& RenderTarget() { return mRT; }
+
+ protected:
+ virtual ~RenderSourceLMC() {}
+
+ RefPtr<CompositingRenderTarget> mRT;
+};
+
+class DownscaleTargetLMC : public profiler_screenshots::DownscaleTarget {
+ public:
+ explicit DownscaleTargetLMC(CompositingRenderTarget* aRT,
+ Compositor* aCompositor)
+ : profiler_screenshots::DownscaleTarget(aRT->GetSize()),
+ mRenderSource(new RenderSourceLMC(aRT)),
+ mCompositor(aCompositor) {}
+
+ already_AddRefed<profiler_screenshots::RenderSource> AsRenderSource()
+ override {
+ return do_AddRef(mRenderSource);
+ }
+
+ bool DownscaleFrom(profiler_screenshots::RenderSource* aSource,
+ const IntRect& aSourceRect,
+ const IntRect& aDestRect) override {
+ MOZ_RELEASE_ASSERT(aSourceRect.TopLeft() == IntPoint());
+ MOZ_RELEASE_ASSERT(aDestRect.TopLeft() == IntPoint());
+ RefPtr<CompositingRenderTarget> previousTarget =
+ mCompositor->GetCurrentRenderTarget();
+
+ mCompositor->SetRenderTarget(mRenderSource->RenderTarget());
+ bool result = mCompositor->BlitRenderTarget(
+ static_cast<RenderSourceLMC*>(aSource)->RenderTarget(),
+ aSourceRect.Size(), aDestRect.Size());
+
+ // Restore the old render target.
+ mCompositor->SetRenderTarget(previousTarget);
+
+ return result;
+ }
+
+ protected:
+ virtual ~DownscaleTargetLMC() {}
+
+ RefPtr<RenderSourceLMC> mRenderSource;
+ Compositor* mCompositor;
+};
+
+class AsyncReadbackBufferLMC
+ : public profiler_screenshots::AsyncReadbackBuffer {
+ public:
+ AsyncReadbackBufferLMC(mozilla::layers::AsyncReadbackBuffer* aARB,
+ Compositor* aCompositor)
+ : profiler_screenshots::AsyncReadbackBuffer(aARB->GetSize()),
+ mARB(aARB),
+ mCompositor(aCompositor) {}
+ void CopyFrom(profiler_screenshots::RenderSource* aSource) override {
+ mCompositor->ReadbackRenderTarget(
+ static_cast<RenderSourceLMC*>(aSource)->RenderTarget(), mARB);
+ }
+ bool MapAndCopyInto(DataSourceSurface* aSurface,
+ const IntSize& aReadSize) override {
+ return mARB->MapAndCopyInto(aSurface, aReadSize);
+ }
+
+ protected:
+ virtual ~AsyncReadbackBufferLMC() {}
+
+ RefPtr<mozilla::layers::AsyncReadbackBuffer> mARB;
+ Compositor* mCompositor;
+};
+
+already_AddRefed<profiler_screenshots::RenderSource>
+WindowLMC::GetWindowContents(const gfx::IntSize& aWindowSize) {
+ RefPtr<CompositingRenderTarget> rt = mCompositor->GetWindowRenderTarget();
+ if (!rt) {
+ return nullptr;
+ }
+ return MakeAndAddRef<RenderSourceLMC>(rt);
+}
+
+already_AddRefed<profiler_screenshots::DownscaleTarget>
+WindowLMC::CreateDownscaleTarget(const gfx::IntSize& aSize) {
+ RefPtr<CompositingRenderTarget> rt =
+ mCompositor->CreateRenderTarget(IntRect({}, aSize), INIT_MODE_NONE);
+ return MakeAndAddRef<DownscaleTargetLMC>(rt, mCompositor);
+}
+
+already_AddRefed<profiler_screenshots::AsyncReadbackBuffer>
+WindowLMC::CreateAsyncReadbackBuffer(const gfx::IntSize& aSize) {
+ RefPtr<AsyncReadbackBuffer> carb =
+ mCompositor->CreateAsyncReadbackBuffer(aSize);
+ if (!carb) {
+ return nullptr;
+ }
+ return MakeAndAddRef<AsyncReadbackBufferLMC>(carb, mCompositor);
+}
+
+bool RenderCompositorLayersSWGL::MaybeRecordFrame(
+ layers::CompositionRecorder& aRecorder) {
+ WindowLMC window(mCompositor);
+ gfx::IntSize size = GetBufferSize().ToUnknownSize();
+ RefPtr<layers::profiler_screenshots::RenderSource> snapshot =
+ window.GetWindowContents(size);
+ if (!snapshot) {
+ return true;
+ }
+
+ RefPtr<layers::profiler_screenshots::AsyncReadbackBuffer> buffer =
+ window.CreateAsyncReadbackBuffer(size);
+ buffer->CopyFrom(snapshot);
+
+ RefPtr<layers::RecordedFrame> frame =
+ new RenderCompositorRecordedFrame(TimeStamp::Now(), std::move(buffer));
+ aRecorder.RecordFrame(frame);
+ return false;
+}
+
+bool RenderCompositorLayersSWGL::MaybeGrabScreenshot(
+ const gfx::IntSize& aWindowSize) {
+ if (!mCompositingStarted) {
+ return true;
+ }
+ WindowLMC window(mCompositor);
+ mProfilerScreenshotGrabber.MaybeGrabScreenshot(window, aWindowSize);
+ return true;
+}
+
+bool RenderCompositorLayersSWGL::MaybeProcessScreenshotQueue() {
+ mProfilerScreenshotGrabber.MaybeProcessQueue();
+ return true;
+}
+
+} // namespace wr
+} // namespace mozilla
diff --git a/gfx/webrender_bindings/RenderCompositorLayersSWGL.h b/gfx/webrender_bindings/RenderCompositorLayersSWGL.h
new file mode 100644
index 0000000000..3cf6bb44f5
--- /dev/null
+++ b/gfx/webrender_bindings/RenderCompositorLayersSWGL.h
@@ -0,0 +1,195 @@
+/* -*- 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_RENDERCOMPOSITOR_Layers_H
+#define MOZILLA_GFX_RENDERCOMPOSITOR_Layers_H
+
+#include <unordered_map>
+
+#include "mozilla/HashFunctions.h"
+#include "mozilla/layers/Compositor.h"
+#include "mozilla/layers/ScreenshotGrabber.h"
+#include "mozilla/webrender/RenderCompositor.h"
+#include "mozilla/webrender/RenderTextureHost.h"
+
+namespace mozilla {
+
+namespace wr {
+
+class SurfaceD3D11SWGL;
+
+class RenderCompositorLayersSWGL : public RenderCompositor {
+ public:
+ static UniquePtr<RenderCompositor> Create(
+ const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError);
+
+ RenderCompositorLayersSWGL(layers::Compositor* aCompositor,
+ const RefPtr<widget::CompositorWidget>& aWidget,
+ void* aContext);
+ virtual ~RenderCompositorLayersSWGL();
+
+ void* swgl() const override { return mContext; }
+
+ bool MakeCurrent() override;
+
+ bool BeginFrame() override;
+ void CancelFrame() override;
+ RenderedFrameId EndFrame(const nsTArray<DeviceIntRect>& aDirtyRects) override;
+
+ bool SurfaceOriginIsTopLeft() override { return true; }
+
+ LayoutDeviceIntSize GetBufferSize() override;
+
+ // Should we support this?
+ bool SupportsExternalBufferTextures() const override { return false; }
+
+ layers::WebRenderBackend BackendType() const override {
+ return layers::WebRenderBackend::SOFTWARE;
+ }
+
+ bool ShouldUseNativeCompositor() override { return true; }
+
+ void StartCompositing(wr::ColorF aClearColor,
+ const wr::DeviceIntRect* aDirtyRects,
+ size_t aNumDirtyRects,
+ const wr::DeviceIntRect* aOpaqueRects,
+ size_t aNumOpaqueRects) override;
+
+ void CompositorBeginFrame() override {}
+ void CompositorEndFrame() override;
+ void Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset, uint32_t* aFboId,
+ wr::DeviceIntRect aDirtyRect,
+ wr::DeviceIntRect aValidRect) override;
+ void Unbind() override;
+ bool MapTile(wr::NativeTileId aId, wr::DeviceIntRect aDirtyRect,
+ wr::DeviceIntRect aValidRect, void** aData,
+ int32_t* aStride) override;
+ void UnmapTile() override;
+ void CreateSurface(wr::NativeSurfaceId aId, wr::DeviceIntPoint aVirtualOffset,
+ wr::DeviceIntSize aTileSize, bool aIsOpaque) override;
+ void CreateExternalSurface(wr::NativeSurfaceId aId, bool aIsOpaque) override;
+ void DestroySurface(NativeSurfaceId aId) override;
+ void CreateTile(wr::NativeSurfaceId, int32_t aX, int32_t aY) override;
+ void DestroyTile(wr::NativeSurfaceId, int32_t aX, int32_t aY) override;
+ void AttachExternalImage(wr::NativeSurfaceId aId,
+ wr::ExternalImageId aExternalImage) override;
+ void AddSurface(wr::NativeSurfaceId aId,
+ const wr::CompositorSurfaceTransform& aTransform,
+ wr::DeviceIntRect aClipRect,
+ wr::ImageRendering aImageRendering) override;
+ void EnableNativeCompositor(bool aEnable) override {}
+ void DeInit() override {}
+
+ void MaybeRequestAllowFrameRecording(bool aWillRecord) override;
+ bool MaybeRecordFrame(layers::CompositionRecorder& aRecorder) override;
+ bool MaybeGrabScreenshot(const gfx::IntSize& aWindowSize) override;
+ bool MaybeProcessScreenshotQueue() override;
+
+ // TODO: Screenshots etc
+
+ struct TileKey {
+ TileKey(int32_t aX, int32_t aY) : mX(aX), mY(aY) {}
+
+ int32_t mX;
+ int32_t mY;
+ };
+
+ // Each tile retains a texture, and a DataSourceSurface of the
+ // same size. We draw into the source surface, and then copy the
+ // changed area into the texture.
+ class Tile {
+ public:
+ Tile() = default;
+ virtual ~Tile() = default;
+
+ virtual bool Map(wr::DeviceIntRect aDirtyRect, wr::DeviceIntRect aValidRect,
+ void** aData, int32_t* aStride) = 0;
+ virtual void Unmap(const gfx::IntRect& aDirtyRect) = 0;
+ virtual layers::DataTextureSource* GetTextureSource() = 0;
+ virtual bool IsValid() = 0;
+
+ gfx::Rect mValidRect;
+
+ struct KeyHashFn {
+ std::size_t operator()(const TileKey& aId) const {
+ return HashGeneric(aId.mX, aId.mY);
+ }
+ };
+ };
+
+ class Surface {
+ public:
+ explicit Surface(wr::DeviceIntSize aTileSize, bool aIsOpaque)
+ : mTileSize(aTileSize), mIsOpaque(aIsOpaque) {}
+ virtual ~Surface() {}
+
+ gfx::IntSize TileSize() {
+ return gfx::IntSize(mTileSize.width, mTileSize.height);
+ }
+ virtual SurfaceD3D11SWGL* AsSurfaceD3D11SWGL() { return nullptr; }
+
+ // External images can change size depending on which image
+ // is attached, so mTileSize will be 0,0 when mIsExternal
+ // is true.
+ wr::DeviceIntSize mTileSize;
+ bool mIsOpaque;
+ bool mIsExternal = false;
+ std::unordered_map<TileKey, UniquePtr<Tile>, Tile::KeyHashFn> mTiles;
+ RefPtr<RenderTextureHost> mExternalImage;
+
+ struct IdHashFn {
+ std::size_t operator()(const wr::NativeSurfaceId& aId) const {
+ return HashGeneric(wr::AsUint64(aId));
+ }
+ };
+ };
+
+ static gfx::SamplingFilter ToSamplingFilter(
+ wr::ImageRendering aImageRendering);
+
+ protected:
+ // The set of surfaces added to be composited for the current frame
+ struct FrameSurface {
+ wr::NativeSurfaceId mId;
+ gfx::Matrix4x4 mTransform;
+ gfx::IntRect mClipRect;
+ gfx::SamplingFilter mFilter;
+ };
+
+ virtual void HandleExternalImage(RenderTextureHost* aExternalImage,
+ FrameSurface& aFrameSurface) = 0;
+ virtual UniquePtr<RenderCompositorLayersSWGL::Surface> DoCreateSurface(
+ wr::DeviceIntSize aTileSize, bool aIsOpaque);
+ virtual UniquePtr<RenderCompositorLayersSWGL::Tile> DoCreateTile(
+ Surface* aSurface) = 0;
+
+ RefPtr<layers::Compositor> mCompositor;
+ void* mContext = nullptr;
+
+ std::unordered_map<wr::NativeSurfaceId, UniquePtr<Surface>, Surface::IdHashFn>
+ mSurfaces;
+
+ // Temporary state held between MapTile and UnmapTile
+ Tile* mCurrentTile = nullptr;
+ gfx::IntRect mCurrentTileDirty;
+ wr::NativeTileId mCurrentTileId;
+
+ nsTArray<FrameSurface> mFrameSurfaces;
+ bool mInFrame = false;
+ bool mCompositingStarted = false;
+
+ layers::ScreenshotGrabber mProfilerScreenshotGrabber;
+};
+
+static inline bool operator==(const RenderCompositorLayersSWGL::TileKey& a0,
+ const RenderCompositorLayersSWGL::TileKey& a1) {
+ return a0.mX == a1.mX && a0.mY == a1.mY;
+}
+
+} // namespace wr
+} // namespace mozilla
+
+#endif
diff --git a/gfx/webrender_bindings/RenderCompositorNative.cpp b/gfx/webrender_bindings/RenderCompositorNative.cpp
new file mode 100644
index 0000000000..81cafd1fd6
--- /dev/null
+++ b/gfx/webrender_bindings/RenderCompositorNative.cpp
@@ -0,0 +1,672 @@
+/* -*- 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 "RenderCompositorNative.h"
+
+#include "GLContext.h"
+#include "GLContextProvider.h"
+#include "mozilla/ProfilerLabels.h"
+#include "mozilla/ProfilerMarkers.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/layers/CompositionRecorder.h"
+#include "mozilla/layers/NativeLayer.h"
+#include "mozilla/layers/SurfacePool.h"
+#include "mozilla/StaticPrefs_gfx.h"
+#include "mozilla/webrender/RenderThread.h"
+#include "mozilla/widget/CompositorWidget.h"
+#include "RenderCompositorRecordedFrame.h"
+
+namespace mozilla::wr {
+
+extern LazyLogModule gRenderThreadLog;
+#define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
+
+RenderCompositorNative::RenderCompositorNative(
+ const RefPtr<widget::CompositorWidget>& aWidget, gl::GLContext* aGL)
+ : RenderCompositor(aWidget),
+ mNativeLayerRoot(GetWidget()->GetNativeLayerRoot()) {
+ LOG("RenderCompositorNative::RenderCompositorNative()");
+
+#if defined(XP_MACOSX) || defined(MOZ_WAYLAND)
+ auto pool = RenderThread::Get()->SharedSurfacePool();
+ if (pool) {
+ mSurfacePoolHandle = pool->GetHandleForGL(aGL);
+ }
+#endif
+ MOZ_RELEASE_ASSERT(mSurfacePoolHandle);
+}
+
+RenderCompositorNative::~RenderCompositorNative() {
+ LOG("RRenderCompositorNative::~RenderCompositorNative()");
+
+ Pause();
+ mProfilerScreenshotGrabber.Destroy();
+ mNativeLayerRoot->SetLayers({});
+ mNativeLayerForEntireWindow = nullptr;
+ mNativeLayerRootSnapshotter = nullptr;
+ mNativeLayerRoot = nullptr;
+}
+
+bool RenderCompositorNative::BeginFrame() {
+ if (!MakeCurrent()) {
+ gfxCriticalNote << "Failed to make render context current, can't draw.";
+ return false;
+ }
+
+ gfx::IntSize bufferSize = GetBufferSize().ToUnknownSize();
+ if (!ShouldUseNativeCompositor()) {
+ if (bufferSize.IsEmpty()) {
+ return false;
+ }
+ if (mNativeLayerForEntireWindow &&
+ mNativeLayerForEntireWindow->GetSize() != bufferSize) {
+ mNativeLayerRoot->RemoveLayer(mNativeLayerForEntireWindow);
+ mNativeLayerForEntireWindow = nullptr;
+ }
+ if (!mNativeLayerForEntireWindow) {
+ mNativeLayerForEntireWindow =
+ mNativeLayerRoot->CreateLayer(bufferSize, false, mSurfacePoolHandle);
+ mNativeLayerRoot->AppendLayer(mNativeLayerForEntireWindow);
+ }
+ }
+
+ gfx::IntRect bounds({}, bufferSize);
+ if (!InitDefaultFramebuffer(bounds)) {
+ return false;
+ }
+
+ return true;
+}
+
+RenderedFrameId RenderCompositorNative::EndFrame(
+ const nsTArray<DeviceIntRect>& aDirtyRects) {
+ RenderedFrameId frameId = GetNextRenderFrameId();
+
+ DoSwap();
+
+ if (mNativeLayerForEntireWindow) {
+ mNativeLayerForEntireWindow->NotifySurfaceReady();
+ mNativeLayerRoot->CommitToScreen();
+ }
+
+ return frameId;
+}
+
+void RenderCompositorNative::Pause() {}
+
+bool RenderCompositorNative::Resume() { return true; }
+
+inline layers::WebRenderCompositor RenderCompositorNative::CompositorType()
+ const {
+ if (gfx::gfxVars::UseWebRenderCompositor()) {
+#if defined(XP_MACOSX)
+ return layers::WebRenderCompositor::CORE_ANIMATION;
+#elif defined(MOZ_WAYLAND)
+ return layers::WebRenderCompositor::WAYLAND;
+#endif
+ }
+ return layers::WebRenderCompositor::DRAW;
+}
+
+LayoutDeviceIntSize RenderCompositorNative::GetBufferSize() {
+ return mWidget->GetClientSize();
+}
+
+bool RenderCompositorNative::ShouldUseNativeCompositor() {
+ return gfx::gfxVars::UseWebRenderCompositor();
+}
+
+void RenderCompositorNative::GetCompositorCapabilities(
+ CompositorCapabilities* aCaps) {
+ RenderCompositor::GetCompositorCapabilities(aCaps);
+#if defined(XP_MACOSX)
+ aCaps->supports_surface_for_backdrop = !gfx::gfxVars::UseSoftwareWebRender();
+#endif
+}
+
+bool RenderCompositorNative::MaybeReadback(
+ const gfx::IntSize& aReadbackSize, const wr::ImageFormat& aReadbackFormat,
+ const Range<uint8_t>& aReadbackBuffer, bool* aNeedsYFlip) {
+ if (!ShouldUseNativeCompositor()) {
+ return false;
+ }
+
+ MOZ_RELEASE_ASSERT(aReadbackFormat == wr::ImageFormat::BGRA8);
+ if (!mNativeLayerRootSnapshotter) {
+ mNativeLayerRootSnapshotter = mNativeLayerRoot->CreateSnapshotter();
+
+ if (!mNativeLayerRootSnapshotter) {
+ return false;
+ }
+ }
+ bool success = mNativeLayerRootSnapshotter->ReadbackPixels(
+ aReadbackSize, gfx::SurfaceFormat::B8G8R8A8, aReadbackBuffer);
+
+ // ReadbackPixels might have changed the current context. Make sure GL is
+ // current again.
+ MakeCurrent();
+
+ if (aNeedsYFlip) {
+ *aNeedsYFlip = true;
+ }
+
+ return success;
+}
+
+bool RenderCompositorNative::MaybeRecordFrame(
+ layers::CompositionRecorder& aRecorder) {
+ if (!ShouldUseNativeCompositor()) {
+ return false;
+ }
+
+ if (!mNativeLayerRootSnapshotter) {
+ mNativeLayerRootSnapshotter = mNativeLayerRoot->CreateSnapshotter();
+ }
+
+ if (!mNativeLayerRootSnapshotter) {
+ return true;
+ }
+
+ gfx::IntSize size = GetBufferSize().ToUnknownSize();
+ RefPtr<layers::profiler_screenshots::RenderSource> snapshot =
+ mNativeLayerRootSnapshotter->GetWindowContents(size);
+ if (!snapshot) {
+ return true;
+ }
+
+ RefPtr<layers::profiler_screenshots::AsyncReadbackBuffer> buffer =
+ mNativeLayerRootSnapshotter->CreateAsyncReadbackBuffer(size);
+ buffer->CopyFrom(snapshot);
+
+ RefPtr<layers::RecordedFrame> frame =
+ new RenderCompositorRecordedFrame(TimeStamp::Now(), std::move(buffer));
+ aRecorder.RecordFrame(frame);
+
+ // GetWindowContents might have changed the current context. Make sure our
+ // context is current again.
+ MakeCurrent();
+ return true;
+}
+
+bool RenderCompositorNative::MaybeGrabScreenshot(
+ const gfx::IntSize& aWindowSize) {
+ if (!ShouldUseNativeCompositor()) {
+ return false;
+ }
+
+ if (!mNativeLayerRootSnapshotter) {
+ mNativeLayerRootSnapshotter = mNativeLayerRoot->CreateSnapshotter();
+ }
+
+ if (mNativeLayerRootSnapshotter) {
+ mProfilerScreenshotGrabber.MaybeGrabScreenshot(*mNativeLayerRootSnapshotter,
+ aWindowSize);
+
+ // MaybeGrabScreenshot might have changed the current context. Make sure our
+ // context is current again.
+ MakeCurrent();
+ }
+
+ return true;
+}
+
+bool RenderCompositorNative::MaybeProcessScreenshotQueue() {
+ if (!ShouldUseNativeCompositor()) {
+ return false;
+ }
+
+ mProfilerScreenshotGrabber.MaybeProcessQueue();
+
+ // MaybeProcessQueue might have changed the current context. Make sure our
+ // context is current again.
+ MakeCurrent();
+
+ return true;
+}
+
+void RenderCompositorNative::CompositorBeginFrame() {
+ mAddedLayers.Clear();
+ mAddedTilePixelCount = 0;
+ mAddedClippedPixelCount = 0;
+ mBeginFrameTimeStamp = TimeStamp::Now();
+ mSurfacePoolHandle->OnBeginFrame();
+ mNativeLayerRoot->PrepareForCommit();
+}
+
+void RenderCompositorNative::CompositorEndFrame() {
+ if (profiler_thread_is_being_profiled_for_markers()) {
+ auto bufferSize = GetBufferSize();
+ [[maybe_unused]] uint64_t windowPixelCount =
+ uint64_t(bufferSize.width) * bufferSize.height;
+ int nativeLayerCount = 0;
+ for (const auto& it : mSurfaces) {
+ nativeLayerCount += int(it.second.mNativeLayers.size());
+ }
+ PROFILER_MARKER_TEXT(
+ "WR OS Compositor frame", GRAPHICS,
+ MarkerTiming::IntervalUntilNowFrom(mBeginFrameTimeStamp),
+ nsPrintfCString("%d%% painting, %d%% overdraw, %d used "
+ "layers (%d%% memory) + %d unused layers (%d%% memory)",
+ int(mDrawnPixelCount * 100 / windowPixelCount),
+ int(mAddedClippedPixelCount * 100 / windowPixelCount),
+ int(mAddedLayers.Length()),
+ int(mAddedTilePixelCount * 100 / windowPixelCount),
+ int(nativeLayerCount - mAddedLayers.Length()),
+ int((mTotalTilePixelCount - mAddedTilePixelCount) *
+ 100 / windowPixelCount)));
+ }
+ mDrawnPixelCount = 0;
+
+ DoFlush();
+
+ mNativeLayerRoot->SetLayers(mAddedLayers);
+ mNativeLayerRoot->CommitToScreen();
+ mSurfacePoolHandle->OnEndFrame();
+}
+
+void RenderCompositorNative::BindNativeLayer(wr::NativeTileId aId,
+ const gfx::IntRect& aDirtyRect) {
+ MOZ_RELEASE_ASSERT(!mCurrentlyBoundNativeLayer);
+
+ auto surfaceCursor = mSurfaces.find(aId.surface_id);
+ MOZ_RELEASE_ASSERT(surfaceCursor != mSurfaces.end());
+ Surface& surface = surfaceCursor->second;
+
+ auto layerCursor = surface.mNativeLayers.find(TileKey(aId.x, aId.y));
+ MOZ_RELEASE_ASSERT(layerCursor != surface.mNativeLayers.end());
+ RefPtr<layers::NativeLayer> layer = layerCursor->second;
+
+ mCurrentlyBoundNativeLayer = layer;
+
+ mDrawnPixelCount += aDirtyRect.Area();
+}
+
+void RenderCompositorNative::UnbindNativeLayer() {
+ MOZ_RELEASE_ASSERT(mCurrentlyBoundNativeLayer);
+
+ mCurrentlyBoundNativeLayer->NotifySurfaceReady();
+ mCurrentlyBoundNativeLayer = nullptr;
+}
+
+void RenderCompositorNative::CreateSurface(wr::NativeSurfaceId aId,
+ wr::DeviceIntPoint aVirtualOffset,
+ wr::DeviceIntSize aTileSize,
+ bool aIsOpaque) {
+ MOZ_RELEASE_ASSERT(mSurfaces.find(aId) == mSurfaces.end());
+ mSurfaces.insert({aId, Surface{aTileSize, aIsOpaque}});
+}
+
+void RenderCompositorNative::CreateExternalSurface(wr::NativeSurfaceId aId,
+ bool aIsOpaque) {
+ MOZ_RELEASE_ASSERT(mSurfaces.find(aId) == mSurfaces.end());
+
+ RefPtr<layers::NativeLayer> layer =
+ mNativeLayerRoot->CreateLayerForExternalTexture(aIsOpaque);
+
+ Surface surface{DeviceIntSize{}, aIsOpaque};
+ surface.mIsExternal = true;
+ surface.mNativeLayers.insert({TileKey(0, 0), layer});
+
+ mSurfaces.insert({aId, std::move(surface)});
+}
+
+void RenderCompositorNative::CreateBackdropSurface(wr::NativeSurfaceId aId,
+ wr::ColorF aColor) {
+ MOZ_RELEASE_ASSERT(mSurfaces.find(aId) == mSurfaces.end());
+
+ gfx::DeviceColor color(aColor.r, aColor.g, aColor.b, aColor.a);
+ RefPtr<layers::NativeLayer> layer =
+ mNativeLayerRoot->CreateLayerForColor(color);
+
+ Surface surface{DeviceIntSize{}, (aColor.a >= 1.0f)};
+ surface.mNativeLayers.insert({TileKey(0, 0), layer});
+
+ mSurfaces.insert({aId, std::move(surface)});
+}
+
+void RenderCompositorNative::AttachExternalImage(
+ wr::NativeSurfaceId aId, wr::ExternalImageId aExternalImage) {
+ RenderTextureHost* image =
+ RenderThread::Get()->GetRenderTexture(aExternalImage);
+ MOZ_RELEASE_ASSERT(image);
+
+ auto surfaceCursor = mSurfaces.find(aId);
+ MOZ_RELEASE_ASSERT(surfaceCursor != mSurfaces.end());
+
+ Surface& surface = surfaceCursor->second;
+ MOZ_RELEASE_ASSERT(surface.mNativeLayers.size() == 1);
+ MOZ_RELEASE_ASSERT(surface.mIsExternal);
+ surface.mNativeLayers.begin()->second->AttachExternalImage(image);
+}
+
+void RenderCompositorNative::DestroySurface(NativeSurfaceId aId) {
+ auto surfaceCursor = mSurfaces.find(aId);
+ MOZ_RELEASE_ASSERT(surfaceCursor != mSurfaces.end());
+
+ Surface& surface = surfaceCursor->second;
+ if (!surface.mIsExternal) {
+ for (const auto& iter : surface.mNativeLayers) {
+ mTotalTilePixelCount -= gfx::IntRect({}, iter.second->GetSize()).Area();
+ }
+ }
+
+ mSurfaces.erase(surfaceCursor);
+}
+
+void RenderCompositorNative::CreateTile(wr::NativeSurfaceId aId, int aX,
+ int aY) {
+ auto surfaceCursor = mSurfaces.find(aId);
+ MOZ_RELEASE_ASSERT(surfaceCursor != mSurfaces.end());
+ Surface& surface = surfaceCursor->second;
+ MOZ_RELEASE_ASSERT(!surface.mIsExternal);
+
+ RefPtr<layers::NativeLayer> layer = mNativeLayerRoot->CreateLayer(
+ surface.TileSize(), surface.mIsOpaque, mSurfacePoolHandle);
+ surface.mNativeLayers.insert({TileKey(aX, aY), layer});
+ mTotalTilePixelCount += gfx::IntRect({}, layer->GetSize()).Area();
+}
+
+void RenderCompositorNative::DestroyTile(wr::NativeSurfaceId aId, int aX,
+ int aY) {
+ auto surfaceCursor = mSurfaces.find(aId);
+ MOZ_RELEASE_ASSERT(surfaceCursor != mSurfaces.end());
+ Surface& surface = surfaceCursor->second;
+ MOZ_RELEASE_ASSERT(!surface.mIsExternal);
+
+ auto layerCursor = surface.mNativeLayers.find(TileKey(aX, aY));
+ MOZ_RELEASE_ASSERT(layerCursor != surface.mNativeLayers.end());
+ RefPtr<layers::NativeLayer> layer = std::move(layerCursor->second);
+ surface.mNativeLayers.erase(layerCursor);
+ mTotalTilePixelCount -= gfx::IntRect({}, layer->GetSize()).Area();
+
+ // If the layer is currently present in mNativeLayerRoot, it will be destroyed
+ // once CompositorEndFrame() replaces mNativeLayerRoot's layers and drops that
+ // reference. So until that happens, the layer still needs to hold on to its
+ // front buffer. However, we can tell it to drop its back buffers now, because
+ // we know that we will never draw to it again.
+ // Dropping the back buffers now puts them back in the surface pool, so those
+ // surfaces can be immediately re-used for drawing in other layers in the
+ // current frame.
+ layer->DiscardBackbuffers();
+}
+
+gfx::SamplingFilter ToSamplingFilter(wr::ImageRendering aImageRendering) {
+ if (aImageRendering == wr::ImageRendering::Auto) {
+ return gfx::SamplingFilter::LINEAR;
+ }
+ return gfx::SamplingFilter::POINT;
+}
+
+void RenderCompositorNative::AddSurface(
+ wr::NativeSurfaceId aId, const wr::CompositorSurfaceTransform& aTransform,
+ wr::DeviceIntRect aClipRect, wr::ImageRendering aImageRendering) {
+ MOZ_RELEASE_ASSERT(!mCurrentlyBoundNativeLayer);
+
+ auto surfaceCursor = mSurfaces.find(aId);
+ MOZ_RELEASE_ASSERT(surfaceCursor != mSurfaces.end());
+ const Surface& surface = surfaceCursor->second;
+
+ float sx = aTransform.scale.x;
+ float sy = aTransform.scale.y;
+ float tx = aTransform.offset.x;
+ float ty = aTransform.offset.y;
+ gfx::Matrix4x4 transform(sx, 0.0, 0.0, 0.0, 0.0, sy, 0.0, 0.0, 0.0, 0.0, 1.0,
+ 0.0, tx, ty, 0.0, 1.0);
+
+ for (auto it = surface.mNativeLayers.begin();
+ it != surface.mNativeLayers.end(); ++it) {
+ RefPtr<layers::NativeLayer> layer = it->second;
+ gfx::IntSize layerSize = layer->GetSize();
+ gfx::IntPoint layerPosition(surface.mTileSize.width * it->first.mX,
+ surface.mTileSize.height * it->first.mY);
+ layer->SetPosition(layerPosition);
+ gfx::IntRect clipRect(aClipRect.min.x, aClipRect.min.y, aClipRect.width(),
+ aClipRect.height());
+ layer->SetClipRect(Some(clipRect));
+ layer->SetTransform(transform);
+ layer->SetSamplingFilter(ToSamplingFilter(aImageRendering));
+ mAddedLayers.AppendElement(layer);
+
+ if (!surface.mIsExternal) {
+ mAddedTilePixelCount += layerSize.width * layerSize.height;
+ }
+ gfx::Rect r = transform.TransformBounds(
+ gfx::Rect(layer->CurrentSurfaceDisplayRect()));
+ gfx::IntRect visibleRect =
+ clipRect.Intersect(RoundedToInt(r) + layerPosition);
+ mAddedClippedPixelCount += visibleRect.Area();
+ }
+}
+
+/* static */
+UniquePtr<RenderCompositor> RenderCompositorNativeOGL::Create(
+ const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError) {
+ RefPtr<gl::GLContext> gl = RenderThread::Get()->SingletonGL();
+ if (!gl) {
+ gl = gl::GLContextProvider::CreateForCompositorWidget(
+ aWidget, /* aHardwareWebRender */ true, /* aForceAccelerated */ true);
+ RenderThread::MaybeEnableGLDebugMessage(gl);
+ }
+ if (!gl || !gl->MakeCurrent()) {
+ gfxCriticalNote << "Failed GL context creation for WebRender: "
+ << gfx::hexa(gl.get());
+ return nullptr;
+ }
+ return MakeUnique<RenderCompositorNativeOGL>(aWidget, std::move(gl));
+}
+
+RenderCompositorNativeOGL::RenderCompositorNativeOGL(
+ const RefPtr<widget::CompositorWidget>& aWidget,
+ RefPtr<gl::GLContext>&& aGL)
+ : RenderCompositorNative(aWidget, aGL), mGL(aGL) {
+ MOZ_ASSERT(mGL);
+}
+
+RenderCompositorNativeOGL::~RenderCompositorNativeOGL() {
+ if (!mGL->MakeCurrent()) {
+ gfxCriticalNote
+ << "Failed to make render context current during destroying.";
+ // Leak resources!
+ mPreviousFrameDoneSync = nullptr;
+ mThisFrameDoneSync = nullptr;
+ return;
+ }
+
+ if (mPreviousFrameDoneSync) {
+ mGL->fDeleteSync(mPreviousFrameDoneSync);
+ }
+ if (mThisFrameDoneSync) {
+ mGL->fDeleteSync(mThisFrameDoneSync);
+ }
+}
+
+bool RenderCompositorNativeOGL::InitDefaultFramebuffer(
+ const gfx::IntRect& aBounds) {
+ if (mNativeLayerForEntireWindow) {
+ Maybe<GLuint> fbo = mNativeLayerForEntireWindow->NextSurfaceAsFramebuffer(
+ aBounds, aBounds, true);
+ if (!fbo) {
+ return false;
+ }
+ mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, *fbo);
+ } else {
+ mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mGL->GetDefaultFramebuffer());
+ }
+ return true;
+}
+
+void RenderCompositorNativeOGL::DoSwap() {
+ InsertFrameDoneSync();
+ if (mNativeLayerForEntireWindow) {
+ mGL->fFlush();
+ }
+}
+
+void RenderCompositorNativeOGL::DoFlush() { mGL->fFlush(); }
+
+void RenderCompositorNativeOGL::InsertFrameDoneSync() {
+#ifdef XP_MACOSX
+ // Only do this on macOS.
+ // On other platforms, SwapBuffers automatically applies back-pressure.
+ if (mThisFrameDoneSync) {
+ mGL->fDeleteSync(mThisFrameDoneSync);
+ }
+ mThisFrameDoneSync = mGL->fFenceSync(LOCAL_GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+#endif
+}
+
+bool RenderCompositorNativeOGL::WaitForGPU() {
+ if (mPreviousFrameDoneSync) {
+ AUTO_PROFILER_LABEL("Waiting for GPU to finish previous frame", GRAPHICS);
+ mGL->fClientWaitSync(mPreviousFrameDoneSync,
+ LOCAL_GL_SYNC_FLUSH_COMMANDS_BIT,
+ LOCAL_GL_TIMEOUT_IGNORED);
+ mGL->fDeleteSync(mPreviousFrameDoneSync);
+ }
+ mPreviousFrameDoneSync = mThisFrameDoneSync;
+ mThisFrameDoneSync = nullptr;
+
+ return true;
+}
+
+void RenderCompositorNativeOGL::Bind(wr::NativeTileId aId,
+ wr::DeviceIntPoint* aOffset,
+ uint32_t* aFboId,
+ wr::DeviceIntRect aDirtyRect,
+ wr::DeviceIntRect aValidRect) {
+ gfx::IntRect validRect(aValidRect.min.x, aValidRect.min.y, aValidRect.width(),
+ aValidRect.height());
+ gfx::IntRect dirtyRect(aDirtyRect.min.x, aDirtyRect.min.y, aDirtyRect.width(),
+ aDirtyRect.height());
+
+ BindNativeLayer(aId, dirtyRect);
+
+ Maybe<GLuint> fbo = mCurrentlyBoundNativeLayer->NextSurfaceAsFramebuffer(
+ validRect, dirtyRect, true);
+
+ *aFboId = *fbo;
+ *aOffset = wr::DeviceIntPoint{0, 0};
+}
+
+void RenderCompositorNativeOGL::Unbind() {
+ mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
+
+ UnbindNativeLayer();
+}
+
+/* static */
+UniquePtr<RenderCompositor> RenderCompositorNativeSWGL::Create(
+ const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError) {
+ void* ctx = wr_swgl_create_context();
+ if (!ctx) {
+ gfxCriticalNote << "Failed SWGL context creation for WebRender";
+ return nullptr;
+ }
+ return MakeUnique<RenderCompositorNativeSWGL>(aWidget, ctx);
+}
+
+RenderCompositorNativeSWGL::RenderCompositorNativeSWGL(
+ const RefPtr<widget::CompositorWidget>& aWidget, void* aContext)
+ : RenderCompositorNative(aWidget), mContext(aContext) {
+ MOZ_ASSERT(mContext);
+}
+
+RenderCompositorNativeSWGL::~RenderCompositorNativeSWGL() {
+ wr_swgl_destroy_context(mContext);
+}
+
+bool RenderCompositorNativeSWGL::MakeCurrent() {
+ wr_swgl_make_current(mContext);
+ return true;
+}
+
+bool RenderCompositorNativeSWGL::InitDefaultFramebuffer(
+ const gfx::IntRect& aBounds) {
+ if (mNativeLayerForEntireWindow) {
+ if (!MapNativeLayer(mNativeLayerForEntireWindow, aBounds, aBounds)) {
+ return false;
+ }
+ wr_swgl_init_default_framebuffer(mContext, aBounds.x, aBounds.y,
+ aBounds.width, aBounds.height,
+ mLayerStride, mLayerValidRectData);
+ }
+ return true;
+}
+
+void RenderCompositorNativeSWGL::CancelFrame() {
+ if (mNativeLayerForEntireWindow && mLayerTarget) {
+ wr_swgl_init_default_framebuffer(mContext, 0, 0, 0, 0, 0, nullptr);
+ UnmapNativeLayer();
+ }
+}
+
+void RenderCompositorNativeSWGL::DoSwap() {
+ if (mNativeLayerForEntireWindow && mLayerTarget) {
+ wr_swgl_init_default_framebuffer(mContext, 0, 0, 0, 0, 0, nullptr);
+ UnmapNativeLayer();
+ }
+}
+
+bool RenderCompositorNativeSWGL::MapNativeLayer(
+ layers::NativeLayer* aLayer, const gfx::IntRect& aDirtyRect,
+ const gfx::IntRect& aValidRect) {
+ uint8_t* data = nullptr;
+ gfx::IntSize size;
+ int32_t stride = 0;
+ gfx::SurfaceFormat format = gfx::SurfaceFormat::UNKNOWN;
+ RefPtr<gfx::DrawTarget> dt = aLayer->NextSurfaceAsDrawTarget(
+ aValidRect, gfx::IntRegion(aDirtyRect), gfx::BackendType::SKIA);
+ if (!dt || !dt->LockBits(&data, &size, &stride, &format)) {
+ return false;
+ }
+ MOZ_ASSERT(format == gfx::SurfaceFormat::B8G8R8A8 ||
+ format == gfx::SurfaceFormat::B8G8R8X8);
+ mLayerTarget = std::move(dt);
+ mLayerData = data;
+ mLayerValidRectData = data + aValidRect.y * stride + aValidRect.x * 4;
+ mLayerStride = stride;
+ return true;
+}
+
+void RenderCompositorNativeSWGL::UnmapNativeLayer() {
+ MOZ_ASSERT(mLayerTarget && mLayerData);
+ mLayerTarget->ReleaseBits(mLayerData);
+ mLayerTarget = nullptr;
+ mLayerData = nullptr;
+ mLayerValidRectData = nullptr;
+ mLayerStride = 0;
+}
+
+bool RenderCompositorNativeSWGL::MapTile(wr::NativeTileId aId,
+ wr::DeviceIntRect aDirtyRect,
+ wr::DeviceIntRect aValidRect,
+ void** aData, int32_t* aStride) {
+ if (mNativeLayerForEntireWindow) {
+ return false;
+ }
+ gfx::IntRect dirtyRect(aDirtyRect.min.x, aDirtyRect.min.y, aDirtyRect.width(),
+ aDirtyRect.height());
+ gfx::IntRect validRect(aValidRect.min.x, aValidRect.min.y, aValidRect.width(),
+ aValidRect.height());
+ BindNativeLayer(aId, dirtyRect);
+ if (!MapNativeLayer(mCurrentlyBoundNativeLayer, dirtyRect, validRect)) {
+ UnbindNativeLayer();
+ return false;
+ }
+ *aData = mLayerValidRectData;
+ *aStride = mLayerStride;
+ return true;
+}
+
+void RenderCompositorNativeSWGL::UnmapTile() {
+ if (!mNativeLayerForEntireWindow && mCurrentlyBoundNativeLayer) {
+ UnmapNativeLayer();
+ UnbindNativeLayer();
+ }
+}
+
+} // namespace mozilla::wr
diff --git a/gfx/webrender_bindings/RenderCompositorNative.h b/gfx/webrender_bindings/RenderCompositorNative.h
new file mode 100644
index 0000000000..936fdd3db8
--- /dev/null
+++ b/gfx/webrender_bindings/RenderCompositorNative.h
@@ -0,0 +1,233 @@
+/* -*- 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_RENDERCOMPOSITOR_NATIVE_H
+#define MOZILLA_GFX_RENDERCOMPOSITOR_NATIVE_H
+
+#include <unordered_map>
+
+#include "GLTypes.h"
+#include "mozilla/HashFunctions.h"
+#include "mozilla/layers/ScreenshotGrabber.h"
+#include "mozilla/webrender/RenderCompositor.h"
+#include "mozilla/TimeStamp.h"
+
+namespace mozilla {
+
+namespace layers {
+class NativeLayerRootSnapshotter;
+class NativeLayerRoot;
+class NativeLayer;
+class SurfacePoolHandle;
+} // namespace layers
+
+namespace wr {
+
+// RenderCompositorNative is a skeleton class for implementing compositors
+// backed by NativeLayer surfaces and tiles. This is not meant to be directly
+// instantiated and is instead derived for various use-cases such as OpenGL or
+// SWGL.
+class RenderCompositorNative : public RenderCompositor {
+ public:
+ virtual ~RenderCompositorNative();
+
+ bool BeginFrame() override;
+ RenderedFrameId EndFrame(const nsTArray<DeviceIntRect>& aDirtyRects) final;
+ void Pause() override;
+ bool Resume() override;
+
+ layers::WebRenderCompositor CompositorType() const override;
+
+ LayoutDeviceIntSize GetBufferSize() override;
+
+ bool ShouldUseNativeCompositor() override;
+ void GetCompositorCapabilities(CompositorCapabilities* aCaps) override;
+
+ bool SurfaceOriginIsTopLeft() override { return true; }
+
+ // Does the readback for the ShouldUseNativeCompositor() case.
+ bool MaybeReadback(const gfx::IntSize& aReadbackSize,
+ const wr::ImageFormat& aReadbackFormat,
+ const Range<uint8_t>& aReadbackBuffer,
+ bool* aNeedsYFlip) override;
+ bool MaybeRecordFrame(layers::CompositionRecorder& aRecorder) override;
+ bool MaybeGrabScreenshot(const gfx::IntSize& aWindowSize) override;
+ bool MaybeProcessScreenshotQueue() override;
+
+ // Interface for wr::Compositor
+ void CompositorBeginFrame() override;
+ void CompositorEndFrame() override;
+ void CreateSurface(wr::NativeSurfaceId aId, wr::DeviceIntPoint aVirtualOffset,
+ wr::DeviceIntSize aTileSize, bool aIsOpaque) override;
+ void CreateExternalSurface(wr::NativeSurfaceId aId, bool aIsOpaque) override;
+ void CreateBackdropSurface(wr::NativeSurfaceId aId,
+ wr::ColorF aColor) override;
+ void DestroySurface(NativeSurfaceId aId) override;
+ void CreateTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY) override;
+ void DestroyTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY) override;
+ void AttachExternalImage(wr::NativeSurfaceId aId,
+ wr::ExternalImageId aExternalImage) override;
+ void AddSurface(wr::NativeSurfaceId aId,
+ const wr::CompositorSurfaceTransform& aTransform,
+ wr::DeviceIntRect aClipRect,
+ wr::ImageRendering aImageRendering) override;
+
+ struct TileKey {
+ TileKey(int32_t aX, int32_t aY) : mX(aX), mY(aY) {}
+
+ int32_t mX;
+ int32_t mY;
+ };
+
+ protected:
+ explicit RenderCompositorNative(
+ const RefPtr<widget::CompositorWidget>& aWidget,
+ gl::GLContext* aGL = nullptr);
+
+ virtual bool InitDefaultFramebuffer(const gfx::IntRect& aBounds) = 0;
+ virtual void DoSwap() = 0;
+ virtual void DoFlush() {}
+
+ void BindNativeLayer(wr::NativeTileId aId, const gfx::IntRect& aDirtyRect);
+ void UnbindNativeLayer();
+
+ // Can be null.
+ RefPtr<layers::NativeLayerRoot> mNativeLayerRoot;
+ UniquePtr<layers::NativeLayerRootSnapshotter> mNativeLayerRootSnapshotter;
+ layers::ScreenshotGrabber mProfilerScreenshotGrabber;
+ RefPtr<layers::NativeLayer> mNativeLayerForEntireWindow;
+ RefPtr<layers::SurfacePoolHandle> mSurfacePoolHandle;
+
+ struct TileKeyHashFn {
+ std::size_t operator()(const TileKey& aId) const {
+ return HashGeneric(aId.mX, aId.mY);
+ }
+ };
+
+ struct Surface {
+ explicit Surface(wr::DeviceIntSize aTileSize, bool aIsOpaque)
+ : mTileSize(aTileSize), mIsOpaque(aIsOpaque) {}
+ gfx::IntSize TileSize() {
+ return gfx::IntSize(mTileSize.width, mTileSize.height);
+ }
+
+ // External images can change size depending on which image
+ // is attached, so mTileSize will be 0,0 when mIsExternal
+ // is true.
+ wr::DeviceIntSize mTileSize;
+ bool mIsOpaque;
+ bool mIsExternal = false;
+ std::unordered_map<TileKey, RefPtr<layers::NativeLayer>, TileKeyHashFn>
+ mNativeLayers;
+ };
+
+ struct SurfaceIdHashFn {
+ std::size_t operator()(const wr::NativeSurfaceId& aId) const {
+ return HashGeneric(wr::AsUint64(aId));
+ }
+ };
+
+ // Used in native compositor mode:
+ RefPtr<layers::NativeLayer> mCurrentlyBoundNativeLayer;
+ nsTArray<RefPtr<layers::NativeLayer>> mAddedLayers;
+ uint64_t mTotalTilePixelCount = 0;
+ uint64_t mAddedTilePixelCount = 0;
+ uint64_t mAddedClippedPixelCount = 0;
+ uint64_t mDrawnPixelCount = 0;
+ gfx::IntRect mVisibleBounds;
+ std::unordered_map<wr::NativeSurfaceId, Surface, SurfaceIdHashFn> mSurfaces;
+ TimeStamp mBeginFrameTimeStamp;
+};
+
+static inline bool operator==(const RenderCompositorNative::TileKey& a0,
+ const RenderCompositorNative::TileKey& a1) {
+ return a0.mX == a1.mX && a0.mY == a1.mY;
+}
+
+// RenderCompositorNativeOGL is a NativeLayer compositor that exposes an
+// OpenGL framebuffer for the respective NativeLayer bound to each tile.
+class RenderCompositorNativeOGL : public RenderCompositorNative {
+ public:
+ static UniquePtr<RenderCompositor> Create(
+ const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError);
+
+ RenderCompositorNativeOGL(const RefPtr<widget::CompositorWidget>& aWidget,
+ RefPtr<gl::GLContext>&& aGL);
+ virtual ~RenderCompositorNativeOGL();
+
+ bool WaitForGPU() override;
+
+ gl::GLContext* gl() const override { return mGL; }
+
+ void Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset, uint32_t* aFboId,
+ wr::DeviceIntRect aDirtyRect,
+ wr::DeviceIntRect aValidRect) override;
+ void Unbind() override;
+
+ protected:
+ void InsertFrameDoneSync();
+
+ bool InitDefaultFramebuffer(const gfx::IntRect& aBounds) override;
+ void DoSwap() override;
+ void DoFlush() override;
+
+ RefPtr<gl::GLContext> mGL;
+
+ // Used to apply back-pressure in WaitForGPU().
+ GLsync mPreviousFrameDoneSync = nullptr;
+ GLsync mThisFrameDoneSync = nullptr;
+};
+
+// RenderCompositorNativeSWGL is a NativeLayer compositor that only
+// deals with mapping the underlying buffer for SWGL usage of a tile.
+class RenderCompositorNativeSWGL : public RenderCompositorNative {
+ public:
+ static UniquePtr<RenderCompositor> Create(
+ const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError);
+
+ RenderCompositorNativeSWGL(const RefPtr<widget::CompositorWidget>& aWidget,
+ void* aContext);
+ virtual ~RenderCompositorNativeSWGL();
+
+ void* swgl() const override { return mContext; }
+
+ bool MakeCurrent() override;
+
+ void CancelFrame() override;
+
+ layers::WebRenderBackend BackendType() const override {
+ return layers::WebRenderBackend::SOFTWARE;
+ }
+
+ // Maps an underlying layer and sets aData to the top left pixel of
+ // aValidRect. The row stride is set to aStride, note this doesn't
+ // mean there are always aStride bytes available per row (the
+ // last row will fall short if aValidRect is not at X==0).
+ bool MapTile(wr::NativeTileId aId, wr::DeviceIntRect aDirtyRect,
+ wr::DeviceIntRect aValidRect, void** aData,
+ int32_t* aStride) override;
+ void UnmapTile() override;
+
+ protected:
+ bool InitDefaultFramebuffer(const gfx::IntRect& aBounds) override;
+ void DoSwap() override;
+
+ bool MapNativeLayer(layers::NativeLayer* aLayer,
+ const gfx::IntRect& aDirtyRect,
+ const gfx::IntRect& aValidRect);
+ void UnmapNativeLayer();
+
+ void* mContext = nullptr;
+ RefPtr<gfx::DrawTarget> mLayerTarget;
+ uint8_t* mLayerData = nullptr;
+ uint8_t* mLayerValidRectData = nullptr;
+ int32_t mLayerStride = 0;
+};
+
+} // namespace wr
+} // namespace mozilla
+
+#endif
diff --git a/gfx/webrender_bindings/RenderCompositorOGL.cpp b/gfx/webrender_bindings/RenderCompositorOGL.cpp
new file mode 100644
index 0000000000..9653e06d43
--- /dev/null
+++ b/gfx/webrender_bindings/RenderCompositorOGL.cpp
@@ -0,0 +1,126 @@
+/* -*- 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 "RenderCompositorOGL.h"
+
+#include "GLContext.h"
+#include "GLContextEGL.h"
+#include "GLContextProvider.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/webrender/RenderThread.h"
+#include "mozilla/widget/CompositorWidget.h"
+
+namespace mozilla::wr {
+
+extern LazyLogModule gRenderThreadLog;
+#define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
+
+/* static */
+UniquePtr<RenderCompositor> RenderCompositorOGL::Create(
+ const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError) {
+ RefPtr<gl::GLContext> gl = RenderThread::Get()->SingletonGL();
+ if (!gl) {
+ gl = gl::GLContextProvider::CreateForCompositorWidget(
+ aWidget, /* aHardwareWebRender */ true, /* aForceAccelerated */ true);
+ RenderThread::MaybeEnableGLDebugMessage(gl);
+ }
+ if (!gl || !gl->MakeCurrent()) {
+ gfxCriticalNote << "Failed GL context creation for WebRender: "
+ << gfx::hexa(gl.get());
+ return nullptr;
+ }
+ return MakeUnique<RenderCompositorOGL>(std::move(gl), aWidget);
+}
+
+RenderCompositorOGL::RenderCompositorOGL(
+ RefPtr<gl::GLContext>&& aGL,
+ const RefPtr<widget::CompositorWidget>& aWidget)
+ : RenderCompositor(aWidget), mGL(aGL) {
+ MOZ_ASSERT(mGL);
+ LOG("RenderCompositorOGL::RenderCompositorOGL()");
+
+ mIsEGL = aGL->GetContextType() == mozilla::gl::GLContextType::EGL;
+}
+
+RenderCompositorOGL::~RenderCompositorOGL() {
+ LOG("RenderCompositorOGL::~RenderCompositorOGL()");
+
+ if (!mGL->MakeCurrent()) {
+ gfxCriticalNote
+ << "Failed to make render context current during destroying.";
+ // Leak resources!
+ return;
+ }
+}
+
+bool RenderCompositorOGL::BeginFrame() {
+ if (!mGL->MakeCurrent()) {
+ gfxCriticalNote << "Failed to make render context current, can't draw.";
+ return false;
+ }
+
+ mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mGL->GetDefaultFramebuffer());
+
+ return true;
+}
+
+RenderedFrameId RenderCompositorOGL::EndFrame(
+ const nsTArray<DeviceIntRect>& aDirtyRects) {
+ RenderedFrameId frameId = GetNextRenderFrameId();
+ if (UsePartialPresent() && aDirtyRects.Length() > 0) {
+ gfx::IntRegion bufferInvalid;
+ const auto bufferSize = GetBufferSize();
+ for (const DeviceIntRect& rect : aDirtyRects) {
+ const auto left = std::max(0, std::min(bufferSize.width, rect.min.x));
+ const auto top = std::max(0, std::min(bufferSize.height, rect.min.y));
+
+ const auto right = std::min(bufferSize.width, std::max(0, rect.max.x));
+ const auto bottom = std::min(bufferSize.height, std::max(0, rect.max.y));
+
+ const auto width = right - left;
+ const auto height = bottom - top;
+
+ bufferInvalid.OrWith(
+ gfx::IntRect(left, (GetBufferSize().height - bottom), width, height));
+ }
+ gl()->SetDamage(bufferInvalid);
+ }
+ mGL->SwapBuffers();
+ return frameId;
+}
+
+void RenderCompositorOGL::Pause() {}
+
+bool RenderCompositorOGL::Resume() { return true; }
+
+LayoutDeviceIntSize RenderCompositorOGL::GetBufferSize() {
+ return mWidget->GetClientSize();
+}
+
+uint32_t RenderCompositorOGL::GetMaxPartialPresentRects() {
+ return gfx::gfxVars::WebRenderMaxPartialPresentRects();
+}
+
+bool RenderCompositorOGL::RequestFullRender() { return false; }
+
+bool RenderCompositorOGL::UsePartialPresent() {
+ return gfx::gfxVars::WebRenderMaxPartialPresentRects() > 0;
+}
+
+bool RenderCompositorOGL::ShouldDrawPreviousPartialPresentRegions() {
+ return true;
+}
+
+size_t RenderCompositorOGL::GetBufferAge() const {
+ if (!StaticPrefs::
+ gfx_webrender_allow_partial_present_buffer_age_AtStartup()) {
+ return 0;
+ }
+ return gl()->GetBufferAge();
+}
+
+} // namespace mozilla::wr
diff --git a/gfx/webrender_bindings/RenderCompositorOGL.h b/gfx/webrender_bindings/RenderCompositorOGL.h
new file mode 100644
index 0000000000..78429a2db4
--- /dev/null
+++ b/gfx/webrender_bindings/RenderCompositorOGL.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_GFX_RENDERCOMPOSITOR_OGL_H
+#define MOZILLA_GFX_RENDERCOMPOSITOR_OGL_H
+
+#include "GLTypes.h"
+#include "mozilla/webrender/RenderCompositor.h"
+
+namespace mozilla {
+namespace wr {
+
+class RenderCompositorOGL : public RenderCompositor {
+ public:
+ static UniquePtr<RenderCompositor> Create(
+ const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError);
+
+ RenderCompositorOGL(RefPtr<gl::GLContext>&& aGL,
+ const RefPtr<widget::CompositorWidget>& aWidget);
+ virtual ~RenderCompositorOGL();
+
+ bool BeginFrame() override;
+ RenderedFrameId EndFrame(const nsTArray<DeviceIntRect>& aDirtyRects) final;
+ void Pause() override;
+ bool Resume() override;
+
+ gl::GLContext* gl() const override { return mGL; }
+
+ LayoutDeviceIntSize GetBufferSize() override;
+
+ // Interface for partial present
+ bool UsePartialPresent() override;
+ bool RequestFullRender() override;
+ uint32_t GetMaxPartialPresentRects() override;
+ bool ShouldDrawPreviousPartialPresentRegions() override;
+ size_t GetBufferAge() const override;
+
+ protected:
+ RefPtr<gl::GLContext> mGL;
+ bool mIsEGL;
+};
+
+} // namespace wr
+} // namespace mozilla
+
+#endif
diff --git a/gfx/webrender_bindings/RenderCompositorOGLSWGL.cpp b/gfx/webrender_bindings/RenderCompositorOGLSWGL.cpp
new file mode 100644
index 0000000000..fac9722bbe
--- /dev/null
+++ b/gfx/webrender_bindings/RenderCompositorOGLSWGL.cpp
@@ -0,0 +1,515 @@
+/* -*- 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 "RenderCompositorOGLSWGL.h"
+
+#include "GLContext.h"
+#include "GLContextEGL.h"
+#include "mozilla/layers/BuildConstants.h"
+#include "mozilla/layers/CompositorOGL.h"
+#include "mozilla/layers/Effects.h"
+#include "mozilla/layers/TextureHostOGL.h"
+#include "mozilla/widget/CompositorWidget.h"
+#include "OGLShaderProgram.h"
+
+#ifdef MOZ_WIDGET_ANDROID
+# include "mozilla/java/GeckoSurfaceTextureWrappers.h"
+# include "mozilla/webrender/RenderAndroidHardwareBufferTextureHost.h"
+# include "mozilla/widget/AndroidCompositorWidget.h"
+# include <android/native_window.h>
+# include <android/native_window_jni.h>
+#endif
+
+#ifdef MOZ_WIDGET_GTK
+# include "mozilla/widget/GtkCompositorWidget.h"
+# include <gdk/gdk.h>
+# ifdef MOZ_X11
+# include <gdk/gdkx.h>
+# endif
+#endif
+
+namespace mozilla {
+using namespace layers;
+using namespace gfx;
+namespace wr {
+
+extern LazyLogModule gRenderThreadLog;
+#define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
+
+UniquePtr<RenderCompositor> RenderCompositorOGLSWGL::Create(
+ const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError) {
+ if (!aWidget->GetCompositorOptions().AllowSoftwareWebRenderOGL()) {
+ return nullptr;
+ }
+
+ RefPtr<Compositor> compositor;
+#ifdef MOZ_WIDGET_ANDROID
+ RefPtr<gl::GLContext> context =
+ RenderThread::Get()->SingletonGLForCompositorOGL();
+ if (!context) {
+ gfxCriticalNote << "SingletonGL does not exist for SWGL";
+ return nullptr;
+ }
+ auto programs = RenderThread::Get()->GetProgramsForCompositorOGL();
+ if (!programs) {
+ gfxCriticalNote << "Failed to get Programs for CompositorOGL for SWGL";
+ return nullptr;
+ }
+
+ nsCString log;
+ RefPtr<CompositorOGL> compositorOGL;
+ compositorOGL = new CompositorOGL(aWidget, /* aSurfaceWidth */ -1,
+ /* aSurfaceHeight */ -1,
+ /* aUseExternalSurfaceSize */ true);
+ if (!compositorOGL->Initialize(context, programs, &log)) {
+ gfxCriticalNote << "Failed to initialize CompositorOGL for SWGL: "
+ << log.get();
+ return nullptr;
+ }
+ compositor = compositorOGL;
+#elif defined(MOZ_WIDGET_GTK)
+ nsCString log;
+ RefPtr<CompositorOGL> compositorOGL;
+ compositorOGL = new CompositorOGL(aWidget);
+ if (!compositorOGL->Initialize(&log)) {
+ gfxCriticalNote << "Failed to initialize CompositorOGL for SWGL: "
+ << log.get();
+ return nullptr;
+ }
+ compositor = compositorOGL;
+#endif
+
+ if (!compositor) {
+ return nullptr;
+ }
+
+ void* ctx = wr_swgl_create_context();
+ if (!ctx) {
+ gfxCriticalNote << "Failed SWGL context creation for WebRender";
+ return nullptr;
+ }
+
+ return MakeUnique<RenderCompositorOGLSWGL>(compositor, aWidget, ctx);
+}
+
+RenderCompositorOGLSWGL::RenderCompositorOGLSWGL(
+ Compositor* aCompositor, const RefPtr<widget::CompositorWidget>& aWidget,
+ void* aContext)
+ : RenderCompositorLayersSWGL(aCompositor, aWidget, aContext) {
+ LOG("RenderCompositorOGLSWGL::RenderCompositorOGLSWGL()");
+}
+
+RenderCompositorOGLSWGL::~RenderCompositorOGLSWGL() {
+ LOG("RRenderCompositorOGLSWGL::~RenderCompositorOGLSWGL()");
+#ifdef MOZ_WIDGET_ANDROID
+ java::GeckoSurfaceTexture::DestroyUnused((int64_t)GetGLContext());
+ DestroyEGLSurface();
+#endif
+}
+
+gl::GLContext* RenderCompositorOGLSWGL::GetGLContext() {
+ return mCompositor->AsCompositorOGL()->gl();
+}
+
+bool RenderCompositorOGLSWGL::MakeCurrent() {
+ GetGLContext()->MakeCurrent();
+#ifdef MOZ_WIDGET_ANDROID
+ if (GetGLContext()->GetContextType() == gl::GLContextType::EGL) {
+ gl::GLContextEGL::Cast(GetGLContext())->SetEGLSurfaceOverride(mEGLSurface);
+ }
+#endif
+ RenderCompositorLayersSWGL::MakeCurrent();
+ return true;
+}
+
+EGLSurface RenderCompositorOGLSWGL::CreateEGLSurface() {
+ MOZ_ASSERT(GetGLContext()->GetContextType() == gl::GLContextType::EGL);
+
+ EGLSurface surface = EGL_NO_SURFACE;
+ surface = gl::GLContextEGL::CreateEGLSurfaceForCompositorWidget(
+ mWidget, gl::GLContextEGL::Cast(GetGLContext())->mSurfaceConfig);
+ if (surface == EGL_NO_SURFACE) {
+ const auto* renderThread = RenderThread::Get();
+ gfxCriticalNote << "Failed to create EGLSurface. "
+ << renderThread->RendererCount() << " renderers, "
+ << renderThread->ActiveRendererCount() << " active.";
+ }
+
+ // The subsequent render after creating a new surface must be a full render.
+ mFullRender = true;
+
+ return surface;
+}
+
+void RenderCompositorOGLSWGL::DestroyEGLSurface() {
+ MOZ_ASSERT(GetGLContext()->GetContextType() == gl::GLContextType::EGL);
+
+ const auto& gle = gl::GLContextEGL::Cast(GetGLContext());
+ const auto& egl = gle->mEgl;
+
+ // Release EGLSurface of back buffer before calling ResizeBuffers().
+ if (mEGLSurface) {
+ gle->SetEGLSurfaceOverride(EGL_NO_SURFACE);
+ if (!egl->fMakeCurrent(EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
+ const EGLint err = egl->mLib->fGetError();
+ gfxCriticalNote << "Error in eglMakeCurrent: " << gfx::hexa(err);
+ }
+ if (!egl->fDestroySurface(mEGLSurface)) {
+ const EGLint err = egl->mLib->fGetError();
+ gfxCriticalNote << "Error in eglDestroySurface: " << gfx::hexa(err);
+ }
+ mEGLSurface = EGL_NO_SURFACE;
+ }
+}
+
+bool RenderCompositorOGLSWGL::BeginFrame() {
+ MOZ_ASSERT(!mInFrame);
+ RenderCompositorLayersSWGL::BeginFrame();
+
+#ifdef MOZ_WIDGET_ANDROID
+ java::GeckoSurfaceTexture::DestroyUnused((int64_t)GetGLContext());
+ GetGLContext()
+ ->MakeCurrent(); // DestroyUnused can change the current context!
+#endif
+
+ return true;
+}
+
+RenderedFrameId RenderCompositorOGLSWGL::EndFrame(
+ const nsTArray<DeviceIntRect>& aDirtyRects) {
+ mFullRender = false;
+
+ return RenderCompositorLayersSWGL::EndFrame(aDirtyRects);
+}
+
+void RenderCompositorOGLSWGL::HandleExternalImage(
+ RenderTextureHost* aExternalImage, FrameSurface& aFrameSurface) {
+ MOZ_ASSERT(aExternalImage);
+
+#ifdef MOZ_WIDGET_ANDROID
+ GLenum target =
+ LOCAL_GL_TEXTURE_EXTERNAL; // This is required by SurfaceTexture
+ GLenum wrapMode = LOCAL_GL_CLAMP_TO_EDGE;
+
+ if (auto* host = aExternalImage->AsRenderAndroidSurfaceTextureHost()) {
+ host->UpdateTexImageIfNecessary();
+
+ // We need to hold the texture source separately from the effect,
+ // since the effect doesn't hold a strong reference.
+ RefPtr<SurfaceTextureSource> layer = new SurfaceTextureSource(
+ (TextureSourceProvider*)mCompositor, host->mSurfTex, host->mFormat,
+ target, wrapMode, host->mSize, host->mTransformOverride);
+ RefPtr<TexturedEffect> texturedEffect =
+ CreateTexturedEffect(host->mFormat, layer, aFrameSurface.mFilter,
+ /* isAlphaPremultiplied */ true);
+
+ gfx::Rect drawRect(0, 0, host->mSize.width, host->mSize.height);
+
+ EffectChain effect;
+ effect.mPrimaryEffect = texturedEffect;
+ mCompositor->DrawQuad(drawRect, aFrameSurface.mClipRect, effect, 1.0,
+ aFrameSurface.mTransform, drawRect);
+ } else if (auto* host =
+ aExternalImage->AsRenderAndroidHardwareBufferTextureHost()) {
+ // We need to hold the texture source separately from the effect,
+ // since the effect doesn't hold a strong reference.
+ RefPtr<AndroidHardwareBufferTextureSource> layer =
+ new AndroidHardwareBufferTextureSource(
+ (TextureSourceProvider*)mCompositor,
+ host->GetAndroidHardwareBuffer(),
+ host->GetAndroidHardwareBuffer()->mFormat, target, wrapMode,
+ host->GetSize());
+ RefPtr<TexturedEffect> texturedEffect = CreateTexturedEffect(
+ host->GetAndroidHardwareBuffer()->mFormat, layer, aFrameSurface.mFilter,
+ /* isAlphaPremultiplied */ true);
+
+ gfx::Rect drawRect(0, 0, host->GetSize().width, host->GetSize().height);
+
+ EffectChain effect;
+ effect.mPrimaryEffect = texturedEffect;
+ mCompositor->DrawQuad(drawRect, aFrameSurface.mClipRect, effect, 1.0,
+ aFrameSurface.mTransform, drawRect);
+ } else if (!aExternalImage->IsWrappingAsyncRemoteTexture()) {
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+ }
+#endif
+}
+
+void RenderCompositorOGLSWGL::GetCompositorCapabilities(
+ CompositorCapabilities* aCaps) {
+ RenderCompositor::GetCompositorCapabilities(aCaps);
+
+ // max_update_rects are not yet handled properly
+ aCaps->max_update_rects = 0;
+}
+
+bool RenderCompositorOGLSWGL::RequestFullRender() { return mFullRender; }
+
+void RenderCompositorOGLSWGL::Pause() {
+#ifdef MOZ_WIDGET_ANDROID
+ DestroyEGLSurface();
+#elif defined(MOZ_WIDGET_GTK)
+ mCompositor->Pause();
+#endif
+}
+
+bool RenderCompositorOGLSWGL::Resume() {
+#ifdef MOZ_WIDGET_ANDROID
+ // Destroy EGLSurface if it exists.
+ DestroyEGLSurface();
+
+ auto size = GetBufferSize();
+ GLint maxTextureSize = 0;
+ GetGLContext()->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE,
+ (GLint*)&maxTextureSize);
+
+ // When window size is too big, hardware buffer allocation could fail.
+ if (maxTextureSize < size.width || maxTextureSize < size.height) {
+ gfxCriticalNote << "Too big ANativeWindow size(" << size.width << ", "
+ << size.height << ") MaxTextureSize " << maxTextureSize;
+ return false;
+ }
+
+ mEGLSurface = CreateEGLSurface();
+ if (mEGLSurface == EGL_NO_SURFACE) {
+ // Often when we fail to create an EGL surface it is because the
+ // Java Surface we have been provided is invalid. Therefore the on
+ // the first occurence we don't raise a WebRenderError and instead
+ // just return failure. This allows the widget a chance to request
+ // a new Java Surface. On subsequent failures, raising the
+ // WebRenderError will result in the compositor being recreated,
+ // falling back through webrender configurations, and eventually
+ // crashing if we still do not succeed.
+ if (!mHandlingNewSurfaceError) {
+ mHandlingNewSurfaceError = true;
+ } else {
+ RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE);
+ }
+ return false;
+ }
+ mHandlingNewSurfaceError = false;
+
+ gl::GLContextEGL::Cast(GetGLContext())->SetEGLSurfaceOverride(mEGLSurface);
+ mCompositor->SetDestinationSurfaceSize(size.ToUnknownSize());
+#elif defined(MOZ_WIDGET_GTK)
+ bool resumed = mCompositor->Resume();
+ if (!resumed) {
+ RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE);
+ return false;
+ }
+#endif
+ return true;
+}
+
+bool RenderCompositorOGLSWGL::IsPaused() {
+#ifdef MOZ_WIDGET_ANDROID
+ return mEGLSurface == EGL_NO_SURFACE;
+#endif
+ return false;
+}
+
+LayoutDeviceIntSize RenderCompositorOGLSWGL::GetBufferSize() {
+ return mWidget->GetClientSize();
+}
+
+UniquePtr<RenderCompositorLayersSWGL::Tile>
+RenderCompositorOGLSWGL::DoCreateTile(Surface* aSurface) {
+ auto source = MakeRefPtr<TextureImageTextureSourceOGL>(
+ mCompositor->AsCompositorOGL(), layers::TextureFlags::NO_FLAGS);
+
+ return MakeUnique<TileOGL>(std::move(source), aSurface->TileSize());
+}
+
+bool RenderCompositorOGLSWGL::MaybeReadback(
+ const gfx::IntSize& aReadbackSize, const wr::ImageFormat& aReadbackFormat,
+ const Range<uint8_t>& aReadbackBuffer, bool* aNeedsYFlip) {
+#ifdef MOZ_WIDGET_ANDROID
+ MOZ_ASSERT(aReadbackFormat == wr::ImageFormat::RGBA8);
+ const GLenum format = LOCAL_GL_RGBA;
+#else
+ MOZ_ASSERT(aReadbackFormat == wr::ImageFormat::BGRA8);
+ const GLenum format = LOCAL_GL_BGRA;
+#endif
+
+ GetGLContext()->fReadPixels(0, 0, aReadbackSize.width, aReadbackSize.height,
+ format, LOCAL_GL_UNSIGNED_BYTE,
+ &aReadbackBuffer[0]);
+
+ if (aNeedsYFlip) {
+ *aNeedsYFlip = true;
+ }
+
+ return true;
+}
+
+// This is a DataSourceSurface that represents a 0-based PBO for GLTextureImage.
+class PBOUnpackSurface : public gfx::DataSourceSurface {
+ public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PBOUnpackSurface, override)
+
+ explicit PBOUnpackSurface(const gfx::IntSize& aSize) : mSize(aSize) {}
+
+ uint8_t* GetData() override { return nullptr; }
+ int32_t Stride() override { return mSize.width * sizeof(uint32_t); }
+ gfx::SurfaceType GetType() const override {
+ return gfx::SurfaceType::DATA_ALIGNED;
+ }
+ gfx::IntSize GetSize() const override { return mSize; }
+ gfx::SurfaceFormat GetFormat() const override {
+ return gfx::SurfaceFormat::B8G8R8A8;
+ }
+
+ // PBO offsets need to start from a 0 address, but DataSourceSurface::Map
+ // checks for failure by comparing the address against nullptr. Override Map
+ // to work around this.
+ bool Map(MapType, MappedSurface* aMappedSurface) override {
+ aMappedSurface->mData = GetData();
+ aMappedSurface->mStride = Stride();
+ return true;
+ }
+
+ void Unmap() override {}
+
+ private:
+ gfx::IntSize mSize;
+};
+
+RenderCompositorOGLSWGL::TileOGL::TileOGL(
+ RefPtr<layers::TextureImageTextureSourceOGL>&& aTexture,
+ const gfx::IntSize& aSize)
+ : mTexture(aTexture) {
+ auto* gl = mTexture->gl();
+ if (gl && gl->HasPBOState() && gl->MakeCurrent()) {
+ mSurface = new PBOUnpackSurface(aSize);
+ // Create a PBO large enough to encompass any valid rects within the tile.
+ gl->fGenBuffers(1, &mPBO);
+ gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, mPBO);
+ gl->fBufferData(LOCAL_GL_PIXEL_UNPACK_BUFFER,
+ mSurface->Stride() * aSize.height, nullptr,
+ LOCAL_GL_DYNAMIC_DRAW);
+ gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0);
+ } else {
+ // Couldn't allocate a PBO, so just use a memory surface instead.
+ mSurface = gfx::Factory::CreateDataSourceSurface(
+ aSize, gfx::SurfaceFormat::B8G8R8A8);
+ }
+}
+
+RenderCompositorOGLSWGL::TileOGL::~TileOGL() {
+ if (mPBO) {
+ auto* gl = mTexture->gl();
+ if (gl && gl->MakeCurrent()) {
+ gl->fDeleteBuffers(1, &mPBO);
+ mPBO = 0;
+ }
+ }
+}
+
+layers::DataTextureSource*
+RenderCompositorOGLSWGL::TileOGL::GetTextureSource() {
+ return mTexture.get();
+}
+
+bool RenderCompositorOGLSWGL::TileOGL::Map(wr::DeviceIntRect aDirtyRect,
+ wr::DeviceIntRect aValidRect,
+ void** aData, int32_t* aStride) {
+ if (mPBO) {
+ auto* gl = mTexture->gl();
+ if (!gl) {
+ return false;
+ }
+ // Map the PBO, but only within the range of the buffer that spans from the
+ // linear start offset to the linear end offset. Since we don't care about
+ // the previous contents of the buffer, we can just tell OpenGL to
+ // invalidate the entire buffer, even though we're only mapping a sub-range.
+ gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, mPBO);
+ size_t stride = mSurface->Stride();
+ size_t offset =
+ stride * aValidRect.min.y + aValidRect.min.x * sizeof(uint32_t);
+ size_t length = stride * (aValidRect.height() - 1) +
+ (aValidRect.width()) * sizeof(uint32_t);
+ void* data = gl->fMapBufferRange(
+ LOCAL_GL_PIXEL_UNPACK_BUFFER, offset, length,
+ LOCAL_GL_MAP_WRITE_BIT | LOCAL_GL_MAP_INVALIDATE_BUFFER_BIT);
+ gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0);
+ if (!data) {
+ return false;
+ }
+ *aData = data;
+ *aStride = stride;
+ } else {
+ // No PBO is available, so just directly write to the memory surface.
+ gfx::DataSourceSurface::MappedSurface map;
+ if (!mSurface->Map(gfx::DataSourceSurface::READ_WRITE, &map)) {
+ return false;
+ }
+ // Verify that we're not somehow using a PBOUnpackSurface.
+ MOZ_ASSERT(map.mData != nullptr);
+ // glTex(Sub)Image on ES doesn't support arbitrary strides without
+ // the EXT_unpack_subimage extension. To avoid needing to make a
+ // copy of the data we'll always draw it with stride = bpp*width
+ // unless we're uploading the entire texture.
+ if (!mTexture->IsValid()) {
+ // If we don't have a texture we need to position our
+ // data in the correct spot because we're going to upload
+ // the entire surface
+ *aData = map.mData + aValidRect.min.y * map.mStride +
+ aValidRect.min.x * sizeof(uint32_t);
+
+ *aStride = map.mStride;
+ mSubSurface = nullptr;
+ } else {
+ // Otherwise, we can just use the top left as a scratch space
+ *aData = map.mData;
+ *aStride = aDirtyRect.width() * BytesPerPixel(mSurface->GetFormat());
+ mSubSurface = Factory::CreateWrappingDataSourceSurface(
+ (uint8_t*)*aData, *aStride,
+ IntSize(aDirtyRect.width(), aDirtyRect.height()),
+ mSurface->GetFormat());
+ }
+ }
+ return true;
+}
+
+void RenderCompositorOGLSWGL::TileOGL::Unmap(const gfx::IntRect& aDirtyRect) {
+ nsIntRegion dirty(aDirtyRect);
+ if (mPBO) {
+ // If there is a PBO, it must be unmapped before it can be sourced from.
+ // Leave the PBO bound before the call to Update so that the texture uploads
+ // will source from it.
+ auto* gl = mTexture->gl();
+ if (!gl) {
+ return;
+ }
+ gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, mPBO);
+ gl->fUnmapBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER);
+ mTexture->Update(mSurface, &dirty);
+ gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0);
+ } else {
+ if (mSubSurface) {
+ mSurface->Unmap();
+ // Our subsurface has a stride = aDirtyRect.width
+ // We use a negative offset to move it to match
+ // the dirty rect's top-left. These two offsets
+ // will cancel each other out by the time we reach
+ // TexSubImage.
+ IntPoint srcOffset = {0, 0};
+ IntPoint dstOffset = aDirtyRect.TopLeft();
+ // adjust the dirty region to be relative to the dstOffset
+ dirty.MoveBy(-dstOffset);
+ mTexture->Update(mSubSurface, &dirty, &srcOffset, &dstOffset);
+ mSubSurface = nullptr;
+ } else {
+ mSurface->Unmap();
+ mTexture->Update(mSurface, &dirty);
+ }
+ }
+}
+
+} // namespace wr
+} // namespace mozilla
diff --git a/gfx/webrender_bindings/RenderCompositorOGLSWGL.h b/gfx/webrender_bindings/RenderCompositorOGLSWGL.h
new file mode 100644
index 0000000000..7752574311
--- /dev/null
+++ b/gfx/webrender_bindings/RenderCompositorOGLSWGL.h
@@ -0,0 +1,104 @@
+/* -*- 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_RENDERCOMPOSITOR_OGL_SWGL_H
+#define MOZILLA_GFX_RENDERCOMPOSITOR_OGL_SWGL_H
+
+#include "mozilla/layers/Compositor.h"
+#include "mozilla/webrender/RenderCompositorLayersSWGL.h"
+
+namespace mozilla {
+
+namespace layers {
+class TextureImageTextureSourceOGL;
+}
+
+namespace wr {
+
+class RenderCompositorOGLSWGL : public RenderCompositorLayersSWGL {
+ public:
+ static UniquePtr<RenderCompositor> Create(
+ const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError);
+
+ RenderCompositorOGLSWGL(layers::Compositor* aCompositor,
+ const RefPtr<widget::CompositorWidget>& aWidget,
+ void* aContext);
+ virtual ~RenderCompositorOGLSWGL();
+
+ gl::GLContext* GetGLContext();
+
+ bool MakeCurrent() override;
+
+ bool BeginFrame() override;
+ RenderedFrameId EndFrame(const nsTArray<DeviceIntRect>& aDirtyRects) override;
+
+ void GetCompositorCapabilities(CompositorCapabilities* aCaps) override;
+
+ // Returns true for requesting rendering during readback.
+ // RenderCompositorOGLSWGL::MaybeReadback() requests rendering.
+ // This value is not used by WebRender, since native compositor API is used
+ // for sw-wr.
+ bool UsePartialPresent() override { return true; }
+ bool RequestFullRender() override;
+
+ void Pause() override;
+ bool Resume() override;
+ bool IsPaused() override;
+
+ LayoutDeviceIntSize GetBufferSize() override;
+
+ layers::WebRenderCompositor CompositorType() const override {
+ return layers::WebRenderCompositor::OPENGL;
+ }
+
+ bool MaybeReadback(const gfx::IntSize& aReadbackSize,
+ const wr::ImageFormat& aReadbackFormat,
+ const Range<uint8_t>& aReadbackBuffer,
+ bool* aNeedsYFlip) override;
+
+ private:
+ void HandleExternalImage(RenderTextureHost* aExternalImage,
+ FrameSurface& aFrameSurface) override;
+ UniquePtr<RenderCompositorLayersSWGL::Tile> DoCreateTile(
+ Surface* aSurface) override;
+
+ EGLSurface CreateEGLSurface();
+ void DestroyEGLSurface();
+
+ EGLSurface mEGLSurface = EGL_NO_SURFACE;
+ bool mFullRender = false;
+
+#ifdef MOZ_WIDGET_ANDROID
+ // Whether we are in the process of handling a NEW_SURFACE error. On Android
+ // this is used to allow the widget an opportunity to recover from the first
+ // instance, before raising a WebRenderError on subsequent occurences.
+ bool mHandlingNewSurfaceError = false;
+#endif
+
+ class TileOGL : public RenderCompositorLayersSWGL::Tile {
+ public:
+ TileOGL(RefPtr<layers::TextureImageTextureSourceOGL>&& aTexture,
+ const gfx::IntSize& aSize);
+ virtual ~TileOGL();
+
+ bool Map(wr::DeviceIntRect aDirtyRect, wr::DeviceIntRect aValidRect,
+ void** aData, int32_t* aStride) override;
+ void Unmap(const gfx::IntRect& aDirtyRect) override;
+ layers::DataTextureSource* GetTextureSource() override;
+ bool IsValid() override { return true; }
+
+ private:
+ RefPtr<layers::TextureImageTextureSourceOGL> mTexture;
+ RefPtr<gfx::DataSourceSurface> mSurface;
+ RefPtr<gfx::DataSourceSurface> mSubSurface;
+ GLuint mPBO = 0;
+ };
+};
+
+} // namespace wr
+} // namespace mozilla
+
+#endif
diff --git a/gfx/webrender_bindings/RenderCompositorRecordedFrame.h b/gfx/webrender_bindings/RenderCompositorRecordedFrame.h
new file mode 100644
index 0000000000..f7a516107b
--- /dev/null
+++ b/gfx/webrender_bindings/RenderCompositorRecordedFrame.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_GFX_RENDERCOMPOSITOR_RECORDEDFRAME_H
+#define MOZILLA_GFX_RENDERCOMPOSITOR_RECORDEDFRAME_H
+
+#include "mozilla/layers/CompositionRecorder.h"
+
+namespace mozilla {
+
+namespace wr {
+
+class RenderCompositorRecordedFrame final : public layers::RecordedFrame {
+ public:
+ RenderCompositorRecordedFrame(
+ const TimeStamp& aTimeStamp,
+ RefPtr<layers::profiler_screenshots::AsyncReadbackBuffer>&& aBuffer)
+ : RecordedFrame(aTimeStamp), mBuffer(aBuffer) {}
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetSourceSurface() override {
+ if (mSurface) {
+ return do_AddRef(mSurface);
+ }
+
+ gfx::IntSize size = mBuffer->Size();
+ mSurface = gfx::Factory::CreateDataSourceSurface(
+ size, gfx::SurfaceFormat::B8G8R8A8,
+ /* aZero = */ false);
+
+ if (!mBuffer->MapAndCopyInto(mSurface, size)) {
+ mSurface = nullptr;
+ return nullptr;
+ }
+
+ return do_AddRef(mSurface);
+ }
+
+ private:
+ RefPtr<layers::profiler_screenshots::AsyncReadbackBuffer> mBuffer;
+ RefPtr<gfx::DataSourceSurface> mSurface;
+};
+
+} // namespace wr
+} // namespace mozilla
+
+#endif
diff --git a/gfx/webrender_bindings/RenderCompositorSWGL.cpp b/gfx/webrender_bindings/RenderCompositorSWGL.cpp
new file mode 100644
index 0000000000..1eec99bb3a
--- /dev/null
+++ b/gfx/webrender_bindings/RenderCompositorSWGL.cpp
@@ -0,0 +1,315 @@
+/* -*- 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 "RenderCompositorSWGL.h"
+
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/widget/CompositorWidget.h"
+
+#ifdef MOZ_WIDGET_GTK
+# include "mozilla/WidgetUtilsGtk.h"
+#endif
+
+namespace mozilla {
+using namespace gfx;
+
+namespace wr {
+
+extern LazyLogModule gRenderThreadLog;
+#define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
+
+/* static */
+UniquePtr<RenderCompositor> RenderCompositorSWGL::Create(
+ const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError) {
+ void* ctx = wr_swgl_create_context();
+ if (!ctx) {
+ gfxCriticalNote << "Failed SWGL context creation for WebRender";
+ return nullptr;
+ }
+ return MakeUnique<RenderCompositorSWGL>(aWidget, ctx);
+}
+
+RenderCompositorSWGL::RenderCompositorSWGL(
+ const RefPtr<widget::CompositorWidget>& aWidget, void* aContext)
+ : RenderCompositor(aWidget), mContext(aContext) {
+ MOZ_ASSERT(mContext);
+ LOG("RenderCompositorSWGL::RenderCompositorSWGL()");
+}
+
+RenderCompositorSWGL::~RenderCompositorSWGL() {
+ LOG("RenderCompositorSWGL::~RenderCompositorSWGL()");
+
+ wr_swgl_destroy_context(mContext);
+}
+
+void RenderCompositorSWGL::ClearMappedBuffer() {
+ mMappedData = nullptr;
+ mMappedStride = 0;
+ mDT = nullptr;
+}
+
+bool RenderCompositorSWGL::MakeCurrent() {
+ wr_swgl_make_current(mContext);
+ return true;
+}
+
+bool RenderCompositorSWGL::BeginFrame() {
+ mRenderWidgetSize = Some(mWidget->GetClientSize());
+#ifdef MOZ_WAYLAND
+ if (mLastRenderWidgetSize != mRenderWidgetSize.value()) {
+ mLastRenderWidgetSize = mRenderWidgetSize.value();
+ mRequestFullRender = true;
+ }
+#endif
+ // Set up a temporary region representing the entire window surface in case a
+ // dirty region is not supplied.
+ ClearMappedBuffer();
+ mDirtyRegion = LayoutDeviceIntRect(LayoutDeviceIntPoint(), GetBufferSize());
+ wr_swgl_make_current(mContext);
+ return true;
+}
+
+bool RenderCompositorSWGL::AllocateMappedBuffer(
+ const wr::DeviceIntRect* aOpaqueRects, size_t aNumOpaqueRects) {
+ // Request a new draw target to use from the widget...
+ MOZ_ASSERT(!mDT);
+ layers::BufferMode bufferMode = layers::BufferMode::BUFFERED;
+ mDT = mWidget->StartRemoteDrawingInRegion(mDirtyRegion, &bufferMode);
+ if (!mDT) {
+ gfxCriticalNoteOnce
+ << "RenderCompositorSWGL failed mapping default framebuffer, no dt";
+ return false;
+ }
+ // Attempt to lock the underlying buffer directly from the draw target.
+ // Verify that the size at least matches what the widget claims and that
+ // the format is BGRA8 as SWGL requires.
+ uint8_t* data = nullptr;
+ gfx::IntSize size;
+ int32_t stride = 0;
+ gfx::SurfaceFormat format = gfx::SurfaceFormat::UNKNOWN;
+ if (bufferMode != layers::BufferMode::BUFFERED && !mSurface &&
+ mDT->LockBits(&data, &size, &stride, &format) &&
+ (format != gfx::SurfaceFormat::B8G8R8A8 &&
+ format != gfx::SurfaceFormat::B8G8R8X8)) {
+ // We tried to lock the DT and it succeeded, but the size or format
+ // of the data is not compatible, so just release it and fall back below...
+ mDT->ReleaseBits(data);
+ data = nullptr;
+ }
+ LayoutDeviceIntRect bounds = mDirtyRegion.GetBounds();
+ // If locking succeeded above, just use that.
+ if (data) {
+ mMappedData = data;
+ mMappedStride = stride;
+ // Disambiguate whether the widget's draw target has its origin at zero or
+ // if it is offset to the dirty region origin. The DT might either enclose
+ // only the region itself, the region including the origin, or the entire
+ // widget. Thus, if the DT doesn't only enclose the region, we assume it
+ // contains the origin.
+ if (size != bounds.Size().ToUnknownSize()) {
+ // Update the bounds to include zero if the origin is at zero.
+ bounds.ExpandToEnclose(LayoutDeviceIntPoint(0, 0));
+ }
+ // Sometimes we end up racing on the widget size, and it can shrink between
+ // BeginFrame and StartCompositing. We calculated our dirty region based on
+ // the previous widget size, so we need to clamp the bounds here to ensure
+ // we remain within the buffer.
+ bounds.IntersectRect(
+ bounds,
+ LayoutDeviceIntRect(bounds.TopLeft(),
+ LayoutDeviceIntSize(size.width, size.height)));
+ } else {
+ // If we couldn't lock the DT above, then allocate a data surface and map
+ // that for usage with SWGL.
+ size = bounds.Size().ToUnknownSize();
+ if (!mSurface || mSurface->GetSize() != size) {
+ mSurface = gfx::Factory::CreateDataSourceSurface(
+ size, gfx::SurfaceFormat::B8G8R8A8);
+ }
+ gfx::DataSourceSurface::MappedSurface map = {nullptr, 0};
+ if (!mSurface || !mSurface->Map(gfx::DataSourceSurface::READ_WRITE, &map)) {
+ // We failed mapping the data surface, so need to cancel the frame.
+ mWidget->EndRemoteDrawingInRegion(mDT, mDirtyRegion);
+ ClearMappedBuffer();
+ gfxCriticalNoteOnce
+ << "RenderCompositorSWGL failed mapping default framebuffer, no surf";
+ return false;
+ }
+ mMappedData = map.mData;
+ mMappedStride = map.mStride;
+ }
+ MOZ_ASSERT(mMappedData != nullptr && mMappedStride > 0);
+ wr_swgl_init_default_framebuffer(mContext, bounds.x, bounds.y, bounds.width,
+ bounds.height, mMappedStride, mMappedData);
+
+ LayoutDeviceIntRegion opaque;
+ for (size_t i = 0; i < aNumOpaqueRects; i++) {
+ const auto& rect = aOpaqueRects[i];
+ opaque.OrWith(LayoutDeviceIntRect(rect.min.x, rect.min.y, rect.width(),
+ rect.height()));
+ }
+
+ LayoutDeviceIntRegion clear = mWidget->GetTransparentRegion();
+ clear.AndWith(mDirtyRegion);
+ clear.SubOut(opaque);
+ for (auto iter = clear.RectIter(); !iter.Done(); iter.Next()) {
+ const auto& rect = iter.Get();
+ wr_swgl_clear_color_rect(mContext, 0, rect.x, rect.y, rect.width,
+ rect.height, 0, 0, 0, 0);
+ }
+
+ return true;
+}
+
+void RenderCompositorSWGL::StartCompositing(
+ wr::ColorF aClearColor, const wr::DeviceIntRect* aDirtyRects,
+ size_t aNumDirtyRects, const wr::DeviceIntRect* aOpaqueRects,
+ size_t aNumOpaqueRects) {
+ if (mDT) {
+ // Cancel any existing buffers that might accidentally be left from updates
+ CommitMappedBuffer(false);
+ // Reset the region to the widget bounds
+ mDirtyRegion = LayoutDeviceIntRect(LayoutDeviceIntPoint(), GetBufferSize());
+ }
+ if (aNumDirtyRects) {
+ // Install the dirty rects into the bounds of the existing region
+ auto bounds = mDirtyRegion.GetBounds();
+ mDirtyRegion.SetEmpty();
+ for (size_t i = 0; i < aNumDirtyRects; i++) {
+ const auto& rect = aDirtyRects[i];
+ mDirtyRegion.OrWith(LayoutDeviceIntRect(rect.min.x, rect.min.y,
+ rect.width(), rect.height()));
+ }
+ // Ensure the region lies within the widget bounds
+ mDirtyRegion.AndWith(bounds);
+ }
+ // Now that the dirty rects have been supplied and the composition region
+ // is known, allocate and install a framebuffer encompassing the composition
+ // region.
+ if (mDirtyRegion.IsEmpty() ||
+ !AllocateMappedBuffer(aOpaqueRects, aNumOpaqueRects)) {
+ // If allocation of the mapped default framebuffer failed, then just install
+ // a temporary framebuffer (with a minimum size of 2x2) so compositing can
+ // still proceed.
+ auto bounds = mDirtyRegion.GetBounds();
+ bounds.width = std::max(bounds.width, 2);
+ bounds.height = std::max(bounds.height, 2);
+ wr_swgl_init_default_framebuffer(mContext, bounds.x, bounds.y, bounds.width,
+ bounds.height, 0, nullptr);
+ }
+}
+
+void RenderCompositorSWGL::CommitMappedBuffer(bool aDirty) {
+ if (!mDT) {
+ mDirtyRegion.SetEmpty();
+ return;
+ }
+ // Force any delayed clears to resolve.
+ if (aDirty) {
+ wr_swgl_resolve_framebuffer(mContext, 0);
+ }
+ // Clear out the old framebuffer in case something tries to access it after
+ // the frame.
+ wr_swgl_init_default_framebuffer(mContext, 0, 0, 0, 0, 0, nullptr);
+ // If we have a draw target at this point, mapping must have succeeded.
+ MOZ_ASSERT(mMappedData != nullptr);
+ if (mSurface) {
+ // If we're using a data surface, unmap it and draw it to the DT if there
+ // are any supplied dirty rects.
+ mSurface->Unmap();
+ if (aDirty) {
+ // The temporary source surface is always a partial region of the widget
+ // that is offset from the origin to the actual bounds of the dirty
+ // region. The destination DT may also be an offset partial region, but we
+ // must check to see if its size matches the region bounds to verify this.
+ LayoutDeviceIntRect bounds = mDirtyRegion.GetBounds();
+ gfx::IntPoint srcOffset = bounds.TopLeft().ToUnknownPoint();
+ gfx::IntPoint dstOffset = mDT->GetSize() == bounds.Size().ToUnknownSize()
+ ? srcOffset
+ : gfx::IntPoint(0, 0);
+ for (auto iter = mDirtyRegion.RectIter(); !iter.Done(); iter.Next()) {
+ gfx::IntRect dirtyRect = iter.Get().ToUnknownRect();
+ mDT->CopySurface(mSurface, dirtyRect - srcOffset,
+ dirtyRect.TopLeft() - dstOffset);
+ }
+ }
+ } else {
+ // Otherwise, we had locked the DT directly. Just release the data.
+ mDT->ReleaseBits(mMappedData);
+ }
+ mDT->Flush();
+
+ // Done with the DT. Hand it back to the widget and clear out any trace of it.
+ mWidget->EndRemoteDrawingInRegion(mDT, mDirtyRegion);
+ mDirtyRegion.SetEmpty();
+ ClearMappedBuffer();
+}
+
+void RenderCompositorSWGL::CancelFrame() {
+ CommitMappedBuffer(false);
+ mRenderWidgetSize = Nothing();
+}
+
+RenderedFrameId RenderCompositorSWGL::EndFrame(
+ const nsTArray<DeviceIntRect>& aDirtyRects) {
+ // Dirty rects have already been set inside StartCompositing. We need to keep
+ // those dirty rects exactly the same here so we supply the same exact region
+ // to EndRemoteDrawingInRegion as for StartRemoteDrawingInRegion.
+ RenderedFrameId frameId = GetNextRenderFrameId();
+ CommitMappedBuffer();
+ mRenderWidgetSize = Nothing();
+ return frameId;
+}
+
+bool RenderCompositorSWGL::RequestFullRender() {
+#ifdef MOZ_WIDGET_ANDROID
+ // XXX Add partial present support.
+ return true;
+#endif
+#ifdef MOZ_WAYLAND
+ // We're requested to do full render after Resume() on Wayland.
+ if (mRequestFullRender) {
+ mRequestFullRender = false;
+ return true;
+ }
+#endif
+ return false;
+}
+
+void RenderCompositorSWGL::Pause() {}
+
+bool RenderCompositorSWGL::Resume() {
+#ifdef MOZ_WAYLAND
+ mRequestFullRender = true;
+#endif
+ return true;
+}
+
+LayoutDeviceIntSize RenderCompositorSWGL::GetBufferSize() {
+ // If we're between BeginFrame() and EndFrame()/CancelFrame() calls
+ // return recent rendering size instead of actual underlying widget
+ // size. It prevents possible rendering artifacts if widget size was changed.
+ return mRenderWidgetSize ? mRenderWidgetSize.value()
+ : mWidget->GetClientSize();
+}
+
+void RenderCompositorSWGL::GetCompositorCapabilities(
+ CompositorCapabilities* aCaps) {
+ // Always support a single update rect for SwCompositor
+ aCaps->max_update_rects = 1;
+
+ // On uncomposited desktops such as X11 without compositor or Window 7 with
+ // Aero disabled we need to force a full redraw when the window contents may
+ // be damaged.
+#ifdef MOZ_WIDGET_GTK
+ aCaps->redraw_on_invalidation = widget::GdkIsX11Display();
+#else
+ aCaps->redraw_on_invalidation = true;
+#endif
+}
+
+} // namespace wr
+} // namespace mozilla
diff --git a/gfx/webrender_bindings/RenderCompositorSWGL.h b/gfx/webrender_bindings/RenderCompositorSWGL.h
new file mode 100644
index 0000000000..2b3ce35454
--- /dev/null
+++ b/gfx/webrender_bindings/RenderCompositorSWGL.h
@@ -0,0 +1,90 @@
+/* -*- 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_RENDERCOMPOSITOR_SWGL_H
+#define MOZILLA_GFX_RENDERCOMPOSITOR_SWGL_H
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/webrender/RenderCompositor.h"
+
+namespace mozilla {
+
+namespace wr {
+
+class RenderCompositorSWGL : public RenderCompositor {
+ public:
+ static UniquePtr<RenderCompositor> Create(
+ const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError);
+
+ RenderCompositorSWGL(const RefPtr<widget::CompositorWidget>& aWidget,
+ void* aContext);
+ virtual ~RenderCompositorSWGL();
+
+ void* swgl() const override { return mContext; }
+
+ bool MakeCurrent() override;
+
+ bool BeginFrame() override;
+ void CancelFrame() override;
+ RenderedFrameId EndFrame(const nsTArray<DeviceIntRect>& aDirtyRects) final;
+
+ void StartCompositing(wr::ColorF aClearColor,
+ const wr::DeviceIntRect* aDirtyRects,
+ size_t aNumDirtyRects,
+ const wr::DeviceIntRect* aOpaqueRects,
+ size_t aNumOpaqueRects) override;
+
+ bool UsePartialPresent() override { return true; }
+ bool RequestFullRender() override;
+
+ void Pause() override;
+ bool Resume() override;
+
+ layers::WebRenderBackend BackendType() const override {
+ return layers::WebRenderBackend::SOFTWARE;
+ }
+ layers::WebRenderCompositor CompositorType() const override {
+ return layers::WebRenderCompositor::SOFTWARE;
+ }
+
+ bool SurfaceOriginIsTopLeft() override { return true; }
+
+ LayoutDeviceIntSize GetBufferSize() override;
+
+ bool SupportsExternalBufferTextures() const override { return true; }
+
+ // Interface for wr::Compositor
+ void GetCompositorCapabilities(CompositorCapabilities* aCaps) override;
+
+ private:
+ void* mContext = nullptr;
+ RefPtr<gfx::DrawTarget> mDT;
+ LayoutDeviceIntRegion mDirtyRegion;
+ // Keep consistent buffer size between BeginFrame and EndFrame/CancelFrame
+ // calls to make sure we don't change buffer size during rendering.
+ Maybe<LayoutDeviceIntSize> mRenderWidgetSize;
+ RefPtr<gfx::DataSourceSurface> mSurface;
+ uint8_t* mMappedData = nullptr;
+ int32_t mMappedStride = 0;
+#ifdef MOZ_WAYLAND
+ // On Wayland we need to request full render if widget size is changed
+ // because SW rendering backend reallocates and clears target surface.
+ LayoutDeviceIntSize mLastRenderWidgetSize;
+ bool mRequestFullRender = false;
+#endif
+
+ void ClearMappedBuffer();
+
+ bool AllocateMappedBuffer(const wr::DeviceIntRect* aOpaqueRects,
+ size_t aNumOpaqueRects);
+
+ void CommitMappedBuffer(bool aDirty = true);
+};
+
+} // namespace wr
+} // namespace mozilla
+
+#endif
diff --git a/gfx/webrender_bindings/RenderD3D11TextureHost.cpp b/gfx/webrender_bindings/RenderD3D11TextureHost.cpp
new file mode 100644
index 0000000000..232f8572cb
--- /dev/null
+++ b/gfx/webrender_bindings/RenderD3D11TextureHost.cpp
@@ -0,0 +1,705 @@
+/* -*- 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 "RenderD3D11TextureHost.h"
+
+#include "GLContextEGL.h"
+#include "GLLibraryEGL.h"
+#include "RenderThread.h"
+#include "RenderCompositor.h"
+#include "RenderCompositorD3D11SWGL.h"
+#include "ScopedGLHelpers.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/layers/TextureD3D11.h"
+
+namespace mozilla {
+namespace wr {
+
+RenderDXGITextureHost::RenderDXGITextureHost(
+ WindowsHandle aHandle,
+ Maybe<layers::GpuProcessTextureId>& aGpuProcessTextureId,
+ uint32_t aArrayIndex, gfx::SurfaceFormat aFormat,
+ gfx::ColorSpace2 aColorSpace, gfx::ColorRange aColorRange,
+ gfx::IntSize aSize)
+ : mHandle(aHandle),
+ mGpuProcessTextureId(aGpuProcessTextureId),
+ mArrayIndex(aArrayIndex),
+ mSurface(0),
+ mStream(0),
+ mTextureHandle{0},
+ mFormat(aFormat),
+ mColorSpace(aColorSpace),
+ mColorRange(aColorRange),
+ mSize(aSize),
+ mLocked(false) {
+ MOZ_COUNT_CTOR_INHERITED(RenderDXGITextureHost, RenderTextureHost);
+ MOZ_ASSERT((mFormat != gfx::SurfaceFormat::NV12 &&
+ mFormat != gfx::SurfaceFormat::P010 &&
+ mFormat != gfx::SurfaceFormat::P016) ||
+ (mSize.width % 2 == 0 && mSize.height % 2 == 0));
+ MOZ_ASSERT((aHandle && aGpuProcessTextureId.isNothing()) ||
+ (!aHandle && aGpuProcessTextureId.isSome()));
+}
+
+RenderDXGITextureHost::~RenderDXGITextureHost() {
+ MOZ_COUNT_DTOR_INHERITED(RenderDXGITextureHost, RenderTextureHost);
+ DeleteTextureHandle();
+}
+
+ID3D11Texture2D* RenderDXGITextureHost::GetD3D11Texture2DWithGL() {
+ if (mTexture) {
+ return mTexture;
+ }
+
+ if (!mGL) {
+ // SingletonGL is always used on Windows with ANGLE.
+ mGL = RenderThread::Get()->SingletonGL();
+ }
+
+ if (!EnsureD3D11Texture2DWithGL()) {
+ return nullptr;
+ }
+
+ return mTexture;
+}
+
+size_t RenderDXGITextureHost::GetPlaneCount() const {
+ if (mFormat == gfx::SurfaceFormat::NV12 ||
+ mFormat == gfx::SurfaceFormat::P010 ||
+ mFormat == gfx::SurfaceFormat::P016) {
+ return 2;
+ }
+ return 1;
+}
+
+template <typename T>
+static bool MapTexture(T* aHost, RenderCompositor* aCompositor,
+ RefPtr<ID3D11Texture2D>& aTexture,
+ RefPtr<ID3D11DeviceContext>& aDeviceContext,
+ RefPtr<ID3D11Texture2D>& aCpuTexture,
+ D3D11_MAPPED_SUBRESOURCE& aMappedSubresource) {
+ if (!aCompositor) {
+ return false;
+ }
+
+ RenderCompositorD3D11SWGL* compositor =
+ aCompositor->AsRenderCompositorD3D11SWGL();
+ if (!compositor) {
+ return false;
+ }
+
+ if (!aHost->EnsureD3D11Texture2D(compositor->GetDevice())) {
+ return false;
+ }
+
+ if (!aHost->LockInternal()) {
+ return false;
+ }
+
+ D3D11_TEXTURE2D_DESC textureDesc = {0};
+ aTexture->GetDesc(&textureDesc);
+
+ compositor->GetDevice()->GetImmediateContext(getter_AddRefs(aDeviceContext));
+
+ textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ textureDesc.Usage = D3D11_USAGE_STAGING;
+ textureDesc.BindFlags = 0;
+ textureDesc.MiscFlags = 0;
+ textureDesc.MipLevels = 1;
+ HRESULT hr = compositor->GetDevice()->CreateTexture2D(
+ &textureDesc, nullptr, getter_AddRefs(aCpuTexture));
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ aDeviceContext->CopyResource(aCpuTexture, aTexture);
+ aHost->Unlock();
+
+ hr = aDeviceContext->Map(aCpuTexture, 0, D3D11_MAP_READ, 0,
+ &aMappedSubresource);
+ return SUCCEEDED(hr);
+}
+
+bool RenderDXGITextureHost::MapPlane(RenderCompositor* aCompositor,
+ uint8_t aChannelIndex,
+ PlaneInfo& aPlaneInfo) {
+ // TODO: We currently readback from the GPU texture into a new
+ // staging texture every time this is mapped. We might be better
+ // off retaining the mapped memory to trade performance for memory
+ // usage.
+ if (!mCpuTexture && !MapTexture(this, aCompositor, mTexture, mDeviceContext,
+ mCpuTexture, mMappedSubresource)) {
+ return false;
+ }
+
+ aPlaneInfo.mSize = GetSize(aChannelIndex);
+ aPlaneInfo.mStride = mMappedSubresource.RowPitch;
+ aPlaneInfo.mData = mMappedSubresource.pData;
+
+ // If this is the second plane, then offset the data pointer by the
+ // size of the first plane.
+ if (aChannelIndex == 1) {
+ aPlaneInfo.mData =
+ (uint8_t*)aPlaneInfo.mData + aPlaneInfo.mStride * GetSize(0).height;
+ }
+ return true;
+}
+
+void RenderDXGITextureHost::UnmapPlanes() {
+ mMappedSubresource.pData = nullptr;
+ if (mCpuTexture) {
+ mDeviceContext->Unmap(mCpuTexture, 0);
+ mCpuTexture = nullptr;
+ }
+ mDeviceContext = nullptr;
+}
+
+bool RenderDXGITextureHost::EnsureD3D11Texture2DWithGL() {
+ if (mTexture) {
+ return true;
+ }
+
+ if (mGpuProcessTextureId.isSome()) {
+ auto* textureMap = layers::GpuProcessD3D11TextureMap::Get();
+ if (textureMap) {
+ RefPtr<ID3D11Texture2D> texture;
+ mTexture = textureMap->GetTexture(mGpuProcessTextureId.ref());
+ if (mTexture) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ const auto& gle = gl::GLContextEGL::Cast(mGL);
+ const auto& egl = gle->mEgl;
+
+ // Fetch the D3D11 device.
+ EGLDeviceEXT eglDevice = nullptr;
+ egl->fQueryDisplayAttribEXT(LOCAL_EGL_DEVICE_EXT, (EGLAttrib*)&eglDevice);
+ MOZ_ASSERT(eglDevice);
+ ID3D11Device* device = nullptr;
+ egl->mLib->fQueryDeviceAttribEXT(eglDevice, LOCAL_EGL_D3D11_DEVICE_ANGLE,
+ (EGLAttrib*)&device);
+ // There's a chance this might fail if we end up on d3d9 angle for some
+ // reason.
+ if (!device) {
+ gfxCriticalNote << "RenderDXGITextureHost device is not available";
+ return false;
+ }
+
+ return EnsureD3D11Texture2D(device);
+}
+
+bool RenderDXGITextureHost::EnsureD3D11Texture2D(ID3D11Device* aDevice) {
+ if (mTexture) {
+ RefPtr<ID3D11Device> device;
+ mTexture->GetDevice(getter_AddRefs(device));
+ if (aDevice != device) {
+ gfxCriticalNote << "RenderDXGITextureHost uses obsoleted device";
+ return false;
+ }
+ return true;
+ }
+
+ // Get the D3D11 texture from shared handle.
+ HRESULT hr = aDevice->OpenSharedResource(
+ (HANDLE)mHandle, __uuidof(ID3D11Texture2D),
+ (void**)(ID3D11Texture2D**)getter_AddRefs(mTexture));
+ if (FAILED(hr)) {
+ MOZ_ASSERT(false,
+ "RenderDXGITextureHost::EnsureLockable(): Failed to open shared "
+ "texture");
+ gfxCriticalNote
+ << "RenderDXGITextureHost Failed to open shared texture, hr="
+ << gfx::hexa(hr);
+ return false;
+ }
+ MOZ_ASSERT(mTexture.get());
+ mTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mKeyedMutex));
+ return true;
+}
+
+bool RenderDXGITextureHost::EnsureLockable() {
+ if (mTextureHandle[0]) {
+ return true;
+ }
+
+ const auto& gle = gl::GLContextEGL::Cast(mGL);
+ const auto& egl = gle->mEgl;
+
+ // We use EGLStream to get the converted gl handle from d3d texture. The
+ // NV_stream_consumer_gltexture_yuv and ANGLE_stream_producer_d3d_texture
+ // could support nv12 and rgb d3d texture format.
+ if (!egl->IsExtensionSupported(
+ gl::EGLExtension::NV_stream_consumer_gltexture_yuv) ||
+ !egl->IsExtensionSupported(
+ gl::EGLExtension::ANGLE_stream_producer_d3d_texture)) {
+ gfxCriticalNote << "RenderDXGITextureHost egl extensions are not suppored";
+ return false;
+ }
+
+ // Get the D3D11 texture from shared handle.
+ if (!EnsureD3D11Texture2DWithGL()) {
+ return false;
+ }
+
+ // Create the EGLStream.
+ mStream = egl->fCreateStreamKHR(nullptr);
+ MOZ_ASSERT(mStream);
+
+ bool ok = true;
+ if (mFormat != gfx::SurfaceFormat::NV12 &&
+ mFormat != gfx::SurfaceFormat::P010 &&
+ mFormat != gfx::SurfaceFormat::P016) {
+ // The non-nv12 format.
+
+ mGL->fGenTextures(1, mTextureHandle);
+ ActivateBindAndTexParameteri(mGL, LOCAL_GL_TEXTURE0,
+ LOCAL_GL_TEXTURE_EXTERNAL_OES,
+ mTextureHandle[0]);
+ ok &=
+ bool(egl->fStreamConsumerGLTextureExternalAttribsNV(mStream, nullptr));
+ ok &= bool(egl->fCreateStreamProducerD3DTextureANGLE(mStream, nullptr));
+ } else {
+ // The nv12/p016 format.
+
+ // Setup the NV12 stream consumer/producer.
+ EGLAttrib consumerAttributes[] = {
+ LOCAL_EGL_COLOR_BUFFER_TYPE,
+ LOCAL_EGL_YUV_BUFFER_EXT,
+ LOCAL_EGL_YUV_NUMBER_OF_PLANES_EXT,
+ 2,
+ LOCAL_EGL_YUV_PLANE0_TEXTURE_UNIT_NV,
+ 0,
+ LOCAL_EGL_YUV_PLANE1_TEXTURE_UNIT_NV,
+ 1,
+ LOCAL_EGL_NONE,
+ };
+ mGL->fGenTextures(2, mTextureHandle);
+ ActivateBindAndTexParameteri(mGL, LOCAL_GL_TEXTURE0,
+ LOCAL_GL_TEXTURE_EXTERNAL_OES,
+ mTextureHandle[0]);
+ ActivateBindAndTexParameteri(mGL, LOCAL_GL_TEXTURE1,
+ LOCAL_GL_TEXTURE_EXTERNAL_OES,
+ mTextureHandle[1]);
+ ok &= bool(egl->fStreamConsumerGLTextureExternalAttribsNV(
+ mStream, consumerAttributes));
+ ok &= bool(egl->fCreateStreamProducerD3DTextureANGLE(mStream, nullptr));
+ }
+
+ const EGLAttrib frameAttributes[] = {
+ LOCAL_EGL_D3D_TEXTURE_SUBRESOURCE_ID_ANGLE,
+ static_cast<EGLAttrib>(mArrayIndex),
+ LOCAL_EGL_NONE,
+ };
+
+ // Insert the d3d texture.
+ ok &= bool(egl->fStreamPostD3DTextureANGLE(mStream, (void*)mTexture.get(),
+ frameAttributes));
+
+ if (!ok) {
+ gfxCriticalNote << "RenderDXGITextureHost init stream failed";
+ DeleteTextureHandle();
+ return false;
+ }
+
+ // Now, we could get the gl handle from the stream.
+ MOZ_ALWAYS_TRUE(egl->fStreamConsumerAcquireKHR(mStream));
+
+ return true;
+}
+
+wr::WrExternalImage RenderDXGITextureHost::Lock(uint8_t aChannelIndex,
+ gl::GLContext* aGL) {
+ if (mGL.get() != aGL) {
+ // Release the texture handle in the previous gl context.
+ DeleteTextureHandle();
+ mGL = aGL;
+ }
+
+ if (!mGL) {
+ // XXX Software WebRender is not handled yet.
+ // Software WebRender does not provide GLContext
+ gfxCriticalNoteOnce
+ << "Software WebRender is not suppored by RenderDXGITextureHost.";
+ return InvalidToWrExternalImage();
+ }
+
+ if (!EnsureLockable()) {
+ return InvalidToWrExternalImage();
+ }
+
+ if (!LockInternal()) {
+ return InvalidToWrExternalImage();
+ }
+
+ const auto uvs = GetUvCoords(GetSize(aChannelIndex));
+ return NativeTextureToWrExternalImage(GetGLHandle(aChannelIndex), uvs.first.x,
+ uvs.first.y, uvs.second.x,
+ uvs.second.y);
+}
+
+bool RenderDXGITextureHost::LockInternal() {
+ if (!mLocked) {
+ if (mKeyedMutex) {
+ HRESULT hr = mKeyedMutex->AcquireSync(0, 10000);
+ if (hr != S_OK) {
+ gfxCriticalError() << "RenderDXGITextureHost AcquireSync timeout, hr="
+ << gfx::hexa(hr);
+ return false;
+ }
+ }
+ mLocked = true;
+ }
+ return true;
+}
+
+void RenderDXGITextureHost::Unlock() {
+ if (mLocked) {
+ if (mKeyedMutex) {
+ mKeyedMutex->ReleaseSync(0);
+ }
+ mLocked = false;
+ }
+}
+
+void RenderDXGITextureHost::ClearCachedResources() {
+ DeleteTextureHandle();
+ mGL = nullptr;
+}
+
+void RenderDXGITextureHost::DeleteTextureHandle() {
+ if (mTextureHandle[0] == 0) {
+ return;
+ }
+
+ MOZ_ASSERT(mGL.get());
+ if (!mGL) {
+ return;
+ }
+
+ if (mGL->MakeCurrent()) {
+ mGL->fDeleteTextures(2, mTextureHandle);
+
+ const auto& gle = gl::GLContextEGL::Cast(mGL);
+ const auto& egl = gle->mEgl;
+ if (mSurface) {
+ egl->fDestroySurface(mSurface);
+ }
+ if (mStream) {
+ egl->fDestroyStreamKHR(mStream);
+ }
+ }
+
+ for (int i = 0; i < 2; ++i) {
+ mTextureHandle[i] = 0;
+ }
+
+ mTexture = nullptr;
+ mKeyedMutex = nullptr;
+ mSurface = 0;
+ mStream = 0;
+}
+
+GLuint RenderDXGITextureHost::GetGLHandle(uint8_t aChannelIndex) const {
+ MOZ_ASSERT(((mFormat == gfx::SurfaceFormat::NV12 ||
+ mFormat == gfx::SurfaceFormat::P010 ||
+ mFormat == gfx::SurfaceFormat::P016) &&
+ aChannelIndex < 2) ||
+ aChannelIndex < 1);
+ return mTextureHandle[aChannelIndex];
+}
+
+gfx::IntSize RenderDXGITextureHost::GetSize(uint8_t aChannelIndex) const {
+ MOZ_ASSERT(((mFormat == gfx::SurfaceFormat::NV12 ||
+ mFormat == gfx::SurfaceFormat::P010 ||
+ mFormat == gfx::SurfaceFormat::P016) &&
+ aChannelIndex < 2) ||
+ aChannelIndex < 1);
+
+ if (aChannelIndex == 0) {
+ return mSize;
+ } else {
+ // The CbCr channel size is a half of Y channel size in NV12 format.
+ return mSize / 2;
+ }
+}
+
+RenderDXGIYCbCrTextureHost::RenderDXGIYCbCrTextureHost(
+ WindowsHandle (&aHandles)[3], gfx::YUVColorSpace aYUVColorSpace,
+ gfx::ColorDepth aColorDepth, gfx::ColorRange aColorRange,
+ gfx::IntSize aSizeY, gfx::IntSize aSizeCbCr)
+ : mHandles{aHandles[0], aHandles[1], aHandles[2]},
+ mSurfaces{0},
+ mStreams{0},
+ mTextureHandles{0},
+ mYUVColorSpace(aYUVColorSpace),
+ mColorDepth(aColorDepth),
+ mColorRange(aColorRange),
+ mSizeY(aSizeY),
+ mSizeCbCr(aSizeCbCr),
+ mLocked(false) {
+ MOZ_COUNT_CTOR_INHERITED(RenderDXGIYCbCrTextureHost, RenderTextureHost);
+ // Assume the chroma planes are rounded up if the luma plane is odd sized.
+ MOZ_ASSERT((mSizeCbCr.width == mSizeY.width ||
+ mSizeCbCr.width == (mSizeY.width + 1) >> 1) &&
+ (mSizeCbCr.height == mSizeY.height ||
+ mSizeCbCr.height == (mSizeY.height + 1) >> 1));
+ MOZ_ASSERT(aHandles[0] && aHandles[1] && aHandles[2]);
+}
+
+bool RenderDXGIYCbCrTextureHost::MapPlane(RenderCompositor* aCompositor,
+ uint8_t aChannelIndex,
+ PlaneInfo& aPlaneInfo) {
+ D3D11_MAPPED_SUBRESOURCE mappedSubresource;
+ if (!MapTexture(this, aCompositor, mTextures[aChannelIndex], mDeviceContext,
+ mCpuTexture[aChannelIndex], mappedSubresource)) {
+ return false;
+ }
+
+ aPlaneInfo.mSize = GetSize(aChannelIndex);
+ aPlaneInfo.mStride = mappedSubresource.RowPitch;
+ aPlaneInfo.mData = mappedSubresource.pData;
+ return true;
+}
+
+void RenderDXGIYCbCrTextureHost::UnmapPlanes() {
+ for (uint32_t i = 0; i < 3; i++) {
+ if (mCpuTexture[i]) {
+ mDeviceContext->Unmap(mCpuTexture[i], 0);
+ mCpuTexture[i] = nullptr;
+ }
+ }
+ mDeviceContext = nullptr;
+}
+
+RenderDXGIYCbCrTextureHost::~RenderDXGIYCbCrTextureHost() {
+ MOZ_COUNT_DTOR_INHERITED(RenderDXGIYCbCrTextureHost, RenderTextureHost);
+ DeleteTextureHandle();
+}
+
+bool RenderDXGIYCbCrTextureHost::EnsureLockable() {
+ if (mTextureHandles[0]) {
+ return true;
+ }
+
+ const auto& gle = gl::GLContextEGL::Cast(mGL);
+ const auto& egl = gle->mEgl;
+
+ // The eglCreatePbufferFromClientBuffer doesn't support R8 format, so we
+ // use EGLStream to get the converted gl handle from d3d R8 texture.
+
+ if (!egl->IsExtensionSupported(
+ gl::EGLExtension::NV_stream_consumer_gltexture_yuv) ||
+ !egl->IsExtensionSupported(
+ gl::EGLExtension::ANGLE_stream_producer_d3d_texture)) {
+ gfxCriticalNote
+ << "RenderDXGIYCbCrTextureHost egl extensions are not suppored";
+ return false;
+ }
+
+ // Fetch the D3D11 device.
+ EGLDeviceEXT eglDevice = nullptr;
+ egl->fQueryDisplayAttribEXT(LOCAL_EGL_DEVICE_EXT, (EGLAttrib*)&eglDevice);
+ MOZ_ASSERT(eglDevice);
+ ID3D11Device* device = nullptr;
+ egl->mLib->fQueryDeviceAttribEXT(eglDevice, LOCAL_EGL_D3D11_DEVICE_ANGLE,
+ (EGLAttrib*)&device);
+ // There's a chance this might fail if we end up on d3d9 angle for some
+ // reason.
+ if (!device) {
+ gfxCriticalNote << "RenderDXGIYCbCrTextureHost device is not available";
+ return false;
+ }
+
+ EnsureD3D11Texture2D(device);
+
+ mGL->fGenTextures(3, mTextureHandles);
+ bool ok = true;
+ for (int i = 0; i < 3; ++i) {
+ ActivateBindAndTexParameteri(mGL, LOCAL_GL_TEXTURE0 + i,
+ LOCAL_GL_TEXTURE_EXTERNAL_OES,
+ mTextureHandles[i]);
+
+ // Create the EGLStream.
+ mStreams[i] = egl->fCreateStreamKHR(nullptr);
+ MOZ_ASSERT(mStreams[i]);
+
+ ok &= bool(
+ egl->fStreamConsumerGLTextureExternalAttribsNV(mStreams[i], nullptr));
+ ok &= bool(egl->fCreateStreamProducerD3DTextureANGLE(mStreams[i], nullptr));
+
+ // Insert the R8 texture.
+ ok &= bool(egl->fStreamPostD3DTextureANGLE(
+ mStreams[i], (void*)mTextures[i].get(), nullptr));
+
+ // Now, we could get the R8 gl handle from the stream.
+ MOZ_ALWAYS_TRUE(egl->fStreamConsumerAcquireKHR(mStreams[i]));
+ }
+
+ if (!ok) {
+ gfxCriticalNote << "RenderDXGIYCbCrTextureHost init stream failed";
+ DeleteTextureHandle();
+ return false;
+ }
+
+ return true;
+}
+
+bool RenderDXGIYCbCrTextureHost::EnsureD3D11Texture2D(ID3D11Device* aDevice) {
+ if (mTextures[0]) {
+ RefPtr<ID3D11Device> device;
+ mTextures[0]->GetDevice(getter_AddRefs(device));
+ if (aDevice != device) {
+ gfxCriticalNote << "RenderDXGIYCbCrTextureHost uses obsoleted device";
+ return false;
+ }
+ }
+
+ if (mTextureHandles[0]) {
+ return true;
+ }
+
+ for (int i = 0; i < 3; ++i) {
+ // Get the R8 D3D11 texture from shared handle.
+ HRESULT hr = aDevice->OpenSharedResource(
+ (HANDLE)mHandles[i], __uuidof(ID3D11Texture2D),
+ (void**)(ID3D11Texture2D**)getter_AddRefs(mTextures[i]));
+ if (FAILED(hr)) {
+ NS_WARNING(
+ "RenderDXGIYCbCrTextureHost::EnsureLockable(): Failed to open "
+ "shared "
+ "texture");
+ gfxCriticalNote
+ << "RenderDXGIYCbCrTextureHost Failed to open shared texture, hr="
+ << gfx::hexa(hr);
+ return false;
+ }
+ }
+
+ for (int i = 0; i < 3; ++i) {
+ mTextures[i]->QueryInterface(
+ (IDXGIKeyedMutex**)getter_AddRefs(mKeyedMutexs[i]));
+ }
+ return true;
+}
+
+bool RenderDXGIYCbCrTextureHost::LockInternal() {
+ if (!mLocked) {
+ if (mKeyedMutexs[0]) {
+ for (const auto& mutex : mKeyedMutexs) {
+ HRESULT hr = mutex->AcquireSync(0, 10000);
+ if (hr != S_OK) {
+ gfxCriticalError()
+ << "RenderDXGIYCbCrTextureHost AcquireSync timeout, hr="
+ << gfx::hexa(hr);
+ return false;
+ }
+ }
+ }
+ mLocked = true;
+ }
+ return true;
+}
+
+wr::WrExternalImage RenderDXGIYCbCrTextureHost::Lock(uint8_t aChannelIndex,
+ gl::GLContext* aGL) {
+ if (mGL.get() != aGL) {
+ // Release the texture handle in the previous gl context.
+ DeleteTextureHandle();
+ mGL = aGL;
+ }
+
+ if (!mGL) {
+ // XXX Software WebRender is not handled yet.
+ // Software WebRender does not provide GLContext
+ gfxCriticalNoteOnce << "Software WebRender is not suppored by "
+ "RenderDXGIYCbCrTextureHost.";
+ return InvalidToWrExternalImage();
+ }
+
+ if (!EnsureLockable()) {
+ return InvalidToWrExternalImage();
+ }
+
+ if (!LockInternal()) {
+ return InvalidToWrExternalImage();
+ }
+
+ const auto uvs = GetUvCoords(GetSize(aChannelIndex));
+ return NativeTextureToWrExternalImage(GetGLHandle(aChannelIndex), uvs.first.x,
+ uvs.first.y, uvs.second.x,
+ uvs.second.y);
+}
+
+void RenderDXGIYCbCrTextureHost::Unlock() {
+ if (mLocked) {
+ if (mKeyedMutexs[0]) {
+ for (const auto& mutex : mKeyedMutexs) {
+ mutex->ReleaseSync(0);
+ }
+ }
+ mLocked = false;
+ }
+}
+
+void RenderDXGIYCbCrTextureHost::ClearCachedResources() {
+ DeleteTextureHandle();
+ mGL = nullptr;
+}
+
+GLuint RenderDXGIYCbCrTextureHost::GetGLHandle(uint8_t aChannelIndex) const {
+ MOZ_ASSERT(aChannelIndex < 3);
+
+ return mTextureHandles[aChannelIndex];
+}
+
+gfx::IntSize RenderDXGIYCbCrTextureHost::GetSize(uint8_t aChannelIndex) const {
+ MOZ_ASSERT(aChannelIndex < 3);
+
+ if (aChannelIndex == 0) {
+ return mSizeY;
+ } else {
+ return mSizeCbCr;
+ }
+}
+
+void RenderDXGIYCbCrTextureHost::DeleteTextureHandle() {
+ if (mTextureHandles[0] == 0) {
+ return;
+ }
+
+ MOZ_ASSERT(mGL.get());
+ if (!mGL) {
+ return;
+ }
+
+ if (mGL->MakeCurrent()) {
+ mGL->fDeleteTextures(3, mTextureHandles);
+
+ const auto& gle = gl::GLContextEGL::Cast(mGL);
+ const auto& egl = gle->mEgl;
+ for (int i = 0; i < 3; ++i) {
+ mTextureHandles[i] = 0;
+ mTextures[i] = nullptr;
+ mKeyedMutexs[i] = nullptr;
+
+ if (mSurfaces[i]) {
+ egl->fDestroySurface(mSurfaces[i]);
+ mSurfaces[i] = 0;
+ }
+ if (mStreams[i]) {
+ egl->fDestroyStreamKHR(mStreams[i]);
+ mStreams[i] = 0;
+ }
+ }
+ }
+}
+
+} // namespace wr
+} // namespace mozilla
diff --git a/gfx/webrender_bindings/RenderD3D11TextureHost.h b/gfx/webrender_bindings/RenderD3D11TextureHost.h
new file mode 100644
index 0000000000..83a7d3a279
--- /dev/null
+++ b/gfx/webrender_bindings/RenderD3D11TextureHost.h
@@ -0,0 +1,213 @@
+/* -*- 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_RENDERD3D11TEXTUREHOST_H
+#define MOZILLA_GFX_RENDERD3D11TEXTUREHOST_H
+
+#include "GLTypes.h"
+#include "RenderTextureHostSWGL.h"
+
+#include <d3d11.h>
+
+struct ID3D11Texture2D;
+struct IDXGIKeyedMutex;
+
+namespace mozilla {
+
+namespace wr {
+
+class RenderDXGITextureHost final : public RenderTextureHostSWGL {
+ public:
+ RenderDXGITextureHost(
+ WindowsHandle aHandle,
+ Maybe<layers::GpuProcessTextureId>& aGpuProcessTextureId,
+ uint32_t aArrayIndex, gfx::SurfaceFormat aFormat, gfx::ColorSpace2,
+ gfx::ColorRange aColorRange, gfx::IntSize aSize);
+
+ wr::WrExternalImage Lock(uint8_t aChannelIndex, gl::GLContext* aGL) override;
+ void Unlock() override;
+ void ClearCachedResources() override;
+
+ gfx::IntSize GetSize(uint8_t aChannelIndex) const;
+ GLuint GetGLHandle(uint8_t aChannelIndex) const;
+
+ bool SyncObjectNeeded() override { return true; }
+
+ RenderDXGITextureHost* AsRenderDXGITextureHost() override { return this; }
+
+ gfx::ColorRange GetColorRange() const { return mColorRange; }
+
+ ID3D11Texture2D* GetD3D11Texture2DWithGL();
+ ID3D11Texture2D* GetD3D11Texture2D() { return mTexture; }
+
+ // RenderTextureHostSWGL
+ gfx::SurfaceFormat GetFormat() const override { return mFormat; }
+ gfx::ColorDepth GetColorDepth() const override {
+ if (mFormat == gfx::SurfaceFormat::P010) {
+ return gfx::ColorDepth::COLOR_10;
+ }
+ if (mFormat == gfx::SurfaceFormat::P016) {
+ return gfx::ColorDepth::COLOR_16;
+ }
+ return gfx::ColorDepth::COLOR_8;
+ }
+ size_t GetPlaneCount() const override;
+ bool MapPlane(RenderCompositor* aCompositor, uint8_t aChannelIndex,
+ PlaneInfo& aPlaneInfo) override;
+ void UnmapPlanes() override;
+ gfx::YUVRangedColorSpace GetYUVColorSpace() const override {
+ return ToYUVRangedColorSpace(ToYUVColorSpace(mColorSpace), mColorRange);
+ }
+
+ bool EnsureD3D11Texture2D(ID3D11Device* aDevice);
+ bool LockInternal();
+
+ size_t Bytes() override {
+ size_t bytes = 0;
+
+ size_t bpp = GetPlaneCount() > 1
+ ? (GetColorDepth() == gfx::ColorDepth::COLOR_8 ? 1 : 2)
+ : 4;
+
+ for (size_t i = 0; i < GetPlaneCount(); i++) {
+ gfx::IntSize size = GetSize(i);
+ bytes += size.width * size.height * bpp;
+ }
+ return bytes;
+ }
+
+ uint32_t ArrayIndex() const { return mArrayIndex; }
+
+ private:
+ virtual ~RenderDXGITextureHost();
+
+ bool EnsureD3D11Texture2DWithGL();
+ bool EnsureLockable();
+
+ void DeleteTextureHandle();
+
+ RefPtr<gl::GLContext> mGL;
+
+ WindowsHandle mHandle;
+ Maybe<layers::GpuProcessTextureId> mGpuProcessTextureId;
+ RefPtr<ID3D11Texture2D> mTexture;
+ uint32_t mArrayIndex = 0;
+ RefPtr<IDXGIKeyedMutex> mKeyedMutex;
+
+ // Temporary state between MapPlane and UnmapPlanes.
+ RefPtr<ID3D11DeviceContext> mDeviceContext;
+ RefPtr<ID3D11Texture2D> mCpuTexture;
+ D3D11_MAPPED_SUBRESOURCE mMappedSubresource;
+
+ EGLSurface mSurface;
+ EGLStreamKHR mStream;
+
+ // We could use NV12 format for this texture. So, we might have 2 gl texture
+ // handles for Y and CbCr data.
+ GLuint mTextureHandle[2];
+
+ public:
+ const gfx::SurfaceFormat mFormat;
+ const gfx::ColorSpace2 mColorSpace;
+ const gfx::ColorRange mColorRange;
+ const gfx::IntSize mSize;
+
+ private:
+ bool mLocked;
+};
+
+class RenderDXGIYCbCrTextureHost final : public RenderTextureHostSWGL {
+ public:
+ explicit RenderDXGIYCbCrTextureHost(WindowsHandle (&aHandles)[3],
+ gfx::YUVColorSpace aYUVColorSpace,
+ gfx::ColorDepth aColorDepth,
+ gfx::ColorRange aColorRange,
+ gfx::IntSize aSizeY,
+ gfx::IntSize aSizeCbCr);
+
+ RenderDXGIYCbCrTextureHost* AsRenderDXGIYCbCrTextureHost() override {
+ return this;
+ }
+
+ wr::WrExternalImage Lock(uint8_t aChannelIndex, gl::GLContext* aGL) override;
+ void Unlock() override;
+ void ClearCachedResources() override;
+
+ gfx::IntSize GetSize(uint8_t aChannelIndex) const;
+ GLuint GetGLHandle(uint8_t aChannelIndex) const;
+
+ bool SyncObjectNeeded() override { return true; }
+
+ gfx::ColorRange GetColorRange() const { return mColorRange; }
+
+ // RenderTextureHostSWGL
+ gfx::SurfaceFormat GetFormat() const override {
+ return gfx::SurfaceFormat::YUV;
+ }
+ gfx::ColorDepth GetColorDepth() const override { return mColorDepth; }
+ size_t GetPlaneCount() const override { return 3; }
+ bool MapPlane(RenderCompositor* aCompositor, uint8_t aChannelIndex,
+ PlaneInfo& aPlaneInfo) override;
+ void UnmapPlanes() override;
+ gfx::YUVRangedColorSpace GetYUVColorSpace() const override {
+ return ToYUVRangedColorSpace(mYUVColorSpace, GetColorRange());
+ }
+
+ bool EnsureD3D11Texture2D(ID3D11Device* aDevice);
+ bool LockInternal();
+
+ ID3D11Texture2D* GetD3D11Texture2D(uint8_t aChannelIndex) {
+ return mTextures[aChannelIndex];
+ }
+
+ size_t Bytes() override {
+ size_t bytes = 0;
+
+ size_t bpp = mColorDepth == gfx::ColorDepth::COLOR_8 ? 1 : 2;
+
+ for (size_t i = 0; i < GetPlaneCount(); i++) {
+ gfx::IntSize size = GetSize(i);
+ bytes += size.width * size.height * bpp;
+ }
+ return bytes;
+ }
+
+ private:
+ virtual ~RenderDXGIYCbCrTextureHost();
+
+ bool EnsureLockable();
+
+ void DeleteTextureHandle();
+
+ RefPtr<gl::GLContext> mGL;
+
+ WindowsHandle mHandles[3];
+ RefPtr<ID3D11Texture2D> mTextures[3];
+ RefPtr<IDXGIKeyedMutex> mKeyedMutexs[3];
+
+ EGLSurface mSurfaces[3];
+ EGLStreamKHR mStreams[3];
+
+ // The gl handles for Y, Cb and Cr data.
+ GLuint mTextureHandles[3];
+
+ // Temporary state between MapPlane and UnmapPlanes.
+ RefPtr<ID3D11DeviceContext> mDeviceContext;
+ RefPtr<ID3D11Texture2D> mCpuTexture[3];
+
+ gfx::YUVColorSpace mYUVColorSpace;
+ gfx::ColorDepth mColorDepth;
+ gfx::ColorRange mColorRange;
+ gfx::IntSize mSizeY;
+ gfx::IntSize mSizeCbCr;
+
+ bool mLocked;
+};
+
+} // namespace wr
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_RENDERD3D11TEXTUREHOST_H
diff --git a/gfx/webrender_bindings/RenderDMABUFTextureHost.cpp b/gfx/webrender_bindings/RenderDMABUFTextureHost.cpp
new file mode 100644
index 0000000000..f52535b95a
--- /dev/null
+++ b/gfx/webrender_bindings/RenderDMABUFTextureHost.cpp
@@ -0,0 +1,67 @@
+/* -*- 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 "RenderDMABUFTextureHost.h"
+
+#include "GLContextEGL.h"
+#include "mozilla/gfx/Logging.h"
+#include "ScopedGLHelpers.h"
+
+namespace mozilla::wr {
+
+RenderDMABUFTextureHost::RenderDMABUFTextureHost(DMABufSurface* aSurface)
+ : mSurface(aSurface) {
+ MOZ_COUNT_CTOR_INHERITED(RenderDMABUFTextureHost, RenderTextureHost);
+}
+
+RenderDMABUFTextureHost::~RenderDMABUFTextureHost() {
+ MOZ_COUNT_DTOR_INHERITED(RenderDMABUFTextureHost, RenderTextureHost);
+ DeleteTextureHandle();
+}
+
+wr::WrExternalImage RenderDMABUFTextureHost::Lock(uint8_t aChannelIndex,
+ gl::GLContext* aGL) {
+ if (mGL.get() != aGL) {
+ if (mGL) {
+ // This should not happen. EGLImage is created only in
+ // parent process.
+ MOZ_ASSERT_UNREACHABLE("Unexpected GL context");
+ return InvalidToWrExternalImage();
+ }
+ mGL = aGL;
+ }
+
+ if (!mGL || !mGL->MakeCurrent()) {
+ return InvalidToWrExternalImage();
+ }
+
+ if (!mSurface->GetTexture(aChannelIndex)) {
+ if (!mSurface->CreateTexture(mGL, aChannelIndex)) {
+ return InvalidToWrExternalImage();
+ }
+ ActivateBindAndTexParameteri(mGL, LOCAL_GL_TEXTURE0, LOCAL_GL_TEXTURE_2D,
+ mSurface->GetTexture(aChannelIndex));
+ }
+
+ const auto uvs = GetUvCoords(gfx::IntSize(
+ mSurface->GetWidth(aChannelIndex), mSurface->GetHeight(aChannelIndex)));
+ return NativeTextureToWrExternalImage(mSurface->GetTexture(aChannelIndex),
+ uvs.first.x, uvs.first.y, uvs.second.x,
+ uvs.second.y);
+}
+
+void RenderDMABUFTextureHost::Unlock() {}
+
+void RenderDMABUFTextureHost::DeleteTextureHandle() {
+ mSurface->ReleaseTextures();
+}
+
+void RenderDMABUFTextureHost::ClearCachedResources() {
+ DeleteTextureHandle();
+ mGL = nullptr;
+}
+
+} // namespace mozilla::wr
diff --git a/gfx/webrender_bindings/RenderDMABUFTextureHost.h b/gfx/webrender_bindings/RenderDMABUFTextureHost.h
new file mode 100644
index 0000000000..a38eeaf596
--- /dev/null
+++ b/gfx/webrender_bindings/RenderDMABUFTextureHost.h
@@ -0,0 +1,46 @@
+/* -*- 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_RENDERDMABUFTEXTUREHOST_H
+#define MOZILLA_GFX_RENDERDMABUFTEXTUREHOST_H
+
+#include "mozilla/layers/TextureHostOGL.h"
+#include "RenderTextureHost.h"
+#include "mozilla/widget/DMABufSurface.h"
+
+namespace mozilla {
+
+namespace layers {
+class SurfaceDescriptorDMABuf;
+}
+
+namespace wr {
+
+class RenderDMABUFTextureHost final : public RenderTextureHost {
+ public:
+ explicit RenderDMABUFTextureHost(DMABufSurface* aSurface);
+
+ wr::WrExternalImage Lock(uint8_t aChannelIndex, gl::GLContext* aGL) override;
+ void Unlock() override;
+ void ClearCachedResources() override;
+
+ size_t Bytes() override {
+ return mSurface->GetWidth() * mSurface->GetHeight() *
+ BytesPerPixel(mSurface->GetFormat());
+ }
+
+ private:
+ virtual ~RenderDMABUFTextureHost();
+ void DeleteTextureHandle();
+
+ RefPtr<DMABufSurface> mSurface;
+ RefPtr<gl::GLContext> mGL;
+};
+
+} // namespace wr
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_RENDERDMABUFTEXTUREHOST_H
diff --git a/gfx/webrender_bindings/RenderDcompSurfaceTextureHost.cpp b/gfx/webrender_bindings/RenderDcompSurfaceTextureHost.cpp
new file mode 100644
index 0000000000..999421e793
--- /dev/null
+++ b/gfx/webrender_bindings/RenderDcompSurfaceTextureHost.cpp
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RenderDcompSurfaceTextureHost.h"
+
+#undef _WIN32_WINNT
+#define _WIN32_WINNT _WIN32_WINNT_WINBLUE
+#undef NTDDI_VERSION
+#define NTDDI_VERSION NTDDI_WINBLUE
+
+#include <dcomp.h>
+
+#define LOG(msg, ...) \
+ MOZ_LOG(gDcompSurface, LogLevel::Debug, \
+ ("RenderDcompSurfaceTextureHost=%p, handle=%p, size=[%u,%u] " msg, \
+ this, this->mHandle, this->mSize.Width(), this->mSize.Height(), \
+ ##__VA_ARGS__))
+
+namespace mozilla::wr {
+
+RenderDcompSurfaceTextureHost::RenderDcompSurfaceTextureHost(
+ HANDLE aHandle, gfx::IntSize aSize, gfx::SurfaceFormat aFormat)
+ : mHandle(aHandle), mSize(aSize), mFormat(aFormat) {
+ MOZ_ASSERT(aHandle && aHandle != INVALID_HANDLE_VALUE);
+}
+
+IDCompositionSurface* RenderDcompSurfaceTextureHost::CreateSurfaceFromDevice(
+ IDCompositionDevice* aDevice) {
+ // Already created surface, no need to recreate it again.
+ if (mDcompSurface) {
+ return mDcompSurface;
+ }
+
+ auto* surface =
+ static_cast<IDCompositionSurface**>(getter_AddRefs(mDcompSurface));
+ auto hr = aDevice->CreateSurfaceFromHandle(
+ mHandle, reinterpret_cast<IUnknown**>(surface));
+ if (FAILED(hr)) {
+ LOG("Failed to create surface from Dcomp handle %p, hr=%lx", mHandle, hr);
+ return nullptr;
+ }
+ LOG("Created surface %p correctly", surface);
+ return mDcompSurface;
+}
+
+} // namespace mozilla::wr
+
+#undef LOG
diff --git a/gfx/webrender_bindings/RenderDcompSurfaceTextureHost.h b/gfx/webrender_bindings/RenderDcompSurfaceTextureHost.h
new file mode 100644
index 0000000000..7533e7c75f
--- /dev/null
+++ b/gfx/webrender_bindings/RenderDcompSurfaceTextureHost.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: ; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_GFX_RENDERDCOMPSURFACETEXTUREHOST_H
+#define MOZILLA_GFX_RENDERDCOMPSURFACETEXTUREHOST_H
+
+#include "GLTypes.h"
+#include "RenderTextureHostSWGL.h"
+#include "mozilla/webrender/RenderThread.h"
+
+struct IDCompositionDevice;
+struct IDCompositionSurface;
+
+inline mozilla::LazyLogModule gDcompSurface("DcompSurface");
+
+namespace mozilla::wr {
+
+/**
+ * A render texture host is responsible to create a dcomp surface from an
+ * existing dcomp handle. Currently usage is that MF media engine will create
+ * a surface handle from another remote process, and we reconstruct the surface
+ * and use it in the DCLayerTree in the GPU process.
+ */
+class RenderDcompSurfaceTextureHost final : public RenderTextureHostSWGL {
+ public:
+ RenderDcompSurfaceTextureHost(HANDLE aHandle, gfx::IntSize aSize,
+ gfx::SurfaceFormat aFormat);
+
+ // RenderTextureHost
+ RenderDcompSurfaceTextureHost* AsRenderDcompSurfaceTextureHost() override {
+ return this;
+ }
+
+ // RenderTextureHostSWGL
+ gfx::SurfaceFormat GetFormat() const override { return mFormat; }
+ gfx::ColorDepth GetColorDepth() const override {
+ return gfx::ColorDepth::COLOR_8;
+ }
+ size_t GetPlaneCount() const override { return 1; }
+ bool MapPlane(RenderCompositor* aCompositor, uint8_t aChannelIndex,
+ PlaneInfo& aPlaneInfo) override {
+ return false;
+ }
+ void UnmapPlanes() override {}
+ gfx::YUVRangedColorSpace GetYUVColorSpace() const override {
+ return gfx::YUVRangedColorSpace::GbrIdentity;
+ }
+ size_t Bytes() override { return 0; }
+
+ gfx::IntSize GetSize() const { return mSize; };
+
+ HANDLE GetDcompSurfaceHandle() const { return mHandle; }
+
+ // Not thread-safe. They should only be called on the renderer thread on the
+ // GPU process.
+ IDCompositionSurface* CreateSurfaceFromDevice(IDCompositionDevice* aDevice);
+ IDCompositionSurface* GetSurface() const { return mDcompSurface; };
+
+ private:
+ const HANDLE mHandle;
+ const gfx::IntSize mSize;
+ const gfx::SurfaceFormat mFormat;
+ RefPtr<IDCompositionSurface> mDcompSurface;
+};
+
+} // namespace mozilla::wr
+
+#endif // MOZILLA_GFX_RENDERDCOMPSURFACETEXTUREHOST_H
diff --git a/gfx/webrender_bindings/RenderEGLImageTextureHost.cpp b/gfx/webrender_bindings/RenderEGLImageTextureHost.cpp
new file mode 100644
index 0000000000..6ca9e74072
--- /dev/null
+++ b/gfx/webrender_bindings/RenderEGLImageTextureHost.cpp
@@ -0,0 +1,96 @@
+/* -*- 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 "RenderEGLImageTextureHost.h"
+
+#include "mozilla/gfx/Logging.h"
+#include "GLContextEGL.h"
+#include "GLLibraryEGL.h"
+
+namespace mozilla {
+namespace wr {
+
+RenderEGLImageTextureHost::RenderEGLImageTextureHost(EGLImage aImage,
+ EGLSync aSync,
+ gfx::IntSize aSize)
+ : mImage(aImage),
+ mSync(aSync),
+ mSize(aSize),
+ mTextureTarget(LOCAL_GL_TEXTURE_2D),
+ mTextureHandle(0) {
+ MOZ_COUNT_CTOR_INHERITED(RenderEGLImageTextureHost, RenderTextureHost);
+}
+
+RenderEGLImageTextureHost::~RenderEGLImageTextureHost() {
+ MOZ_COUNT_DTOR_INHERITED(RenderEGLImageTextureHost, RenderTextureHost);
+ DeleteTextureHandle();
+}
+
+wr::WrExternalImage RenderEGLImageTextureHost::Lock(uint8_t aChannelIndex,
+ gl::GLContext* aGL) {
+ MOZ_ASSERT(aChannelIndex == 0);
+
+ if (mGL.get() != aGL) {
+ if (mGL) {
+ // This should not happen. SharedSurface_EGLImage is created only in
+ // parent process.
+ MOZ_ASSERT_UNREACHABLE("Unexpected GL context");
+ return InvalidToWrExternalImage();
+ }
+ mGL = aGL;
+ }
+
+ if (!mImage || !mGL || !mGL->MakeCurrent()) {
+ return InvalidToWrExternalImage();
+ }
+
+ EGLint status = LOCAL_EGL_CONDITION_SATISFIED;
+ if (mSync) {
+ const auto& gle = gl::GLContextEGL::Cast(mGL);
+ const auto& egl = gle->mEgl;
+ MOZ_ASSERT(egl->IsExtensionSupported(gl::EGLExtension::KHR_fence_sync));
+ status = egl->fClientWaitSync(mSync, 0, LOCAL_EGL_FOREVER);
+ // We do not need to delete sync here. It is deleted by
+ // SharedSurface_EGLImage.
+ mSync = 0;
+ }
+
+ if (status != LOCAL_EGL_CONDITION_SATISFIED) {
+ MOZ_ASSERT(
+ status != 0,
+ "ClientWaitSync generated an error. Has mSync already been destroyed?");
+ return InvalidToWrExternalImage();
+ }
+
+ if (!mTextureHandle) {
+ mTextureTarget = mGL->GetPreferredEGLImageTextureTarget();
+ MOZ_ASSERT(mTextureTarget == LOCAL_GL_TEXTURE_2D ||
+ mTextureTarget == LOCAL_GL_TEXTURE_EXTERNAL);
+
+ mGL->fGenTextures(1, &mTextureHandle);
+ ActivateBindAndTexParameteri(mGL, LOCAL_GL_TEXTURE0, mTextureTarget,
+ mTextureHandle);
+ mGL->fEGLImageTargetTexture2D(mTextureTarget, mImage);
+ }
+
+ const auto uvs = GetUvCoords(mSize);
+ return NativeTextureToWrExternalImage(
+ mTextureHandle, uvs.first.x, uvs.first.y, uvs.second.x, uvs.second.y);
+}
+
+void RenderEGLImageTextureHost::Unlock() {}
+
+void RenderEGLImageTextureHost::DeleteTextureHandle() {
+ if (mTextureHandle) {
+ // XXX recycle gl texture, since SharedSurface_EGLImage and
+ // RenderEGLImageTextureHost is not recycled.
+ mGL->fDeleteTextures(1, &mTextureHandle);
+ mTextureHandle = 0;
+ }
+}
+
+} // namespace wr
+} // namespace mozilla
diff --git a/gfx/webrender_bindings/RenderEGLImageTextureHost.h b/gfx/webrender_bindings/RenderEGLImageTextureHost.h
new file mode 100644
index 0000000000..c2d1b512d2
--- /dev/null
+++ b/gfx/webrender_bindings/RenderEGLImageTextureHost.h
@@ -0,0 +1,46 @@
+/* -*- 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_RENDEREGLIMAGETEXTUREHOST_H
+#define MOZILLA_GFX_RENDEREGLIMAGETEXTUREHOST_H
+
+#include "mozilla/layers/TextureHostOGL.h"
+#include "RenderTextureHost.h"
+
+namespace mozilla {
+
+namespace wr {
+
+// RenderEGLImageTextureHost is created only for SharedSurface_EGLImage that is
+// created in parent process.
+class RenderEGLImageTextureHost final : public RenderTextureHost {
+ public:
+ RenderEGLImageTextureHost(EGLImage aImage, EGLSync aSync, gfx::IntSize aSize);
+
+ wr::WrExternalImage Lock(uint8_t aChannelIndex, gl::GLContext* aGL) override;
+ void Unlock() override;
+ size_t Bytes() override {
+ // XXX: we don't have a format so we can't get bpp.
+ return mSize.width * mSize.height;
+ }
+
+ private:
+ virtual ~RenderEGLImageTextureHost();
+ void DeleteTextureHandle();
+
+ const EGLImage mImage;
+ EGLSync mSync;
+ const gfx::IntSize mSize;
+
+ RefPtr<gl::GLContext> mGL;
+ GLenum mTextureTarget;
+ GLuint mTextureHandle;
+};
+
+} // namespace wr
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_RENDEREGLIMAGETEXTUREHOST_H
diff --git a/gfx/webrender_bindings/RenderExternalTextureHost.cpp b/gfx/webrender_bindings/RenderExternalTextureHost.cpp
new file mode 100644
index 0000000000..db8107ff0a
--- /dev/null
+++ b/gfx/webrender_bindings/RenderExternalTextureHost.cpp
@@ -0,0 +1,265 @@
+/* -*- 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 "RenderExternalTextureHost.h"
+
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+
+#include "GLContext.h"
+
+namespace mozilla {
+namespace wr {
+
+RenderExternalTextureHost::RenderExternalTextureHost(
+ uint8_t* aBuffer, const layers::BufferDescriptor& aDescriptor)
+ : mBuffer(aBuffer),
+ mDescriptor(aDescriptor),
+ mInitialized(false),
+ mTextureUpdateNeeded(true) {
+ MOZ_COUNT_CTOR_INHERITED(RenderExternalTextureHost, RenderTextureHost);
+
+ switch (mDescriptor.type()) {
+ case layers::BufferDescriptor::TYCbCrDescriptor: {
+ const layers::YCbCrDescriptor& ycbcr = mDescriptor.get_YCbCrDescriptor();
+ mSize = ycbcr.display().Size();
+ mFormat = gfx::SurfaceFormat::YUV;
+ break;
+ }
+ case layers::BufferDescriptor::TRGBDescriptor: {
+ const layers::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");
+ }
+}
+
+RenderExternalTextureHost::~RenderExternalTextureHost() {
+ MOZ_COUNT_DTOR_INHERITED(RenderExternalTextureHost, RenderTextureHost);
+
+ if (NS_WARN_IF(!IsReadyForDeletion())) {
+ gfxCriticalNote << "RenderExternalTextureHost sync failed";
+ }
+
+ DeleteTextures();
+}
+
+bool RenderExternalTextureHost::CreateSurfaces() {
+ if (!IsYUV()) {
+ mSurfaces[0] = gfx::Factory::CreateWrappingDataSourceSurface(
+ GetBuffer(),
+ layers::ImageDataSerializer::GetRGBStride(
+ mDescriptor.get_RGBDescriptor()),
+ mSize, mFormat);
+ } else {
+ const layers::YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
+ const gfx::SurfaceFormat surfaceFormat =
+ SurfaceFormatForColorDepth(desc.colorDepth());
+ auto cbcrSize = layers::ImageDataSerializer::GetCroppedCbCrSize(desc);
+
+ mSurfaces[0] = gfx::Factory::CreateWrappingDataSourceSurface(
+ layers::ImageDataSerializer::GetYChannel(GetBuffer(), desc),
+ desc.yStride(), desc.display().Size(), surfaceFormat);
+ mSurfaces[1] = gfx::Factory::CreateWrappingDataSourceSurface(
+ layers::ImageDataSerializer::GetCbChannel(GetBuffer(), desc),
+ desc.cbCrStride(), cbcrSize, surfaceFormat);
+ mSurfaces[2] = gfx::Factory::CreateWrappingDataSourceSurface(
+ layers::ImageDataSerializer::GetCrChannel(GetBuffer(), desc),
+ desc.cbCrStride(), cbcrSize, surfaceFormat);
+ }
+
+ for (size_t i = 0; i < PlaneCount(); ++i) {
+ if (NS_WARN_IF(!mSurfaces[i])) {
+ gfxCriticalNote << "Surface is null";
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void RenderExternalTextureHost::DeleteSurfaces() {
+ for (size_t i = 0; i < PlaneCount(); ++i) {
+ mSurfaces[i] = nullptr;
+ }
+}
+
+void RenderExternalTextureHost::DeleteTextures() {
+ for (size_t i = 0; i < PlaneCount(); ++i) {
+ mTextureSources[i] = nullptr;
+ mImages[i] = InvalidToWrExternalImage();
+ }
+}
+
+bool RenderExternalTextureHost::InitializeIfNeeded() {
+ if (mInitialized) {
+ return true;
+ }
+
+ if (!GetBuffer()) {
+ // We hit some problems to get the shmem.
+ gfxCriticalNote << "GetBuffer Failed";
+ return false;
+ }
+
+ if (!CreateSurfaces()) {
+ DeleteSurfaces();
+ return false;
+ }
+
+ mInitialized = true;
+ return mInitialized;
+}
+
+bool RenderExternalTextureHost::IsReadyForDeletion() {
+ if (!mInitialized) {
+ return true;
+ }
+
+ auto& textureSource = mTextureSources[0];
+ if (textureSource) {
+ return textureSource->Sync(false);
+ }
+
+ return true;
+}
+
+wr::WrExternalImage RenderExternalTextureHost::Lock(uint8_t aChannelIndex,
+ gl::GLContext* aGL) {
+ if (mGL.get() != aGL) {
+ mGL = aGL;
+ mGL->MakeCurrent();
+ }
+
+ if (!mGL || !mGL->MakeCurrent()) {
+ return InvalidToWrExternalImage();
+ }
+
+ if (!InitializeIfNeeded()) {
+ return InvalidToWrExternalImage();
+ }
+
+ UpdateTextures();
+ return mImages[aChannelIndex];
+}
+
+void RenderExternalTextureHost::PrepareForUse() { mTextureUpdateNeeded = true; }
+
+void RenderExternalTextureHost::Unlock() {}
+
+void RenderExternalTextureHost::UpdateTexture(size_t aIndex) {
+ MOZ_ASSERT(mSurfaces[aIndex]);
+
+ auto& texture = mTextureSources[aIndex];
+
+ if (texture) {
+ texture->Update(mSurfaces[aIndex]);
+ } else {
+ texture = new layers::DirectMapTextureSource(mGL, mSurfaces[aIndex]);
+
+ const GLuint handle = texture->GetTextureHandle();
+ const auto uvs = GetUvCoords(texture->GetSize());
+ mImages[aIndex] = NativeTextureToWrExternalImage(
+ handle, uvs.first.x, uvs.first.y, uvs.second.x, uvs.second.y);
+ }
+
+ MOZ_ASSERT(mGL->GetError() == LOCAL_GL_NO_ERROR);
+}
+
+void RenderExternalTextureHost::UpdateTextures() {
+ if (!mTextureUpdateNeeded) {
+ // Nothing to do here.
+ return;
+ }
+
+ for (size_t i = 0; i < PlaneCount(); ++i) {
+ UpdateTexture(i);
+ }
+
+ mTextureSources[0]->MaybeFenceTexture();
+ mTextureUpdateNeeded = false;
+}
+
+size_t RenderExternalTextureHost::GetPlaneCount() const { return PlaneCount(); }
+
+gfx::SurfaceFormat RenderExternalTextureHost::GetFormat() const {
+ return mFormat;
+}
+
+gfx::ColorDepth RenderExternalTextureHost::GetColorDepth() const {
+ switch (mDescriptor.type()) {
+ case layers::BufferDescriptor::TYCbCrDescriptor:
+ return mDescriptor.get_YCbCrDescriptor().colorDepth();
+ default:
+ return gfx::ColorDepth::COLOR_8;
+ }
+}
+
+gfx::YUVRangedColorSpace RenderExternalTextureHost::GetYUVColorSpace() const {
+ switch (mDescriptor.type()) {
+ case layers::BufferDescriptor::TYCbCrDescriptor:
+ return gfx::GetYUVRangedColorSpace(mDescriptor.get_YCbCrDescriptor());
+ default:
+ return gfx::YUVRangedColorSpace::Default;
+ }
+}
+
+bool RenderExternalTextureHost::MapPlane(RenderCompositor* aCompositor,
+ uint8_t aChannelIndex,
+ PlaneInfo& aPlaneInfo) {
+ if (!mBuffer) {
+ // We hit some problems to get the shmem.
+ gfxCriticalNote << "GetBuffer Failed";
+ return false;
+ }
+
+ switch (mDescriptor.type()) {
+ case layers::BufferDescriptor::TYCbCrDescriptor: {
+ const layers::YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
+ switch (aChannelIndex) {
+ case 0:
+ aPlaneInfo.mData =
+ layers::ImageDataSerializer::GetYChannel(mBuffer, desc);
+ aPlaneInfo.mStride = desc.yStride();
+ aPlaneInfo.mSize = desc.display().Size();
+ break;
+ case 1:
+ aPlaneInfo.mData =
+ layers::ImageDataSerializer::GetCbChannel(mBuffer, desc);
+ aPlaneInfo.mStride = desc.cbCrStride();
+ aPlaneInfo.mSize =
+ layers::ImageDataSerializer::GetCroppedCbCrSize(desc);
+ break;
+ case 2:
+ aPlaneInfo.mData =
+ layers::ImageDataSerializer::GetCrChannel(mBuffer, desc);
+ aPlaneInfo.mStride = desc.cbCrStride();
+ aPlaneInfo.mSize =
+ layers::ImageDataSerializer::GetCroppedCbCrSize(desc);
+ break;
+ }
+ break;
+ }
+ default: {
+ const layers::RGBDescriptor& desc = mDescriptor.get_RGBDescriptor();
+ aPlaneInfo.mData = mBuffer;
+ aPlaneInfo.mStride = layers::ImageDataSerializer::GetRGBStride(desc);
+ aPlaneInfo.mSize = desc.size();
+ break;
+ }
+ }
+ return true;
+}
+
+void RenderExternalTextureHost::UnmapPlanes() {}
+
+} // namespace wr
+} // namespace mozilla
diff --git a/gfx/webrender_bindings/RenderExternalTextureHost.h b/gfx/webrender_bindings/RenderExternalTextureHost.h
new file mode 100644
index 0000000000..c3b13250c1
--- /dev/null
+++ b/gfx/webrender_bindings/RenderExternalTextureHost.h
@@ -0,0 +1,81 @@
+/* -*- 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_RENDEREXTERNALTEXTUREHOST_H
+#define MOZILLA_GFX_RENDEREXTERNALTEXTUREHOST_H
+
+#include "mozilla/layers/TextureHostOGL.h"
+#include "RenderTextureHostSWGL.h"
+
+namespace mozilla {
+namespace wr {
+
+/**
+ * RenderExternalTextureHost manages external textures used by WebRender on Mac.
+ * The motivation for this is to be able to use Apple Client Storage OpenGL
+ * extension, which makes it possible to avoid some copies during texture
+ * upload. This is especially helpful for high resolution video.
+ */
+class RenderExternalTextureHost final : public RenderTextureHostSWGL {
+ public:
+ RenderExternalTextureHost(uint8_t* aBuffer,
+ const layers::BufferDescriptor& aDescriptor);
+
+ wr::WrExternalImage Lock(uint8_t aChannelIndex, gl::GLContext* aGL) override;
+ void Unlock() override;
+ void PrepareForUse() override;
+ size_t Bytes() override {
+ return mSize.width * mSize.height * BytesPerPixel(mFormat);
+ }
+
+ // RenderTextureHostSWGL
+ size_t GetPlaneCount() const override;
+
+ gfx::SurfaceFormat GetFormat() const override;
+
+ gfx::ColorDepth GetColorDepth() const override;
+
+ gfx::YUVRangedColorSpace GetYUVColorSpace() const override;
+
+ bool MapPlane(RenderCompositor* aCompositor, uint8_t aChannelIndex,
+ PlaneInfo& aPlaneInfo) override;
+
+ void UnmapPlanes() override;
+
+ private:
+ ~RenderExternalTextureHost();
+
+ bool CreateSurfaces();
+ void DeleteSurfaces();
+ void DeleteTextures();
+
+ uint8_t* GetBuffer() const { return mBuffer; }
+ bool InitializeIfNeeded();
+ bool IsReadyForDeletion();
+ bool IsYUV() const { return mFormat == gfx::SurfaceFormat::YUV; }
+ size_t PlaneCount() const { return IsYUV() ? 3 : 1; }
+ void UpdateTexture(size_t aIndex);
+ void UpdateTextures();
+
+ uint8_t* mBuffer;
+ layers::BufferDescriptor mDescriptor;
+
+ bool mInitialized;
+ bool mTextureUpdateNeeded;
+
+ gfx::IntSize mSize;
+ gfx::SurfaceFormat mFormat;
+
+ RefPtr<gl::GLContext> mGL;
+ RefPtr<gfx::DataSourceSurface> mSurfaces[3];
+ RefPtr<layers::DirectMapTextureSource> mTextureSources[3];
+ wr::WrExternalImage mImages[3];
+};
+
+} // namespace wr
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_RENDEREXTERNALTEXTUREHOST_H
diff --git a/gfx/webrender_bindings/RenderMacIOSurfaceTextureHost.cpp b/gfx/webrender_bindings/RenderMacIOSurfaceTextureHost.cpp
new file mode 100644
index 0000000000..bb0575949b
--- /dev/null
+++ b/gfx/webrender_bindings/RenderMacIOSurfaceTextureHost.cpp
@@ -0,0 +1,157 @@
+/* -*- 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 "RenderMacIOSurfaceTextureHost.h"
+
+#include "GLContextCGL.h"
+#include "mozilla/gfx/Logging.h"
+#include "ScopedGLHelpers.h"
+
+namespace mozilla {
+namespace wr {
+
+static CGLError CreateTextureForPlane(uint8_t aPlaneID, gl::GLContext* aGL,
+ MacIOSurface* aSurface,
+ GLuint* aTexture) {
+ MOZ_ASSERT(aGL && aSurface && aTexture);
+
+ aGL->fGenTextures(1, aTexture);
+ ActivateBindAndTexParameteri(aGL, LOCAL_GL_TEXTURE0,
+ LOCAL_GL_TEXTURE_RECTANGLE_ARB, *aTexture);
+ aGL->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_T,
+ LOCAL_GL_CLAMP_TO_EDGE);
+ aGL->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_S,
+ LOCAL_GL_CLAMP_TO_EDGE);
+
+ CGLError result = kCGLNoError;
+ gfx::SurfaceFormat readFormat = gfx::SurfaceFormat::UNKNOWN;
+ result = aSurface->CGLTexImageIOSurface2D(
+ aGL, gl::GLContextCGL::Cast(aGL)->GetCGLContext(), aPlaneID, &readFormat);
+ // If this is a yuv format, the Webrender only supports YUV422 interleaving
+ // format.
+ MOZ_ASSERT(aSurface->GetFormat() != gfx::SurfaceFormat::YUV422 ||
+ readFormat == gfx::SurfaceFormat::YUV422);
+
+ return result;
+}
+
+RenderMacIOSurfaceTextureHost::RenderMacIOSurfaceTextureHost(
+ MacIOSurface* aSurface)
+ : mSurface(aSurface), mTextureHandles{0, 0, 0} {
+ MOZ_COUNT_CTOR_INHERITED(RenderMacIOSurfaceTextureHost, RenderTextureHost);
+}
+
+RenderMacIOSurfaceTextureHost::~RenderMacIOSurfaceTextureHost() {
+ MOZ_COUNT_DTOR_INHERITED(RenderMacIOSurfaceTextureHost, RenderTextureHost);
+ DeleteTextureHandle();
+}
+
+GLuint RenderMacIOSurfaceTextureHost::GetGLHandle(uint8_t aChannelIndex) const {
+ MOZ_ASSERT(mSurface);
+ MOZ_ASSERT((mSurface->GetPlaneCount() == 0)
+ ? (aChannelIndex == mSurface->GetPlaneCount())
+ : (aChannelIndex < mSurface->GetPlaneCount()));
+ return mTextureHandles[aChannelIndex];
+}
+
+gfx::IntSize RenderMacIOSurfaceTextureHost::GetSize(
+ uint8_t aChannelIndex) const {
+ MOZ_ASSERT(mSurface);
+ MOZ_ASSERT((mSurface->GetPlaneCount() == 0)
+ ? (aChannelIndex == mSurface->GetPlaneCount())
+ : (aChannelIndex < mSurface->GetPlaneCount()));
+
+ if (!mSurface) {
+ return gfx::IntSize();
+ }
+ return gfx::IntSize(mSurface->GetDevicePixelWidth(aChannelIndex),
+ mSurface->GetDevicePixelHeight(aChannelIndex));
+}
+
+size_t RenderMacIOSurfaceTextureHost::Bytes() {
+ return mSurface->GetAllocSize();
+}
+
+wr::WrExternalImage RenderMacIOSurfaceTextureHost::Lock(uint8_t aChannelIndex,
+ gl::GLContext* aGL) {
+ if (mGL.get() != aGL) {
+ // release the texture handle in the previous gl context
+ DeleteTextureHandle();
+ mGL = aGL;
+ mGL->MakeCurrent();
+ }
+
+ if (!mSurface || !mGL || !mGL->MakeCurrent()) {
+ return InvalidToWrExternalImage();
+ }
+
+ if (!mTextureHandles[0]) {
+ MOZ_ASSERT(gl::GLContextCGL::Cast(mGL.get())->GetCGLContext());
+
+ // The result of GetPlaneCount() is 0 for single plane format, but it will
+ // be 2 if the format has 2 planar data.
+ CreateTextureForPlane(0, mGL, mSurface, &(mTextureHandles[0]));
+ for (size_t i = 1; i < mSurface->GetPlaneCount(); ++i) {
+ CreateTextureForPlane(i, mGL, mSurface, &(mTextureHandles[i]));
+ }
+ }
+
+ const auto uvs = GetUvCoords(GetSize(aChannelIndex));
+ return NativeTextureToWrExternalImage(GetGLHandle(aChannelIndex), uvs.first.x,
+ uvs.first.y, uvs.second.x,
+ uvs.second.y);
+}
+
+void RenderMacIOSurfaceTextureHost::Unlock() {}
+
+void RenderMacIOSurfaceTextureHost::DeleteTextureHandle() {
+ if (mTextureHandles[0] != 0 && mGL && mGL->MakeCurrent()) {
+ // Calling glDeleteTextures on 0 isn't an error. So, just make them a single
+ // call.
+ mGL->fDeleteTextures(3, mTextureHandles);
+ for (size_t i = 0; i < 3; ++i) {
+ mTextureHandles[i] = 0;
+ }
+ }
+}
+
+size_t RenderMacIOSurfaceTextureHost::GetPlaneCount() const {
+ size_t planeCount = mSurface->GetPlaneCount();
+ return planeCount > 0 ? planeCount : 1;
+}
+
+gfx::SurfaceFormat RenderMacIOSurfaceTextureHost::GetFormat() const {
+ return mSurface->GetFormat();
+}
+
+gfx::ColorDepth RenderMacIOSurfaceTextureHost::GetColorDepth() const {
+ return mSurface->GetColorDepth();
+}
+
+gfx::YUVRangedColorSpace RenderMacIOSurfaceTextureHost::GetYUVColorSpace()
+ const {
+ return ToYUVRangedColorSpace(mSurface->GetYUVColorSpace(),
+ mSurface->GetColorRange());
+}
+
+bool RenderMacIOSurfaceTextureHost::MapPlane(RenderCompositor* aCompositor,
+ uint8_t aChannelIndex,
+ PlaneInfo& aPlaneInfo) {
+ if (!aChannelIndex) {
+ mSurface->Lock();
+ }
+ aPlaneInfo.mData = mSurface->GetBaseAddressOfPlane(aChannelIndex);
+ aPlaneInfo.mStride = mSurface->GetBytesPerRow(aChannelIndex);
+ aPlaneInfo.mSize =
+ gfx::IntSize(mSurface->GetDevicePixelWidth(aChannelIndex),
+ mSurface->GetDevicePixelHeight(aChannelIndex));
+ return true;
+}
+
+void RenderMacIOSurfaceTextureHost::UnmapPlanes() { mSurface->Unlock(); }
+
+} // namespace wr
+} // namespace mozilla
diff --git a/gfx/webrender_bindings/RenderMacIOSurfaceTextureHost.h b/gfx/webrender_bindings/RenderMacIOSurfaceTextureHost.h
new file mode 100644
index 0000000000..173cd3c223
--- /dev/null
+++ b/gfx/webrender_bindings/RenderMacIOSurfaceTextureHost.h
@@ -0,0 +1,61 @@
+/* -*- 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_RENDERMACIOSURFACETEXTUREHOST_H
+#define MOZILLA_GFX_RENDERMACIOSURFACETEXTUREHOST_H
+
+#include "mozilla/gfx/MacIOSurface.h"
+#include "mozilla/layers/TextureHostOGL.h"
+#include "RenderTextureHostSWGL.h"
+
+namespace mozilla {
+
+namespace layers {
+class SurfaceDescriptorMacIOSurface;
+}
+
+namespace wr {
+
+class RenderMacIOSurfaceTextureHost final : public RenderTextureHostSWGL {
+ public:
+ explicit RenderMacIOSurfaceTextureHost(MacIOSurface* aSurface);
+
+ wr::WrExternalImage Lock(uint8_t aChannelIndex, gl::GLContext* aGL) override;
+ void Unlock() override;
+
+ gfx::IntSize GetSize(uint8_t aChannelIndex) const;
+ GLuint GetGLHandle(uint8_t aChannelIndex) const;
+
+ RenderMacIOSurfaceTextureHost* AsRenderMacIOSurfaceTextureHost() override {
+ return this;
+ }
+
+ size_t Bytes() override;
+
+ MacIOSurface* GetSurface() { return mSurface; }
+
+ // RenderTextureHostSWGL
+ size_t GetPlaneCount() const override;
+ gfx::SurfaceFormat GetFormat() const override;
+ gfx::ColorDepth GetColorDepth() const override;
+ gfx::YUVRangedColorSpace GetYUVColorSpace() const override;
+ bool MapPlane(RenderCompositor* aCompositor, uint8_t aChannelIndex,
+ PlaneInfo& aPlaneInfo) override;
+ void UnmapPlanes() override;
+
+ private:
+ virtual ~RenderMacIOSurfaceTextureHost();
+ void DeleteTextureHandle();
+
+ RefPtr<MacIOSurface> mSurface;
+ RefPtr<gl::GLContext> mGL;
+ GLuint mTextureHandles[3];
+};
+
+} // namespace wr
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_RENDERMACIOSURFACETEXTUREHOST_H
diff --git a/gfx/webrender_bindings/RenderSharedSurfaceTextureHost.cpp b/gfx/webrender_bindings/RenderSharedSurfaceTextureHost.cpp
new file mode 100644
index 0000000000..db085f8f2f
--- /dev/null
+++ b/gfx/webrender_bindings/RenderSharedSurfaceTextureHost.cpp
@@ -0,0 +1,78 @@
+/* -*- 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 "RenderSharedSurfaceTextureHost.h"
+
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/SourceSurfaceSharedData.h"
+
+namespace mozilla {
+namespace wr {
+
+RenderSharedSurfaceTextureHost::RenderSharedSurfaceTextureHost(
+ gfx::SourceSurfaceSharedDataWrapper* aSurface)
+ : mSurface(aSurface), mMap(), mLocked(false) {
+ MOZ_COUNT_CTOR_INHERITED(RenderSharedSurfaceTextureHost, RenderTextureHost);
+ MOZ_ASSERT(aSurface);
+}
+
+RenderSharedSurfaceTextureHost::~RenderSharedSurfaceTextureHost() {
+ MOZ_COUNT_DTOR_INHERITED(RenderSharedSurfaceTextureHost, RenderTextureHost);
+}
+
+wr::WrExternalImage RenderSharedSurfaceTextureHost::Lock(uint8_t aChannelIndex,
+ gl::GLContext* aGL) {
+ if (!mLocked) {
+ if (NS_WARN_IF(
+ !mSurface->Map(gfx::DataSourceSurface::MapType::READ, &mMap))) {
+ return InvalidToWrExternalImage();
+ }
+ mLocked = true;
+ }
+
+ return RawDataToWrExternalImage(mMap.mData,
+ mMap.mStride * mSurface->GetSize().height);
+}
+
+void RenderSharedSurfaceTextureHost::Unlock() {
+ if (mLocked) {
+ mSurface->Unmap();
+ mLocked = false;
+ }
+}
+
+size_t RenderSharedSurfaceTextureHost::Bytes() {
+ return mSurface->Stride() * mSurface->GetSize().height;
+}
+
+size_t RenderSharedSurfaceTextureHost::GetPlaneCount() const { return 1; }
+
+gfx::SurfaceFormat RenderSharedSurfaceTextureHost::GetFormat() const {
+ return mSurface->GetFormat();
+}
+
+gfx::ColorDepth RenderSharedSurfaceTextureHost::GetColorDepth() const {
+ return gfx::ColorDepth::COLOR_8;
+}
+
+bool RenderSharedSurfaceTextureHost::MapPlane(RenderCompositor* aCompositor,
+ uint8_t aChannelIndex,
+ PlaneInfo& aPlaneInfo) {
+ if (NS_WARN_IF(
+ !mSurface->Map(gfx::DataSourceSurface::MapType::READ, &mMap))) {
+ return false;
+ }
+ aPlaneInfo.mData = mMap.mData;
+ aPlaneInfo.mStride = mMap.mStride;
+ aPlaneInfo.mSize = mSurface->GetSize();
+ return true;
+}
+
+void RenderSharedSurfaceTextureHost::UnmapPlanes() { mSurface->Unmap(); }
+
+} // namespace wr
+} // namespace mozilla
diff --git a/gfx/webrender_bindings/RenderSharedSurfaceTextureHost.h b/gfx/webrender_bindings/RenderSharedSurfaceTextureHost.h
new file mode 100644
index 0000000000..2575abdac3
--- /dev/null
+++ b/gfx/webrender_bindings/RenderSharedSurfaceTextureHost.h
@@ -0,0 +1,57 @@
+/* -*- 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_RENDERSHAREDSURFACETEXTUREHOST_H
+#define MOZILLA_GFX_RENDERSHAREDSURFACETEXTUREHOST_H
+
+#include "RenderTextureHostSWGL.h"
+
+namespace mozilla {
+namespace gfx {
+class SourceSurfaceSharedDataWrapper;
+}
+
+namespace wr {
+
+/**
+ * This class allows for surfaces managed by SharedSurfacesParent to be inserted
+ * into the render texture cache by wrapping an existing surface wrapper. These
+ * surfaces are backed by BGRA/X shared memory buffers.
+ */
+class RenderSharedSurfaceTextureHost final : public RenderTextureHostSWGL {
+ public:
+ explicit RenderSharedSurfaceTextureHost(
+ gfx::SourceSurfaceSharedDataWrapper* aSurface);
+
+ // RenderTextureHost
+ wr::WrExternalImage Lock(uint8_t aChannelIndex, gl::GLContext* aGL) override;
+ void Unlock() override;
+ size_t Bytes() override;
+
+ // RenderTextureHostSWGL
+ size_t GetPlaneCount() const override;
+
+ gfx::SurfaceFormat GetFormat() const override;
+
+ gfx::ColorDepth GetColorDepth() const override;
+
+ bool MapPlane(RenderCompositor* aCompositor, uint8_t aChannelIndex,
+ PlaneInfo& aPlaneInfo) override;
+
+ void UnmapPlanes() override;
+
+ private:
+ virtual ~RenderSharedSurfaceTextureHost();
+
+ RefPtr<gfx::SourceSurfaceSharedDataWrapper> mSurface;
+ gfx::DataSourceSurface::MappedSurface mMap;
+ bool mLocked;
+};
+
+} // namespace wr
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_RENDERSHAREDSURFACETEXTUREHOST_H
diff --git a/gfx/webrender_bindings/RenderTextureHost.cpp b/gfx/webrender_bindings/RenderTextureHost.cpp
new file mode 100644
index 0000000000..71391bd1f7
--- /dev/null
+++ b/gfx/webrender_bindings/RenderTextureHost.cpp
@@ -0,0 +1,58 @@
+/* -*- 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 "RenderTextureHost.h"
+
+#include "GLContext.h"
+#include "RenderThread.h"
+
+namespace mozilla {
+namespace wr {
+
+void ActivateBindAndTexParameteri(gl::GLContext* aGL, GLenum aActiveTexture,
+ GLenum aBindTarget, GLuint aBindTexture) {
+ aGL->fActiveTexture(aActiveTexture);
+ aGL->fBindTexture(aBindTarget, aBindTexture);
+ // Initialize the mip filters to linear by default.
+ aGL->fTexParameteri(aBindTarget, LOCAL_GL_TEXTURE_MIN_FILTER,
+ LOCAL_GL_LINEAR);
+ aGL->fTexParameteri(aBindTarget, LOCAL_GL_TEXTURE_MAG_FILTER,
+ LOCAL_GL_LINEAR);
+}
+
+RenderTextureHost::RenderTextureHost() : mIsFromDRMSource(false) {
+ MOZ_COUNT_CTOR(RenderTextureHost);
+}
+
+RenderTextureHost::~RenderTextureHost() {
+ MOZ_ASSERT(RenderThread::IsInRenderThread());
+ MOZ_COUNT_DTOR(RenderTextureHost);
+}
+
+wr::WrExternalImage RenderTextureHost::Lock(uint8_t aChannelIndex,
+ gl::GLContext* aGL) {
+ return InvalidToWrExternalImage();
+}
+
+wr::WrExternalImage RenderTextureHost::LockSWGL(uint8_t aChannelIndex,
+ void* aContext,
+ RenderCompositor* aCompositor) {
+ return InvalidToWrExternalImage();
+}
+
+std::pair<gfx::Point, gfx::Point> RenderTextureHost::GetUvCoords(
+ gfx::IntSize aTextureSize) const {
+ return std::make_pair(gfx::Point(0.0, 0.0),
+ gfx::Point(static_cast<float>(aTextureSize.width),
+ static_cast<float>(aTextureSize.height)));
+}
+
+void RenderTextureHost::Destroy() {
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+}
+
+} // namespace wr
+} // namespace mozilla
diff --git a/gfx/webrender_bindings/RenderTextureHost.h b/gfx/webrender_bindings/RenderTextureHost.h
new file mode 100644
index 0000000000..19680cc7fa
--- /dev/null
+++ b/gfx/webrender_bindings/RenderTextureHost.h
@@ -0,0 +1,135 @@
+/* -*- 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_RENDERTEXTUREHOST_H
+#define MOZILLA_GFX_RENDERTEXTUREHOST_H
+
+#include "GLConsts.h"
+#include "GLTypes.h"
+#include "nsISupportsImpl.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/layers/LayersSurfaces.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/webrender/webrender_ffi.h"
+#include "mozilla/webrender/WebRenderTypes.h"
+
+namespace mozilla {
+
+namespace gl {
+class GLContext;
+}
+
+namespace wr {
+
+class RenderAndroidHardwareBufferTextureHost;
+class RenderAndroidSurfaceTextureHost;
+class RenderCompositor;
+class RenderDXGITextureHost;
+class RenderDXGIYCbCrTextureHost;
+class RenderDcompSurfaceTextureHost;
+class RenderMacIOSurfaceTextureHost;
+class RenderBufferTextureHost;
+class RenderTextureHostSWGL;
+class RenderTextureHostWrapper;
+
+void ActivateBindAndTexParameteri(gl::GLContext* aGL, GLenum aActiveTexture,
+ GLenum aBindTarget, GLuint aBindTexture);
+
+class RenderTextureHost {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RenderTextureHost)
+
+ public:
+ RenderTextureHost();
+
+ virtual gfx::SurfaceFormat GetFormat() const {
+ return gfx::SurfaceFormat::UNKNOWN;
+ }
+
+ virtual gfx::YUVRangedColorSpace GetYUVColorSpace() const {
+ return gfx::YUVRangedColorSpace::Default;
+ }
+
+ virtual wr::WrExternalImage Lock(uint8_t aChannelIndex, gl::GLContext* aGL);
+
+ virtual void Unlock() {}
+
+ virtual wr::WrExternalImage LockSWGL(uint8_t aChannelIndex, void* aContext,
+ RenderCompositor* aCompositor);
+
+ virtual void UnlockSWGL() {}
+
+ virtual void ClearCachedResources() {}
+
+ // Called asynchronouly when corresponding TextureHost's mCompositableCount
+ // becomes from 0 to 1. For now, it is used only for
+ // SurfaceTextureHost/RenderAndroidSurfaceTextureHost.
+ virtual void PrepareForUse() {}
+ // Called asynchronouly when corresponding TextureHost's is actually going to
+ // be used by WebRender. For now, it is used only for
+ // SurfaceTextureHost/RenderAndroidSurfaceTextureHost.
+ virtual void NotifyForUse() {}
+ // Called asynchronouly when corresponding TextureHost's mCompositableCount
+ // becomes 0. For now, it is used only for
+ // SurfaceTextureHost/RenderAndroidSurfaceTextureHost.
+ virtual void NotifyNotUsed() {}
+ // Returns true when RenderTextureHost needs SyncObjectHost::Synchronize()
+ // call, before its usage.
+ virtual bool SyncObjectNeeded() { return false; }
+ // Returns true when this texture was generated from a DRM-protected source.
+ bool IsFromDRMSource() { return mIsFromDRMSource; }
+ void SetIsFromDRMSource(bool aIsFromDRMSource) {
+ mIsFromDRMSource = aIsFromDRMSource;
+ }
+
+ virtual size_t Bytes() = 0;
+
+ virtual RenderDXGITextureHost* AsRenderDXGITextureHost() { return nullptr; }
+ virtual RenderDXGIYCbCrTextureHost* AsRenderDXGIYCbCrTextureHost() {
+ return nullptr;
+ }
+
+ virtual RenderMacIOSurfaceTextureHost* AsRenderMacIOSurfaceTextureHost() {
+ return nullptr;
+ }
+
+ virtual RenderAndroidHardwareBufferTextureHost*
+ AsRenderAndroidHardwareBufferTextureHost() {
+ return nullptr;
+ }
+
+ virtual RenderAndroidSurfaceTextureHost* AsRenderAndroidSurfaceTextureHost() {
+ return nullptr;
+ }
+
+ virtual RenderTextureHostSWGL* AsRenderTextureHostSWGL() { return nullptr; }
+
+ virtual RenderDcompSurfaceTextureHost* AsRenderDcompSurfaceTextureHost() {
+ return nullptr;
+ }
+
+ virtual bool IsWrappingAsyncRemoteTexture() { return false; }
+
+ virtual void Destroy();
+
+ protected:
+ virtual ~RenderTextureHost();
+
+ // Returns the UV coordinates to be used when sampling the texture, in pixels.
+ // For most implementations these will be (0, 0) and (size.x, size.y), but
+ // some texture types (such as RenderAndroidSurfaceTextureHost) require an
+ // additional transform to be applied to the coordinates.
+ virtual std::pair<gfx::Point, gfx::Point> GetUvCoords(
+ gfx::IntSize aTextureSize) const;
+
+ bool mIsFromDRMSource;
+
+ friend class RenderTextureHostWrapper;
+};
+
+} // namespace wr
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_RENDERTEXTUREHOST_H
diff --git a/gfx/webrender_bindings/RenderTextureHostSWGL.cpp b/gfx/webrender_bindings/RenderTextureHostSWGL.cpp
new file mode 100644
index 0000000000..ad2d960837
--- /dev/null
+++ b/gfx/webrender_bindings/RenderTextureHostSWGL.cpp
@@ -0,0 +1,231 @@
+/* -*- 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 "RenderTextureHostSWGL.h"
+
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/layers/TextureHost.h"
+#include "RenderThread.h"
+
+namespace mozilla {
+namespace wr {
+
+bool RenderTextureHostSWGL::UpdatePlanes(RenderCompositor* aCompositor) {
+ wr_swgl_make_current(mContext);
+ size_t planeCount = GetPlaneCount();
+ bool texInit = false;
+ if (mPlanes.size() < planeCount) {
+ mPlanes.reserve(planeCount);
+ while (mPlanes.size() < planeCount) {
+ mPlanes.push_back(PlaneInfo(wr_swgl_gen_texture(mContext)));
+ }
+ texInit = true;
+ }
+ gfx::SurfaceFormat format = GetFormat();
+ gfx::ColorDepth colorDepth = GetColorDepth();
+ for (size_t i = 0; i < planeCount; i++) {
+ PlaneInfo& plane = mPlanes[i];
+ if (!MapPlane(aCompositor, i, plane)) {
+ if (i > 0) {
+ UnmapPlanes();
+ }
+ return false;
+ }
+ GLenum internalFormat = 0;
+ switch (format) {
+ case gfx::SurfaceFormat::B8G8R8A8:
+ case gfx::SurfaceFormat::B8G8R8X8:
+ MOZ_ASSERT(colorDepth == gfx::ColorDepth::COLOR_8);
+ internalFormat = LOCAL_GL_RGBA8;
+ break;
+ case gfx::SurfaceFormat::YUV:
+ switch (colorDepth) {
+ case gfx::ColorDepth::COLOR_8:
+ internalFormat = LOCAL_GL_R8;
+ break;
+ case gfx::ColorDepth::COLOR_10:
+ case gfx::ColorDepth::COLOR_12:
+ case gfx::ColorDepth::COLOR_16:
+ internalFormat = LOCAL_GL_R16;
+ break;
+ }
+ break;
+ case gfx::SurfaceFormat::NV12:
+ switch (colorDepth) {
+ case gfx::ColorDepth::COLOR_8:
+ internalFormat = i > 0 ? LOCAL_GL_RG8 : LOCAL_GL_R8;
+ break;
+ case gfx::ColorDepth::COLOR_10:
+ case gfx::ColorDepth::COLOR_12:
+ case gfx::ColorDepth::COLOR_16:
+ internalFormat = i > 0 ? LOCAL_GL_RG16 : LOCAL_GL_R16;
+ break;
+ }
+ break;
+ case gfx::SurfaceFormat::P010:
+ MOZ_ASSERT(colorDepth == gfx::ColorDepth::COLOR_10);
+ internalFormat = i > 0 ? LOCAL_GL_RG16 : LOCAL_GL_R16;
+ break;
+ case gfx::SurfaceFormat::YUV422:
+ MOZ_ASSERT(colorDepth == gfx::ColorDepth::COLOR_8);
+ internalFormat = LOCAL_GL_RGB_RAW_422_APPLE;
+ break;
+ default:
+ MOZ_RELEASE_ASSERT(false, "Unhandled external image format");
+ break;
+ }
+ wr_swgl_set_texture_buffer(mContext, plane.mTexture, internalFormat,
+ plane.mSize.width, plane.mSize.height,
+ plane.mStride, plane.mData, 0, 0);
+ }
+ if (texInit) {
+ // Initialize the mip filters to linear by default.
+ for (const auto& plane : mPlanes) {
+ wr_swgl_set_texture_parameter(mContext, plane.mTexture,
+ LOCAL_GL_TEXTURE_MIN_FILTER,
+ LOCAL_GL_LINEAR);
+ wr_swgl_set_texture_parameter(mContext, plane.mTexture,
+ LOCAL_GL_TEXTURE_MAG_FILTER,
+ LOCAL_GL_LINEAR);
+ }
+ }
+ return true;
+}
+
+bool RenderTextureHostSWGL::SetContext(void* aContext) {
+ if (mContext != aContext) {
+ CleanupPlanes();
+ mContext = aContext;
+ wr_swgl_reference_context(mContext);
+ }
+ return mContext != nullptr;
+}
+
+wr::WrExternalImage RenderTextureHostSWGL::LockSWGL(
+ uint8_t aChannelIndex, void* aContext, RenderCompositor* aCompositor) {
+ if (!SetContext(aContext)) {
+ return InvalidToWrExternalImage();
+ }
+ if (!mLocked) {
+ if (!UpdatePlanes(aCompositor)) {
+ return InvalidToWrExternalImage();
+ }
+ mLocked = true;
+ }
+ if (aChannelIndex >= mPlanes.size()) {
+ return InvalidToWrExternalImage();
+ }
+ const PlaneInfo& plane = mPlanes[aChannelIndex];
+
+ const auto uvs = GetUvCoords(plane.mSize);
+
+ // Prefer native textures, unless our backend forbids it.
+ // If the GetUvCoords call above returned anything other than the default,
+ // for example if this is a RenderAndroidSurfaceTextureHost, then this won't
+ // be handled correctly in the RawDataToWrExternalImage path. But we shouldn't
+ // hit this path in practice with a RenderAndroidSurfaceTextureHost.
+ layers::TextureHost::NativeTexturePolicy policy =
+ layers::TextureHost::BackendNativeTexturePolicy(
+ layers::WebRenderBackend::SOFTWARE, plane.mSize);
+ return policy == layers::TextureHost::NativeTexturePolicy::FORBID
+ ? RawDataToWrExternalImage((uint8_t*)plane.mData,
+ plane.mStride * plane.mSize.height)
+ : NativeTextureToWrExternalImage(plane.mTexture, uvs.first.x,
+ uvs.first.y, uvs.second.x,
+ uvs.second.y);
+}
+
+void RenderTextureHostSWGL::UnlockSWGL() {
+ if (mLocked) {
+ mLocked = false;
+ UnmapPlanes();
+ }
+}
+
+void RenderTextureHostSWGL::CleanupPlanes() {
+ if (!mContext) {
+ return;
+ }
+ if (!mPlanes.empty()) {
+ wr_swgl_make_current(mContext);
+ for (const auto& plane : mPlanes) {
+ wr_swgl_delete_texture(mContext, plane.mTexture);
+ }
+ mPlanes.clear();
+ }
+ wr_swgl_destroy_context(mContext);
+ mContext = nullptr;
+}
+
+RenderTextureHostSWGL::~RenderTextureHostSWGL() { CleanupPlanes(); }
+
+bool RenderTextureHostSWGL::LockSWGLCompositeSurface(
+ void* aContext, wr::SWGLCompositeSurfaceInfo* aInfo) {
+ if (!SetContext(aContext)) {
+ return false;
+ }
+ if (!mLocked) {
+ if (!UpdatePlanes(nullptr)) {
+ return false;
+ }
+ mLocked = true;
+ }
+ MOZ_ASSERT(mPlanes.size() <= 3);
+ for (size_t i = 0; i < mPlanes.size(); i++) {
+ aInfo->textures[i] = mPlanes[i].mTexture;
+ }
+ switch (GetFormat()) {
+ case gfx::SurfaceFormat::YUV:
+ case gfx::SurfaceFormat::NV12:
+ case gfx::SurfaceFormat::P010:
+ case gfx::SurfaceFormat::YUV422: {
+ aInfo->yuv_planes = mPlanes.size();
+ auto colorSpace = GetYUVColorSpace();
+ aInfo->color_space = ToWrYuvRangedColorSpace(colorSpace);
+ auto colorDepth = GetColorDepth();
+ aInfo->color_depth = ToWrColorDepth(colorDepth);
+ break;
+ }
+ case gfx::SurfaceFormat::B8G8R8A8:
+ case gfx::SurfaceFormat::B8G8R8X8:
+ break;
+ default:
+ gfxCriticalNote << "Unhandled external image format: " << GetFormat();
+ MOZ_RELEASE_ASSERT(false, "Unhandled external image format");
+ break;
+ }
+ aInfo->size.width = mPlanes[0].mSize.width;
+ aInfo->size.height = mPlanes[0].mSize.height;
+ return true;
+}
+
+bool wr_swgl_lock_composite_surface(void* aContext, wr::ExternalImageId aId,
+ wr::SWGLCompositeSurfaceInfo* aInfo) {
+ RenderTextureHost* texture = RenderThread::Get()->GetRenderTexture(aId);
+ if (!texture) {
+ return false;
+ }
+ RenderTextureHostSWGL* swglTex = texture->AsRenderTextureHostSWGL();
+ if (!swglTex) {
+ return false;
+ }
+ return swglTex->LockSWGLCompositeSurface(aContext, aInfo);
+}
+
+void wr_swgl_unlock_composite_surface(void* aContext, wr::ExternalImageId aId) {
+ RenderTextureHost* texture = RenderThread::Get()->GetRenderTexture(aId);
+ if (!texture) {
+ return;
+ }
+ RenderTextureHostSWGL* swglTex = texture->AsRenderTextureHostSWGL();
+ if (!swglTex) {
+ return;
+ }
+ swglTex->UnlockSWGL();
+}
+
+} // namespace wr
+} // namespace mozilla
diff --git a/gfx/webrender_bindings/RenderTextureHostSWGL.h b/gfx/webrender_bindings/RenderTextureHostSWGL.h
new file mode 100644
index 0000000000..b5d1e92056
--- /dev/null
+++ b/gfx/webrender_bindings/RenderTextureHostSWGL.h
@@ -0,0 +1,80 @@
+/* -*- 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_RENDERTEXTUREHOSTSWGL_H
+#define MOZILLA_GFX_RENDERTEXTUREHOSTSWGL_H
+
+#include "GLTypes.h"
+#include "RenderTextureHost.h"
+
+namespace mozilla {
+namespace wr {
+
+class RenderTextureHostSWGL : public RenderTextureHost {
+ public:
+ RenderTextureHostSWGL() {}
+
+ wr::WrExternalImage LockSWGL(uint8_t aChannelIndex, void* aContext,
+ RenderCompositor* aCompositor) override;
+
+ void UnlockSWGL() override;
+
+ RenderTextureHostSWGL* AsRenderTextureHostSWGL() override { return this; }
+
+ virtual size_t GetPlaneCount() const = 0;
+
+ virtual gfx::ColorDepth GetColorDepth() const {
+ return gfx::ColorDepth::COLOR_8;
+ }
+
+ struct PlaneInfo {
+ explicit PlaneInfo(GLuint aTexture) : mTexture(aTexture) {}
+
+ GLuint mTexture = 0;
+ void* mData = nullptr;
+ int32_t mStride = 0;
+ gfx::IntSize mSize;
+ };
+
+ virtual bool MapPlane(RenderCompositor* aCompositor, uint8_t aChannelIndex,
+ PlaneInfo& aPlaneInfo) = 0;
+
+ virtual void UnmapPlanes() = 0;
+
+ // Lock this texture host as an attached external image for a SWGL compositor
+ // surface. See swgl_bindings.rs for a description of the resulting
+ // WrSWGLCompositeSurfaceInfo. This is paired with a call to UnlockSWGL when
+ // composition is done.
+ bool LockSWGLCompositeSurface(void* aContext,
+ wr::SWGLCompositeSurfaceInfo* aInfo);
+
+ size_t BytesFromPlanes() {
+ NS_ASSERTION(mPlanes.size(), "Can't compute bytes without any planes");
+ size_t bytes = 0;
+ for (auto& plane : mPlanes) {
+ bytes += plane.mStride * plane.mSize.height;
+ }
+ return bytes;
+ }
+
+ protected:
+ bool mLocked = false;
+ void* mContext = nullptr;
+ std::vector<PlaneInfo> mPlanes;
+
+ bool SetContext(void* aContext);
+
+ bool UpdatePlanes(RenderCompositor* aCompositor);
+
+ void CleanupPlanes();
+
+ virtual ~RenderTextureHostSWGL();
+};
+
+} // namespace wr
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_RENDERTEXTUREHOSTSWGL_H
diff --git a/gfx/webrender_bindings/RenderTextureHostWrapper.cpp b/gfx/webrender_bindings/RenderTextureHostWrapper.cpp
new file mode 100644
index 0000000000..7cc03fb978
--- /dev/null
+++ b/gfx/webrender_bindings/RenderTextureHostWrapper.cpp
@@ -0,0 +1,262 @@
+/* -*- 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 "RenderTextureHostWrapper.h"
+
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/layers/RemoteTextureMap.h"
+#include "mozilla/webrender/RenderThread.h"
+
+namespace mozilla {
+namespace wr {
+
+RenderTextureHostWrapper::RenderTextureHostWrapper(
+ ExternalImageId aExternalImageId)
+ : mExternalImageId(aExternalImageId) {
+ MOZ_COUNT_CTOR_INHERITED(RenderTextureHostWrapper, RenderTextureHost);
+ EnsureTextureHost();
+}
+
+RenderTextureHostWrapper::RenderTextureHostWrapper(
+ const layers::RemoteTextureId aTextureId,
+ const layers::RemoteTextureOwnerId aOwnerId, const base::ProcessId aForPid)
+ : mExternalImageId({}),
+ mTextureId(Some(aTextureId)),
+ mOwnerId(Some(aOwnerId)),
+ mForPid(Some(aForPid)) {
+ MOZ_COUNT_CTOR_INHERITED(RenderTextureHostWrapper, RenderTextureHost);
+}
+
+RenderTextureHostWrapper::~RenderTextureHostWrapper() {
+ MOZ_COUNT_DTOR_INHERITED(RenderTextureHostWrapper, RenderTextureHost);
+}
+
+void RenderTextureHostWrapper::EnsureTextureHost() const {
+ MOZ_ASSERT(mTextureId.isNothing());
+
+ if (mTextureHost) {
+ return;
+ }
+
+ mTextureHost = RenderThread::Get()->GetRenderTexture(mExternalImageId);
+ MOZ_ASSERT(mTextureHost);
+ if (!mTextureHost) {
+ gfxCriticalNoteOnce << "Failed to get RenderTextureHost for extId:"
+ << AsUint64(mExternalImageId);
+ }
+}
+
+void RenderTextureHostWrapper::EnsureRemoteTexture() const {
+ MOZ_ASSERT(mTextureId.isSome());
+
+ if (mTextureHost) {
+ return;
+ }
+
+ auto externalImageId =
+ layers::RemoteTextureMap::Get()->GetExternalImageIdOfRemoteTexture(
+ *mTextureId, *mOwnerId, *mForPid);
+ if (externalImageId.isNothing()) {
+ // This could happen with IPC abnormal shutdown
+ return;
+ }
+
+ mTextureHost = RenderThread::Get()->GetRenderTexture(*externalImageId);
+ MOZ_ASSERT(mTextureHost);
+ if (!mTextureHost) {
+ gfxCriticalNoteOnce << "Failed to get RenderTextureHost for extId:"
+ << AsUint64(*externalImageId);
+ }
+}
+
+wr::WrExternalImage RenderTextureHostWrapper::Lock(uint8_t aChannelIndex,
+ gl::GLContext* aGL) {
+ if (mTextureId.isSome()) {
+ EnsureRemoteTexture();
+ }
+
+ if (!mTextureHost) {
+ return InvalidToWrExternalImage();
+ }
+
+ return mTextureHost->Lock(aChannelIndex, aGL);
+}
+
+void RenderTextureHostWrapper::Unlock() {
+ if (mTextureHost) {
+ mTextureHost->Unlock();
+ }
+}
+
+std::pair<gfx::Point, gfx::Point> RenderTextureHostWrapper::GetUvCoords(
+ gfx::IntSize aTextureSize) const {
+ if (mTextureHost) {
+ return mTextureHost->GetUvCoords(aTextureSize);
+ }
+ return RenderTextureHost::GetUvCoords(aTextureSize);
+}
+
+void RenderTextureHostWrapper::ClearCachedResources() {
+ if (mTextureHost) {
+ mTextureHost->ClearCachedResources();
+ }
+}
+
+void RenderTextureHostWrapper::PrepareForUse() {
+ if (!mTextureHost) {
+ return;
+ }
+ mTextureHost->PrepareForUse();
+}
+
+void RenderTextureHostWrapper::NotifyForUse() {
+ if (!mTextureHost) {
+ return;
+ }
+ mTextureHost->NotifyForUse();
+}
+
+void RenderTextureHostWrapper::NotifyNotUsed() {
+ if (!mTextureHost) {
+ return;
+ }
+ mTextureHost->NotifyNotUsed();
+}
+
+bool RenderTextureHostWrapper::SyncObjectNeeded() { return false; }
+
+RenderMacIOSurfaceTextureHost*
+RenderTextureHostWrapper::AsRenderMacIOSurfaceTextureHost() {
+ if (mTextureId.isSome()) {
+ EnsureRemoteTexture();
+ }
+ if (!mTextureHost) {
+ return nullptr;
+ }
+ return mTextureHost->AsRenderMacIOSurfaceTextureHost();
+}
+
+RenderDXGITextureHost* RenderTextureHostWrapper::AsRenderDXGITextureHost() {
+ if (mTextureId.isSome()) {
+ EnsureRemoteTexture();
+ }
+ if (!mTextureHost) {
+ return nullptr;
+ }
+ return mTextureHost->AsRenderDXGITextureHost();
+}
+
+RenderDXGIYCbCrTextureHost*
+RenderTextureHostWrapper::AsRenderDXGIYCbCrTextureHost() {
+ if (mTextureId.isSome()) {
+ EnsureRemoteTexture();
+ }
+ if (!mTextureHost) {
+ return nullptr;
+ }
+ return mTextureHost->AsRenderDXGIYCbCrTextureHost();
+}
+
+RenderDcompSurfaceTextureHost*
+RenderTextureHostWrapper::AsRenderDcompSurfaceTextureHost() {
+ if (!mTextureHost) {
+ return nullptr;
+ }
+ return mTextureHost->AsRenderDcompSurfaceTextureHost();
+}
+
+RenderTextureHostSWGL* RenderTextureHostWrapper::AsRenderTextureHostSWGL() {
+ if (mTextureId.isSome()) {
+ EnsureRemoteTexture();
+ }
+ if (!mTextureHost) {
+ return nullptr;
+ }
+ return mTextureHost->AsRenderTextureHostSWGL();
+}
+
+RenderAndroidHardwareBufferTextureHost*
+RenderTextureHostWrapper::AsRenderAndroidHardwareBufferTextureHost() {
+ if (mTextureId.isSome()) {
+ EnsureRemoteTexture();
+ }
+ if (!mTextureHost) {
+ return nullptr;
+ }
+ return mTextureHost->AsRenderAndroidHardwareBufferTextureHost();
+}
+
+RenderAndroidSurfaceTextureHost*
+RenderTextureHostWrapper::AsRenderAndroidSurfaceTextureHost() {
+ if (mTextureId.isSome()) {
+ EnsureRemoteTexture();
+ }
+ if (!mTextureHost) {
+ return nullptr;
+ }
+ return mTextureHost->AsRenderAndroidSurfaceTextureHost();
+}
+
+RenderTextureHostSWGL* RenderTextureHostWrapper::EnsureRenderTextureHostSWGL()
+ const {
+ if (mTextureId.isSome()) {
+ EnsureRemoteTexture();
+ }
+ if (!mTextureHost) {
+ return nullptr;
+ }
+ return mTextureHost->AsRenderTextureHostSWGL();
+}
+
+bool RenderTextureHostWrapper::IsWrappingAsyncRemoteTexture() {
+ return mTextureId.isSome();
+}
+
+size_t RenderTextureHostWrapper::GetPlaneCount() const {
+ if (RenderTextureHostSWGL* swglHost = EnsureRenderTextureHostSWGL()) {
+ return swglHost->GetPlaneCount();
+ }
+ return 0;
+}
+
+gfx::SurfaceFormat RenderTextureHostWrapper::GetFormat() const {
+ if (RenderTextureHostSWGL* swglHost = EnsureRenderTextureHostSWGL()) {
+ return swglHost->GetFormat();
+ }
+ return gfx::SurfaceFormat::UNKNOWN;
+}
+
+gfx::ColorDepth RenderTextureHostWrapper::GetColorDepth() const {
+ if (RenderTextureHostSWGL* swglHost = EnsureRenderTextureHostSWGL()) {
+ return swglHost->GetColorDepth();
+ }
+ return gfx::ColorDepth::COLOR_8;
+}
+
+gfx::YUVRangedColorSpace RenderTextureHostWrapper::GetYUVColorSpace() const {
+ if (RenderTextureHostSWGL* swglHost = EnsureRenderTextureHostSWGL()) {
+ return swglHost->GetYUVColorSpace();
+ }
+ return gfx::YUVRangedColorSpace::Default;
+}
+
+bool RenderTextureHostWrapper::MapPlane(RenderCompositor* aCompositor,
+ uint8_t aChannelIndex,
+ PlaneInfo& aPlaneInfo) {
+ if (RenderTextureHostSWGL* swglHost = EnsureRenderTextureHostSWGL()) {
+ return swglHost->MapPlane(aCompositor, aChannelIndex, aPlaneInfo);
+ }
+ return false;
+}
+
+void RenderTextureHostWrapper::UnmapPlanes() {
+ if (RenderTextureHostSWGL* swglHost = EnsureRenderTextureHostSWGL()) {
+ swglHost->UnmapPlanes();
+ }
+}
+
+} // namespace wr
+} // namespace mozilla
diff --git a/gfx/webrender_bindings/RenderTextureHostWrapper.h b/gfx/webrender_bindings/RenderTextureHostWrapper.h
new file mode 100644
index 0000000000..b08aaf7c19
--- /dev/null
+++ b/gfx/webrender_bindings/RenderTextureHostWrapper.h
@@ -0,0 +1,88 @@
+/* -*- 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_RENDERTEXTUREHOSTWRAPPER_H
+#define MOZILLA_GFX_RENDERTEXTUREHOSTWRAPPER_H
+
+#include "RenderTextureHostSWGL.h"
+
+namespace mozilla {
+
+namespace wr {
+
+/**
+ * RenderTextureHost of GPUVideoTextureHost.
+ *
+ * GPUVideoTextureHost wraps TextureHost. This class wraps RenderTextureHost of
+ * the wrapped TextureHost. Lifetime of the wrapped TextureHost is usually
+ * longer than GPUVideoTextureHost and the wrapped TextureHost is used by
+ * multiple GPUVideoTextureHosts. This class is used to reduce recreations of
+ * the wrappded RenderTextureHost. Initializations of some
+ * RenderTextureHosts(RenderDXGITextureHost and
+ * RenderDXGIYCbCrTextureHost) have overhead.
+ */
+class RenderTextureHostWrapper final : public RenderTextureHostSWGL {
+ public:
+ explicit RenderTextureHostWrapper(ExternalImageId aExternalImageId);
+ RenderTextureHostWrapper(const layers::RemoteTextureId aTextureId,
+ const layers::RemoteTextureOwnerId aOwnerId,
+ const base::ProcessId aForPid);
+
+ // RenderTextureHost
+ wr::WrExternalImage Lock(uint8_t aChannelIndex, gl::GLContext* aGL) override;
+ void Unlock() override;
+ void ClearCachedResources() override;
+ void PrepareForUse() override;
+ void NotifyForUse() override;
+ void NotifyNotUsed() override;
+ bool SyncObjectNeeded() override;
+ RenderMacIOSurfaceTextureHost* AsRenderMacIOSurfaceTextureHost() override;
+ RenderDXGITextureHost* AsRenderDXGITextureHost() override;
+ RenderDXGIYCbCrTextureHost* AsRenderDXGIYCbCrTextureHost() override;
+ RenderDcompSurfaceTextureHost* AsRenderDcompSurfaceTextureHost() override;
+ RenderAndroidHardwareBufferTextureHost*
+ AsRenderAndroidHardwareBufferTextureHost() override;
+ RenderAndroidSurfaceTextureHost* AsRenderAndroidSurfaceTextureHost() override;
+ RenderTextureHostSWGL* AsRenderTextureHostSWGL() override;
+ bool IsWrappingAsyncRemoteTexture() override;
+
+ // RenderTextureHostSWGL
+ size_t GetPlaneCount() const override;
+ gfx::SurfaceFormat GetFormat() const override;
+ gfx::ColorDepth GetColorDepth() const override;
+ gfx::YUVRangedColorSpace GetYUVColorSpace() const override;
+ bool MapPlane(RenderCompositor* aCompositor, uint8_t aChannelIndex,
+ PlaneInfo& aPlaneInfo) override;
+ void UnmapPlanes() override;
+
+ // This is just a wrapper, so doesn't need to report the
+ // size of the wrapped object (which reports itself).
+ size_t Bytes() override { return 0; }
+
+ protected:
+ // RenderTextureHost
+ std::pair<gfx::Point, gfx::Point> GetUvCoords(
+ gfx::IntSize aTextureSize) const override;
+
+ private:
+ ~RenderTextureHostWrapper() override;
+
+ void EnsureTextureHost() const;
+ void EnsureRemoteTexture() const;
+ RenderTextureHostSWGL* EnsureRenderTextureHostSWGL() const;
+
+ ExternalImageId mExternalImageId;
+ mutable RefPtr<RenderTextureHost> mTextureHost;
+
+ Maybe<layers::RemoteTextureId> mTextureId;
+ Maybe<layers::RemoteTextureOwnerId> mOwnerId;
+ Maybe<base::ProcessId> mForPid;
+};
+
+} // namespace wr
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_RENDERTEXTUREHOSTWRAPPER_H
diff --git a/gfx/webrender_bindings/RenderThread.cpp b/gfx/webrender_bindings/RenderThread.cpp
new file mode 100644
index 0000000000..ec47c9a213
--- /dev/null
+++ b/gfx/webrender_bindings/RenderThread.cpp
@@ -0,0 +1,1648 @@
+/* -*- 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 "base/task.h"
+#include "GeckoProfiler.h"
+#include "gfxPlatform.h"
+#include "GLContext.h"
+#include "RenderThread.h"
+#include "nsThread.h"
+#include "nsThreadUtils.h"
+#include "transport/runnable_utils.h"
+#include "mozilla/BackgroundHangMonitor.h"
+#include "mozilla/layers/AsyncImagePipelineManager.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/GPUParent.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/glean/GleanMetrics.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/CompositorManagerParent.h"
+#include "mozilla/layers/WebRenderBridgeParent.h"
+#include "mozilla/layers/SharedSurfacesParent.h"
+#include "mozilla/layers/SurfacePool.h"
+#include "mozilla/layers/SynchronousTask.h"
+#include "mozilla/PerfStats.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/webrender/RendererOGL.h"
+#include "mozilla/webrender/RenderTextureHost.h"
+#include "mozilla/widget/CompositorWidget.h"
+#include "OGLShaderProgram.h"
+
+#ifdef XP_WIN
+# include "GLContextEGL.h"
+# include "GLLibraryEGL.h"
+# include "mozilla/widget/WinCompositorWindowThread.h"
+# include "mozilla/gfx/DeviceManagerDx.h"
+# include "mozilla/webrender/DCLayerTree.h"
+// # include "nsWindowsHelpers.h"
+// # include <d3d11.h>
+#endif
+
+#ifdef MOZ_WIDGET_ANDROID
+# include "GLLibraryEGL.h"
+# include "mozilla/webrender/RenderAndroidSurfaceTextureHost.h"
+#endif
+
+#ifdef MOZ_WIDGET_GTK
+# include "mozilla/WidgetUtilsGtk.h"
+#endif
+
+#ifdef MOZ_WAYLAND
+# include "GLLibraryEGL.h"
+#endif
+
+using namespace mozilla;
+
+static already_AddRefed<gl::GLContext> CreateGLContext(nsACString& aError);
+
+MOZ_DEFINE_MALLOC_SIZE_OF(WebRenderRendererMallocSizeOf)
+
+namespace mozilla::wr {
+
+LazyLogModule gRenderThreadLog("RenderThread");
+// Should be called only on RenderThread, since LazyLogModule is not thread safe
+#define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
+
+static StaticRefPtr<RenderThread> sRenderThread;
+static mozilla::BackgroundHangMonitor* sBackgroundHangMonitor;
+#ifdef DEBUG
+static bool sRenderThreadEverStarted = false;
+#endif
+
+RenderThread::RenderThread(RefPtr<nsIThread> aThread)
+ : mThread(std::move(aThread)),
+ mThreadPool(false),
+ mThreadPoolLP(true),
+ mSingletonGLIsForHardwareWebRender(true),
+ mWindowInfos("RenderThread.mWindowInfos"),
+ mRenderTextureMapLock("RenderThread.mRenderTextureMapLock"),
+ mHasShutdown(false),
+ mHandlingDeviceReset(false),
+ mHandlingWebRenderError(false) {}
+
+RenderThread::~RenderThread() { MOZ_ASSERT(mRenderTexturesDeferred.empty()); }
+
+// static
+RenderThread* RenderThread::Get() { return sRenderThread; }
+
+// static
+void RenderThread::Start(uint32_t aNamespace) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!sRenderThread);
+
+#ifdef DEBUG
+ // Check to ensure nobody will try to ever start us more than once during
+ // the process' lifetime (in particular after ShutDown).
+ MOZ_ASSERT(!sRenderThreadEverStarted);
+ sRenderThreadEverStarted = true;
+#endif
+
+ // When the CanvasRenderer thread is disabled, WebGL may be handled on this
+ // thread, requiring a bigger stack size. See: CanvasManagerParent::Init
+ //
+ // This is 4M, which is higher than the default 256K.
+ // Increased with bug 1753349 to accommodate the `chromium/5359` branch of
+ // ANGLE, which has large peak stack usage for some pathological shader
+ // compilations.
+ //
+ // Previously increased to 512K to accommodate Mesa in bug 1753340.
+ //
+ // Previously increased to 320K to avoid a stack overflow in the
+ // Intel Vulkan driver initialization in bug 1716120.
+ //
+ // Note: we only override it if it's limited already.
+ uint32_t stackSize = nsIThreadManager::DEFAULT_STACK_SIZE;
+ if (stackSize && !gfx::gfxVars::SupportsThreadsafeGL()) {
+ stackSize = std::max(stackSize, 4096U << 10);
+ }
+
+ RefPtr<nsIThread> thread;
+ nsresult rv = NS_NewNamedThread(
+ "Renderer", getter_AddRefs(thread),
+ NS_NewRunnableFunction(
+ "Renderer::BackgroundHanSetup",
+ []() {
+ sBackgroundHangMonitor = new mozilla::BackgroundHangMonitor(
+ "Render",
+ /* Timeout values are powers-of-two to enable us get better
+ data. 128ms is chosen for transient hangs because 8Hz should
+ be the minimally acceptable goal for Render
+ responsiveness (normal goal is 60Hz). */
+ 128,
+ /* 2048ms is chosen for permanent hangs because it's longer than
+ * most Render hangs seen in the wild, but is short enough
+ * to not miss getting native hang stacks. */
+ 2048);
+ nsCOMPtr<nsIThread> thread = NS_GetCurrentThread();
+ nsThread* nsthread = static_cast<nsThread*>(thread.get());
+ nsthread->SetUseHangMonitor(true);
+ nsthread->SetPriority(nsISupportsPriority::PRIORITY_HIGH);
+ }),
+ {.stackSize = stackSize});
+
+ if (NS_FAILED(rv)) {
+ gfxCriticalNote << "Failed to create Renderer thread: "
+ << gfx::hexa((uint32_t)rv);
+ return;
+ }
+
+ sRenderThread = new RenderThread(thread);
+#ifdef XP_WIN
+ widget::WinCompositorWindowThread::Start();
+#endif
+ layers::SharedSurfacesParent::Initialize();
+
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<RenderThread>(sRenderThread.get()), &RenderThread::InitDeviceTask);
+ sRenderThread->PostRunnable(runnable.forget());
+}
+
+// static
+void RenderThread::ShutDown() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(sRenderThread);
+
+ {
+ MutexAutoLock lock(sRenderThread->mRenderTextureMapLock);
+ sRenderThread->mHasShutdown = true;
+ }
+
+ RefPtr<Runnable> runnable = WrapRunnable(
+ RefPtr<RenderThread>(sRenderThread.get()), &RenderThread::ShutDownTask);
+ sRenderThread->PostRunnable(runnable.forget());
+
+ // This will empty the thread queue and thus run the above runnable while
+ // spinning the MT event loop.
+ nsCOMPtr<nsIThread> oldThread = sRenderThread->GetRenderThread();
+ oldThread->Shutdown();
+
+ layers::SharedSurfacesParent::Shutdown();
+
+#ifdef XP_WIN
+ if (widget::WinCompositorWindowThread::Get()) {
+ widget::WinCompositorWindowThread::ShutDown();
+ }
+#endif
+
+ // We null this out only after we finished shutdown to give everbody the
+ // chance to check for sRenderThread->mHasShutdown. Hopefully everybody
+ // checks this before using us!
+ sRenderThread = nullptr;
+}
+
+extern void ClearAllBlobImageResources();
+
+void RenderThread::ShutDownTask() {
+ MOZ_ASSERT(IsInRenderThread());
+ LOG("RenderThread::ShutDownTask()");
+
+ {
+ // Clear RenderTextureHosts
+ MutexAutoLock lock(mRenderTextureMapLock);
+ mRenderTexturesDeferred.clear();
+ mRenderTextures.clear();
+ mSyncObjectNeededRenderTextures.clear();
+ mRenderTextureOps.clear();
+ }
+
+ // Let go of our handle to the (internally ref-counted) thread pool.
+ mThreadPool.Release();
+ mThreadPoolLP.Release();
+
+ // Releasing on the render thread will allow us to avoid dispatching to remove
+ // remaining textures from the texture map.
+ layers::SharedSurfacesParent::ShutdownRenderThread();
+
+#ifdef XP_WIN
+ DCLayerTree::Shutdown();
+#endif
+
+ ClearAllBlobImageResources();
+ ClearSingletonGL();
+ ClearSharedSurfacePool();
+}
+
+// static
+bool RenderThread::IsInRenderThread() {
+ return sRenderThread && sRenderThread->mThread == NS_GetCurrentThread();
+}
+
+// static
+already_AddRefed<nsIThread> RenderThread::GetRenderThread() {
+ nsCOMPtr<nsIThread> thread;
+ if (sRenderThread) {
+ thread = sRenderThread->mThread;
+ }
+ return thread.forget();
+}
+
+void RenderThread::DoAccumulateMemoryReport(
+ MemoryReport aReport,
+ const RefPtr<MemoryReportPromise::Private>& aPromise) {
+ MOZ_ASSERT(IsInRenderThread());
+
+ for (auto& r : mRenderers) {
+ r.second->AccumulateMemoryReport(&aReport);
+ }
+
+ // Note memory used by the shader cache, which is shared across all WR
+ // instances.
+ MOZ_ASSERT(aReport.shader_cache == 0);
+ if (mProgramCache) {
+ aReport.shader_cache = wr_program_cache_report_memory(
+ mProgramCache->Raw(), &WebRenderRendererMallocSizeOf);
+ }
+
+ size_t renderTextureMemory = 0;
+ {
+ MutexAutoLock lock(mRenderTextureMapLock);
+ for (const auto& entry : mRenderTextures) {
+ renderTextureMemory += entry.second->Bytes();
+ }
+ }
+ aReport.render_texture_hosts = renderTextureMemory;
+
+ aPromise->Resolve(aReport, __func__);
+}
+
+// static
+RefPtr<MemoryReportPromise> RenderThread::AccumulateMemoryReport(
+ MemoryReport aInitial) {
+ RefPtr<MemoryReportPromise::Private> p =
+ new MemoryReportPromise::Private(__func__);
+ MOZ_ASSERT(!IsInRenderThread());
+ if (!Get()) {
+ // This happens when the GPU process fails to start and we fall back to the
+ // basic compositor in the parent process. We could assert against this if
+ // we made the webrender detection code in gfxPlatform.cpp smarter. See bug
+ // 1494430 comment 12.
+ NS_WARNING("No render thread, returning empty memory report");
+ p->Resolve(aInitial, __func__);
+ return p;
+ }
+
+ Get()->PostRunnable(
+ NewRunnableMethod<MemoryReport, RefPtr<MemoryReportPromise::Private>>(
+ "wr::RenderThread::DoAccumulateMemoryReport", Get(),
+ &RenderThread::DoAccumulateMemoryReport, aInitial, p));
+
+ return p;
+}
+
+void RenderThread::AddRenderer(wr::WindowId aWindowId,
+ UniquePtr<RendererOGL> aRenderer) {
+ MOZ_ASSERT(IsInRenderThread());
+ LOG("RenderThread::AddRenderer() aWindowId %" PRIx64 "", AsUint64(aWindowId));
+
+ if (mHasShutdown) {
+ return;
+ }
+
+ mRenderers[aWindowId] = std::move(aRenderer);
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::GraphicsNumRenderers,
+ (unsigned int)mRenderers.size());
+
+ auto windows = mWindowInfos.Lock();
+ windows->emplace(AsUint64(aWindowId), new WindowInfo());
+ mWrNotifierEventsQueues.emplace(AsUint64(aWindowId),
+ new std::queue<WrNotifierEvent>);
+}
+
+void RenderThread::RemoveRenderer(wr::WindowId aWindowId) {
+ MOZ_ASSERT(IsInRenderThread());
+ LOG("RenderThread::RemoveRenderer() aWindowId %" PRIx64 "",
+ AsUint64(aWindowId));
+
+ if (mHasShutdown) {
+ return;
+ }
+
+ mRenderers.erase(aWindowId);
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::GraphicsNumRenderers,
+ (unsigned int)mRenderers.size());
+
+ if (mRenderers.empty()) {
+ if (mHandlingDeviceReset) {
+ ClearSingletonGL();
+ }
+ mHandlingDeviceReset = false;
+ mHandlingWebRenderError = false;
+ }
+
+ auto windows = mWindowInfos.Lock();
+ auto it = windows->find(AsUint64(aWindowId));
+ MOZ_ASSERT(it != windows->end());
+ windows->erase(it);
+
+ // Defer std::deque<WrNotifierEvent> remove, RemoveRenderer() is called in
+ // HandleWrNotifierEvents().
+ RefPtr<Runnable> runnable =
+ NS_NewRunnableFunction("RenderThread::RemoveRenderer", [aWindowId]() {
+ auto* self = RenderThread::Get();
+ auto it = self->mWrNotifierEventsQueues.find(AsUint64(aWindowId));
+ if (it == self->mWrNotifierEventsQueues.end()) {
+ return;
+ }
+ self->mWrNotifierEventsQueues.erase(it);
+ });
+ RenderThread::Get()->PostRunnable(runnable.forget());
+}
+
+RendererOGL* RenderThread::GetRenderer(wr::WindowId aWindowId) {
+ MOZ_ASSERT(IsInRenderThread());
+
+ auto it = mRenderers.find(aWindowId);
+ MOZ_ASSERT(it != mRenderers.end());
+
+ if (it == mRenderers.end()) {
+ return nullptr;
+ }
+
+ return it->second.get();
+}
+
+size_t RenderThread::RendererCount() const {
+ MOZ_ASSERT(IsInRenderThread());
+ return mRenderers.size();
+}
+
+size_t RenderThread::ActiveRendererCount() const {
+ MOZ_ASSERT(IsInRenderThread());
+ size_t num_active = 0;
+ for (const auto& it : mRenderers) {
+ if (!it.second->IsPaused()) {
+ num_active++;
+ }
+ }
+ return num_active;
+}
+
+void RenderThread::WrNotifierEvent_WakeUp(WrWindowId aWindowId,
+ bool aCompositeNeeded) {
+ auto windows = mWindowInfos.Lock();
+ auto it = windows->find(AsUint64(aWindowId));
+ if (it == windows->end()) {
+ MOZ_ASSERT(false);
+ return;
+ }
+
+ WindowInfo* info = it->second.get();
+
+ info->mPendingWrNotifierEvents.emplace(
+ WrNotifierEvent::WakeUp(aCompositeNeeded));
+ PostWrNotifierEvents(aWindowId, info);
+}
+
+void RenderThread::WrNotifierEvent_NewFrameReady(WrWindowId aWindowId,
+ bool aCompositeNeeded,
+ FramePublishId aPublishId) {
+ auto windows = mWindowInfos.Lock();
+ auto it = windows->find(AsUint64(aWindowId));
+ if (it == windows->end()) {
+ MOZ_ASSERT(false);
+ return;
+ }
+ WindowInfo* info = it->second.get();
+
+ info->mPendingWrNotifierEvents.emplace(
+ WrNotifierEvent::NewFrameReady(aCompositeNeeded, aPublishId));
+ PostWrNotifierEvents(aWindowId, info);
+}
+
+void RenderThread::WrNotifierEvent_ExternalEvent(WrWindowId aWindowId,
+ size_t aRawEvent) {
+ UniquePtr<RendererEvent> evt(reinterpret_cast<RendererEvent*>(aRawEvent));
+ {
+ auto windows = mWindowInfos.Lock();
+ auto it = windows->find(AsUint64(aWindowId));
+ if (it == windows->end()) {
+ MOZ_ASSERT(false);
+ return;
+ }
+ WindowInfo* info = it->second.get();
+
+ info->mPendingWrNotifierEvents.emplace(
+ WrNotifierEvent::ExternalEvent(std::move(evt)));
+ PostWrNotifierEvents(aWindowId, info);
+ }
+}
+
+void RenderThread::PostWrNotifierEvents(WrWindowId aWindowId) {
+ {
+ auto windows = mWindowInfos.Lock();
+ auto it = windows->find(AsUint64(aWindowId));
+ if (it == windows->end()) {
+ MOZ_ASSERT(false);
+ return;
+ }
+ WindowInfo* info = it->second.get();
+ PostWrNotifierEvents(aWindowId, info);
+ }
+}
+
+void RenderThread::PostWrNotifierEvents(WrWindowId aWindowId,
+ WindowInfo* aInfo) {
+ // Runnable has already been triggered.
+ if (aInfo->mWrNotifierEventsRunnable) {
+ return;
+ }
+
+ // Runnable has not been triggered yet.
+ RefPtr<nsIRunnable> runnable = NewRunnableMethod<WrWindowId>(
+ "RenderThread::HandleWrNotifierEvents", this,
+ &RenderThread::HandleWrNotifierEvents, aWindowId);
+ aInfo->mWrNotifierEventsRunnable = runnable;
+ PostRunnable(runnable.forget());
+}
+
+void RenderThread::HandleWrNotifierEvents(WrWindowId aWindowId) {
+ MOZ_ASSERT(IsInRenderThread());
+
+ auto eventsIt = mWrNotifierEventsQueues.find(AsUint64(aWindowId));
+ if (eventsIt == mWrNotifierEventsQueues.end()) {
+ return;
+ }
+ auto* events = eventsIt->second.get();
+
+ {
+ auto windows = mWindowInfos.Lock();
+ auto infoIt = windows->find(AsUint64(aWindowId));
+ if (infoIt == windows->end()) {
+ MOZ_ASSERT(false);
+ return;
+ }
+ WindowInfo* info = infoIt->second.get();
+ info->mWrNotifierEventsRunnable = nullptr;
+
+ if (events->empty() && !info->mPendingWrNotifierEvents.empty()) {
+ events->swap(info->mPendingWrNotifierEvents);
+ }
+ }
+
+ bool handleNext = true;
+
+ while (!events->empty() && handleNext) {
+ auto& front = events->front();
+ switch (front.mTag) {
+ case WrNotifierEvent::Tag::WakeUp:
+ WrNotifierEvent_HandleWakeUp(aWindowId, front.CompositeNeeded());
+ handleNext = false;
+ break;
+ case WrNotifierEvent::Tag::NewFrameReady:
+ WrNotifierEvent_HandleNewFrameReady(aWindowId, front.CompositeNeeded(),
+ front.PublishId());
+ handleNext = false;
+ break;
+ case WrNotifierEvent::Tag::ExternalEvent:
+ WrNotifierEvent_HandleExternalEvent(aWindowId, front.ExternalEvent());
+ break;
+ }
+ events->pop();
+ }
+
+ {
+ auto windows = mWindowInfos.Lock();
+ auto it = windows->find(AsUint64(aWindowId));
+ if (it == windows->end()) {
+ return;
+ }
+ WindowInfo* info = it->second.get();
+
+ if (!events->empty() || !info->mPendingWrNotifierEvents.empty()) {
+ PostWrNotifierEvents(aWindowId, info);
+ }
+ }
+}
+
+void RenderThread::WrNotifierEvent_HandleWakeUp(wr::WindowId aWindowId,
+ bool aCompositeNeeded) {
+ MOZ_ASSERT(IsInRenderThread());
+
+ bool isTrackedFrame = false;
+ HandleFrameOneDoc(aWindowId, aCompositeNeeded, isTrackedFrame, Nothing());
+}
+
+void RenderThread::WrNotifierEvent_HandleNewFrameReady(
+ wr::WindowId aWindowId, bool aCompositeNeeded, FramePublishId aPublishId) {
+ MOZ_ASSERT(IsInRenderThread());
+
+ bool isTrackedFrame = true;
+ HandleFrameOneDoc(aWindowId, aCompositeNeeded, isTrackedFrame,
+ Some(aPublishId));
+}
+
+void RenderThread::WrNotifierEvent_HandleExternalEvent(
+ wr::WindowId aWindowId, UniquePtr<RendererEvent> aRendererEvent) {
+ MOZ_ASSERT(IsInRenderThread());
+
+ RunEvent(aWindowId, std::move(aRendererEvent));
+}
+
+void RenderThread::BeginRecordingForWindow(wr::WindowId aWindowId,
+ const TimeStamp& aRecordingStart,
+ wr::PipelineId aRootPipelineId) {
+ MOZ_ASSERT(IsInRenderThread());
+ RendererOGL* renderer = GetRenderer(aWindowId);
+ MOZ_ASSERT(renderer);
+
+ renderer->BeginRecording(aRecordingStart, aRootPipelineId);
+}
+
+Maybe<layers::FrameRecording> RenderThread::EndRecordingForWindow(
+ wr::WindowId aWindowId) {
+ MOZ_ASSERT(IsInRenderThread());
+
+ RendererOGL* renderer = GetRenderer(aWindowId);
+ MOZ_ASSERT(renderer);
+ return renderer->EndRecording();
+}
+
+void RenderThread::HandleFrameOneDoc(wr::WindowId aWindowId, bool aRender,
+ bool aTrackedFrame,
+ Maybe<FramePublishId> aPublishId) {
+ MOZ_ASSERT(IsInRenderThread());
+
+ if (mHasShutdown) {
+ return;
+ }
+
+ HandleFrameOneDocInner(aWindowId, aRender, aTrackedFrame, aPublishId);
+
+ if (aTrackedFrame) {
+ DecPendingFrameCount(aWindowId);
+ }
+}
+
+void RenderThread::HandleFrameOneDocInner(wr::WindowId aWindowId, bool aRender,
+ bool aTrackedFrame,
+ Maybe<FramePublishId> aPublishId) {
+ if (IsDestroyed(aWindowId)) {
+ return;
+ }
+
+ if (mHandlingDeviceReset) {
+ return;
+ }
+
+ bool render = aRender;
+ PendingFrameInfo frame;
+ if (aTrackedFrame) {
+ // scope lock
+ auto windows = mWindowInfos.Lock();
+ auto it = windows->find(AsUint64(aWindowId));
+ if (it == windows->end()) {
+ MOZ_ASSERT(false);
+ return;
+ }
+
+ WindowInfo* info = it->second.get();
+ PendingFrameInfo& frameInfo = info->mPendingFrames.front();
+
+ frame = frameInfo;
+ } else {
+ // Just give the frame info default values.
+ frame = {TimeStamp::Now(), VsyncId()};
+ }
+
+ // Sadly this doesn't include the lock, since we don't have the frame there
+ // yet.
+ glean::wr::time_to_render_start.AccumulateRawDuration(TimeStamp::Now() -
+ frame.mStartTime);
+
+ // It is for ensuring that PrepareForUse() is called before
+ // RenderTextureHost::Lock().
+ HandleRenderTextureOps();
+
+ if (aPublishId.isSome()) {
+ SetFramePublishId(aWindowId, aPublishId.ref());
+ }
+
+ UpdateAndRender(aWindowId, frame.mStartId, frame.mStartTime, render,
+ /* aReadbackSize */ Nothing(),
+ /* aReadbackFormat */ Nothing(),
+ /* aReadbackBuffer */ Nothing());
+
+ // The start time is from WebRenderBridgeParent::CompositeToTarget. From that
+ // point until now (when the frame is finally pushed to the screen) is
+ // equivalent to the COMPOSITE_TIME metric in the non-WR codepath.
+ TimeDuration compositeDuration = TimeStamp::Now() - frame.mStartTime;
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::COMPOSITE_TIME,
+ uint32_t(compositeDuration.ToMilliseconds()));
+ PerfStats::RecordMeasurement(PerfStats::Metric::Compositing,
+ compositeDuration);
+}
+
+void RenderThread::SetClearColor(wr::WindowId aWindowId, wr::ColorF aColor) {
+ if (mHasShutdown) {
+ return;
+ }
+
+ if (!IsInRenderThread()) {
+ PostRunnable(NewRunnableMethod<wr::WindowId, wr::ColorF>(
+ "wr::RenderThread::SetClearColor", this, &RenderThread::SetClearColor,
+ aWindowId, aColor));
+ return;
+ }
+
+ if (IsDestroyed(aWindowId)) {
+ return;
+ }
+
+ auto it = mRenderers.find(aWindowId);
+ MOZ_ASSERT(it != mRenderers.end());
+ if (it != mRenderers.end()) {
+ wr_renderer_set_clear_color(it->second->GetRenderer(), aColor);
+ }
+}
+
+void RenderThread::SetProfilerUI(wr::WindowId aWindowId,
+ const nsACString& aUI) {
+ if (mHasShutdown) {
+ return;
+ }
+
+ if (!IsInRenderThread()) {
+ PostRunnable(NewRunnableMethod<wr::WindowId, nsCString>(
+ "wr::RenderThread::SetProfilerUI", this, &RenderThread::SetProfilerUI,
+ aWindowId, nsCString(aUI)));
+ return;
+ }
+
+ auto it = mRenderers.find(aWindowId);
+ if (it != mRenderers.end()) {
+ it->second->SetProfilerUI(aUI);
+ }
+}
+
+void RenderThread::PostEvent(wr::WindowId aWindowId,
+ UniquePtr<RendererEvent> aEvent) {
+ PostRunnable(NewRunnableMethod<wr::WindowId, UniquePtr<RendererEvent>&&>(
+ "wr::RenderThread::PostEvent", this, &RenderThread::RunEvent, aWindowId,
+ std::move(aEvent)));
+}
+
+void RenderThread::RunEvent(wr::WindowId aWindowId,
+ UniquePtr<RendererEvent> aEvent) {
+ MOZ_ASSERT(IsInRenderThread());
+
+ aEvent->Run(*this, aWindowId);
+ aEvent = nullptr;
+}
+
+static void NotifyDidRender(layers::CompositorBridgeParent* aBridge,
+ const RefPtr<const WebRenderPipelineInfo>& aInfo,
+ VsyncId aCompositeStartId,
+ TimeStamp aCompositeStart, TimeStamp aRenderStart,
+ TimeStamp aEnd, bool aRender,
+ RendererStats aStats) {
+ if (aRender && aBridge->GetWrBridge()) {
+ // We call this here to mimic the behavior in LayerManagerComposite, as to
+ // not change what Talos measures. That is, we do not record an empty frame
+ // as a frame.
+ aBridge->GetWrBridge()->RecordFrame();
+ }
+
+ aBridge->NotifyDidRender(aCompositeStartId, aCompositeStart, aRenderStart,
+ aEnd, &aStats);
+
+ for (const auto& epoch : aInfo->Raw().epochs) {
+ aBridge->NotifyPipelineRendered(epoch.pipeline_id, epoch.epoch,
+ aCompositeStartId, aCompositeStart,
+ aRenderStart, aEnd, &aStats);
+ }
+
+ if (aBridge->GetWrBridge()) {
+ aBridge->GetWrBridge()->RetrySkippedComposite();
+ }
+}
+
+static void NotifyDidStartRender(layers::CompositorBridgeParent* aBridge) {
+ if (aBridge->GetWrBridge()) {
+ aBridge->GetWrBridge()->RetrySkippedComposite();
+ }
+}
+
+void RenderThread::SetFramePublishId(wr::WindowId aWindowId,
+ FramePublishId aPublishId) {
+ MOZ_ASSERT(IsInRenderThread());
+
+ auto it = mRenderers.find(aWindowId);
+ MOZ_ASSERT(it != mRenderers.end());
+ if (it == mRenderers.end()) {
+ return;
+ }
+ auto& renderer = it->second;
+
+ renderer->SetFramePublishId(aPublishId);
+}
+
+void RenderThread::UpdateAndRender(
+ wr::WindowId aWindowId, const VsyncId& aStartId,
+ const TimeStamp& aStartTime, bool aRender,
+ const Maybe<gfx::IntSize>& aReadbackSize,
+ const Maybe<wr::ImageFormat>& aReadbackFormat,
+ const Maybe<Range<uint8_t>>& aReadbackBuffer, bool* aNeedsYFlip) {
+ AUTO_PROFILER_LABEL("RenderThread::UpdateAndRender", GRAPHICS);
+ MOZ_ASSERT(IsInRenderThread());
+ MOZ_ASSERT(aRender || aReadbackBuffer.isNothing());
+
+ auto it = mRenderers.find(aWindowId);
+ MOZ_ASSERT(it != mRenderers.end());
+ if (it == mRenderers.end()) {
+ return;
+ }
+
+ TimeStamp start = TimeStamp::Now();
+
+ auto& renderer = it->second;
+
+ std::string markerName = "Composite #" + std::to_string(AsUint64(aWindowId));
+ AutoProfilerTracing tracingCompositeMarker(
+ "Paint", markerName.c_str(), geckoprofiler::category::GRAPHICS,
+ Some(renderer->GetCompositorBridge()->GetInnerWindowId()));
+
+ if (renderer->IsPaused()) {
+ aRender = false;
+ }
+ LOG("RenderThread::UpdateAndRender() aWindowId %" PRIx64 " aRender %d",
+ AsUint64(aWindowId), aRender);
+
+ layers::CompositorThread()->Dispatch(
+ NewRunnableFunction("NotifyDidStartRenderRunnable", &NotifyDidStartRender,
+ renderer->GetCompositorBridge()));
+
+ wr::RenderedFrameId latestFrameId;
+ RendererStats stats = {0};
+ if (aRender) {
+ latestFrameId = renderer->UpdateAndRender(
+ aReadbackSize, aReadbackFormat, aReadbackBuffer, aNeedsYFlip, &stats);
+ } else {
+ renderer->Update();
+ }
+ // Check graphics reset status even when rendering is skipped.
+ renderer->CheckGraphicsResetStatus("PostUpdate", /* aForce */ false);
+
+ TimeStamp end = TimeStamp::Now();
+ RefPtr<const WebRenderPipelineInfo> info = renderer->GetLastPipelineInfo();
+
+ layers::CompositorThread()->Dispatch(
+ NewRunnableFunction("NotifyDidRenderRunnable", &NotifyDidRender,
+ renderer->GetCompositorBridge(), info, aStartId,
+ aStartTime, start, end, aRender, stats));
+
+ ipc::FileDescriptor fenceFd;
+
+ if (latestFrameId.IsValid()) {
+ fenceFd = renderer->GetAndResetReleaseFence();
+
+ // Wait for GPU after posting NotifyDidRender, since the wait is not
+ // necessary for the NotifyDidRender.
+ // The wait is necessary for Textures recycling of AsyncImagePipelineManager
+ // and for avoiding GPU queue is filled with too much tasks.
+ // WaitForGPU's implementation is different for each platform.
+ auto timerId = glean::wr::gpu_wait_time.Start();
+ renderer->WaitForGPU();
+ glean::wr::gpu_wait_time.StopAndAccumulate(std::move(timerId));
+ } else {
+ // Update frame id for NotifyPipelinesUpdated() when rendering does not
+ // happen, either because rendering was not requested or the frame was
+ // canceled. Rendering can sometimes be canceled if UpdateAndRender is
+ // called when the window is not yet ready (not mapped or 0 size).
+ latestFrameId = renderer->UpdateFrameId();
+ }
+
+ RenderedFrameId lastCompletedFrameId = renderer->GetLastCompletedFrameId();
+
+ RefPtr<layers::AsyncImagePipelineManager> pipelineMgr =
+ renderer->GetCompositorBridge()->GetAsyncImagePipelineManager();
+ // pipelineMgr should always be non-null here because it is only nulled out
+ // after the WebRenderAPI instance for the CompositorBridgeParent is
+ // destroyed, and that destruction blocks until the renderer thread has
+ // removed the relevant renderer. And after that happens we should never reach
+ // this code at all; it would bail out at the mRenderers.find check above.
+ MOZ_ASSERT(pipelineMgr);
+ pipelineMgr->NotifyPipelinesUpdated(info, latestFrameId, lastCompletedFrameId,
+ std::move(fenceFd));
+}
+
+void RenderThread::Pause(wr::WindowId aWindowId) {
+ MOZ_ASSERT(IsInRenderThread());
+ LOG("RenderThread::Pause() aWindowId %" PRIx64 "", AsUint64(aWindowId));
+
+ auto it = mRenderers.find(aWindowId);
+ MOZ_ASSERT(it != mRenderers.end());
+ if (it == mRenderers.end()) {
+ gfxCriticalNote << "RenderThread cannot find renderer for window "
+ << gfx::hexa(aWindowId) << " to pause.";
+ return;
+ }
+ auto& renderer = it->second;
+ renderer->Pause();
+
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::GraphicsNumActiveRenderers,
+ (unsigned int)ActiveRendererCount());
+}
+
+bool RenderThread::Resume(wr::WindowId aWindowId) {
+ MOZ_ASSERT(IsInRenderThread());
+ LOG("enderThread::Resume() aWindowId %" PRIx64 "", AsUint64(aWindowId));
+
+ auto it = mRenderers.find(aWindowId);
+ MOZ_ASSERT(it != mRenderers.end());
+ if (it == mRenderers.end()) {
+ gfxCriticalNote << "RenderThread cannot find renderer for window "
+ << gfx::hexa(aWindowId) << " to resume.";
+ return false;
+ }
+ auto& renderer = it->second;
+ bool resumed = renderer->Resume();
+
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::GraphicsNumActiveRenderers,
+ (unsigned int)ActiveRendererCount());
+
+ return resumed;
+}
+
+bool RenderThread::TooManyPendingFrames(wr::WindowId aWindowId) {
+ const int64_t maxFrameCount = 1;
+
+ // Too many pending frames if pending frames exit more than maxFrameCount
+ // or if RenderBackend is still processing a frame.
+
+ auto windows = mWindowInfos.Lock();
+ auto it = windows->find(AsUint64(aWindowId));
+ if (it == windows->end()) {
+ MOZ_ASSERT(false);
+ return true;
+ }
+ WindowInfo* info = it->second.get();
+
+ if (info->PendingCount() > maxFrameCount) {
+ return true;
+ }
+ // If there is no ongoing frame build, we accept a new frame.
+ return info->mPendingFrameBuild > 0;
+}
+
+bool RenderThread::IsDestroyed(wr::WindowId aWindowId) {
+ auto windows = mWindowInfos.Lock();
+ auto it = windows->find(AsUint64(aWindowId));
+ if (it == windows->end()) {
+ return true;
+ }
+
+ return it->second->mIsDestroyed;
+}
+
+void RenderThread::SetDestroyed(wr::WindowId aWindowId) {
+ auto windows = mWindowInfos.Lock();
+ auto it = windows->find(AsUint64(aWindowId));
+ if (it == windows->end()) {
+ MOZ_ASSERT(false);
+ return;
+ }
+ it->second->mIsDestroyed = true;
+}
+
+void RenderThread::IncPendingFrameCount(wr::WindowId aWindowId,
+ const VsyncId& aStartId,
+ const TimeStamp& aStartTime) {
+ auto windows = mWindowInfos.Lock();
+ auto it = windows->find(AsUint64(aWindowId));
+ if (it == windows->end()) {
+ MOZ_ASSERT(false);
+ return;
+ }
+ it->second->mPendingFrameBuild++;
+ it->second->mPendingFrames.push(PendingFrameInfo{aStartTime, aStartId});
+}
+
+void RenderThread::DecPendingFrameBuildCount(wr::WindowId aWindowId) {
+ auto windows = mWindowInfos.Lock();
+ auto it = windows->find(AsUint64(aWindowId));
+ if (it == windows->end()) {
+ MOZ_ASSERT(false);
+ return;
+ }
+ WindowInfo* info = it->second.get();
+ MOZ_RELEASE_ASSERT(info->mPendingFrameBuild >= 1);
+ info->mPendingFrameBuild--;
+}
+
+void RenderThread::DecPendingFrameCount(wr::WindowId aWindowId) {
+ auto windows = mWindowInfos.Lock();
+ auto it = windows->find(AsUint64(aWindowId));
+ if (it == windows->end()) {
+ MOZ_ASSERT(false);
+ return;
+ }
+ WindowInfo* info = it->second.get();
+ info->mPendingFrames.pop();
+}
+
+void RenderThread::RegisterExternalImage(
+ const wr::ExternalImageId& aExternalImageId,
+ already_AddRefed<RenderTextureHost> aTexture) {
+ MutexAutoLock lock(mRenderTextureMapLock);
+
+ if (mHasShutdown) {
+ return;
+ }
+ MOZ_ASSERT(mRenderTextures.find(aExternalImageId) == mRenderTextures.end());
+ RefPtr<RenderTextureHost> texture = aTexture;
+ if (texture->SyncObjectNeeded()) {
+ mSyncObjectNeededRenderTextures.emplace(aExternalImageId, texture);
+ }
+ mRenderTextures.emplace(aExternalImageId, texture);
+}
+
+void RenderThread::UnregisterExternalImage(
+ const wr::ExternalImageId& aExternalImageId) {
+ MutexAutoLock lock(mRenderTextureMapLock);
+ if (mHasShutdown) {
+ return;
+ }
+ auto it = mRenderTextures.find(aExternalImageId);
+ if (it == mRenderTextures.end()) {
+ return;
+ }
+
+ auto& texture = it->second;
+ if (texture->SyncObjectNeeded()) {
+ MOZ_RELEASE_ASSERT(
+ mSyncObjectNeededRenderTextures.erase(aExternalImageId) == 1);
+ }
+
+ if (!IsInRenderThread()) {
+ // The RenderTextureHost should be released in render thread. So, post the
+ // deletion task here.
+ // The shmem and raw buffer are owned by compositor ipc channel. It's
+ // possible that RenderTextureHost is still exist after the shmem/raw buffer
+ // deletion. Then the buffer in RenderTextureHost becomes invalid. It's fine
+ // for this situation. Gecko will only release the buffer if WR doesn't need
+ // it. So, no one will access the invalid buffer in RenderTextureHost.
+ RefPtr<RenderTextureHost> texture = it->second;
+ mRenderTextures.erase(it);
+ mRenderTexturesDeferred.emplace_back(std::move(texture));
+ PostRunnable(NewRunnableMethod(
+ "RenderThread::DeferredRenderTextureHostDestroy", this,
+ &RenderThread::DeferredRenderTextureHostDestroy));
+ } else {
+ mRenderTextures.erase(it);
+ }
+}
+
+void RenderThread::DestroyExternalImagesSyncWait(
+ const std::vector<wr::ExternalImageId>&& aIds) {
+ if (!IsInRenderThread()) {
+ layers::SynchronousTask task("Destroy external images");
+
+ RefPtr<Runnable> runnable = NS_NewRunnableFunction(
+ "RenderThread::DestroyExternalImagesSyncWait::Runnable",
+ [&task, ids = std::move(aIds)]() {
+ layers::AutoCompleteTask complete(&task);
+ RenderThread::Get()->DestroyExternalImages(std::move(ids));
+ });
+
+ PostRunnable(runnable.forget());
+ task.Wait();
+ return;
+ }
+ DestroyExternalImages(std::move(aIds));
+}
+
+void RenderThread::DestroyExternalImages(
+ const std::vector<wr::ExternalImageId>&& aIds) {
+ MOZ_ASSERT(IsInRenderThread());
+
+ std::vector<RefPtr<RenderTextureHost>> hosts;
+ {
+ MutexAutoLock lock(mRenderTextureMapLock);
+ if (mHasShutdown) {
+ return;
+ }
+
+ for (auto& id : aIds) {
+ auto it = mRenderTextures.find(id);
+ if (it == mRenderTextures.end()) {
+ continue;
+ }
+ hosts.emplace_back(it->second);
+ }
+ }
+
+ for (auto& host : hosts) {
+ host->Destroy();
+ }
+}
+
+void RenderThread::PrepareForUse(const wr::ExternalImageId& aExternalImageId) {
+ AddRenderTextureOp(RenderTextureOp::PrepareForUse, aExternalImageId);
+}
+
+void RenderThread::NotifyNotUsed(const wr::ExternalImageId& aExternalImageId) {
+ AddRenderTextureOp(RenderTextureOp::NotifyNotUsed, aExternalImageId);
+}
+
+void RenderThread::NotifyForUse(const wr::ExternalImageId& aExternalImageId) {
+ AddRenderTextureOp(RenderTextureOp::NotifyForUse, aExternalImageId);
+}
+
+void RenderThread::AddRenderTextureOp(
+ RenderTextureOp aOp, const wr::ExternalImageId& aExternalImageId) {
+ MOZ_ASSERT(!IsInRenderThread());
+
+ MutexAutoLock lock(mRenderTextureMapLock);
+
+ auto it = mRenderTextures.find(aExternalImageId);
+ MOZ_ASSERT(it != mRenderTextures.end());
+ if (it == mRenderTextures.end()) {
+ return;
+ }
+
+ RefPtr<RenderTextureHost> texture = it->second;
+ mRenderTextureOps.emplace_back(aOp, std::move(texture));
+
+ if (mRenderTextureOpsRunnable) {
+ // Runnable was already triggered
+ return;
+ }
+
+ RefPtr<nsIRunnable> runnable =
+ NewRunnableMethod("RenderThread::HandleRenderTextureOps", this,
+ &RenderThread::HandleRenderTextureOps);
+ mRenderTextureOpsRunnable = runnable;
+ PostRunnable(runnable.forget());
+}
+
+void RenderThread::HandleRenderTextureOps() {
+ MOZ_ASSERT(IsInRenderThread());
+
+ std::list<std::pair<RenderTextureOp, RefPtr<RenderTextureHost>>>
+ renderTextureOps;
+ {
+ MutexAutoLock lock(mRenderTextureMapLock);
+ mRenderTextureOps.swap(renderTextureOps);
+ mRenderTextureOpsRunnable = nullptr;
+ }
+
+ for (auto& it : renderTextureOps) {
+ switch (it.first) {
+ case RenderTextureOp::PrepareForUse:
+ it.second->PrepareForUse();
+ break;
+ case RenderTextureOp::NotifyForUse:
+ it.second->NotifyForUse();
+ break;
+ case RenderTextureOp::NotifyNotUsed:
+ it.second->NotifyNotUsed();
+ break;
+ }
+ }
+}
+
+void RenderThread::UnregisterExternalImageDuringShutdown(
+ const wr::ExternalImageId& aExternalImageId) {
+ MOZ_ASSERT(IsInRenderThread());
+ MutexAutoLock lock(mRenderTextureMapLock);
+ MOZ_ASSERT(mHasShutdown);
+ MOZ_ASSERT(mRenderTextures.find(aExternalImageId) != mRenderTextures.end());
+ mRenderTextures.erase(aExternalImageId);
+}
+
+bool RenderThread::SyncObjectNeeded() {
+ MOZ_ASSERT(IsInRenderThread());
+ MutexAutoLock lock(mRenderTextureMapLock);
+ return !mSyncObjectNeededRenderTextures.empty();
+}
+
+void RenderThread::DeferredRenderTextureHostDestroy() {
+ MutexAutoLock lock(mRenderTextureMapLock);
+ mRenderTexturesDeferred.clear();
+}
+
+RenderTextureHost* RenderThread::GetRenderTexture(
+ const wr::ExternalImageId& aExternalImageId) {
+ MutexAutoLock lock(mRenderTextureMapLock);
+ auto it = mRenderTextures.find(aExternalImageId);
+ MOZ_ASSERT(it != mRenderTextures.end());
+ if (it == mRenderTextures.end()) {
+ return nullptr;
+ }
+ return it->second;
+}
+
+void RenderThread::InitDeviceTask() {
+ MOZ_ASSERT(IsInRenderThread());
+ MOZ_ASSERT(!mSingletonGL);
+ LOG("RenderThread::InitDeviceTask()");
+
+ if (gfx::gfxVars::UseSoftwareWebRender()) {
+ // Ensure we don't instantiate any shared GL context when SW-WR is used.
+ return;
+ }
+
+ nsAutoCString err;
+ CreateSingletonGL(err);
+ if (gfx::gfxVars::UseWebRenderProgramBinaryDisk()) {
+ mProgramCache = MakeUnique<WebRenderProgramCache>(ThreadPool().Raw());
+ }
+ // Query the shared GL context to force the
+ // lazy initialization to happen now.
+ SingletonGL();
+}
+
+void RenderThread::PostRunnable(already_AddRefed<nsIRunnable> aRunnable) {
+ nsCOMPtr<nsIRunnable> runnable = aRunnable;
+ mThread->Dispatch(runnable.forget());
+}
+
+#ifndef XP_WIN
+static DeviceResetReason GLenumToResetReason(GLenum aReason) {
+ switch (aReason) {
+ case LOCAL_GL_NO_ERROR:
+ return DeviceResetReason::FORCED_RESET;
+ case LOCAL_GL_INNOCENT_CONTEXT_RESET_ARB:
+ return DeviceResetReason::DRIVER_ERROR;
+ case LOCAL_GL_PURGED_CONTEXT_RESET_NV:
+ return DeviceResetReason::NVIDIA_VIDEO;
+ case LOCAL_GL_GUILTY_CONTEXT_RESET_ARB:
+ return DeviceResetReason::RESET;
+ case LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB:
+ return DeviceResetReason::UNKNOWN;
+ case LOCAL_GL_OUT_OF_MEMORY:
+ return DeviceResetReason::OUT_OF_MEMORY;
+ default:
+ return DeviceResetReason::OTHER;
+ }
+}
+#endif
+
+void RenderThread::HandleDeviceReset(const char* aWhere, GLenum aReason) {
+ MOZ_ASSERT(IsInRenderThread());
+
+ // This happens only on simulate device reset.
+ if (aReason == LOCAL_GL_NO_ERROR) {
+ if (!mHandlingDeviceReset) {
+ mHandlingDeviceReset = true;
+
+ MutexAutoLock lock(mRenderTextureMapLock);
+ mRenderTexturesDeferred.clear();
+ for (const auto& entry : mRenderTextures) {
+ entry.second->ClearCachedResources();
+ }
+
+ // All RenderCompositors will be destroyed by the GPUProcessManager in
+ // either OnRemoteProcessDeviceReset via the GPUChild, or
+ // OnInProcessDeviceReset here directly.
+ if (XRE_IsGPUProcess()) {
+ gfx::GPUParent::GetSingleton()->NotifyDeviceReset();
+ } else {
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "gfx::GPUProcessManager::OnInProcessDeviceReset", []() -> void {
+ gfx::GPUProcessManager::Get()->OnInProcessDeviceReset(
+ /* aTrackThreshold */ false);
+ }));
+ }
+ }
+ return;
+ }
+
+ if (mHandlingDeviceReset) {
+ return;
+ }
+
+ mHandlingDeviceReset = true;
+
+#ifndef XP_WIN
+ // On Windows, see DeviceManagerDx::MaybeResetAndReacquireDevices.
+ gfx::GPUProcessManager::RecordDeviceReset(GLenumToResetReason(aReason));
+#endif
+
+ {
+ MutexAutoLock lock(mRenderTextureMapLock);
+ mRenderTexturesDeferred.clear();
+ for (const auto& entry : mRenderTextures) {
+ entry.second->ClearCachedResources();
+ }
+ }
+
+ // All RenderCompositors will be destroyed by the GPUProcessManager in
+ // either OnRemoteProcessDeviceReset via the GPUChild, or
+ // OnInProcessDeviceReset here directly.
+ // On Windows, device will be re-created before sessions re-creation.
+ gfxCriticalNote << "GFX: RenderThread detected a device reset in " << aWhere;
+ if (XRE_IsGPUProcess()) {
+ gfx::GPUParent::GetSingleton()->NotifyDeviceReset();
+ } else {
+#ifndef XP_WIN
+ // FIXME(aosmond): Do we need to do this on Windows? nsWindow::OnPaint
+ // seems to do its own detection for the parent process.
+ bool guilty = aReason == LOCAL_GL_GUILTY_CONTEXT_RESET_ARB;
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "gfx::GPUProcessManager::OnInProcessDeviceReset", [guilty]() -> void {
+ gfx::GPUProcessManager::Get()->OnInProcessDeviceReset(guilty);
+ }));
+#endif
+ }
+}
+
+bool RenderThread::IsHandlingDeviceReset() {
+ MOZ_ASSERT(IsInRenderThread());
+ return mHandlingDeviceReset;
+}
+
+void RenderThread::SimulateDeviceReset() {
+ if (!IsInRenderThread()) {
+ PostRunnable(NewRunnableMethod("RenderThread::SimulateDeviceReset", this,
+ &RenderThread::SimulateDeviceReset));
+ } else {
+ // When this function is called GPUProcessManager::SimulateDeviceReset()
+ // already triggers destroying all CompositorSessions before re-creating
+ // them.
+ HandleDeviceReset("SimulateDeviceReset", LOCAL_GL_NO_ERROR);
+ }
+}
+
+static void DoNotifyWebRenderError(WebRenderError aError) {
+ layers::CompositorManagerParent::NotifyWebRenderError(aError);
+}
+
+void RenderThread::NotifyWebRenderError(WebRenderError aError) {
+ MOZ_ASSERT(IsInRenderThread());
+
+ layers::CompositorThread()->Dispatch(NewRunnableFunction(
+ "DoNotifyWebRenderErrorRunnable", &DoNotifyWebRenderError, aError));
+}
+
+void RenderThread::HandleWebRenderError(WebRenderError aError) {
+ if (mHandlingWebRenderError) {
+ return;
+ }
+
+ NotifyWebRenderError(aError);
+
+ {
+ MutexAutoLock lock(mRenderTextureMapLock);
+ mRenderTexturesDeferred.clear();
+ for (const auto& entry : mRenderTextures) {
+ entry.second->ClearCachedResources();
+ }
+ }
+ mHandlingWebRenderError = true;
+ // WebRender is going to be disabled by
+ // GPUProcessManager::NotifyWebRenderError()
+}
+
+bool RenderThread::IsHandlingWebRenderError() {
+ MOZ_ASSERT(IsInRenderThread());
+ return mHandlingWebRenderError;
+}
+
+gl::GLContext* RenderThread::SingletonGL() {
+ nsAutoCString err;
+ auto* gl = SingletonGL(err);
+ if (!err.IsEmpty()) {
+ gfxCriticalNote << err.get();
+ }
+ return gl;
+}
+
+void RenderThread::CreateSingletonGL(nsACString& aError) {
+ MOZ_ASSERT(IsInRenderThread());
+ LOG("RenderThread::CreateSingletonGL()");
+
+ mSingletonGL = CreateGLContext(aError);
+ mSingletonGLIsForHardwareWebRender = !gfx::gfxVars::UseSoftwareWebRender();
+}
+
+gl::GLContext* RenderThread::SingletonGL(nsACString& aError) {
+ MOZ_ASSERT(IsInRenderThread());
+ if (!mSingletonGL) {
+ CreateSingletonGL(aError);
+ mShaders = nullptr;
+ }
+ if (mSingletonGL && mSingletonGLIsForHardwareWebRender && !mShaders) {
+ mShaders = MakeUnique<WebRenderShaders>(mSingletonGL, mProgramCache.get());
+ }
+
+ return mSingletonGL.get();
+}
+
+gl::GLContext* RenderThread::SingletonGLForCompositorOGL() {
+ MOZ_RELEASE_ASSERT(gfx::gfxVars::UseSoftwareWebRender());
+
+ if (mSingletonGLIsForHardwareWebRender) {
+ // Clear singleton GL, since GLContext is for hardware WebRender.
+ ClearSingletonGL();
+ }
+ return SingletonGL();
+}
+
+void RenderThread::ClearSingletonGL() {
+ MOZ_ASSERT(IsInRenderThread());
+ LOG("RenderThread::ClearSingletonGL()");
+
+ if (mSurfacePool) {
+ mSurfacePool->DestroyGLResourcesForContext(mSingletonGL);
+ }
+ if (mProgramsForCompositorOGL) {
+ mProgramsForCompositorOGL->Clear();
+ mProgramsForCompositorOGL = nullptr;
+ }
+ mShaders = nullptr;
+ mSingletonGL = nullptr;
+}
+
+RefPtr<layers::ShaderProgramOGLsHolder>
+RenderThread::GetProgramsForCompositorOGL() {
+ if (!mSingletonGL) {
+ return nullptr;
+ }
+
+ if (!mProgramsForCompositorOGL) {
+ mProgramsForCompositorOGL =
+ MakeAndAddRef<layers::ShaderProgramOGLsHolder>(mSingletonGL);
+ }
+ return mProgramsForCompositorOGL;
+}
+
+RefPtr<layers::SurfacePool> RenderThread::SharedSurfacePool() {
+#if defined(XP_MACOSX) || defined(MOZ_WAYLAND)
+ if (!mSurfacePool) {
+ size_t poolSizeLimit =
+ StaticPrefs::gfx_webrender_compositor_surface_pool_size_AtStartup();
+ mSurfacePool = layers::SurfacePool::Create(poolSizeLimit);
+ }
+#endif
+ return mSurfacePool;
+}
+
+void RenderThread::ClearSharedSurfacePool() { mSurfacePool = nullptr; }
+
+static void GLAPIENTRY DebugMessageCallback(GLenum aSource, GLenum aType,
+ GLuint aId, GLenum aSeverity,
+ GLsizei aLength,
+ const GLchar* aMessage,
+ const GLvoid* aUserParam) {
+ constexpr const char* kContextLost = "Context has been lost.";
+
+ if (StaticPrefs::gfx_webrender_gl_debug_message_critical_note_AtStartup() &&
+ aSeverity == LOCAL_GL_DEBUG_SEVERITY_HIGH) {
+ auto message = std::string(aMessage, aLength);
+ // When content lost happned, error messages are flooded by its message.
+ if (message != kContextLost) {
+ gfxCriticalNote << message;
+ } else {
+ gfxCriticalNoteOnce << message;
+ }
+ }
+
+ if (StaticPrefs::gfx_webrender_gl_debug_message_print_AtStartup()) {
+ gl::GLContext* gl = (gl::GLContext*)aUserParam;
+ gl->DebugCallback(aSource, aType, aId, aSeverity, aLength, aMessage);
+ }
+}
+
+// static
+void RenderThread::MaybeEnableGLDebugMessage(gl::GLContext* aGLContext) {
+ if (!aGLContext) {
+ return;
+ }
+
+ bool enableDebugMessage =
+ StaticPrefs::gfx_webrender_gl_debug_message_critical_note_AtStartup() ||
+ StaticPrefs::gfx_webrender_gl_debug_message_print_AtStartup();
+
+ if (enableDebugMessage &&
+ aGLContext->IsExtensionSupported(gl::GLContext::KHR_debug)) {
+ aGLContext->fEnable(LOCAL_GL_DEBUG_OUTPUT);
+ aGLContext->fDisable(LOCAL_GL_DEBUG_OUTPUT_SYNCHRONOUS);
+ aGLContext->fDebugMessageCallback(&DebugMessageCallback, (void*)aGLContext);
+ aGLContext->fDebugMessageControl(LOCAL_GL_DONT_CARE, LOCAL_GL_DONT_CARE,
+ LOCAL_GL_DONT_CARE, 0, nullptr, true);
+ }
+}
+
+WebRenderShaders::WebRenderShaders(gl::GLContext* gl,
+ WebRenderProgramCache* programCache) {
+ mGL = gl;
+ mShaders =
+ wr_shaders_new(gl, programCache ? programCache->Raw() : nullptr,
+ StaticPrefs::gfx_webrender_precache_shaders_AtStartup());
+}
+
+WebRenderShaders::~WebRenderShaders() {
+ wr_shaders_delete(mShaders, mGL.get());
+}
+
+WebRenderThreadPool::WebRenderThreadPool(bool low_priority) {
+ mThreadPool = wr_thread_pool_new(low_priority);
+}
+
+WebRenderThreadPool::~WebRenderThreadPool() { Release(); }
+
+void WebRenderThreadPool::Release() {
+ if (mThreadPool) {
+ wr_thread_pool_delete(mThreadPool);
+ mThreadPool = nullptr;
+ }
+}
+
+WebRenderProgramCache::WebRenderProgramCache(wr::WrThreadPool* aThreadPool) {
+ MOZ_ASSERT(aThreadPool);
+
+ nsAutoString path;
+ if (gfx::gfxVars::UseWebRenderProgramBinaryDisk()) {
+ path.Append(gfx::gfxVars::ProfDirectory());
+ }
+ mProgramCache = wr_program_cache_new(&path, aThreadPool);
+ if (gfx::gfxVars::UseWebRenderProgramBinaryDisk()) {
+ wr_try_load_startup_shaders_from_disk(mProgramCache);
+ }
+}
+
+WebRenderProgramCache::~WebRenderProgramCache() {
+ wr_program_cache_delete(mProgramCache);
+}
+
+} // namespace mozilla::wr
+
+#ifdef XP_WIN
+static already_AddRefed<gl::GLContext> CreateGLContextANGLE(
+ nsACString& aError) {
+ const RefPtr<ID3D11Device> d3d11Device =
+ gfx::DeviceManagerDx::Get()->GetCompositorDevice();
+ if (!d3d11Device) {
+ aError.Assign("RcANGLE(no compositor device for EGLDisplay)"_ns);
+ return nullptr;
+ }
+
+ nsCString failureId;
+ const auto lib = gl::GLLibraryEGL::Get(&failureId);
+ if (!lib) {
+ aError.Assign(
+ nsPrintfCString("RcANGLE(load EGL lib failed: %s)", failureId.get()));
+ return nullptr;
+ }
+
+ const auto egl = lib->CreateDisplay(d3d11Device.get());
+ if (!egl) {
+ aError.Assign(nsPrintfCString("RcANGLE(create EGLDisplay failed: %s)",
+ failureId.get()));
+ return nullptr;
+ }
+
+ gl::CreateContextFlags flags = gl::CreateContextFlags::PREFER_ES3;
+
+ if (StaticPrefs::gfx_webrender_prefer_robustness_AtStartup()) {
+ flags |= gl::CreateContextFlags::PREFER_ROBUSTNESS;
+ }
+
+ if (egl->IsExtensionSupported(
+ gl::EGLExtension::MOZ_create_context_provoking_vertex_dont_care)) {
+ flags |= gl::CreateContextFlags::PROVOKING_VERTEX_DONT_CARE;
+ }
+
+ // Create GLContext with dummy EGLSurface, the EGLSurface is not used.
+ // Instread we override it with EGLSurface of SwapChain's back buffer.
+
+ auto gl = gl::GLContextEGL::CreateWithoutSurface(egl, {flags}, &failureId);
+ if (!gl || !gl->IsANGLE()) {
+ aError.Assign(nsPrintfCString("RcANGLE(create GL context failed: %p, %s)",
+ gl.get(), failureId.get()));
+ return nullptr;
+ }
+
+ if (!gl->MakeCurrent()) {
+ aError.Assign(
+ nsPrintfCString("RcANGLE(make current GL context failed: %p, %x)",
+ gl.get(), gl->mEgl->mLib->fGetError()));
+ return nullptr;
+ }
+
+ return gl.forget();
+}
+#endif
+
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WAYLAND) || defined(MOZ_X11)
+static already_AddRefed<gl::GLContext> CreateGLContextEGL() {
+ // Create GLContext with dummy EGLSurface.
+ bool forHardwareWebRender = true;
+ // SW-WR uses CompositorOGL in native compositor.
+ if (gfx::gfxVars::UseSoftwareWebRender()) {
+ forHardwareWebRender = false;
+ }
+ RefPtr<gl::GLContext> gl =
+ gl::GLContextProviderEGL::CreateForCompositorWidget(
+ nullptr, forHardwareWebRender, /* aForceAccelerated */ true);
+ if (!gl || !gl->MakeCurrent()) {
+ gfxCriticalNote << "Failed GL context creation for hardware WebRender: "
+ << forHardwareWebRender;
+ return nullptr;
+ }
+ return gl.forget();
+}
+#endif
+
+#ifdef XP_MACOSX
+static already_AddRefed<gl::GLContext> CreateGLContextCGL() {
+ nsCString failureUnused;
+ return gl::GLContextProvider::CreateHeadless(
+ {gl::CreateContextFlags::ALLOW_OFFLINE_RENDERER |
+ gl::CreateContextFlags::FORBID_SOFTWARE},
+ &failureUnused);
+}
+#endif
+
+static already_AddRefed<gl::GLContext> CreateGLContext(nsACString& aError) {
+ RefPtr<gl::GLContext> gl;
+
+#ifdef XP_WIN
+ if (gfx::gfxVars::UseWebRenderANGLE()) {
+ gl = CreateGLContextANGLE(aError);
+ }
+#elif defined(MOZ_WIDGET_ANDROID)
+ gl = CreateGLContextEGL();
+#elif defined(MOZ_WAYLAND) || defined(MOZ_X11)
+ if (gfx::gfxVars::UseEGL()) {
+ gl = CreateGLContextEGL();
+ }
+#elif XP_MACOSX
+ gl = CreateGLContextCGL();
+#endif
+
+ wr::RenderThread::MaybeEnableGLDebugMessage(gl);
+
+ return gl.forget();
+}
+
+extern "C" {
+
+void wr_notifier_wake_up(mozilla::wr::WrWindowId aWindowId,
+ bool aCompositeNeeded) {
+ // wake_up is used for things like propagating debug options or memory
+ // pressure events, so we are not tracking pending frame counts.
+ mozilla::wr::RenderThread::Get()->WrNotifierEvent_WakeUp(aWindowId,
+ aCompositeNeeded);
+}
+
+void wr_notifier_new_frame_ready(mozilla::wr::WrWindowId aWindowId,
+ bool aCompositeNeeded,
+ mozilla::wr::FramePublishId aPublishId) {
+ auto* renderThread = mozilla::wr::RenderThread::Get();
+ renderThread->DecPendingFrameBuildCount(aWindowId);
+
+ renderThread->WrNotifierEvent_NewFrameReady(aWindowId, aCompositeNeeded,
+ aPublishId);
+}
+
+void wr_notifier_external_event(mozilla::wr::WrWindowId aWindowId,
+ size_t aRawEvent) {
+ mozilla::wr::RenderThread::Get()->WrNotifierEvent_ExternalEvent(
+ mozilla::wr::WindowId(aWindowId), aRawEvent);
+}
+
+static void NotifyScheduleRender(mozilla::wr::WrWindowId aWindowId,
+ wr::RenderReasons aReasons) {
+ RefPtr<mozilla::layers::CompositorBridgeParent> cbp = mozilla::layers::
+ CompositorBridgeParent::GetCompositorBridgeParentFromWindowId(aWindowId);
+ if (cbp) {
+ cbp->ScheduleComposition(aReasons);
+ }
+}
+
+void wr_schedule_render(mozilla::wr::WrWindowId aWindowId,
+ wr::RenderReasons aReasons) {
+ layers::CompositorThread()->Dispatch(NewRunnableFunction(
+ "NotifyScheduleRender", &NotifyScheduleRender, aWindowId, aReasons));
+}
+
+static void NotifyDidSceneBuild(
+ mozilla::wr::WrWindowId aWindowId,
+ const RefPtr<const wr::WebRenderPipelineInfo>& aInfo) {
+ RefPtr<mozilla::layers::CompositorBridgeParent> cbp = mozilla::layers::
+ CompositorBridgeParent::GetCompositorBridgeParentFromWindowId(aWindowId);
+ if (cbp) {
+ cbp->NotifyDidSceneBuild(aInfo);
+ }
+}
+
+void wr_finished_scene_build(mozilla::wr::WrWindowId aWindowId,
+ mozilla::wr::WrPipelineInfo* aPipelineInfo) {
+ RefPtr<wr::WebRenderPipelineInfo> info = new wr::WebRenderPipelineInfo();
+ info->Raw() = std::move(*aPipelineInfo);
+ layers::CompositorThread()->Dispatch(NewRunnableFunction(
+ "NotifyDidSceneBuild", &NotifyDidSceneBuild, aWindowId, info));
+}
+
+} // extern C
diff --git a/gfx/webrender_bindings/RenderThread.h b/gfx/webrender_bindings/RenderThread.h
new file mode 100644
index 0000000000..ba4e34b591
--- /dev/null
+++ b/gfx/webrender_bindings/RenderThread.h
@@ -0,0 +1,489 @@
+/* -*- 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_RENDERTHREAD_H
+#define MOZILLA_LAYERS_RENDERTHREAD_H
+
+#include "base/basictypes.h" // for DISALLOW_EVIL_CONSTRUCTORS
+#include "base/platform_thread.h" // for PlatformThreadId
+#include "base/thread.h" // for Thread
+#include "base/message_loop.h"
+#include "GLTypes.h" // for GLenum
+#include "nsISupportsImpl.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/MozPromise.h"
+#include "mozilla/DataMutex.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/webrender/webrender_ffi.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/webrender/WebRenderTypes.h"
+#include "mozilla/layers/CompositionRecorder.h"
+#include "mozilla/layers/SynchronousTask.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/VsyncDispatcher.h"
+
+#include <list>
+#include <queue>
+#include <unordered_map>
+
+namespace mozilla {
+namespace gl {
+class GLContext;
+} // namespace gl
+namespace layers {
+class CompositorBridgeParent;
+class ShaderProgramOGLsHolder;
+class SurfacePool;
+} // namespace layers
+namespace wr {
+
+typedef MozPromise<MemoryReport, bool, true> MemoryReportPromise;
+
+class RendererOGL;
+class RenderTextureHost;
+class RenderThread;
+
+/// A rayon thread pool that is shared by all WebRender instances within a
+/// process.
+class WebRenderThreadPool {
+ public:
+ explicit WebRenderThreadPool(bool low_priority);
+
+ ~WebRenderThreadPool();
+
+ wr::WrThreadPool* Raw() {
+ // If this pointer is null we are likely at some late shutdown stage,
+ // when threads are no longer safe to interact with.
+ MOZ_RELEASE_ASSERT(mThreadPool);
+ return mThreadPool;
+ }
+
+ /// Prematurely destroys this handle to the thread pool.
+ /// After calling this the object is useless.
+ void Release();
+
+ protected:
+ wr::WrThreadPool* mThreadPool;
+};
+
+class WebRenderProgramCache final {
+ public:
+ explicit WebRenderProgramCache(wr::WrThreadPool* aThreadPool);
+
+ ~WebRenderProgramCache();
+
+ wr::WrProgramCache* Raw() { return mProgramCache; }
+
+ protected:
+ wr::WrProgramCache* mProgramCache;
+};
+
+class WebRenderShaders final {
+ public:
+ WebRenderShaders(gl::GLContext* gl, WebRenderProgramCache* programCache);
+ ~WebRenderShaders();
+
+ wr::WrShaders* RawShaders() { return mShaders; }
+
+ protected:
+ RefPtr<gl::GLContext> mGL;
+ wr::WrShaders* mShaders;
+};
+
+class WebRenderPipelineInfo final {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebRenderPipelineInfo);
+
+ const wr::WrPipelineInfo& Raw() const { return mPipelineInfo; }
+ wr::WrPipelineInfo& Raw() { return mPipelineInfo; }
+
+ protected:
+ ~WebRenderPipelineInfo() = default;
+ wr::WrPipelineInfo mPipelineInfo;
+};
+
+/// Base class for an event that can be scheduled to run on the render thread.
+///
+/// The event can be passed through the same channels as regular WebRender
+/// messages to preserve ordering.
+class RendererEvent {
+ public:
+ virtual ~RendererEvent() = default;
+ virtual void Run(RenderThread& aRenderThread, wr::WindowId aWindow) = 0;
+};
+
+/// The render thread is where WebRender issues all of its GPU work, and as much
+/// as possible this thread should only serve this purpose.
+///
+/// The render thread owns the different RendererOGLs (one per window) and
+/// implements the RenderNotifier api exposed by the WebRender bindings.
+///
+/// Callers are not allowed to post tasks to the render thread's event loop
+/// directly and must instead use the RendererEvent mechanism which avoids races
+/// between the events and WebRender's own messages.
+///
+/// The GL context(s) should be created and used on this thread only.
+/// XXX - I've tried to organize code so that we can potentially avoid making
+/// this a singleton since this bad habit has a tendency to bite us later, but
+/// I haven't gotten all the way there either, in order to focus on the more
+/// important pieces first. So we are a bit in-between (this is totally a
+/// singleton but in some places we pretend it's not). Hopefully we can evolve
+/// this in a way that keeps the door open to removing the singleton bits.
+class RenderThread final {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_MAIN_THREAD(RenderThread)
+
+ public:
+ /// Can be called from any thread.
+ static RenderThread* Get();
+
+ /// Can only be called from the main thread.
+ static void Start(uint32_t aNamespace);
+
+ /// Can only be called from the main thread.
+ static void ShutDown();
+
+ /// Can be called from any thread.
+ static bool IsInRenderThread();
+
+ /// Can be called from any thread.
+ static already_AddRefed<nsIThread> GetRenderThread();
+
+ // Can be called from any thread. Dispatches an event to the Renderer thread
+ // to iterate over all Renderers, accumulates memory statistics, and resolves
+ // the return promise.
+ static RefPtr<MemoryReportPromise> AccumulateMemoryReport(
+ MemoryReport aInitial);
+
+ /// Can only be called from the render thread.
+ void AddRenderer(wr::WindowId aWindowId, UniquePtr<RendererOGL> aRenderer);
+
+ /// Can only be called from the render thread.
+ void RemoveRenderer(wr::WindowId aWindowId);
+
+ /// Can only be called from the render thread.
+ RendererOGL* GetRenderer(wr::WindowId aWindowId);
+
+ /// Automatically forwarded to the render thread.
+ void SetClearColor(wr::WindowId aWindowId, wr::ColorF aColor);
+
+ /// Automatically forwarded to the render thread.
+ void SetProfilerUI(wr::WindowId aWindowId, const nsACString& aUI);
+
+ /// Automatically forwarded to the render thread.
+ void PipelineSizeChanged(wr::WindowId aWindowId, uint64_t aPipelineId,
+ float aWidth, float aHeight);
+
+ /// Post RendererEvent to the render thread.
+ void PostEvent(wr::WindowId aWindowId, UniquePtr<RendererEvent> aEvent);
+
+ /// Can only be called from the render thread.
+ void SetFramePublishId(wr::WindowId aWindowId, FramePublishId aPublishId);
+
+ /// Can only be called from the render thread.
+ void UpdateAndRender(wr::WindowId aWindowId, const VsyncId& aStartId,
+ const TimeStamp& aStartTime, bool aRender,
+ const Maybe<gfx::IntSize>& aReadbackSize,
+ const Maybe<wr::ImageFormat>& aReadbackFormat,
+ const Maybe<Range<uint8_t>>& aReadbackBuffer,
+ bool* aNeedsYFlip = nullptr);
+
+ void Pause(wr::WindowId aWindowId);
+ bool Resume(wr::WindowId aWindowId);
+
+ /// Can be called from any thread.
+ void RegisterExternalImage(const wr::ExternalImageId& aExternalImageId,
+ already_AddRefed<RenderTextureHost> aTexture);
+
+ /// Can be called from any thread.
+ void UnregisterExternalImage(const wr::ExternalImageId& aExternalImageId);
+
+ /// Can be called from any thread.
+ void DestroyExternalImagesSyncWait(
+ const std::vector<wr::ExternalImageId>&& aIds);
+
+ /// Can be called from any thread.
+ void PrepareForUse(const wr::ExternalImageId& aExternalImageId);
+
+ /// Can be called from any thread.
+ void NotifyNotUsed(const wr::ExternalImageId& aExternalImageId);
+
+ /// Can be called from any thread.
+ void NotifyForUse(const wr::ExternalImageId& aExternalImageId);
+
+ void HandleRenderTextureOps();
+
+ /// Can only be called from the render thread.
+ void UnregisterExternalImageDuringShutdown(
+ const wr::ExternalImageId& aExternalImageId);
+
+ /// Can only be called from the render thread.
+ RenderTextureHost* GetRenderTexture(
+ const wr::ExternalImageId& aExternalImageId);
+
+ /// Can be called from any thread.
+ bool IsDestroyed(wr::WindowId aWindowId);
+ /// Can be called from any thread.
+ void SetDestroyed(wr::WindowId aWindowId);
+ /// Can be called from any thread.
+ bool TooManyPendingFrames(wr::WindowId aWindowId);
+ /// Can be called from any thread.
+ void IncPendingFrameCount(wr::WindowId aWindowId, const VsyncId& aStartId,
+ const TimeStamp& aStartTime);
+ /// Can be called from any thread.
+ void DecPendingFrameBuildCount(wr::WindowId aWindowId);
+ void DecPendingFrameCount(wr::WindowId aWindowId);
+
+ // RenderNotifier implementation
+ void WrNotifierEvent_WakeUp(WrWindowId aWindowId, bool aCompositeNeeded);
+ void WrNotifierEvent_NewFrameReady(WrWindowId aWindowId,
+ bool aCompositeNeeded,
+ FramePublishId aPublishId);
+ void WrNotifierEvent_ExternalEvent(WrWindowId aWindowId, size_t aRawEvent);
+
+ /// Can be called from any thread.
+ WebRenderThreadPool& ThreadPool() { return mThreadPool; }
+
+ /// Thread pool for low priority scene building
+ /// Can be called from any thread.
+ WebRenderThreadPool& ThreadPoolLP() { return mThreadPoolLP; }
+
+ /// Returns the cache used to serialize shader programs to disk, if enabled.
+ ///
+ /// Can only be called from the render thread.
+ WebRenderProgramCache* GetProgramCache() {
+ MOZ_ASSERT(IsInRenderThread());
+ return mProgramCache.get();
+ }
+
+ /// Can only be called from the render thread.
+ WebRenderShaders* GetShaders() {
+ MOZ_ASSERT(IsInRenderThread());
+ return mShaders.get();
+ }
+
+ /// Can only be called from the render thread.
+ gl::GLContext* SingletonGL(nsACString& aError);
+ gl::GLContext* SingletonGL();
+ gl::GLContext* SingletonGLForCompositorOGL();
+ void ClearSingletonGL();
+ RefPtr<layers::SurfacePool> SharedSurfacePool();
+ void ClearSharedSurfacePool();
+
+ RefPtr<layers::ShaderProgramOGLsHolder> GetProgramsForCompositorOGL();
+
+ /// Can only be called from the render thread.
+ void HandleDeviceReset(const char* aWhere, GLenum aReason);
+ /// Can only be called from the render thread.
+ bool IsHandlingDeviceReset();
+ /// Can be called from any thread.
+ void SimulateDeviceReset();
+
+ /// Can only be called from the render thread.
+ void NotifyWebRenderError(WebRenderError aError);
+
+ /// Can only be called from the render thread.
+ void HandleWebRenderError(WebRenderError aError);
+ /// Can only be called from the render thread.
+ bool IsHandlingWebRenderError();
+
+ /// Can only be called from the render thread.
+ bool SyncObjectNeeded();
+
+ size_t RendererCount() const;
+ size_t ActiveRendererCount() const;
+
+ void BeginRecordingForWindow(wr::WindowId aWindowId,
+ const TimeStamp& aRecordingStart,
+ wr::PipelineId aRootPipelineId);
+
+ Maybe<layers::FrameRecording> EndRecordingForWindow(wr::WindowId aWindowId);
+
+ static void MaybeEnableGLDebugMessage(gl::GLContext* aGLContext);
+
+ private:
+ enum class RenderTextureOp {
+ PrepareForUse,
+ NotifyForUse,
+ NotifyNotUsed,
+ };
+ class WrNotifierEvent {
+ public:
+ enum class Tag {
+ WakeUp,
+ NewFrameReady,
+ ExternalEvent,
+ };
+ const Tag mTag;
+
+ private:
+ WrNotifierEvent(const Tag aTag, const bool aCompositeNeeded)
+ : mTag(aTag), mCompositeNeeded(aCompositeNeeded) {
+ MOZ_ASSERT(mTag == Tag::WakeUp);
+ }
+ WrNotifierEvent(const Tag aTag, bool aCompositeNeeded,
+ FramePublishId aPublishId)
+ : mTag(aTag),
+ mCompositeNeeded(aCompositeNeeded),
+ mPublishId(aPublishId) {
+ MOZ_ASSERT(mTag == Tag::NewFrameReady);
+ }
+ WrNotifierEvent(const Tag aTag, UniquePtr<RendererEvent>&& aRendererEvent)
+ : mTag(aTag), mRendererEvent(std::move(aRendererEvent)) {
+ MOZ_ASSERT(mTag == Tag::ExternalEvent);
+ }
+
+ const bool mCompositeNeeded = false;
+ const FramePublishId mPublishId = FramePublishId::INVALID;
+ UniquePtr<RendererEvent> mRendererEvent;
+
+ public:
+ static WrNotifierEvent WakeUp(const bool aCompositeNeeded) {
+ return WrNotifierEvent(Tag::WakeUp, aCompositeNeeded);
+ }
+
+ static WrNotifierEvent NewFrameReady(const bool aCompositeNeeded,
+ const FramePublishId aPublishId) {
+ return WrNotifierEvent(Tag::NewFrameReady, aCompositeNeeded, aPublishId);
+ }
+
+ static WrNotifierEvent ExternalEvent(
+ UniquePtr<RendererEvent>&& aRendererEvent) {
+ return WrNotifierEvent(Tag::ExternalEvent, std::move(aRendererEvent));
+ }
+
+ bool CompositeNeeded() {
+ if (mTag == Tag::WakeUp || mTag == Tag::NewFrameReady) {
+ return mCompositeNeeded;
+ }
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+ return false;
+ }
+ FramePublishId PublishId() {
+ if (mTag == Tag::NewFrameReady) {
+ return mPublishId;
+ }
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+ return FramePublishId::INVALID;
+ }
+ UniquePtr<RendererEvent> ExternalEvent() {
+ if (mTag == Tag::ExternalEvent) {
+ MOZ_ASSERT(mRendererEvent);
+ return std::move(mRendererEvent);
+ }
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+ return nullptr;
+ }
+ };
+
+ explicit RenderThread(RefPtr<nsIThread> aThread);
+
+ void HandleFrameOneDocInner(wr::WindowId aWindowId, bool aRender,
+ bool aTrackedFrame,
+ Maybe<FramePublishId> aPublishId);
+
+ void DeferredRenderTextureHostDestroy();
+ void ShutDownTask();
+ void InitDeviceTask();
+ void HandleFrameOneDoc(wr::WindowId aWindowId, bool aRender,
+ bool aTrackedFrame, Maybe<FramePublishId> aPublishId);
+ void RunEvent(wr::WindowId aWindowId, UniquePtr<RendererEvent> aEvent);
+ void PostRunnable(already_AddRefed<nsIRunnable> aRunnable);
+
+ void DoAccumulateMemoryReport(MemoryReport,
+ const RefPtr<MemoryReportPromise::Private>&);
+
+ void AddRenderTextureOp(RenderTextureOp aOp,
+ const wr::ExternalImageId& aExternalImageId);
+
+ void CreateSingletonGL(nsACString& aError);
+
+ void DestroyExternalImages(const std::vector<wr::ExternalImageId>&& aIds);
+
+ struct WindowInfo;
+
+ void PostWrNotifierEvents(WrWindowId aWindowId);
+ void PostWrNotifierEvents(WrWindowId aWindowId, WindowInfo* aInfo);
+ void HandleWrNotifierEvents(WrWindowId aWindowId);
+ void WrNotifierEvent_HandleWakeUp(wr::WindowId aWindowId,
+ bool aCompositeNeeded);
+ void WrNotifierEvent_HandleNewFrameReady(wr::WindowId aWindowId,
+ bool aCompositeNeeded,
+ FramePublishId aPublishId);
+ void WrNotifierEvent_HandleExternalEvent(
+ wr::WindowId aWindowId, UniquePtr<RendererEvent> aRendererEvent);
+
+ ~RenderThread();
+
+ RefPtr<nsIThread> const mThread;
+
+ WebRenderThreadPool mThreadPool;
+ WebRenderThreadPool mThreadPoolLP;
+
+ UniquePtr<WebRenderProgramCache> mProgramCache;
+ UniquePtr<WebRenderShaders> mShaders;
+ RefPtr<layers::ShaderProgramOGLsHolder> mProgramsForCompositorOGL;
+
+ // An optional shared GLContext to be used for all
+ // windows.
+ RefPtr<gl::GLContext> mSingletonGL;
+ bool mSingletonGLIsForHardwareWebRender;
+
+ RefPtr<layers::SurfacePool> mSurfacePool;
+
+ std::map<wr::WindowId, UniquePtr<RendererOGL>> mRenderers;
+
+ struct PendingFrameInfo {
+ TimeStamp mStartTime;
+ VsyncId mStartId;
+ };
+
+ struct WindowInfo {
+ int64_t PendingCount() { return mPendingFrames.size(); }
+ std::queue<PendingFrameInfo> mPendingFrames;
+ uint8_t mPendingFrameBuild = 0;
+ bool mIsDestroyed = false;
+ RefPtr<nsIRunnable> mWrNotifierEventsRunnable;
+ std::queue<WrNotifierEvent> mPendingWrNotifierEvents;
+ };
+
+ DataMutex<std::unordered_map<uint64_t, UniquePtr<WindowInfo>>> mWindowInfos;
+
+ std::unordered_map<uint64_t, UniquePtr<std::queue<WrNotifierEvent>>>
+ mWrNotifierEventsQueues;
+
+ struct ExternalImageIdHashFn {
+ std::size_t operator()(const wr::ExternalImageId& aId) const {
+ return HashGeneric(wr::AsUint64(aId));
+ }
+ };
+
+ Mutex mRenderTextureMapLock MOZ_UNANNOTATED;
+ std::unordered_map<wr::ExternalImageId, RefPtr<RenderTextureHost>,
+ ExternalImageIdHashFn>
+ mRenderTextures;
+ std::unordered_map<wr::ExternalImageId, RefPtr<RenderTextureHost>,
+ ExternalImageIdHashFn>
+ mSyncObjectNeededRenderTextures;
+ std::list<std::pair<RenderTextureOp, RefPtr<RenderTextureHost>>>
+ mRenderTextureOps;
+
+ // Used to remove all RenderTextureHost that are going to be removed by
+ // a deferred callback and remove them right away without waiting for the
+ // callback. On device reset we have to remove all GL related resources right
+ // away.
+ std::list<RefPtr<RenderTextureHost>> mRenderTexturesDeferred;
+
+ RefPtr<nsIRunnable> mRenderTextureOpsRunnable;
+
+ bool mHasShutdown;
+
+ bool mHandlingDeviceReset;
+ bool mHandlingWebRenderError;
+};
+
+} // namespace wr
+} // namespace mozilla
+
+#endif
diff --git a/gfx/webrender_bindings/RendererOGL.cpp b/gfx/webrender_bindings/RendererOGL.cpp
new file mode 100644
index 0000000000..3c4d13e376
--- /dev/null
+++ b/gfx/webrender_bindings/RendererOGL.cpp
@@ -0,0 +1,430 @@
+/* -*- 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 "RendererOGL.h"
+
+#include "base/task.h"
+#include "GLContext.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/Types.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/ProfilerScreenshots.h"
+#include "mozilla/webrender/RenderCompositor.h"
+#include "mozilla/webrender/RenderTextureHost.h"
+#include "mozilla/widget/CompositorWidget.h"
+
+namespace mozilla {
+namespace wr {
+
+class RendererRecordedFrame final : public layers::RecordedFrame {
+ public:
+ RendererRecordedFrame(const TimeStamp& aTimeStamp, wr::Renderer* aRenderer,
+ const wr::RecordedFrameHandle aHandle,
+ const gfx::IntSize& aSize)
+ : RecordedFrame(aTimeStamp),
+ mRenderer(aRenderer),
+ mSize(aSize),
+ mHandle(aHandle) {}
+
+ already_AddRefed<gfx::DataSourceSurface> GetSourceSurface() override {
+ if (!mSurface) {
+ mSurface = gfx::Factory::CreateDataSourceSurface(
+ mSize, gfx::SurfaceFormat::B8G8R8A8, /* aZero = */ false);
+
+ gfx::DataSourceSurface::ScopedMap map(mSurface,
+ gfx::DataSourceSurface::WRITE);
+
+ if (!wr_renderer_map_recorded_frame(mRenderer, mHandle, map.GetData(),
+ map.GetStride() * mSize.height,
+ map.GetStride())) {
+ return nullptr;
+ }
+ }
+
+ return do_AddRef(mSurface);
+ }
+
+ private:
+ wr::Renderer* mRenderer;
+ RefPtr<gfx::DataSourceSurface> mSurface;
+ gfx::IntSize mSize;
+ wr::RecordedFrameHandle mHandle;
+};
+
+wr::WrExternalImage wr_renderer_lock_external_image(void* aObj,
+ wr::ExternalImageId aId,
+ uint8_t aChannelIndex) {
+ RendererOGL* renderer = reinterpret_cast<RendererOGL*>(aObj);
+ RenderTextureHost* texture = renderer->GetRenderTexture(aId);
+ MOZ_ASSERT(texture);
+ if (!texture) {
+ gfxCriticalNoteOnce << "Failed to lock ExternalImage for extId:"
+ << AsUint64(aId);
+ return InvalidToWrExternalImage();
+ }
+ if (auto* gl = renderer->gl()) {
+ return texture->Lock(aChannelIndex, gl);
+ } else if (auto* swgl = renderer->swgl()) {
+ return texture->LockSWGL(aChannelIndex, swgl, renderer->GetCompositor());
+ } else {
+ gfxCriticalNoteOnce
+ << "No GL or SWGL context available to lock ExternalImage for extId:"
+ << AsUint64(aId);
+ return InvalidToWrExternalImage();
+ }
+}
+
+void wr_renderer_unlock_external_image(void* aObj, wr::ExternalImageId aId,
+ uint8_t aChannelIndex) {
+ RendererOGL* renderer = reinterpret_cast<RendererOGL*>(aObj);
+ RenderTextureHost* texture = renderer->GetRenderTexture(aId);
+ MOZ_ASSERT(texture);
+ if (!texture) {
+ return;
+ }
+ if (renderer->gl()) {
+ texture->Unlock();
+ } else if (renderer->swgl()) {
+ texture->UnlockSWGL();
+ }
+}
+
+RendererOGL::RendererOGL(RefPtr<RenderThread>&& aThread,
+ UniquePtr<RenderCompositor> aCompositor,
+ wr::WindowId aWindowId, wr::Renderer* aRenderer,
+ layers::CompositorBridgeParent* aBridge)
+ : mThread(aThread),
+ mCompositor(std::move(aCompositor)),
+ mRenderer(aRenderer),
+ mBridge(aBridge),
+ mWindowId(aWindowId),
+ mDisableNativeCompositor(false),
+ mLastPipelineInfo(new WebRenderPipelineInfo) {
+ MOZ_ASSERT(mThread);
+ MOZ_ASSERT(mCompositor);
+ MOZ_ASSERT(mRenderer);
+ MOZ_ASSERT(mBridge);
+ MOZ_COUNT_CTOR(RendererOGL);
+}
+
+RendererOGL::~RendererOGL() {
+ MOZ_COUNT_DTOR(RendererOGL);
+ if (!mCompositor->MakeCurrent()) {
+ gfxCriticalNote
+ << "Failed to make render context current during destroying.";
+ // Leak resources!
+ } else {
+ wr_renderer_delete(mRenderer);
+ }
+}
+
+wr::WrExternalImageHandler RendererOGL::GetExternalImageHandler() {
+ return wr::WrExternalImageHandler{
+ this,
+ };
+}
+
+void RendererOGL::SetFramePublishId(FramePublishId aPublishId) {
+ wr_renderer_set_target_frame_publish_id(mRenderer, aPublishId);
+}
+
+void RendererOGL::Update() {
+ mCompositor->Update();
+ if (mCompositor->MakeCurrent()) {
+ wr_renderer_update(mRenderer);
+ FlushPipelineInfo();
+ }
+}
+
+static void DoWebRenderDisableNativeCompositor(
+ layers::CompositorBridgeParent* aBridge) {
+ aBridge->NotifyWebRenderDisableNativeCompositor();
+}
+
+RenderedFrameId RendererOGL::UpdateAndRender(
+ const Maybe<gfx::IntSize>& aReadbackSize,
+ const Maybe<wr::ImageFormat>& aReadbackFormat,
+ const Maybe<Range<uint8_t>>& aReadbackBuffer, bool* aNeedsYFlip,
+ RendererStats* aOutStats) {
+ mozilla::widget::WidgetRenderingContext widgetContext;
+
+#if defined(XP_MACOSX)
+ widgetContext.mGL = mCompositor->gl();
+#endif
+
+ if (!mCompositor->GetWidget()->PreRender(&widgetContext)) {
+ // XXX This could cause oom in webrender since pending_texture_updates is
+ // not handled. It needs to be addressed.
+ return RenderedFrameId();
+ }
+ // XXX set clear color if MOZ_WIDGET_ANDROID is defined.
+
+ if (mThread->IsHandlingDeviceReset() || !mCompositor->BeginFrame()) {
+ CheckGraphicsResetStatus("BeginFrame", /* aForce */ true);
+ mCompositor->GetWidget()->PostRender(&widgetContext);
+ return RenderedFrameId();
+ }
+
+ auto size = mCompositor->GetBufferSize();
+ auto bufferAge = mCompositor->GetBufferAge();
+
+ wr_renderer_update(mRenderer);
+
+ bool fullRender = mCompositor->RequestFullRender();
+ // When we're rendering to an external target, we want to render everything.
+ if (mCompositor->UsePartialPresent() &&
+ (aReadbackBuffer.isSome() || layers::ProfilerScreenshots::IsEnabled())) {
+ fullRender = true;
+ }
+ if (fullRender) {
+ wr_renderer_force_redraw(mRenderer);
+ }
+
+ nsTArray<DeviceIntRect> dirtyRects;
+ bool rendered = wr_renderer_render(mRenderer, size.width, size.height,
+ bufferAge, aOutStats, &dirtyRects);
+ FlushPipelineInfo();
+ if (!rendered) {
+ mCompositor->CancelFrame();
+ RenderThread::Get()->HandleWebRenderError(WebRenderError::RENDER);
+ mCompositor->GetWidget()->PostRender(&widgetContext);
+ return RenderedFrameId();
+ }
+
+ if (aReadbackBuffer.isSome()) {
+ MOZ_ASSERT(aReadbackSize.isSome());
+ MOZ_ASSERT(aReadbackFormat.isSome());
+ if (!mCompositor->MaybeReadback(aReadbackSize.ref(), aReadbackFormat.ref(),
+ aReadbackBuffer.ref(), aNeedsYFlip)) {
+ wr_renderer_readback(mRenderer, aReadbackSize.ref().width,
+ aReadbackSize.ref().height, aReadbackFormat.ref(),
+ &aReadbackBuffer.ref()[0],
+ aReadbackBuffer.ref().length());
+ if (aNeedsYFlip) {
+ *aNeedsYFlip = !mCompositor->SurfaceOriginIsTopLeft();
+ }
+ }
+ }
+
+ if (size.Width() != 0 && size.Height() != 0) {
+ if (!mCompositor->MaybeGrabScreenshot(size.ToUnknownSize())) {
+ mScreenshotGrabber.MaybeGrabScreenshot(this, size.ToUnknownSize());
+ }
+ }
+
+ // Frame recording must happen before EndFrame, as we must ensure we read the
+ // contents of the back buffer before any calls to SwapBuffers which might
+ // invalidate it.
+ MaybeRecordFrame(mLastPipelineInfo);
+
+ RenderedFrameId frameId = mCompositor->EndFrame(dirtyRects);
+
+ mCompositor->GetWidget()->PostRender(&widgetContext);
+
+#if defined(ENABLE_FRAME_LATENCY_LOG)
+ if (mFrameStartTime) {
+ uint32_t latencyMs =
+ round((TimeStamp::Now() - mFrameStartTime).ToMilliseconds());
+ printf_stderr("generate frame latencyMs latencyMs %d\n", latencyMs);
+ }
+ // Clear frame start time
+ mFrameStartTime = TimeStamp();
+#endif
+
+ if (!mCompositor->MaybeProcessScreenshotQueue()) {
+ mScreenshotGrabber.MaybeProcessQueue(this);
+ }
+
+ // TODO: Flush pending actions such as texture deletions/unlocks and
+ // textureHosts recycling.
+
+ return frameId;
+}
+
+bool RendererOGL::EnsureAsyncScreenshot() {
+ if (mCompositor->SupportAsyncScreenshot()) {
+ return true;
+ }
+ if (!mDisableNativeCompositor) {
+ layers::CompositorThread()->Dispatch(
+ NewRunnableFunction("DoWebRenderDisableNativeCompositorRunnable",
+ &DoWebRenderDisableNativeCompositor, mBridge));
+
+ mDisableNativeCompositor = true;
+ gfxCriticalNote << "Disable native compositor for async screenshot";
+ }
+ return false;
+}
+
+void RendererOGL::CheckGraphicsResetStatus(const char* aCaller, bool aForce) {
+ if (mCompositor) {
+ auto reason = mCompositor->IsContextLost(aForce);
+ if (reason != LOCAL_GL_NO_ERROR) {
+ RenderThread::Get()->HandleDeviceReset(aCaller, reason);
+ }
+ }
+}
+
+void RendererOGL::WaitForGPU() {
+ if (!mCompositor->WaitForGPU()) {
+ CheckGraphicsResetStatus("WaitForGPU", /* aForce */ true);
+ }
+}
+
+ipc::FileDescriptor RendererOGL::GetAndResetReleaseFence() {
+ return mCompositor->GetAndResetReleaseFence();
+}
+
+RenderedFrameId RendererOGL::GetLastCompletedFrameId() {
+ return mCompositor->GetLastCompletedFrameId();
+}
+
+RenderedFrameId RendererOGL::UpdateFrameId() {
+ return mCompositor->UpdateFrameId();
+}
+
+void RendererOGL::Pause() { mCompositor->Pause(); }
+
+bool RendererOGL::Resume() { return mCompositor->Resume(); }
+
+bool RendererOGL::IsPaused() { return mCompositor->IsPaused(); }
+
+layers::SyncObjectHost* RendererOGL::GetSyncObject() const {
+ return mCompositor->GetSyncObject();
+}
+
+gl::GLContext* RendererOGL::gl() const { return mCompositor->gl(); }
+
+void* RendererOGL::swgl() const { return mCompositor->swgl(); }
+
+void RendererOGL::SetFrameStartTime(const TimeStamp& aTime) {
+ if (mFrameStartTime) {
+ // frame start time is already set. This could happen when multiple
+ // generate frame requests are merged by webrender.
+ return;
+ }
+ mFrameStartTime = aTime;
+}
+
+void RendererOGL::BeginRecording(const TimeStamp& aRecordingStart,
+ wr::PipelineId aRootPipelineId) {
+ MOZ_ASSERT(!mCompositionRecorder);
+
+ mRootPipelineId = aRootPipelineId;
+ mCompositionRecorder =
+ MakeUnique<layers::CompositionRecorder>(aRecordingStart);
+ mCompositor->MaybeRequestAllowFrameRecording(true);
+}
+
+void RendererOGL::MaybeRecordFrame(const WebRenderPipelineInfo* aPipelineInfo) {
+ if (!mCompositionRecorder || !EnsureAsyncScreenshot()) {
+ return;
+ }
+
+ if (!mRenderer || !aPipelineInfo || !DidPaintContent(aPipelineInfo)) {
+ return;
+ }
+
+ if (mCompositor->MaybeRecordFrame(*mCompositionRecorder)) {
+ return;
+ }
+
+ wr::RecordedFrameHandle handle{0};
+ gfx::IntSize size(0, 0);
+
+ if (wr_renderer_record_frame(mRenderer, wr::ImageFormat::BGRA8, &handle,
+ &size.width, &size.height)) {
+ RefPtr<layers::RecordedFrame> frame =
+ new RendererRecordedFrame(TimeStamp::Now(), mRenderer, handle, size);
+
+ mCompositionRecorder->RecordFrame(frame);
+ }
+}
+
+bool RendererOGL::DidPaintContent(const WebRenderPipelineInfo* aFrameEpochs) {
+ const wr::WrPipelineInfo& info = aFrameEpochs->Raw();
+ bool didPaintContent = false;
+
+ // Check if a non-root pipeline has updated to a new epoch.
+ // We treat all non-root pipelines as "content" pipelines, even if they're
+ // not fed by content paints, such as videos (see bug 1665512).
+ for (const auto& epoch : info.epochs) {
+ const wr::PipelineId pipelineId = epoch.pipeline_id;
+
+ if (pipelineId == mRootPipelineId) {
+ continue;
+ }
+
+ const auto it = mContentPipelineEpochs.find(AsUint64(pipelineId));
+ if (it == mContentPipelineEpochs.end() || it->second != epoch.epoch) {
+ // This pipeline has updated since last render or has newly rendered.
+ didPaintContent = true;
+ mContentPipelineEpochs[AsUint64(pipelineId)] = epoch.epoch;
+ }
+ }
+
+ for (const auto& removedPipeline : info.removed_pipelines) {
+ const wr::PipelineId pipelineId = removedPipeline.pipeline_id;
+ if (pipelineId == mRootPipelineId) {
+ continue;
+ }
+ mContentPipelineEpochs.erase(AsUint64(pipelineId));
+ }
+
+ return didPaintContent;
+}
+
+Maybe<layers::FrameRecording> RendererOGL::EndRecording() {
+ if (!mCompositionRecorder) {
+ MOZ_DIAGNOSTIC_ASSERT(
+ false, "Attempted to get frames from a window that was not recording.");
+ return Nothing();
+ }
+
+ auto maybeRecording = mCompositionRecorder->GetRecording();
+
+ wr_renderer_release_composition_recorder_structures(mRenderer);
+
+ mCompositor->MaybeRequestAllowFrameRecording(false);
+ mCompositionRecorder = nullptr;
+
+ return maybeRecording;
+}
+
+void RendererOGL::FlushPipelineInfo() {
+ RefPtr<WebRenderPipelineInfo> info = new WebRenderPipelineInfo;
+ wr_renderer_flush_pipeline_info(mRenderer, &info->Raw());
+ mLastPipelineInfo = info;
+}
+
+RenderTextureHost* RendererOGL::GetRenderTexture(
+ wr::ExternalImageId aExternalImageId) {
+ return mThread->GetRenderTexture(aExternalImageId);
+}
+
+void RendererOGL::AccumulateMemoryReport(MemoryReport* aReport) {
+ wr_renderer_accumulate_memory_report(GetRenderer(), aReport, swgl());
+
+ LayoutDeviceIntSize size = mCompositor->GetBufferSize();
+
+ // Assume BGRA8 for the format since it's not exposed anywhere,
+ // and all compositor backends should be using that.
+ uintptr_t swapChainSize = size.width * size.height *
+ BytesPerPixel(gfx::SurfaceFormat::B8G8R8A8) *
+ (mCompositor->UseTripleBuffering() ? 3 : 2);
+ aReport->swap_chain += swapChainSize;
+}
+
+void RendererOGL::SetProfilerUI(const nsACString& aUI) {
+ wr_renderer_set_profiler_ui(GetRenderer(), (const uint8_t*)aUI.BeginReading(),
+ aUI.Length());
+}
+
+} // namespace wr
+} // namespace mozilla
diff --git a/gfx/webrender_bindings/RendererOGL.h b/gfx/webrender_bindings/RendererOGL.h
new file mode 100644
index 0000000000..bf79f70d16
--- /dev/null
+++ b/gfx/webrender_bindings/RendererOGL.h
@@ -0,0 +1,176 @@
+/* -*- 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_RENDEREROGL_H
+#define MOZILLA_LAYERS_RENDEREROGL_H
+
+#include "mozilla/ipc/FileDescriptor.h"
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/webrender/RenderThread.h"
+#include "mozilla/webrender/WebRenderTypes.h"
+#include "mozilla/webrender/webrender_ffi.h"
+#include "mozilla/webrender/RendererScreenshotGrabber.h"
+
+namespace mozilla {
+
+namespace gfx {
+class DrawTarget;
+}
+
+namespace gl {
+class GLContext;
+}
+
+namespace layers {
+class CompositorBridgeParent;
+class SyncObjectHost;
+} // namespace layers
+
+namespace widget {
+class CompositorWidget;
+}
+
+namespace wr {
+
+class RenderCompositor;
+class RenderTextureHost;
+
+/// Owns the WebRender renderer and GL context.
+///
+/// There is one renderer per window, all owned by the render thread.
+/// This class is a similar abstraction to CompositorOGL except that it is used
+/// on the render thread instead of the compositor thread.
+class RendererOGL {
+ friend wr::WrExternalImage LockExternalImage(void* aObj,
+ wr::ExternalImageId aId,
+ uint8_t aChannelIndex,
+ wr::ImageRendering);
+ friend void UnlockExternalImage(void* aObj, wr::ExternalImageId aId,
+ uint8_t aChannelIndex);
+
+ public:
+ wr::WrExternalImageHandler GetExternalImageHandler();
+
+ void SetFramePublishId(FramePublishId aPublishId);
+
+ /// This can be called on the render thread only.
+ void Update();
+
+ /// This can be called on the render thread only.
+ RenderedFrameId UpdateAndRender(const Maybe<gfx::IntSize>& aReadbackSize,
+ const Maybe<wr::ImageFormat>& aReadbackFormat,
+ const Maybe<Range<uint8_t>>& aReadbackBuffer,
+ bool* aNeedsYFlip, RendererStats* aOutStats);
+
+ /// This can be called on the render thread only.
+ void WaitForGPU();
+
+ /// This can be called on the render thread only.
+ ipc::FileDescriptor GetAndResetReleaseFence();
+
+ /// This can be called on the render thread only.
+ RenderedFrameId GetLastCompletedFrameId();
+
+ /// This can be called on the render thread only.
+ RenderedFrameId UpdateFrameId();
+
+ /// This can be called on the render thread only.
+ void SetProfilerEnabled(bool aEnabled);
+
+ /// This can be called on the render thread only.
+ void SetFrameStartTime(const TimeStamp& aTime);
+
+ /// These can be called on the render thread only.
+ void BeginRecording(const TimeStamp& aRecordingStart,
+ wr::PipelineId aPipelineId);
+ void MaybeRecordFrame(const WebRenderPipelineInfo* aPipelineInfo);
+
+ Maybe<layers::FrameRecording> EndRecording();
+
+ /// This can be called on the render thread only.
+ ~RendererOGL();
+
+ /// This can be called on the render thread only.
+ RendererOGL(RefPtr<RenderThread>&& aThread,
+ UniquePtr<RenderCompositor> aCompositor, wr::WindowId aWindowId,
+ wr::Renderer* aRenderer, layers::CompositorBridgeParent* aBridge);
+
+ /// This can be called on the render thread only.
+ void Pause();
+
+ /// This can be called on the render thread only.
+ bool Resume();
+
+ /// This can be called on the render thread only.
+ bool IsPaused();
+
+ /// This can be called on the render thread only.
+ void CheckGraphicsResetStatus(const char* aCaller, bool aForce);
+
+ layers::SyncObjectHost* GetSyncObject() const;
+
+ layers::CompositorBridgeParent* GetCompositorBridge() { return mBridge; }
+
+ void FlushPipelineInfo();
+
+ RefPtr<const WebRenderPipelineInfo> GetLastPipelineInfo() const {
+ return mLastPipelineInfo;
+ }
+
+ RenderTextureHost* GetRenderTexture(wr::ExternalImageId aExternalImageId);
+
+ RenderCompositor* GetCompositor() { return mCompositor.get(); }
+
+ void AccumulateMemoryReport(MemoryReport* aReport);
+
+ void SetProfilerUI(const nsACString& aUI);
+
+ wr::Renderer* GetRenderer() { return mRenderer; }
+
+ gl::GLContext* gl() const;
+
+ void* swgl() const;
+
+ bool EnsureAsyncScreenshot();
+
+ protected:
+ /**
+ * Determine if any content pipelines updated, and update
+ * mContentPipelineEpochs.
+ */
+ bool DidPaintContent(const wr::WebRenderPipelineInfo* aFrameEpochs);
+
+ RefPtr<RenderThread> mThread;
+ UniquePtr<RenderCompositor> mCompositor;
+ UniquePtr<layers::CompositionRecorder> mCompositionRecorder; // can be null
+ wr::Renderer* mRenderer;
+ layers::CompositorBridgeParent* mBridge;
+ wr::WindowId mWindowId;
+ TimeStamp mFrameStartTime;
+
+ bool mDisableNativeCompositor;
+
+ RendererScreenshotGrabber mScreenshotGrabber;
+
+ // The id of the root WebRender pipeline.
+ //
+ // All other pipelines are considered content.
+ wr::PipelineId mRootPipelineId;
+
+ // A mapping of wr::PipelineId to the epochs when last they updated.
+ //
+ // We need to use uint64_t here since wr::PipelineId is not default
+ // constructable.
+ std::unordered_map<uint64_t, wr::Epoch> mContentPipelineEpochs;
+
+ RefPtr<WebRenderPipelineInfo> mLastPipelineInfo;
+};
+
+} // namespace wr
+} // namespace mozilla
+
+#endif
diff --git a/gfx/webrender_bindings/RendererScreenshotGrabber.cpp b/gfx/webrender_bindings/RendererScreenshotGrabber.cpp
new file mode 100644
index 0000000000..1d001d4727
--- /dev/null
+++ b/gfx/webrender_bindings/RendererScreenshotGrabber.cpp
@@ -0,0 +1,104 @@
+/* -*- 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 "RendererScreenshotGrabber.h"
+
+#include "RendererOGL.h"
+
+#include "mozilla/gfx/2D.h"
+
+using mozilla::layers::ProfilerScreenshots;
+
+namespace mozilla {
+namespace wr {
+
+RendererScreenshotGrabber::RendererScreenshotGrabber() {
+ mMaxScreenshotSize = ProfilerScreenshots::ScreenshotSize();
+}
+
+void RendererScreenshotGrabber::MaybeGrabScreenshot(
+ RendererOGL* aRendererOGL, const gfx::IntSize& aWindowSize) {
+ bool isEnabled =
+ ProfilerScreenshots::IsEnabled() && aRendererOGL->EnsureAsyncScreenshot();
+
+ if (isEnabled) {
+ if (!mProfilerScreenshots) {
+ mProfilerScreenshots = new ProfilerScreenshots();
+ }
+
+ GrabScreenshot(aRendererOGL->GetRenderer(), aWindowSize);
+ } else if (mProfilerScreenshots) {
+ Destroy(aRendererOGL->GetRenderer());
+ }
+}
+
+void RendererScreenshotGrabber::MaybeProcessQueue(RendererOGL* aRendererOGL) {
+ bool isEnabled =
+ ProfilerScreenshots::IsEnabled() && aRendererOGL->EnsureAsyncScreenshot();
+
+ if (isEnabled) {
+ if (!mProfilerScreenshots) {
+ mProfilerScreenshots = new ProfilerScreenshots();
+ }
+
+ ProcessQueue(aRendererOGL->GetRenderer());
+ } else if (mProfilerScreenshots) {
+ Destroy(aRendererOGL->GetRenderer());
+ }
+}
+
+void RendererScreenshotGrabber::Destroy(Renderer* aRenderer) {
+ mQueue.Clear();
+ mCurrentFrameQueueItem = Nothing();
+ mProfilerScreenshots = nullptr;
+
+ wr_renderer_release_profiler_structures(aRenderer);
+}
+
+void RendererScreenshotGrabber::GrabScreenshot(
+ Renderer* aRenderer, const gfx::IntSize& aWindowSize) {
+ gfx::IntSize screenshotSize;
+
+ AsyncScreenshotHandle handle = wr_renderer_get_screenshot_async(
+ aRenderer, 0, 0, aWindowSize.width, aWindowSize.height,
+ mMaxScreenshotSize.width, mMaxScreenshotSize.height, ImageFormat::BGRA8,
+ &screenshotSize.width, &screenshotSize.height);
+
+ mCurrentFrameQueueItem = Some(QueueItem{
+ TimeStamp::Now(),
+ handle,
+ screenshotSize,
+ aWindowSize,
+ });
+}
+
+void RendererScreenshotGrabber::ProcessQueue(Renderer* aRenderer) {
+ for (const auto& item : mQueue) {
+ mProfilerScreenshots->SubmitScreenshot(
+ item.mWindowSize, item.mScreenshotSize, item.mTimeStamp,
+ [&item, aRenderer](gfx::DataSourceSurface* aTargetSurface) {
+ gfx::DataSourceSurface::ScopedMap map(aTargetSurface,
+ gfx::DataSourceSurface::WRITE);
+ int32_t destStride = map.GetStride();
+
+ bool success = wr_renderer_map_and_recycle_screenshot(
+ aRenderer, item.mHandle, map.GetData(),
+ destStride * aTargetSurface->GetSize().height, destStride);
+
+ return success;
+ });
+ }
+
+ mQueue.Clear();
+
+ if (mCurrentFrameQueueItem) {
+ mQueue.AppendElement(*mCurrentFrameQueueItem);
+ mCurrentFrameQueueItem = Nothing();
+ }
+}
+
+} // namespace wr
+} // namespace mozilla
diff --git a/gfx/webrender_bindings/RendererScreenshotGrabber.h b/gfx/webrender_bindings/RendererScreenshotGrabber.h
new file mode 100644
index 0000000000..81f11a392d
--- /dev/null
+++ b/gfx/webrender_bindings/RendererScreenshotGrabber.h
@@ -0,0 +1,101 @@
+/* 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_RendererScreenshotGrabber_h
+#define mozilla_layers_RendererScreenshotGrabber_h
+
+#include "mozilla/TimeStamp.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/layers/ProfilerScreenshots.h"
+#include "mozilla/webrender/webrender_ffi.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace wr {
+
+struct Renderer;
+class RendererOGL;
+
+/**
+ * Used by |RendererOGL| to grab screenshots from WebRender and submit them to
+ * the Gecko profiler.
+ *
+ * If the profiler is not running or the screenshots feature is disabled, no
+ * work will be done.
+ */
+class RendererScreenshotGrabber final {
+ public:
+ RendererScreenshotGrabber();
+
+ /**
+ * Grab a screenshot from WebRender if we are profiling and screenshots are
+ * enabled.
+ *
+ * The captured screenshot will not be mapped until the second call to
+ * |MaybeProcessQueue| after this call to |MaybeGrabScreenshot|.
+ */
+ void MaybeGrabScreenshot(RendererOGL* aRendererOGL,
+ const gfx::IntSize& aWindowSize);
+
+ /**
+ * Process the screenshots pending in the queue if we are profiling and
+ * screenshots are enabled.
+ */
+ void MaybeProcessQueue(RendererOGL* aRenderer);
+
+ private:
+ /**
+ * Drop all our allocated memory when we are no longer profiling.
+ *
+ * This will also instruct WebRender to drop all its Gecko profiler
+ * associated memory.
+ */
+ void Destroy(Renderer* aRenderer);
+
+ /**
+ * Actually grab a screenshot from WebRender.
+ */
+ void GrabScreenshot(Renderer* aRenderer, const gfx::IntSize& aWindowSize);
+
+ /**
+ * Process the screenshots pending in the queue.
+ */
+ void ProcessQueue(Renderer* aRenderer);
+
+ struct QueueItem {
+ mozilla::TimeStamp mTimeStamp;
+ AsyncScreenshotHandle mHandle;
+ gfx::IntSize mScreenshotSize;
+ gfx::IntSize mWindowSize;
+ };
+
+ /**
+ * The maximum size for screenshots, as dictated by
+ * |ProfilerScrenshots::ScreenshotSize|.
+ */
+ gfx::IntSize mMaxScreenshotSize;
+
+ /**
+ * The queue of screenshots waiting to be processed and submitted.
+ */
+ nsTArray<QueueItem> mQueue;
+
+ /**
+ * The queue item for the current frame. This will be inserted into the queue
+ * after a call to |MaybeProcessQueue| so it will be not be processed until
+ * the next frame.
+ */
+ Maybe<QueueItem> mCurrentFrameQueueItem;
+
+ /**
+ * Our handle to the profiler screenshots object.
+ */
+ RefPtr<mozilla::layers::ProfilerScreenshots> mProfilerScreenshots;
+};
+
+} // namespace wr
+} // namespace mozilla
+
+#endif // mozilla_layers_RendererScreenshotGrabber_h
diff --git a/gfx/webrender_bindings/WebRenderAPI.cpp b/gfx/webrender_bindings/WebRenderAPI.cpp
new file mode 100644
index 0000000000..c07ef299d2
--- /dev/null
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -0,0 +1,1796 @@
+/* -*- 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 "WebRenderAPI.h"
+
+#include "mozilla/ipc/ByteBuf.h"
+#include "mozilla/webrender/RendererOGL.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/StaticPrefs_gfx.h"
+#include "mozilla/StaticPrefs_webgl.h"
+#include "mozilla/ToString.h"
+#include "mozilla/webrender/RenderCompositor.h"
+#include "mozilla/widget/CompositorWidget.h"
+#include "mozilla/layers/SynchronousTask.h"
+#include "nsThreadUtils.h"
+#include "TextDrawTarget.h"
+#include "malloc_decls.h"
+#include "GLContext.h"
+
+// clang-format off
+#define WRDL_LOG(...)
+//#define WRDL_LOG(...) printf_stderr("WRDL(%p): " __VA_ARGS__)
+//#define WRDL_LOG(...) if (XRE_IsContentProcess()) printf_stderr("WRDL(%p): " __VA_ARGS__)
+// clang-format on
+
+namespace mozilla {
+using namespace layers;
+
+namespace wr {
+
+MOZ_DEFINE_MALLOC_SIZE_OF(WebRenderMallocSizeOf)
+MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(WebRenderMallocEnclosingSizeOf)
+
+class NewRenderer : public RendererEvent {
+ public:
+ NewRenderer(wr::DocumentHandle** aDocHandle,
+ layers::CompositorBridgeParent* aBridge,
+ WebRenderBackend* aBackend, WebRenderCompositor* aCompositor,
+ int32_t* aMaxTextureSize, bool* aUseANGLE, bool* aUseDComp,
+ bool* aUseTripleBuffering, bool* aSupportsExternalBufferTextures,
+ RefPtr<widget::CompositorWidget>&& aWidget,
+ layers::SynchronousTask* aTask, LayoutDeviceIntSize aSize,
+ layers::WindowKind aWindowKind, layers::SyncHandle* aHandle,
+ nsACString* aError)
+ : mDocHandle(aDocHandle),
+ mBackend(aBackend),
+ mCompositor(aCompositor),
+ mMaxTextureSize(aMaxTextureSize),
+ mUseANGLE(aUseANGLE),
+ mUseDComp(aUseDComp),
+ mUseTripleBuffering(aUseTripleBuffering),
+ mSupportsExternalBufferTextures(aSupportsExternalBufferTextures),
+ mBridge(aBridge),
+ mCompositorWidget(std::move(aWidget)),
+ mTask(aTask),
+ mSize(aSize),
+ mWindowKind(aWindowKind),
+ mSyncHandle(aHandle),
+ mError(aError) {
+ MOZ_COUNT_CTOR(NewRenderer);
+ }
+
+ MOZ_COUNTED_DTOR(NewRenderer)
+
+ void Run(RenderThread& aRenderThread, WindowId aWindowId) override {
+ layers::AutoCompleteTask complete(mTask);
+
+ UniquePtr<RenderCompositor> compositor =
+ RenderCompositor::Create(std::move(mCompositorWidget), *mError);
+ if (!compositor) {
+ if (!mError->IsEmpty()) {
+ gfxCriticalNote << mError->BeginReading();
+ }
+ return;
+ }
+
+ compositor->MakeCurrent();
+
+ *mBackend = compositor->BackendType();
+ *mCompositor = compositor->CompositorType();
+ *mUseANGLE = compositor->UseANGLE();
+ *mUseDComp = compositor->UseDComp();
+ *mUseTripleBuffering = compositor->UseTripleBuffering();
+ *mSupportsExternalBufferTextures =
+ compositor->SupportsExternalBufferTextures();
+
+ // Only allow the panic on GL error functionality in nightly builds,
+ // since it (deliberately) crashes the GPU process if any GL call
+ // returns an error code.
+ bool panic_on_gl_error = false;
+#ifdef NIGHTLY_BUILD
+ panic_on_gl_error =
+ StaticPrefs::gfx_webrender_panic_on_gl_error_AtStartup();
+#endif
+
+ bool isMainWindow = true; // TODO!
+ bool supportLowPriorityTransactions = isMainWindow;
+ bool supportLowPriorityThreadpool =
+ supportLowPriorityTransactions &&
+ StaticPrefs::gfx_webrender_enable_low_priority_pool();
+ wr::Renderer* wrRenderer = nullptr;
+ char* errorMessage = nullptr;
+ int picTileWidth = StaticPrefs::gfx_webrender_picture_tile_width();
+ int picTileHeight = StaticPrefs::gfx_webrender_picture_tile_height();
+ auto* swgl = compositor->swgl();
+ auto* gl = (compositor->gl() && !swgl) ? compositor->gl() : nullptr;
+ auto* progCache = (aRenderThread.GetProgramCache() && !swgl)
+ ? aRenderThread.GetProgramCache()->Raw()
+ : nullptr;
+ auto* shaders = (aRenderThread.GetShaders() && !swgl)
+ ? aRenderThread.GetShaders()->RawShaders()
+ : nullptr;
+
+ // Check That if we are not using SWGL, we have at least a GL or GLES 3.0
+ // context.
+ if (gl && !swgl) {
+ bool versionCheck =
+ gl->IsAtLeast(gl::ContextProfile::OpenGLCore, 300) ||
+ gl->IsAtLeast(gl::ContextProfile::OpenGLCompatibility, 300) ||
+ gl->IsAtLeast(gl::ContextProfile::OpenGLES, 300);
+
+ if (!versionCheck) {
+ gfxCriticalNote << "GL context version (" << gl->Version()
+ << ") insufficent for hardware WebRender";
+
+ mError->AssignASCII("GL context version insufficient");
+ return;
+ }
+ }
+
+ if (!wr_window_new(
+ aWindowId, mSize.width, mSize.height,
+ mWindowKind == WindowKind::MAIN, supportLowPriorityTransactions,
+ supportLowPriorityThreadpool, gfx::gfxVars::UseGLSwizzle(),
+ gfx::gfxVars::UseWebRenderScissoredCacheClears(), swgl, gl,
+ compositor->SurfaceOriginIsTopLeft(), progCache, shaders,
+ aRenderThread.ThreadPool().Raw(),
+ aRenderThread.ThreadPoolLP().Raw(), &WebRenderMallocSizeOf,
+ &WebRenderMallocEnclosingSizeOf, 0, compositor.get(),
+ compositor->ShouldUseNativeCompositor(),
+ compositor->UsePartialPresent(),
+ compositor->GetMaxPartialPresentRects(),
+ compositor->ShouldDrawPreviousPartialPresentRegions(), mDocHandle,
+ &wrRenderer, mMaxTextureSize, &errorMessage,
+ StaticPrefs::gfx_webrender_enable_gpu_markers_AtStartup(),
+ panic_on_gl_error, picTileWidth, picTileHeight,
+ gfx::gfxVars::WebRenderRequiresHardwareDriver(),
+ StaticPrefs::gfx_webrender_low_quality_pinch_zoom_AtStartup(),
+ StaticPrefs::gfx_webrender_max_shared_surface_size_AtStartup())) {
+ // wr_window_new puts a message into gfxCriticalNote if it returns false
+ MOZ_ASSERT(errorMessage);
+ mError->AssignASCII(errorMessage);
+ wr_api_free_error_msg(errorMessage);
+ return;
+ }
+ MOZ_ASSERT(wrRenderer);
+
+ RefPtr<RenderThread> thread = &aRenderThread;
+ auto renderer =
+ MakeUnique<RendererOGL>(std::move(thread), std::move(compositor),
+ aWindowId, wrRenderer, mBridge);
+ if (wrRenderer && renderer) {
+ wr::WrExternalImageHandler handler = renderer->GetExternalImageHandler();
+ wr_renderer_set_external_image_handler(wrRenderer, &handler);
+ }
+
+ if (renderer) {
+ layers::SyncObjectHost* syncObj = renderer->GetSyncObject();
+ if (syncObj) {
+ *mSyncHandle = syncObj->GetSyncHandle();
+ }
+ }
+
+ aRenderThread.AddRenderer(aWindowId, std::move(renderer));
+ }
+
+ private:
+ wr::DocumentHandle** mDocHandle;
+ WebRenderBackend* mBackend;
+ WebRenderCompositor* mCompositor;
+ int32_t* mMaxTextureSize;
+ bool* mUseANGLE;
+ bool* mUseDComp;
+ bool* mUseTripleBuffering;
+ bool* mSupportsExternalBufferTextures;
+ layers::CompositorBridgeParent* mBridge;
+ RefPtr<widget::CompositorWidget> mCompositorWidget;
+ layers::SynchronousTask* mTask;
+ LayoutDeviceIntSize mSize;
+ layers::WindowKind mWindowKind;
+ layers::SyncHandle* mSyncHandle;
+ nsACString* mError;
+};
+
+class RemoveRenderer : public RendererEvent {
+ public:
+ explicit RemoveRenderer(layers::SynchronousTask* aTask) : mTask(aTask) {
+ MOZ_COUNT_CTOR(RemoveRenderer);
+ }
+
+ MOZ_COUNTED_DTOR_OVERRIDE(RemoveRenderer)
+
+ void Run(RenderThread& aRenderThread, WindowId aWindowId) override {
+ aRenderThread.RemoveRenderer(aWindowId);
+ layers::AutoCompleteTask complete(mTask);
+ }
+
+ private:
+ layers::SynchronousTask* mTask;
+};
+
+TransactionBuilder::TransactionBuilder(WebRenderAPI* aApi,
+ bool aUseSceneBuilderThread)
+ : mUseSceneBuilderThread(aUseSceneBuilderThread),
+ mApiBackend(aApi->GetBackendType()) {
+ mTxn = wr_transaction_new(mUseSceneBuilderThread);
+}
+
+TransactionBuilder::~TransactionBuilder() { wr_transaction_delete(mTxn); }
+
+void TransactionBuilder::SetLowPriority(bool aIsLowPriority) {
+ wr_transaction_set_low_priority(mTxn, aIsLowPriority);
+}
+
+void TransactionBuilder::UpdateEpoch(PipelineId aPipelineId, Epoch aEpoch) {
+ wr_transaction_update_epoch(mTxn, aPipelineId, aEpoch);
+}
+
+void TransactionBuilder::SetRootPipeline(PipelineId aPipelineId) {
+ wr_transaction_set_root_pipeline(mTxn, aPipelineId);
+}
+
+void TransactionBuilder::RemovePipeline(PipelineId aPipelineId) {
+ wr_transaction_remove_pipeline(mTxn, aPipelineId);
+}
+
+void TransactionBuilder::SetDisplayList(
+ Epoch aEpoch, wr::WrPipelineId pipeline_id,
+ wr::BuiltDisplayListDescriptor dl_descriptor,
+ wr::Vec<uint8_t>& dl_items_data, wr::Vec<uint8_t>& dl_cache_data,
+ wr::Vec<uint8_t>& dl_spatial_tree) {
+ wr_transaction_set_display_list(mTxn, aEpoch, pipeline_id, dl_descriptor,
+ &dl_items_data.inner, &dl_cache_data.inner,
+ &dl_spatial_tree.inner);
+}
+
+void TransactionBuilder::ClearDisplayList(Epoch aEpoch,
+ wr::WrPipelineId aPipelineId) {
+ wr_transaction_clear_display_list(mTxn, aEpoch, aPipelineId);
+}
+
+void TransactionBuilder::GenerateFrame(const VsyncId& aVsyncId,
+ wr::RenderReasons aReasons) {
+ wr_transaction_generate_frame(mTxn, aVsyncId.mId, aReasons);
+}
+
+void TransactionBuilder::InvalidateRenderedFrame(wr::RenderReasons aReasons) {
+ wr_transaction_invalidate_rendered_frame(mTxn, aReasons);
+}
+
+bool TransactionBuilder::IsEmpty() const {
+ return wr_transaction_is_empty(mTxn);
+}
+
+bool TransactionBuilder::IsResourceUpdatesEmpty() const {
+ return wr_transaction_resource_updates_is_empty(mTxn);
+}
+
+bool TransactionBuilder::IsRenderedFrameInvalidated() const {
+ return wr_transaction_is_rendered_frame_invalidated(mTxn);
+}
+
+void TransactionBuilder::SetDocumentView(
+ const LayoutDeviceIntRect& aDocumentRect) {
+ wr::DeviceIntRect wrDocRect;
+ wrDocRect.min.x = aDocumentRect.x;
+ wrDocRect.min.y = aDocumentRect.y;
+ wrDocRect.max.x = aDocumentRect.x + aDocumentRect.width;
+ wrDocRect.max.y = aDocumentRect.y + aDocumentRect.height;
+ wr_transaction_set_document_view(mTxn, &wrDocRect);
+}
+
+TransactionWrapper::TransactionWrapper(Transaction* aTxn) : mTxn(aTxn) {}
+
+void TransactionWrapper::AppendDynamicProperties(
+ const nsTArray<wr::WrOpacityProperty>& aOpacityArray,
+ const nsTArray<wr::WrTransformProperty>& aTransformArray,
+ const nsTArray<wr::WrColorProperty>& aColorArray) {
+ wr_transaction_append_dynamic_properties(
+ mTxn, aOpacityArray.IsEmpty() ? nullptr : aOpacityArray.Elements(),
+ aOpacityArray.Length(),
+ aTransformArray.IsEmpty() ? nullptr : aTransformArray.Elements(),
+ aTransformArray.Length(),
+ aColorArray.IsEmpty() ? nullptr : aColorArray.Elements(),
+ aColorArray.Length());
+}
+
+void TransactionWrapper::AppendTransformProperties(
+ const nsTArray<wr::WrTransformProperty>& aTransformArray) {
+ wr_transaction_append_transform_properties(
+ mTxn, aTransformArray.IsEmpty() ? nullptr : aTransformArray.Elements(),
+ aTransformArray.Length());
+}
+
+void TransactionWrapper::UpdateScrollPosition(
+ const wr::WrPipelineId& aPipelineId,
+ const layers::ScrollableLayerGuid::ViewID& aScrollId,
+ const nsTArray<wr::SampledScrollOffset>& aSampledOffsets) {
+ wr_transaction_scroll_layer(mTxn, aPipelineId, aScrollId, &aSampledOffsets);
+}
+
+void TransactionWrapper::UpdateIsTransformAsyncZooming(uint64_t aAnimationId,
+ bool aIsZooming) {
+ wr_transaction_set_is_transform_async_zooming(mTxn, aAnimationId, aIsZooming);
+}
+
+/*static*/
+already_AddRefed<WebRenderAPI> WebRenderAPI::Create(
+ layers::CompositorBridgeParent* aBridge,
+ RefPtr<widget::CompositorWidget>&& aWidget, const wr::WrWindowId& aWindowId,
+ LayoutDeviceIntSize aSize, layers::WindowKind aWindowKind,
+ nsACString& aError) {
+ MOZ_ASSERT(aBridge);
+ MOZ_ASSERT(aWidget);
+ static_assert(
+ sizeof(size_t) == sizeof(uintptr_t),
+ "The FFI bindings assume size_t is the same size as uintptr_t!");
+
+ wr::DocumentHandle* docHandle = nullptr;
+ WebRenderBackend backend = WebRenderBackend::HARDWARE;
+ WebRenderCompositor compositor = WebRenderCompositor::DRAW;
+ int32_t maxTextureSize = 0;
+ bool useANGLE = false;
+ bool useDComp = false;
+ bool useTripleBuffering = false;
+ bool supportsExternalBufferTextures = false;
+ layers::SyncHandle syncHandle = 0;
+
+ // Dispatch a synchronous task because the DocumentHandle object needs to be
+ // created on the render thread. If need be we could delay waiting on this
+ // task until the next time we need to access the DocumentHandle object.
+ layers::SynchronousTask task("Create Renderer");
+ auto event = MakeUnique<NewRenderer>(
+ &docHandle, aBridge, &backend, &compositor, &maxTextureSize, &useANGLE,
+ &useDComp, &useTripleBuffering, &supportsExternalBufferTextures,
+ std::move(aWidget), &task, aSize, aWindowKind, &syncHandle, &aError);
+ RenderThread::Get()->PostEvent(aWindowId, std::move(event));
+
+ task.Wait();
+
+ if (!docHandle) {
+ return nullptr;
+ }
+
+ return RefPtr<WebRenderAPI>(
+ new WebRenderAPI(docHandle, aWindowId, backend, compositor,
+ maxTextureSize, useANGLE, useDComp,
+ useTripleBuffering,
+ supportsExternalBufferTextures, syncHandle))
+ .forget();
+}
+
+already_AddRefed<WebRenderAPI> WebRenderAPI::Clone() {
+ wr::DocumentHandle* docHandle = nullptr;
+ wr_api_clone(mDocHandle, &docHandle);
+
+ RefPtr<WebRenderAPI> renderApi =
+ new WebRenderAPI(docHandle, mId, mBackend, mCompositor, mMaxTextureSize,
+ mUseANGLE, mUseDComp, mUseTripleBuffering,
+ mSupportsExternalBufferTextures, mSyncHandle);
+ renderApi->mRootApi = this; // Hold root api
+ renderApi->mRootDocumentApi = this;
+
+ return renderApi.forget();
+}
+
+wr::WrIdNamespace WebRenderAPI::GetNamespace() {
+ return wr_api_get_namespace(mDocHandle);
+}
+
+WebRenderAPI::WebRenderAPI(wr::DocumentHandle* aHandle, wr::WindowId aId,
+ WebRenderBackend aBackend,
+ WebRenderCompositor aCompositor,
+ uint32_t aMaxTextureSize, bool aUseANGLE,
+ bool aUseDComp, bool aUseTripleBuffering,
+ bool aSupportsExternalBufferTextures,
+ layers::SyncHandle aSyncHandle)
+ : mDocHandle(aHandle),
+ mId(aId),
+ mBackend(aBackend),
+ mCompositor(aCompositor),
+ mMaxTextureSize(aMaxTextureSize),
+ mUseANGLE(aUseANGLE),
+ mUseDComp(aUseDComp),
+ mUseTripleBuffering(aUseTripleBuffering),
+ mSupportsExternalBufferTextures(aSupportsExternalBufferTextures),
+ mCaptureSequence(false),
+ mSyncHandle(aSyncHandle),
+ mRendererDestroyed(false) {}
+
+WebRenderAPI::~WebRenderAPI() {
+ if (!mRootDocumentApi) {
+ wr_api_delete_document(mDocHandle);
+ }
+
+ if (!mRootApi) {
+ MOZ_RELEASE_ASSERT(mRendererDestroyed);
+ wr_api_shut_down(mDocHandle);
+ }
+
+ wr_api_delete(mDocHandle);
+}
+
+void WebRenderAPI::DestroyRenderer() {
+ MOZ_RELEASE_ASSERT(!mRootApi);
+
+ RenderThread::Get()->SetDestroyed(GetId());
+ // Call wr_api_stop_render_backend() before RemoveRenderer.
+ wr_api_stop_render_backend(mDocHandle);
+
+ layers::SynchronousTask task("Destroy WebRenderAPI");
+ auto event = MakeUnique<RemoveRenderer>(&task);
+ RunOnRenderThread(std::move(event));
+ task.Wait();
+
+ mRendererDestroyed = true;
+}
+
+void WebRenderAPI::UpdateDebugFlags(uint32_t aFlags) {
+ wr_api_set_debug_flags(mDocHandle, wr::DebugFlags{aFlags});
+}
+
+void WebRenderAPI::SendTransaction(TransactionBuilder& aTxn) {
+ if (mRootApi && mRootApi->mRendererDestroyed) {
+ return;
+ }
+
+ if (mPendingRemoteTextureInfoList &&
+ !mPendingRemoteTextureInfoList->mList.empty()) {
+ mPendingWrTransactionEvents.emplace(
+ WrTransactionEvent::PendingRemoteTextures(
+ std::move(mPendingRemoteTextureInfoList)));
+ }
+
+ if (!mPendingWrTransactionEvents.empty()) {
+ mPendingWrTransactionEvents.emplace(WrTransactionEvent::Transaction(
+ aTxn.Take(), aTxn.UseSceneBuilderThread()));
+ HandleWrTransactionEvents(RemoteTextureWaitType::AsyncWait);
+ } else {
+ wr_api_send_transaction(mDocHandle, aTxn.Raw(),
+ aTxn.UseSceneBuilderThread());
+ }
+}
+
+layers::RemoteTextureInfoList* WebRenderAPI::GetPendingRemoteTextureInfoList() {
+ if (!mRootApi) {
+ // root api does not support async wait RemoteTexture.
+ return nullptr;
+ }
+
+ if (!gfx::gfxVars::UseCanvasRenderThread() ||
+ !StaticPrefs::webgl_out_of_process_async_present() ||
+ gfx::gfxVars::WebglOopAsyncPresentForceSync()) {
+ return nullptr;
+ }
+
+ // async remote texture is enabled
+ MOZ_ASSERT(gfx::gfxVars::UseCanvasRenderThread());
+ MOZ_ASSERT(StaticPrefs::webgl_out_of_process_async_present());
+ MOZ_ASSERT(!gfx::gfxVars::WebglOopAsyncPresentForceSync());
+
+ if (!mPendingRemoteTextureInfoList) {
+ mPendingRemoteTextureInfoList = MakeUnique<layers::RemoteTextureInfoList>();
+ }
+ return mPendingRemoteTextureInfoList.get();
+}
+
+bool WebRenderAPI::CheckIsRemoteTextureReady(
+ layers::RemoteTextureInfoList* aList) {
+ MOZ_ASSERT(layers::CompositorThreadHolder::IsInCompositorThread());
+ MOZ_ASSERT(aList);
+ MOZ_ASSERT(gfx::gfxVars::UseCanvasRenderThread());
+ MOZ_ASSERT(StaticPrefs::webgl_out_of_process_async_present());
+ MOZ_ASSERT(!gfx::gfxVars::WebglOopAsyncPresentForceSync());
+
+ RefPtr<WebRenderAPI> self = this;
+ auto callback = [self](const layers::RemoteTextureInfo&) {
+ RefPtr<nsIRunnable> runnable = NewRunnableMethod<RemoteTextureWaitType>(
+ "WebRenderAPI::HandleWrTransactionEvents", self,
+ &WebRenderAPI::HandleWrTransactionEvents,
+ RemoteTextureWaitType::AsyncWait);
+ layers::CompositorThread()->Dispatch(runnable.forget());
+ };
+
+ bool isReady = true;
+ while (!aList->mList.empty() && isReady) {
+ auto& front = aList->mList.front();
+ isReady &= layers::RemoteTextureMap::Get()->CheckRemoteTextureReady(
+ front, callback);
+ if (isReady) {
+ aList->mList.pop();
+ }
+ }
+
+ return isReady;
+}
+
+void WebRenderAPI::WaitRemoteTextureReady(
+ layers::RemoteTextureInfoList* aList) {
+ MOZ_ASSERT(layers::CompositorThreadHolder::IsInCompositorThread());
+ MOZ_ASSERT(aList);
+ MOZ_ASSERT(gfx::gfxVars::UseCanvasRenderThread());
+ MOZ_ASSERT(StaticPrefs::webgl_out_of_process_async_present());
+ MOZ_ASSERT(!gfx::gfxVars::WebglOopAsyncPresentForceSync());
+
+ while (!aList->mList.empty()) {
+ auto& front = aList->mList.front();
+ layers::RemoteTextureMap::Get()->WaitRemoteTextureReady(front);
+ aList->mList.pop();
+ }
+}
+
+void WebRenderAPI::FlushPendingWrTransactionEventsWithoutWait() {
+ HandleWrTransactionEvents(RemoteTextureWaitType::FlushWithoutWait);
+}
+
+void WebRenderAPI::FlushPendingWrTransactionEventsWithWait() {
+ HandleWrTransactionEvents(RemoteTextureWaitType::FlushWithWait);
+}
+
+void WebRenderAPI::HandleWrTransactionEvents(RemoteTextureWaitType aType) {
+ auto& events = mPendingWrTransactionEvents;
+
+ while (!events.empty()) {
+ auto& front = events.front();
+ switch (front.mTag) {
+ case WrTransactionEvent::Tag::Transaction:
+ wr_api_send_transaction(mDocHandle, front.Transaction(),
+ front.UseSceneBuilderThread());
+ break;
+ case WrTransactionEvent::Tag::PendingRemoteTextures:
+ bool isReady = true;
+ if (aType == RemoteTextureWaitType::AsyncWait) {
+ isReady = CheckIsRemoteTextureReady(front.RemoteTextureInfoList());
+ } else if (aType == RemoteTextureWaitType::FlushWithWait) {
+ WaitRemoteTextureReady(front.RemoteTextureInfoList());
+ } else {
+ MOZ_ASSERT(aType == RemoteTextureWaitType::FlushWithoutWait);
+ auto* list = front.RemoteTextureInfoList();
+ while (!list->mList.empty()) {
+ auto& front = list->mList.front();
+ layers::RemoteTextureMap::Get()->SuppressRemoteTextureReadyCheck(
+ front.mTextureId, front.mForPid);
+ list->mList.pop();
+ }
+ }
+ if (!isReady) {
+ return;
+ }
+ break;
+ }
+ events.pop();
+ }
+}
+
+std::vector<WrHitResult> WebRenderAPI::HitTest(const wr::WorldPoint& aPoint) {
+ static_assert(gfx::DoesCompositorHitTestInfoFitIntoBits<12>(),
+ "CompositorHitTestFlags MAX value has to be less than number "
+ "of bits in uint16_t minus 4 for SideBitsPacked");
+
+ nsTArray<wr::HitResult> wrResults;
+ wr_api_hit_test(mDocHandle, aPoint, &wrResults);
+
+ std::vector<WrHitResult> geckoResults;
+ for (wr::HitResult wrResult : wrResults) {
+ WrHitResult geckoResult;
+ geckoResult.mLayersId = wr::AsLayersId(wrResult.pipeline_id);
+ geckoResult.mScrollId =
+ static_cast<layers::ScrollableLayerGuid::ViewID>(wrResult.scroll_id);
+ geckoResult.mHitInfo.deserialize(wrResult.hit_info & 0x0fff);
+ geckoResult.mSideBits = static_cast<SideBits>(wrResult.hit_info >> 12);
+
+ if (wrResult.animation_id != 0) {
+ geckoResult.mAnimationId = Some(wrResult.animation_id);
+ } else {
+ geckoResult.mAnimationId = Nothing();
+ }
+ geckoResults.push_back(geckoResult);
+ }
+ return geckoResults;
+}
+
+void WebRenderAPI::Readback(const TimeStamp& aStartTime, gfx::IntSize size,
+ const gfx::SurfaceFormat& aFormat,
+ const Range<uint8_t>& buffer, bool* aNeedsYFlip) {
+ class Readback : public RendererEvent {
+ public:
+ explicit Readback(layers::SynchronousTask* aTask, TimeStamp aStartTime,
+ gfx::IntSize aSize, const gfx::SurfaceFormat& aFormat,
+ const Range<uint8_t>& aBuffer, bool* aNeedsYFlip)
+ : mTask(aTask),
+ mStartTime(aStartTime),
+ mSize(aSize),
+ mFormat(aFormat),
+ mBuffer(aBuffer),
+ mNeedsYFlip(aNeedsYFlip) {
+ MOZ_COUNT_CTOR(Readback);
+ }
+
+ MOZ_COUNTED_DTOR_OVERRIDE(Readback)
+
+ void Run(RenderThread& aRenderThread, WindowId aWindowId) override {
+ aRenderThread.UpdateAndRender(aWindowId, VsyncId(), mStartTime,
+ /* aRender */ true, Some(mSize),
+ wr::SurfaceFormatToImageFormat(mFormat),
+ Some(mBuffer), mNeedsYFlip);
+ layers::AutoCompleteTask complete(mTask);
+ }
+
+ layers::SynchronousTask* mTask;
+ TimeStamp mStartTime;
+ gfx::IntSize mSize;
+ gfx::SurfaceFormat mFormat;
+ const Range<uint8_t>& mBuffer;
+ bool* mNeedsYFlip;
+ };
+
+ // Disable debug flags during readback. See bug 1436020.
+ UpdateDebugFlags(0);
+
+ layers::SynchronousTask task("Readback");
+ auto event = MakeUnique<Readback>(&task, aStartTime, size, aFormat, buffer,
+ aNeedsYFlip);
+ // This event will be passed from wr_backend thread to renderer thread. That
+ // implies that all frame data have been processed when the renderer runs this
+ // read-back event. Then, we could make sure this read-back event gets the
+ // latest result.
+ RunOnRenderThread(std::move(event));
+
+ task.Wait();
+
+ UpdateDebugFlags(gfx::gfxVars::WebRenderDebugFlags());
+}
+
+void WebRenderAPI::ClearAllCaches() { wr_api_clear_all_caches(mDocHandle); }
+
+void WebRenderAPI::EnableNativeCompositor(bool aEnable) {
+ wr_api_enable_native_compositor(mDocHandle, aEnable);
+}
+
+void WebRenderAPI::SetBatchingLookback(uint32_t aCount) {
+ wr_api_set_batching_lookback(mDocHandle, aCount);
+}
+
+void WebRenderAPI::SetBool(wr::BoolParameter aKey, bool aValue) {
+ wr_api_set_bool(mDocHandle, aKey, aValue);
+}
+
+void WebRenderAPI::SetInt(wr::IntParameter aKey, int32_t aValue) {
+ wr_api_set_int(mDocHandle, aKey, aValue);
+}
+
+void WebRenderAPI::SetClearColor(const gfx::DeviceColor& aColor) {
+ RenderThread::Get()->SetClearColor(mId, ToColorF(aColor));
+}
+
+void WebRenderAPI::SetProfilerUI(const nsACString& aUIString) {
+ RenderThread::Get()->SetProfilerUI(mId, aUIString);
+}
+
+void WebRenderAPI::Pause() {
+ class PauseEvent : public RendererEvent {
+ public:
+ explicit PauseEvent(layers::SynchronousTask* aTask) : mTask(aTask) {
+ MOZ_COUNT_CTOR(PauseEvent);
+ }
+
+ MOZ_COUNTED_DTOR_OVERRIDE(PauseEvent)
+
+ void Run(RenderThread& aRenderThread, WindowId aWindowId) override {
+ aRenderThread.Pause(aWindowId);
+ layers::AutoCompleteTask complete(mTask);
+ }
+
+ layers::SynchronousTask* mTask;
+ };
+
+ layers::SynchronousTask task("Pause");
+ auto event = MakeUnique<PauseEvent>(&task);
+ // This event will be passed from wr_backend thread to renderer thread. That
+ // implies that all frame data have been processed when the renderer runs this
+ // event.
+ RunOnRenderThread(std::move(event));
+
+ task.Wait();
+}
+
+bool WebRenderAPI::Resume() {
+ class ResumeEvent : public RendererEvent {
+ public:
+ explicit ResumeEvent(layers::SynchronousTask* aTask, bool* aResult)
+ : mTask(aTask), mResult(aResult) {
+ MOZ_COUNT_CTOR(ResumeEvent);
+ }
+
+ MOZ_COUNTED_DTOR_OVERRIDE(ResumeEvent)
+
+ void Run(RenderThread& aRenderThread, WindowId aWindowId) override {
+ *mResult = aRenderThread.Resume(aWindowId);
+ layers::AutoCompleteTask complete(mTask);
+ }
+
+ layers::SynchronousTask* mTask;
+ bool* mResult;
+ };
+
+ bool result = false;
+ layers::SynchronousTask task("Resume");
+ auto event = MakeUnique<ResumeEvent>(&task, &result);
+ // This event will be passed from wr_backend thread to renderer thread. That
+ // implies that all frame data have been processed when the renderer runs this
+ // event.
+ RunOnRenderThread(std::move(event));
+
+ task.Wait();
+ return result;
+}
+
+void WebRenderAPI::NotifyMemoryPressure() {
+ wr_api_notify_memory_pressure(mDocHandle);
+}
+
+void WebRenderAPI::AccumulateMemoryReport(MemoryReport* aReport) {
+ wr_api_accumulate_memory_report(mDocHandle, aReport, &WebRenderMallocSizeOf,
+ &WebRenderMallocEnclosingSizeOf);
+}
+
+void WebRenderAPI::WakeSceneBuilder() { wr_api_wake_scene_builder(mDocHandle); }
+
+void WebRenderAPI::FlushSceneBuilder() {
+ wr_api_flush_scene_builder(mDocHandle);
+}
+
+void WebRenderAPI::WaitFlushed() {
+ class WaitFlushedEvent : public RendererEvent {
+ public:
+ explicit WaitFlushedEvent(layers::SynchronousTask* aTask) : mTask(aTask) {
+ MOZ_COUNT_CTOR(WaitFlushedEvent);
+ }
+
+ MOZ_COUNTED_DTOR_OVERRIDE(WaitFlushedEvent)
+
+ void Run(RenderThread& aRenderThread, WindowId aWindowId) override {
+ layers::AutoCompleteTask complete(mTask);
+ }
+
+ layers::SynchronousTask* mTask;
+ };
+
+ layers::SynchronousTask task("WaitFlushed");
+ auto event = MakeUnique<WaitFlushedEvent>(&task);
+ // This event will be passed from wr_backend thread to renderer thread. That
+ // implies that all frame data have been processed when the renderer runs this
+ // event.
+ RunOnRenderThread(std::move(event));
+
+ task.Wait();
+}
+
+void WebRenderAPI::Capture() {
+ // see CaptureBits
+ // SCENE | FRAME | TILE_CACHE
+ uint8_t bits = 15; // TODO: get from JavaScript
+ const char* path = "wr-capture"; // TODO: get from JavaScript
+ wr_api_capture(mDocHandle, path, bits);
+}
+
+void WebRenderAPI::StartCaptureSequence(const nsACString& aPath,
+ uint32_t aFlags) {
+ if (mCaptureSequence) {
+ wr_api_stop_capture_sequence(mDocHandle);
+ }
+
+ wr_api_start_capture_sequence(mDocHandle, PromiseFlatCString(aPath).get(),
+ aFlags);
+
+ mCaptureSequence = true;
+}
+
+void WebRenderAPI::StopCaptureSequence() {
+ if (mCaptureSequence) {
+ wr_api_stop_capture_sequence(mDocHandle);
+ }
+
+ mCaptureSequence = false;
+}
+
+void WebRenderAPI::BeginRecording(const TimeStamp& aRecordingStart,
+ wr::PipelineId aRootPipelineId) {
+ class BeginRecordingEvent final : public RendererEvent {
+ public:
+ explicit BeginRecordingEvent(const TimeStamp& aRecordingStart,
+ wr::PipelineId aRootPipelineId)
+ : mRecordingStart(aRecordingStart), mRootPipelineId(aRootPipelineId) {
+ MOZ_COUNT_CTOR(BeginRecordingEvent);
+ }
+
+ ~BeginRecordingEvent() { MOZ_COUNT_DTOR(BeginRecordingEvent); }
+
+ void Run(RenderThread& aRenderThread, WindowId aWindowId) override {
+ aRenderThread.BeginRecordingForWindow(aWindowId, mRecordingStart,
+ mRootPipelineId);
+ }
+
+ private:
+ TimeStamp mRecordingStart;
+ wr::PipelineId mRootPipelineId;
+ };
+
+ auto event =
+ MakeUnique<BeginRecordingEvent>(aRecordingStart, aRootPipelineId);
+ RunOnRenderThread(std::move(event));
+}
+
+RefPtr<WebRenderAPI::EndRecordingPromise> WebRenderAPI::EndRecording() {
+ class EndRecordingEvent final : public RendererEvent {
+ public:
+ explicit EndRecordingEvent() { MOZ_COUNT_CTOR(EndRecordingEvent); }
+
+ MOZ_COUNTED_DTOR(EndRecordingEvent);
+
+ void Run(RenderThread& aRenderThread, WindowId aWindowId) override {
+ Maybe<layers::FrameRecording> recording =
+ aRenderThread.EndRecordingForWindow(aWindowId);
+
+ if (recording) {
+ mPromise.Resolve(recording.extract(), __func__);
+ } else {
+ mPromise.Reject(NS_ERROR_UNEXPECTED, __func__);
+ }
+ }
+
+ RefPtr<WebRenderAPI::EndRecordingPromise> GetPromise() {
+ return mPromise.Ensure(__func__);
+ }
+
+ private:
+ MozPromiseHolder<WebRenderAPI::EndRecordingPromise> mPromise;
+ };
+
+ auto event = MakeUnique<EndRecordingEvent>();
+ auto promise = event->GetPromise();
+
+ RunOnRenderThread(std::move(event));
+ return promise;
+}
+
+void TransactionBuilder::Clear() { wr_resource_updates_clear(mTxn); }
+
+Transaction* TransactionBuilder::Take() {
+ Transaction* txn = mTxn;
+ mTxn = wr_transaction_new(mUseSceneBuilderThread);
+ return txn;
+}
+
+void TransactionBuilder::Notify(wr::Checkpoint aWhen,
+ UniquePtr<NotificationHandler> aEvent) {
+ wr_transaction_notify(mTxn, aWhen,
+ reinterpret_cast<uintptr_t>(aEvent.release()));
+}
+
+void TransactionBuilder::AddImage(ImageKey key,
+ const ImageDescriptor& aDescriptor,
+ wr::Vec<uint8_t>& aBytes) {
+ wr_resource_updates_add_image(mTxn, key, &aDescriptor, &aBytes.inner);
+}
+
+void TransactionBuilder::AddBlobImage(BlobImageKey key,
+ const ImageDescriptor& aDescriptor,
+ uint16_t aTileSize,
+ wr::Vec<uint8_t>& aBytes,
+ const wr::DeviceIntRect& aVisibleRect) {
+ wr_resource_updates_add_blob_image(mTxn, key, &aDescriptor, aTileSize,
+ &aBytes.inner, aVisibleRect);
+}
+
+void TransactionBuilder::AddExternalImage(ImageKey key,
+ const ImageDescriptor& aDescriptor,
+ ExternalImageId aExtID,
+ wr::ExternalImageType aImageType,
+ uint8_t aChannelIndex) {
+ wr_resource_updates_add_external_image(mTxn, key, &aDescriptor, aExtID,
+ &aImageType, aChannelIndex);
+}
+
+void TransactionBuilder::AddExternalImageBuffer(
+ ImageKey aKey, const ImageDescriptor& aDescriptor,
+ ExternalImageId aHandle) {
+ auto channelIndex = 0;
+ AddExternalImage(aKey, aDescriptor, aHandle, wr::ExternalImageType::Buffer(),
+ channelIndex);
+}
+
+void TransactionBuilder::UpdateImageBuffer(ImageKey aKey,
+ const ImageDescriptor& aDescriptor,
+ wr::Vec<uint8_t>& aBytes) {
+ wr_resource_updates_update_image(mTxn, aKey, &aDescriptor, &aBytes.inner);
+}
+
+void TransactionBuilder::UpdateBlobImage(BlobImageKey aKey,
+ const ImageDescriptor& aDescriptor,
+ wr::Vec<uint8_t>& aBytes,
+ const wr::DeviceIntRect& aVisibleRect,
+ const wr::LayoutIntRect& aDirtyRect) {
+ wr_resource_updates_update_blob_image(mTxn, aKey, &aDescriptor, &aBytes.inner,
+ aVisibleRect, aDirtyRect);
+}
+
+void TransactionBuilder::UpdateExternalImage(ImageKey aKey,
+ const ImageDescriptor& aDescriptor,
+ ExternalImageId aExtID,
+ wr::ExternalImageType aImageType,
+ uint8_t aChannelIndex) {
+ wr_resource_updates_update_external_image(mTxn, aKey, &aDescriptor, aExtID,
+ &aImageType, aChannelIndex);
+}
+
+void TransactionBuilder::UpdateExternalImageWithDirtyRect(
+ ImageKey aKey, const ImageDescriptor& aDescriptor, ExternalImageId aExtID,
+ wr::ExternalImageType aImageType, const wr::DeviceIntRect& aDirtyRect,
+ uint8_t aChannelIndex) {
+ wr_resource_updates_update_external_image_with_dirty_rect(
+ mTxn, aKey, &aDescriptor, aExtID, &aImageType, aChannelIndex, aDirtyRect);
+}
+
+void TransactionBuilder::SetBlobImageVisibleArea(
+ BlobImageKey aKey, const wr::DeviceIntRect& aArea) {
+ wr_resource_updates_set_blob_image_visible_area(mTxn, aKey, &aArea);
+}
+
+void TransactionBuilder::DeleteImage(ImageKey aKey) {
+ wr_resource_updates_delete_image(mTxn, aKey);
+}
+
+void TransactionBuilder::DeleteBlobImage(BlobImageKey aKey) {
+ wr_resource_updates_delete_blob_image(mTxn, aKey);
+}
+
+void TransactionBuilder::AddRawFont(wr::FontKey aKey, wr::Vec<uint8_t>& aBytes,
+ uint32_t aIndex) {
+ wr_resource_updates_add_raw_font(mTxn, aKey, &aBytes.inner, aIndex);
+}
+
+void TransactionBuilder::AddFontDescriptor(wr::FontKey aKey,
+ wr::Vec<uint8_t>& aBytes,
+ uint32_t aIndex) {
+ wr_resource_updates_add_font_descriptor(mTxn, aKey, &aBytes.inner, aIndex);
+}
+
+void TransactionBuilder::DeleteFont(wr::FontKey aKey) {
+ wr_resource_updates_delete_font(mTxn, aKey);
+}
+
+void TransactionBuilder::AddFontInstance(
+ wr::FontInstanceKey aKey, wr::FontKey aFontKey, float aGlyphSize,
+ const wr::FontInstanceOptions* aOptions,
+ const wr::FontInstancePlatformOptions* aPlatformOptions,
+ wr::Vec<uint8_t>& aVariations) {
+ wr_resource_updates_add_font_instance(mTxn, aKey, aFontKey, aGlyphSize,
+ aOptions, aPlatformOptions,
+ &aVariations.inner);
+}
+
+void TransactionBuilder::DeleteFontInstance(wr::FontInstanceKey aKey) {
+ wr_resource_updates_delete_font_instance(mTxn, aKey);
+}
+
+void TransactionBuilder::UpdateQualitySettings(
+ bool aForceSubpixelAAWherePossible) {
+ wr_transaction_set_quality_settings(mTxn, aForceSubpixelAAWherePossible);
+}
+
+class FrameStartTime : public RendererEvent {
+ public:
+ explicit FrameStartTime(const TimeStamp& aTime) : mTime(aTime) {
+ MOZ_COUNT_CTOR(FrameStartTime);
+ }
+
+ MOZ_COUNTED_DTOR_OVERRIDE(FrameStartTime)
+
+ void Run(RenderThread& aRenderThread, WindowId aWindowId) override {
+ auto renderer = aRenderThread.GetRenderer(aWindowId);
+ if (renderer) {
+ renderer->SetFrameStartTime(mTime);
+ }
+ }
+
+ private:
+ TimeStamp mTime;
+};
+
+void WebRenderAPI::SetFrameStartTime(const TimeStamp& aTime) {
+ auto event = MakeUnique<FrameStartTime>(aTime);
+ RunOnRenderThread(std::move(event));
+}
+
+void WebRenderAPI::RunOnRenderThread(UniquePtr<RendererEvent> aEvent) {
+ auto event = reinterpret_cast<uintptr_t>(aEvent.release());
+ wr_api_send_external_event(mDocHandle, event);
+}
+
+DisplayListBuilder::DisplayListBuilder(PipelineId aId,
+ WebRenderBackend aBackend)
+ : mCurrentSpaceAndClipChain(wr::RootScrollNodeWithChain()),
+ mActiveFixedPosTracker(nullptr),
+ mPipelineId(aId),
+ mBackend(aBackend),
+ mDisplayItemCache(nullptr) {
+ MOZ_COUNT_CTOR(DisplayListBuilder);
+ mWrState = wr_state_new(aId);
+
+ if (mDisplayItemCache && mDisplayItemCache->IsEnabled()) {
+ mDisplayItemCache->SetPipelineId(aId);
+ }
+}
+
+DisplayListBuilder::~DisplayListBuilder() {
+ MOZ_COUNT_DTOR(DisplayListBuilder);
+ wr_state_delete(mWrState);
+}
+
+void DisplayListBuilder::Save() { wr_dp_save(mWrState); }
+void DisplayListBuilder::Restore() { wr_dp_restore(mWrState); }
+void DisplayListBuilder::ClearSave() { wr_dp_clear_save(mWrState); }
+
+usize DisplayListBuilder::Dump(usize aIndent, const Maybe<usize>& aStart,
+ const Maybe<usize>& aEnd) {
+ return wr_dump_display_list(mWrState, aIndent, aStart.ptrOr(nullptr),
+ aEnd.ptrOr(nullptr));
+}
+
+void DisplayListBuilder::DumpSerializedDisplayList() {
+ wr_dump_serialized_display_list(mWrState);
+}
+
+void DisplayListBuilder::Begin(layers::DisplayItemCache* aCache) {
+ wr_api_begin_builder(mWrState);
+
+ mScrollIds.clear();
+ mCurrentSpaceAndClipChain = wr::RootScrollNodeWithChain();
+ mClipChainLeaf = Nothing();
+ mSuspendedSpaceAndClipChain = Nothing();
+ mSuspendedClipChainLeaf = Nothing();
+ mCachedTextDT = nullptr;
+ mCachedContext = nullptr;
+ mActiveFixedPosTracker = nullptr;
+ mDisplayItemCache = aCache;
+ mCurrentCacheSlot = Nothing();
+ mRemotePipelineIds.Clear();
+}
+
+void DisplayListBuilder::End(BuiltDisplayList& aOutDisplayList) {
+ wr_api_end_builder(
+ mWrState, &aOutDisplayList.dl_desc, &aOutDisplayList.dl_items.inner,
+ &aOutDisplayList.dl_cache.inner, &aOutDisplayList.dl_spatial_tree.inner);
+
+ mDisplayItemCache = nullptr;
+}
+
+void DisplayListBuilder::End(layers::DisplayListData& aOutTransaction) {
+ if (mDisplayItemCache && mDisplayItemCache->IsEnabled()) {
+ wr_dp_set_cache_size(mWrState, mDisplayItemCache->CurrentSize());
+ }
+
+ wr::VecU8 dlItems, dlCache, dlSpatialTree;
+ wr_api_end_builder(mWrState, &aOutTransaction.mDLDesc, &dlItems.inner,
+ &dlCache.inner, &dlSpatialTree.inner);
+ aOutTransaction.mDLItems.emplace(dlItems.inner.data, dlItems.inner.length,
+ dlItems.inner.capacity);
+ aOutTransaction.mDLCache.emplace(dlCache.inner.data, dlCache.inner.length,
+ dlCache.inner.capacity);
+ aOutTransaction.mDLSpatialTree.emplace(dlSpatialTree.inner.data,
+ dlSpatialTree.inner.length,
+ dlSpatialTree.inner.capacity);
+ aOutTransaction.mRemotePipelineIds = mRemotePipelineIds.Clone();
+ dlItems.inner.capacity = 0;
+ dlItems.inner.data = nullptr;
+ dlCache.inner.capacity = 0;
+ dlCache.inner.data = nullptr;
+ dlSpatialTree.inner.capacity = 0;
+ dlSpatialTree.inner.data = nullptr;
+}
+
+Maybe<wr::WrSpatialId> DisplayListBuilder::PushStackingContext(
+ const wr::StackingContextParams& aParams, const wr::LayoutRect& aBounds,
+ const wr::RasterSpace& aRasterSpace) {
+ MOZ_ASSERT(mClipChainLeaf.isNothing(),
+ "Non-empty leaf from clip chain given, but not used with SC!");
+
+ WRDL_LOG(
+ "PushStackingContext b=%s t=%s id=0x%" PRIx64 "\n", mWrState,
+ ToString(aBounds).c_str(),
+ aParams.mTransformPtr ? ToString(*aParams.mTransformPtr).c_str() : "none",
+ aParams.animation ? aParams.animation->id : 0);
+
+ auto spatialId = wr_dp_push_stacking_context(
+ mWrState, aBounds, mCurrentSpaceAndClipChain.space, &aParams,
+ aParams.mTransformPtr, aParams.mFilters.Elements(),
+ aParams.mFilters.Length(), aParams.mFilterDatas.Elements(),
+ aParams.mFilterDatas.Length(), aRasterSpace);
+
+ return spatialId.id != 0 ? Some(spatialId) : Nothing();
+}
+
+void DisplayListBuilder::PopStackingContext(bool aIsReferenceFrame) {
+ WRDL_LOG("PopStackingContext\n", mWrState);
+ wr_dp_pop_stacking_context(mWrState, aIsReferenceFrame);
+}
+
+wr::WrClipChainId DisplayListBuilder::DefineClipChain(
+ const nsTArray<wr::WrClipId>& aClips, bool aParentWithCurrentChain) {
+ CancelGroup();
+
+ const uint64_t* parent = nullptr;
+ if (aParentWithCurrentChain &&
+ mCurrentSpaceAndClipChain.clip_chain != wr::ROOT_CLIP_CHAIN) {
+ parent = &mCurrentSpaceAndClipChain.clip_chain;
+ }
+ uint64_t clipchainId = wr_dp_define_clipchain(
+ mWrState, parent, aClips.Elements(), aClips.Length());
+ WRDL_LOG("DefineClipChain id=%" PRIu64 " clips=%zu\n", mWrState, clipchainId,
+ aClips.Length());
+ return wr::WrClipChainId{clipchainId};
+}
+
+wr::WrClipId DisplayListBuilder::DefineImageMaskClip(
+ const wr::ImageMask& aMask, const nsTArray<wr::LayoutPoint>& aPoints,
+ wr::FillRule aFillRule) {
+ CancelGroup();
+
+ WrClipId clipId = wr_dp_define_image_mask_clip_with_parent_clip_chain(
+ mWrState, mCurrentSpaceAndClipChain.space, aMask, aPoints.Elements(),
+ aPoints.Length(), aFillRule);
+
+ return clipId;
+}
+
+wr::WrClipId DisplayListBuilder::DefineRoundedRectClip(
+ Maybe<wr::WrSpatialId> aSpace, const wr::ComplexClipRegion& aComplex) {
+ CancelGroup();
+
+ WrClipId clipId;
+ if (aSpace) {
+ clipId = wr_dp_define_rounded_rect_clip(mWrState, *aSpace, aComplex);
+ } else {
+ clipId = wr_dp_define_rounded_rect_clip(
+ mWrState, mCurrentSpaceAndClipChain.space, aComplex);
+ }
+
+ return clipId;
+}
+
+wr::WrClipId DisplayListBuilder::DefineRectClip(Maybe<wr::WrSpatialId> aSpace,
+ wr::LayoutRect aClipRect) {
+ CancelGroup();
+
+ WrClipId clipId;
+ if (aSpace) {
+ clipId = wr_dp_define_rect_clip(mWrState, *aSpace, aClipRect);
+ } else {
+ clipId = wr_dp_define_rect_clip(mWrState, mCurrentSpaceAndClipChain.space,
+ aClipRect);
+ }
+
+ return clipId;
+}
+
+wr::WrSpatialId DisplayListBuilder::DefineStickyFrame(
+ const wr::LayoutRect& aContentRect, const float* aTopMargin,
+ const float* aRightMargin, const float* aBottomMargin,
+ const float* aLeftMargin, const StickyOffsetBounds& aVerticalBounds,
+ const StickyOffsetBounds& aHorizontalBounds,
+ const wr::LayoutVector2D& aAppliedOffset, wr::SpatialTreeItemKey aKey) {
+ auto spatialId = wr_dp_define_sticky_frame(
+ mWrState, mCurrentSpaceAndClipChain.space, aContentRect, aTopMargin,
+ aRightMargin, aBottomMargin, aLeftMargin, aVerticalBounds,
+ aHorizontalBounds, aAppliedOffset, aKey);
+
+ WRDL_LOG("DefineSticky id=%zu c=%s t=%s r=%s b=%s l=%s v=%s h=%s a=%s\n",
+ mWrState, spatialId.id, ToString(aContentRect).c_str(),
+ aTopMargin ? ToString(*aTopMargin).c_str() : "none",
+ aRightMargin ? ToString(*aRightMargin).c_str() : "none",
+ aBottomMargin ? ToString(*aBottomMargin).c_str() : "none",
+ aLeftMargin ? ToString(*aLeftMargin).c_str() : "none",
+ ToString(aVerticalBounds).c_str(),
+ ToString(aHorizontalBounds).c_str(),
+ ToString(aAppliedOffset).c_str());
+
+ return spatialId;
+}
+
+Maybe<wr::WrSpatialId> DisplayListBuilder::GetScrollIdForDefinedScrollLayer(
+ layers::ScrollableLayerGuid::ViewID aViewId) const {
+ if (aViewId == layers::ScrollableLayerGuid::NULL_SCROLL_ID) {
+ return Some(wr::RootScrollNode());
+ }
+
+ auto it = mScrollIds.find(aViewId);
+ if (it == mScrollIds.end()) {
+ return Nothing();
+ }
+
+ return Some(it->second);
+}
+
+wr::WrSpatialId DisplayListBuilder::DefineScrollLayer(
+ const layers::ScrollableLayerGuid::ViewID& aViewId,
+ const Maybe<wr::WrSpatialId>& aParent, const wr::LayoutRect& aContentRect,
+ const wr::LayoutRect& aClipRect, const wr::LayoutVector2D& aScrollOffset,
+ wr::APZScrollGeneration aScrollOffsetGeneration,
+ wr::HasScrollLinkedEffect aHasScrollLinkedEffect,
+ wr::SpatialTreeItemKey aKey) {
+ auto it = mScrollIds.find(aViewId);
+ if (it != mScrollIds.end()) {
+ return it->second;
+ }
+
+ // We haven't defined aViewId before, so let's define it now.
+ wr::WrSpatialId defaultParent = mCurrentSpaceAndClipChain.space;
+
+ auto space = wr_dp_define_scroll_layer(
+ mWrState, aViewId, aParent ? aParent.ptr() : &defaultParent, aContentRect,
+ aClipRect, aScrollOffset, aScrollOffsetGeneration, aHasScrollLinkedEffect,
+ aKey);
+
+ WRDL_LOG("DefineScrollLayer id=%" PRIu64
+ "/%zu p=%s co=%s cl=%s generation=%s hasScrollLinkedEffect=%s\n",
+ mWrState, aViewId, space.id,
+ aParent ? ToString(aParent->id).c_str() : "(nil)",
+ ToString(aContentRect).c_str(), ToString(aClipRect).c_str(),
+ ToString(aScrollOffsetGeneration).c_str(),
+ ToString(aHasScrollLinkedEffect).c_str());
+
+ mScrollIds[aViewId] = space;
+ return space;
+}
+
+void DisplayListBuilder::PushRect(const wr::LayoutRect& aBounds,
+ const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible,
+ bool aForceAntiAliasing, bool aIsCheckerboard,
+ const wr::ColorF& aColor) {
+ wr::LayoutRect clip = MergeClipLeaf(aClip);
+ WRDL_LOG("PushRect b=%s cl=%s c=%s\n", mWrState, ToString(aBounds).c_str(),
+ ToString(clip).c_str(), ToString(aColor).c_str());
+ wr_dp_push_rect(mWrState, aBounds, clip, aIsBackfaceVisible,
+ aForceAntiAliasing, aIsCheckerboard,
+ &mCurrentSpaceAndClipChain, aColor);
+}
+
+void DisplayListBuilder::PushRoundedRect(const wr::LayoutRect& aBounds,
+ const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible,
+ const wr::ColorF& aColor) {
+ wr::LayoutRect clip = MergeClipLeaf(aClip);
+ WRDL_LOG("PushRoundedRect b=%s cl=%s c=%s\n", mWrState,
+ ToString(aBounds).c_str(), ToString(clip).c_str(),
+ ToString(aColor).c_str());
+
+ // Draw the rounded rectangle as a border with rounded corners. We could also
+ // draw this as a rectangle clipped to a rounded rectangle, but:
+ // - clips are not cached; borders are
+ // - a simple border like this will be drawn as an image
+ // - Processing lots of clips is not WebRender's strong point.
+ //
+ // Made the borders thicker than one half the width/height, to avoid
+ // little white dots at the center at some magnifications.
+ wr::BorderSide side = {aColor, wr::BorderStyle::Solid};
+ float h = aBounds.width() * 0.6f;
+ float v = aBounds.height() * 0.6f;
+ wr::LayoutSideOffsets widths = {v, h, v, h};
+ wr::BorderRadius radii = {{h, v}, {h, v}, {h, v}, {h, v}};
+
+ // Anti-aliased borders are required for rounded borders.
+ wr_dp_push_border(mWrState, aBounds, clip, aIsBackfaceVisible,
+ &mCurrentSpaceAndClipChain, wr::AntialiasBorder::Yes,
+ widths, side, side, side, side, radii);
+}
+
+void DisplayListBuilder::PushHitTest(
+ const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible,
+ const layers::ScrollableLayerGuid::ViewID& aScrollId,
+ const gfx::CompositorHitTestInfo& aHitInfo, SideBits aSideBits) {
+ wr::LayoutRect clip = MergeClipLeaf(aClip);
+ WRDL_LOG("PushHitTest b=%s cl=%s\n", mWrState, ToString(aBounds).c_str(),
+ ToString(clip).c_str());
+
+ static_assert(gfx::DoesCompositorHitTestInfoFitIntoBits<12>(),
+ "CompositorHitTestFlags MAX value has to be less than number "
+ "of bits in uint16_t minus 4 for SideBitsPacked");
+
+ uint16_t hitInfoBits = static_cast<uint16_t>(aHitInfo.serialize()) |
+ (static_cast<uint16_t>(aSideBits) << 12);
+
+ wr_dp_push_hit_test(mWrState, aBounds, clip, aIsBackfaceVisible,
+ &mCurrentSpaceAndClipChain, aScrollId, hitInfoBits);
+}
+
+void DisplayListBuilder::PushRectWithAnimation(
+ const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible, const wr::ColorF& aColor,
+ const WrAnimationProperty* aAnimation) {
+ wr::LayoutRect clip = MergeClipLeaf(aClip);
+ WRDL_LOG("PushRectWithAnimation b=%s cl=%s c=%s\n", mWrState,
+ ToString(aBounds).c_str(), ToString(clip).c_str(),
+ ToString(aColor).c_str());
+
+ wr_dp_push_rect_with_animation(mWrState, aBounds, clip, aIsBackfaceVisible,
+ &mCurrentSpaceAndClipChain, aColor,
+ aAnimation);
+}
+
+void DisplayListBuilder::PushClearRect(const wr::LayoutRect& aBounds) {
+ wr::LayoutRect clip = MergeClipLeaf(aBounds);
+ WRDL_LOG("PushClearRect b=%s c=%s\n", mWrState, ToString(aBounds).c_str(),
+ ToString(clip).c_str());
+ wr_dp_push_clear_rect(mWrState, aBounds, clip, &mCurrentSpaceAndClipChain);
+}
+
+void DisplayListBuilder::PushBackdropFilter(
+ const wr::LayoutRect& aBounds, const wr::ComplexClipRegion& aRegion,
+ const nsTArray<wr::FilterOp>& aFilters,
+ const nsTArray<wr::WrFilterData>& aFilterDatas, bool aIsBackfaceVisible) {
+ wr::LayoutRect clip = MergeClipLeaf(aBounds);
+ WRDL_LOG("PushBackdropFilter b=%s c=%s\n", mWrState,
+ ToString(aBounds).c_str(), ToString(clip).c_str());
+
+ auto clipId = DefineRoundedRectClip(Nothing(), aRegion);
+ auto clipChainId = DefineClipChain({clipId}, true);
+ auto spaceAndClip =
+ WrSpaceAndClipChain{mCurrentSpaceAndClipChain.space, clipChainId.id};
+
+ wr_dp_push_backdrop_filter(mWrState, aBounds, clip, aIsBackfaceVisible,
+ &spaceAndClip, aFilters.Elements(),
+ aFilters.Length(), aFilterDatas.Elements(),
+ aFilterDatas.Length());
+}
+
+void DisplayListBuilder::PushLinearGradient(
+ const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible, const wr::LayoutPoint& aStartPoint,
+ const wr::LayoutPoint& aEndPoint, const nsTArray<wr::GradientStop>& aStops,
+ wr::ExtendMode aExtendMode, const wr::LayoutSize aTileSize,
+ const wr::LayoutSize aTileSpacing) {
+ wr_dp_push_linear_gradient(
+ mWrState, aBounds, MergeClipLeaf(aClip), aIsBackfaceVisible,
+ &mCurrentSpaceAndClipChain, aStartPoint, aEndPoint, aStops.Elements(),
+ aStops.Length(), aExtendMode, aTileSize, aTileSpacing);
+}
+
+void DisplayListBuilder::PushRadialGradient(
+ const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible, const wr::LayoutPoint& aCenter,
+ const wr::LayoutSize& aRadius, const nsTArray<wr::GradientStop>& aStops,
+ wr::ExtendMode aExtendMode, const wr::LayoutSize aTileSize,
+ const wr::LayoutSize aTileSpacing) {
+ wr_dp_push_radial_gradient(
+ mWrState, aBounds, MergeClipLeaf(aClip), aIsBackfaceVisible,
+ &mCurrentSpaceAndClipChain, aCenter, aRadius, aStops.Elements(),
+ aStops.Length(), aExtendMode, aTileSize, aTileSpacing);
+}
+
+void DisplayListBuilder::PushConicGradient(
+ const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible, const wr::LayoutPoint& aCenter, const float aAngle,
+ const nsTArray<wr::GradientStop>& aStops, wr::ExtendMode aExtendMode,
+ const wr::LayoutSize aTileSize, const wr::LayoutSize aTileSpacing) {
+ wr_dp_push_conic_gradient(mWrState, aBounds, MergeClipLeaf(aClip),
+ aIsBackfaceVisible, &mCurrentSpaceAndClipChain,
+ aCenter, aAngle, aStops.Elements(), aStops.Length(),
+ aExtendMode, aTileSize, aTileSpacing);
+}
+
+void DisplayListBuilder::PushImage(
+ const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible, bool aForceAntiAliasing,
+ wr::ImageRendering aFilter, wr::ImageKey aImage, bool aPremultipliedAlpha,
+ const wr::ColorF& aColor, bool aPreferCompositorSurface,
+ bool aSupportsExternalCompositing) {
+ wr::LayoutRect clip = MergeClipLeaf(aClip);
+ WRDL_LOG("PushImage b=%s cl=%s\n", mWrState, ToString(aBounds).c_str(),
+ ToString(clip).c_str());
+ wr_dp_push_image(mWrState, aBounds, clip, aIsBackfaceVisible,
+ aForceAntiAliasing, &mCurrentSpaceAndClipChain, aFilter,
+ aImage, aPremultipliedAlpha, aColor,
+ aPreferCompositorSurface, aSupportsExternalCompositing);
+}
+
+void DisplayListBuilder::PushRepeatingImage(
+ const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible, const wr::LayoutSize& aStretchSize,
+ const wr::LayoutSize& aTileSpacing, wr::ImageRendering aFilter,
+ wr::ImageKey aImage, bool aPremultipliedAlpha, const wr::ColorF& aColor) {
+ wr::LayoutRect clip = MergeClipLeaf(aClip);
+ WRDL_LOG("PushImage b=%s cl=%s s=%s t=%s\n", mWrState,
+ ToString(aBounds).c_str(), ToString(clip).c_str(),
+ ToString(aStretchSize).c_str(), ToString(aTileSpacing).c_str());
+ wr_dp_push_repeating_image(
+ mWrState, aBounds, clip, aIsBackfaceVisible, &mCurrentSpaceAndClipChain,
+ aStretchSize, aTileSpacing, aFilter, aImage, aPremultipliedAlpha, aColor);
+}
+
+void DisplayListBuilder::PushYCbCrPlanarImage(
+ const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible, wr::ImageKey aImageChannel0,
+ wr::ImageKey aImageChannel1, wr::ImageKey aImageChannel2,
+ wr::WrColorDepth aColorDepth, wr::WrYuvColorSpace aColorSpace,
+ wr::WrColorRange aColorRange, wr::ImageRendering aRendering,
+ bool aPreferCompositorSurface, bool aSupportsExternalCompositing) {
+ wr_dp_push_yuv_planar_image(
+ mWrState, aBounds, MergeClipLeaf(aClip), aIsBackfaceVisible,
+ &mCurrentSpaceAndClipChain, aImageChannel0, aImageChannel1,
+ aImageChannel2, aColorDepth, aColorSpace, aColorRange, aRendering,
+ aPreferCompositorSurface, aSupportsExternalCompositing);
+}
+
+void DisplayListBuilder::PushNV12Image(
+ const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible, wr::ImageKey aImageChannel0,
+ wr::ImageKey aImageChannel1, wr::WrColorDepth aColorDepth,
+ wr::WrYuvColorSpace aColorSpace, wr::WrColorRange aColorRange,
+ wr::ImageRendering aRendering, bool aPreferCompositorSurface,
+ bool aSupportsExternalCompositing) {
+ wr_dp_push_yuv_NV12_image(
+ mWrState, aBounds, MergeClipLeaf(aClip), aIsBackfaceVisible,
+ &mCurrentSpaceAndClipChain, aImageChannel0, aImageChannel1, aColorDepth,
+ aColorSpace, aColorRange, aRendering, aPreferCompositorSurface,
+ aSupportsExternalCompositing);
+}
+
+void DisplayListBuilder::PushP010Image(
+ const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible, wr::ImageKey aImageChannel0,
+ wr::ImageKey aImageChannel1, wr::WrColorDepth aColorDepth,
+ wr::WrYuvColorSpace aColorSpace, wr::WrColorRange aColorRange,
+ wr::ImageRendering aRendering, bool aPreferCompositorSurface,
+ bool aSupportsExternalCompositing) {
+ wr_dp_push_yuv_P010_image(
+ mWrState, aBounds, MergeClipLeaf(aClip), aIsBackfaceVisible,
+ &mCurrentSpaceAndClipChain, aImageChannel0, aImageChannel1, aColorDepth,
+ aColorSpace, aColorRange, aRendering, aPreferCompositorSurface,
+ aSupportsExternalCompositing);
+}
+
+void DisplayListBuilder::PushYCbCrInterleavedImage(
+ const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible, wr::ImageKey aImageChannel0,
+ wr::WrColorDepth aColorDepth, wr::WrYuvColorSpace aColorSpace,
+ wr::WrColorRange aColorRange, wr::ImageRendering aRendering,
+ bool aPreferCompositorSurface, bool aSupportsExternalCompositing) {
+ wr_dp_push_yuv_interleaved_image(
+ mWrState, aBounds, MergeClipLeaf(aClip), aIsBackfaceVisible,
+ &mCurrentSpaceAndClipChain, aImageChannel0, aColorDepth, aColorSpace,
+ aColorRange, aRendering, aPreferCompositorSurface,
+ aSupportsExternalCompositing);
+}
+
+void DisplayListBuilder::PushIFrame(const LayoutDeviceRect& aDevPxBounds,
+ bool aIsBackfaceVisible,
+ PipelineId aPipeline,
+ bool aIgnoreMissingPipeline) {
+ mRemotePipelineIds.AppendElement(aPipeline);
+ // If the incoming bounds size has decimals (As it could when zoom is
+ // involved), and is pushed straight through here, the compositor would end up
+ // calculating the destination rect to paint the rendered iframe into
+ // with those decimal values, rounding the result, instead of snapping. This
+ // can cause the rendered iframe rect and its destination rect to be
+ // mismatched, resulting in interpolation artifacts.
+ auto snapped = aDevPxBounds;
+ auto tl = snapped.TopLeft().Round();
+ auto br = snapped.BottomRight().Round();
+
+ snapped.SizeTo(LayoutDeviceSize(br.x - tl.x, br.y - tl.y));
+
+ const auto bounds = wr::ToLayoutRect(snapped);
+ wr_dp_push_iframe(mWrState, bounds, MergeClipLeaf(bounds), aIsBackfaceVisible,
+ &mCurrentSpaceAndClipChain, aPipeline,
+ aIgnoreMissingPipeline);
+}
+
+void DisplayListBuilder::PushBorder(const wr::LayoutRect& aBounds,
+ const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible,
+ const wr::LayoutSideOffsets& aWidths,
+ const Range<const wr::BorderSide>& aSides,
+ const wr::BorderRadius& aRadius,
+ wr::AntialiasBorder aAntialias) {
+ MOZ_ASSERT(aSides.length() == 4);
+ if (aSides.length() != 4) {
+ return;
+ }
+ wr_dp_push_border(mWrState, aBounds, MergeClipLeaf(aClip), aIsBackfaceVisible,
+ &mCurrentSpaceAndClipChain, aAntialias, aWidths, aSides[0],
+ aSides[1], aSides[2], aSides[3], aRadius);
+}
+
+void DisplayListBuilder::PushBorderImage(const wr::LayoutRect& aBounds,
+ const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible,
+ const wr::WrBorderImage& aParams) {
+ wr_dp_push_border_image(mWrState, aBounds, MergeClipLeaf(aClip),
+ aIsBackfaceVisible, &mCurrentSpaceAndClipChain,
+ &aParams);
+}
+
+void DisplayListBuilder::PushBorderGradient(
+ const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible, const wr::LayoutSideOffsets& aWidths,
+ const int32_t aWidth, const int32_t aHeight, bool aFill,
+ const wr::DeviceIntSideOffsets& aSlice, const wr::LayoutPoint& aStartPoint,
+ const wr::LayoutPoint& aEndPoint, const nsTArray<wr::GradientStop>& aStops,
+ wr::ExtendMode aExtendMode) {
+ wr_dp_push_border_gradient(
+ mWrState, aBounds, MergeClipLeaf(aClip), aIsBackfaceVisible,
+ &mCurrentSpaceAndClipChain, aWidths, aWidth, aHeight, aFill, aSlice,
+ aStartPoint, aEndPoint, aStops.Elements(), aStops.Length(), aExtendMode);
+}
+
+void DisplayListBuilder::PushBorderRadialGradient(
+ const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible, const wr::LayoutSideOffsets& aWidths, bool aFill,
+ const wr::LayoutPoint& aCenter, const wr::LayoutSize& aRadius,
+ const nsTArray<wr::GradientStop>& aStops, wr::ExtendMode aExtendMode) {
+ wr_dp_push_border_radial_gradient(
+ mWrState, aBounds, MergeClipLeaf(aClip), aIsBackfaceVisible,
+ &mCurrentSpaceAndClipChain, aWidths, aFill, aCenter, aRadius,
+ aStops.Elements(), aStops.Length(), aExtendMode);
+}
+
+void DisplayListBuilder::PushBorderConicGradient(
+ const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible, const wr::LayoutSideOffsets& aWidths, bool aFill,
+ const wr::LayoutPoint& aCenter, const float aAngle,
+ const nsTArray<wr::GradientStop>& aStops, wr::ExtendMode aExtendMode) {
+ wr_dp_push_border_conic_gradient(
+ mWrState, aBounds, MergeClipLeaf(aClip), aIsBackfaceVisible,
+ &mCurrentSpaceAndClipChain, aWidths, aFill, aCenter, aAngle,
+ aStops.Elements(), aStops.Length(), aExtendMode);
+}
+
+void DisplayListBuilder::PushText(const wr::LayoutRect& aBounds,
+ const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible,
+ const wr::ColorF& aColor,
+ wr::FontInstanceKey aFontKey,
+ Range<const wr::GlyphInstance> aGlyphBuffer,
+ const wr::GlyphOptions* aGlyphOptions) {
+ wr_dp_push_text(mWrState, aBounds, MergeClipLeaf(aClip), aIsBackfaceVisible,
+ &mCurrentSpaceAndClipChain, aColor, aFontKey,
+ &aGlyphBuffer[0], aGlyphBuffer.length(), aGlyphOptions);
+}
+
+void DisplayListBuilder::PushLine(const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible,
+ const wr::Line& aLine) {
+ wr::LayoutRect clip = MergeClipLeaf(aClip);
+ wr_dp_push_line(mWrState, &clip, aIsBackfaceVisible,
+ &mCurrentSpaceAndClipChain, &aLine.bounds,
+ aLine.wavyLineThickness, aLine.orientation, &aLine.color,
+ aLine.style);
+}
+
+void DisplayListBuilder::PushShadow(const wr::LayoutRect& aRect,
+ const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible,
+ const wr::Shadow& aShadow,
+ bool aShouldInflate) {
+ // Local clip_rects are translated inside of shadows, as they are assumed to
+ // be part of the element drawing itself, and not a parent frame clipping it.
+ // As such, it is not sound to apply the MergeClipLeaf optimization inside of
+ // shadows. So we disable the optimization when we encounter a shadow.
+ // Shadows don't span frames, so we don't have to worry about MergeClipLeaf
+ // being re-enabled mid-shadow. The optimization is restored in PopAllShadows.
+ SuspendClipLeafMerging();
+ wr_dp_push_shadow(mWrState, aRect, aClip, aIsBackfaceVisible,
+ &mCurrentSpaceAndClipChain, aShadow, aShouldInflate);
+}
+
+void DisplayListBuilder::PopAllShadows() {
+ wr_dp_pop_all_shadows(mWrState);
+ ResumeClipLeafMerging();
+}
+
+void DisplayListBuilder::SuspendClipLeafMerging() {
+ if (mClipChainLeaf) {
+ // No one should reinitialize mClipChainLeaf while we're suspended
+ MOZ_ASSERT(!mSuspendedClipChainLeaf);
+
+ mSuspendedClipChainLeaf = mClipChainLeaf;
+ mSuspendedSpaceAndClipChain = Some(mCurrentSpaceAndClipChain);
+
+ auto clipId = DefineRectClip(Nothing(), *mClipChainLeaf);
+ auto clipChainId = DefineClipChain({clipId}, true);
+
+ mCurrentSpaceAndClipChain.clip_chain = clipChainId.id;
+ mClipChainLeaf = Nothing();
+ }
+}
+
+void DisplayListBuilder::ResumeClipLeafMerging() {
+ if (mSuspendedClipChainLeaf) {
+ mCurrentSpaceAndClipChain = *mSuspendedSpaceAndClipChain;
+ mClipChainLeaf = mSuspendedClipChainLeaf;
+
+ mSuspendedClipChainLeaf = Nothing();
+ mSuspendedSpaceAndClipChain = Nothing();
+ }
+}
+
+void DisplayListBuilder::PushBoxShadow(
+ const wr::LayoutRect& aRect, const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible, const wr::LayoutRect& aBoxBounds,
+ const wr::LayoutVector2D& aOffset, const wr::ColorF& aColor,
+ const float& aBlurRadius, const float& aSpreadRadius,
+ const wr::BorderRadius& aBorderRadius,
+ const wr::BoxShadowClipMode& aClipMode) {
+ wr_dp_push_box_shadow(mWrState, aRect, MergeClipLeaf(aClip),
+ aIsBackfaceVisible, &mCurrentSpaceAndClipChain,
+ aBoxBounds, aOffset, aColor, aBlurRadius, aSpreadRadius,
+ aBorderRadius, aClipMode);
+}
+
+void DisplayListBuilder::StartGroup(nsPaintedDisplayItem* aItem) {
+ if (!mDisplayItemCache || mDisplayItemCache->IsFull()) {
+ return;
+ }
+
+ MOZ_ASSERT(!mCurrentCacheSlot);
+ mCurrentCacheSlot = mDisplayItemCache->AssignSlot(aItem);
+
+ if (mCurrentCacheSlot) {
+ wr_dp_start_item_group(mWrState);
+ }
+}
+
+void DisplayListBuilder::CancelGroup(const bool aDiscard) {
+ if (!mDisplayItemCache || !mCurrentCacheSlot) {
+ return;
+ }
+
+ wr_dp_cancel_item_group(mWrState, aDiscard);
+ mCurrentCacheSlot = Nothing();
+}
+
+void DisplayListBuilder::FinishGroup() {
+ if (!mDisplayItemCache || !mCurrentCacheSlot) {
+ return;
+ }
+
+ MOZ_ASSERT(mCurrentCacheSlot);
+
+ if (wr_dp_finish_item_group(mWrState, mCurrentCacheSlot.ref())) {
+ mDisplayItemCache->MarkSlotOccupied(mCurrentCacheSlot.ref(),
+ CurrentSpaceAndClipChain());
+ mDisplayItemCache->Stats().AddCached();
+ }
+
+ mCurrentCacheSlot = Nothing();
+}
+
+bool DisplayListBuilder::ReuseItem(nsPaintedDisplayItem* aItem) {
+ if (!mDisplayItemCache) {
+ return false;
+ }
+
+ mDisplayItemCache->Stats().AddTotal();
+
+ if (mDisplayItemCache->IsEmpty()) {
+ return false;
+ }
+
+ Maybe<uint16_t> slot =
+ mDisplayItemCache->CanReuseItem(aItem, CurrentSpaceAndClipChain());
+
+ if (slot) {
+ mDisplayItemCache->Stats().AddReused();
+ wr_dp_push_reuse_items(mWrState, slot.ref());
+ return true;
+ }
+
+ return false;
+}
+
+Maybe<layers::ScrollableLayerGuid::ViewID>
+DisplayListBuilder::GetContainingFixedPosScrollTarget(
+ const ActiveScrolledRoot* aAsr) {
+ return mActiveFixedPosTracker
+ ? mActiveFixedPosTracker->GetScrollTargetForASR(aAsr)
+ : Nothing();
+}
+
+Maybe<SideBits> DisplayListBuilder::GetContainingFixedPosSideBits(
+ const ActiveScrolledRoot* aAsr) {
+ return mActiveFixedPosTracker
+ ? mActiveFixedPosTracker->GetSideBitsForASR(aAsr)
+ : Nothing();
+}
+
+DisplayListBuilder::FixedPosScrollTargetTracker::FixedPosScrollTargetTracker(
+ DisplayListBuilder& aBuilder, const ActiveScrolledRoot* aAsr,
+ layers::ScrollableLayerGuid::ViewID aScrollId, SideBits aSideBits)
+ : mParentTracker(aBuilder.mActiveFixedPosTracker),
+ mBuilder(aBuilder),
+ mAsr(aAsr),
+ mScrollId(aScrollId),
+ mSideBits(aSideBits) {
+ aBuilder.mActiveFixedPosTracker = this;
+}
+
+DisplayListBuilder::FixedPosScrollTargetTracker::
+ ~FixedPosScrollTargetTracker() {
+ mBuilder.mActiveFixedPosTracker = mParentTracker;
+}
+
+Maybe<layers::ScrollableLayerGuid::ViewID>
+DisplayListBuilder::FixedPosScrollTargetTracker::GetScrollTargetForASR(
+ const ActiveScrolledRoot* aAsr) {
+ return aAsr == mAsr ? Some(mScrollId) : Nothing();
+}
+
+Maybe<SideBits>
+DisplayListBuilder::FixedPosScrollTargetTracker::GetSideBitsForASR(
+ const ActiveScrolledRoot* aAsr) {
+ return aAsr == mAsr ? Some(mSideBits) : Nothing();
+}
+
+gfxContext* DisplayListBuilder::GetTextContext(
+ wr::IpcResourceUpdateQueue& aResources,
+ const layers::StackingContextHelper& aSc,
+ layers::RenderRootStateManager* aManager, nsDisplayItem* aItem,
+ nsRect& aBounds, const gfx::Point& aDeviceOffset) {
+ if (!mCachedTextDT) {
+ mCachedTextDT = new layout::TextDrawTarget(*this, aResources, aSc, aManager,
+ aItem, aBounds);
+ if (mCachedTextDT->IsValid()) {
+ mCachedContext = MakeUnique<gfxContext>(mCachedTextDT, aDeviceOffset);
+ }
+ } else {
+ mCachedTextDT->Reinitialize(aResources, aSc, aManager, aItem, aBounds);
+ mCachedContext->SetDeviceOffset(aDeviceOffset);
+ mCachedContext->SetMatrix(gfx::Matrix());
+ }
+
+ return mCachedContext.get();
+}
+
+void DisplayListBuilder::PushInheritedClipChain(
+ nsDisplayListBuilder* aBuilder, const DisplayItemClipChain* aClipChain) {
+ if (!aClipChain || mInheritedClipChain == aClipChain) {
+ return;
+ }
+ if (!mInheritedClipChain) {
+ mInheritedClipChain = aClipChain;
+ return;
+ }
+
+ mInheritedClipChain =
+ aBuilder->CreateClipChainIntersection(mInheritedClipChain, aClipChain);
+}
+
+} // namespace wr
+} // namespace mozilla
+
+extern "C" {
+
+void wr_transaction_notification_notified(uintptr_t aHandler,
+ mozilla::wr::Checkpoint aWhen) {
+ auto handler = reinterpret_cast<mozilla::wr::NotificationHandler*>(aHandler);
+ handler->Notify(aWhen);
+ // TODO: it would be better to get a callback when the object is destroyed on
+ // the rust side and delete then.
+ delete handler;
+}
+
+void wr_register_thread_local_arena() {
+#ifdef MOZ_MEMORY
+ jemalloc_thread_local_arena(true);
+#endif
+}
+
+} // extern C
diff --git a/gfx/webrender_bindings/WebRenderAPI.h b/gfx/webrender_bindings/WebRenderAPI.h
new file mode 100644
index 0000000000..c276b0e687
--- /dev/null
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -0,0 +1,913 @@
+/* -*- 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_WEBRENDERAPI_H
+#define MOZILLA_LAYERS_WEBRENDERAPI_H
+
+#include <queue>
+#include <stdint.h>
+#include <vector>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/gfx/CompositorHitTestInfo.h"
+#include "mozilla/layers/IpcResourceUpdateQueue.h"
+#include "mozilla/layers/RemoteTextureMap.h"
+#include "mozilla/layers/ScrollableLayerGuid.h"
+#include "mozilla/layers/SyncObject.h"
+#include "mozilla/layers/CompositionRecorder.h"
+#include "mozilla/MozPromise.h"
+#include "mozilla/Range.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/VsyncDispatcher.h"
+#include "mozilla/webrender/webrender_ffi.h"
+#include "mozilla/webrender/WebRenderTypes.h"
+#include "nsString.h"
+#include "GLTypes.h"
+#include "Units.h"
+
+class gfxContext;
+
+#undef None
+
+namespace mozilla {
+
+class nsDisplayItem;
+class nsPaintedDisplayItem;
+class nsDisplayTransform;
+class nsDisplayListBuilder;
+struct DisplayItemClipChain;
+
+struct ActiveScrolledRoot;
+
+namespace widget {
+class CompositorWidget;
+}
+
+namespace layers {
+class CompositorBridgeParent;
+class DisplayItemCache;
+class WebRenderBridgeParent;
+class RenderRootStateManager;
+class StackingContextHelper;
+struct DisplayListData;
+} // namespace layers
+
+namespace layout {
+class TextDrawTarget;
+}
+
+namespace wr {
+
+class DisplayListBuilder;
+class RendererOGL;
+class RendererEvent;
+class WebRenderAPI;
+
+// This isn't part of WR's API, but we define it here to simplify layout's
+// logic and data plumbing.
+struct Line {
+ wr::LayoutRect bounds;
+ float wavyLineThickness;
+ wr::LineOrientation orientation;
+ wr::ColorF color;
+ wr::LineStyle style;
+};
+
+/// A handler that can be bundled into a transaction and notified at specific
+/// points in the rendering pipeline, such as after scene building or after
+/// frame building.
+///
+/// If for any reason the handler is dropped before reaching the requested
+/// point, it is notified with the value Checkpoint::TransactionDropped.
+/// So it is safe to assume that the handler will be notified "at some point".
+class NotificationHandler {
+ public:
+ virtual void Notify(wr::Checkpoint aCheckpoint) = 0;
+ virtual ~NotificationHandler() = default;
+};
+
+struct WrHitResult {
+ layers::LayersId mLayersId;
+ layers::ScrollableLayerGuid::ViewID mScrollId;
+ gfx::CompositorHitTestInfo mHitInfo;
+ SideBits mSideBits;
+ Maybe<uint64_t> mAnimationId;
+};
+
+class TransactionBuilder final {
+ public:
+ explicit TransactionBuilder(WebRenderAPI* aApi,
+ bool aUseSceneBuilderThread = true);
+
+ ~TransactionBuilder();
+
+ void SetLowPriority(bool aIsLowPriority);
+
+ void UpdateEpoch(PipelineId aPipelineId, Epoch aEpoch);
+
+ void SetRootPipeline(PipelineId aPipelineId);
+
+ void RemovePipeline(PipelineId aPipelineId);
+
+ void SetDisplayList(Epoch aEpoch, wr::WrPipelineId pipeline_id,
+ wr::BuiltDisplayListDescriptor dl_descriptor,
+ wr::Vec<uint8_t>& dl_items_data,
+ wr::Vec<uint8_t>& dl_cache_data,
+ wr::Vec<uint8_t>& dl_spatial_tree);
+
+ void ClearDisplayList(Epoch aEpoch, wr::WrPipelineId aPipeline);
+
+ void GenerateFrame(const VsyncId& aVsyncId, wr::RenderReasons aReasons);
+
+ void InvalidateRenderedFrame(wr::RenderReasons aReasons);
+
+ void SetDocumentView(const LayoutDeviceIntRect& aDocRect);
+
+ bool IsEmpty() const;
+
+ bool IsResourceUpdatesEmpty() const;
+
+ bool IsRenderedFrameInvalidated() const;
+
+ void AddImage(wr::ImageKey aKey, const ImageDescriptor& aDescriptor,
+ wr::Vec<uint8_t>& aBytes);
+
+ void AddBlobImage(wr::BlobImageKey aKey, const ImageDescriptor& aDescriptor,
+ uint16_t aTileSize, wr::Vec<uint8_t>& aBytes,
+ const wr::DeviceIntRect& aVisibleRect);
+
+ void AddExternalImageBuffer(ImageKey key, const ImageDescriptor& aDescriptor,
+ ExternalImageId aHandle);
+
+ void AddExternalImage(ImageKey key, const ImageDescriptor& aDescriptor,
+ ExternalImageId aExtID,
+ wr::ExternalImageType aImageType,
+ uint8_t aChannelIndex = 0);
+
+ void UpdateImageBuffer(wr::ImageKey aKey, const ImageDescriptor& aDescriptor,
+ wr::Vec<uint8_t>& aBytes);
+
+ void UpdateBlobImage(wr::BlobImageKey aKey,
+ const ImageDescriptor& aDescriptor,
+ wr::Vec<uint8_t>& aBytes,
+ const wr::DeviceIntRect& aVisibleRect,
+ const wr::LayoutIntRect& aDirtyRect);
+
+ void UpdateExternalImage(ImageKey aKey, const ImageDescriptor& aDescriptor,
+ ExternalImageId aExtID,
+ wr::ExternalImageType aImageType,
+ uint8_t aChannelIndex = 0);
+
+ void UpdateExternalImageWithDirtyRect(ImageKey aKey,
+ const ImageDescriptor& aDescriptor,
+ ExternalImageId aExtID,
+ wr::ExternalImageType aImageType,
+ const wr::DeviceIntRect& aDirtyRect,
+ uint8_t aChannelIndex = 0);
+
+ void SetBlobImageVisibleArea(BlobImageKey aKey,
+ const wr::DeviceIntRect& aArea);
+
+ void DeleteImage(wr::ImageKey aKey);
+
+ void DeleteBlobImage(wr::BlobImageKey aKey);
+
+ void AddRawFont(wr::FontKey aKey, wr::Vec<uint8_t>& aBytes, uint32_t aIndex);
+
+ void AddFontDescriptor(wr::FontKey aKey, wr::Vec<uint8_t>& aBytes,
+ uint32_t aIndex);
+
+ void DeleteFont(wr::FontKey aKey);
+
+ void AddFontInstance(wr::FontInstanceKey aKey, wr::FontKey aFontKey,
+ float aGlyphSize,
+ const wr::FontInstanceOptions* aOptions,
+ const wr::FontInstancePlatformOptions* aPlatformOptions,
+ wr::Vec<uint8_t>& aVariations);
+
+ void DeleteFontInstance(wr::FontInstanceKey aKey);
+
+ void UpdateQualitySettings(bool aForceSubpixelAAWherePossible);
+
+ void Notify(wr::Checkpoint aWhen, UniquePtr<NotificationHandler> aHandler);
+
+ void Clear();
+
+ Transaction* Take();
+
+ bool UseSceneBuilderThread() const { return mUseSceneBuilderThread; }
+ layers::WebRenderBackend GetBackendType() { return mApiBackend; }
+ Transaction* Raw() { return mTxn; }
+
+ protected:
+ bool mUseSceneBuilderThread;
+ layers::WebRenderBackend mApiBackend;
+ Transaction* mTxn;
+};
+
+class TransactionWrapper final {
+ public:
+ explicit TransactionWrapper(Transaction* aTxn);
+
+ void AppendDynamicProperties(
+ const nsTArray<wr::WrOpacityProperty>& aOpacityArray,
+ const nsTArray<wr::WrTransformProperty>& aTransformArray,
+ const nsTArray<wr::WrColorProperty>& aColorArray);
+ void AppendTransformProperties(
+ const nsTArray<wr::WrTransformProperty>& aTransformArray);
+ void UpdateScrollPosition(
+ const wr::WrPipelineId& aPipelineId,
+ const layers::ScrollableLayerGuid::ViewID& aScrollId,
+ const nsTArray<wr::SampledScrollOffset>& aSampledOffsets);
+ void UpdateIsTransformAsyncZooming(uint64_t aAnimationId, bool aIsZooming);
+
+ private:
+ Transaction* mTxn;
+};
+
+class WebRenderAPI final {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebRenderAPI);
+
+ public:
+ /// This can be called on the compositor thread only.
+ static already_AddRefed<WebRenderAPI> Create(
+ layers::CompositorBridgeParent* aBridge,
+ RefPtr<widget::CompositorWidget>&& aWidget,
+ const wr::WrWindowId& aWindowId, LayoutDeviceIntSize aSize,
+ layers::WindowKind aWindowKind, nsACString& aError);
+
+ already_AddRefed<WebRenderAPI> Clone();
+
+ void DestroyRenderer();
+
+ wr::WindowId GetId() const { return mId; }
+
+ /// Do a non-blocking hit-testing query on a shared version of the hit
+ /// testing information.
+ std::vector<WrHitResult> HitTest(const wr::WorldPoint& aPoint);
+
+ void SendTransaction(TransactionBuilder& aTxn);
+
+ void SetFrameStartTime(const TimeStamp& aTime);
+
+ void RunOnRenderThread(UniquePtr<RendererEvent> aEvent);
+
+ void Readback(const TimeStamp& aStartTime, gfx::IntSize aSize,
+ const gfx::SurfaceFormat& aFormat,
+ const Range<uint8_t>& aBuffer, bool* aNeedsYFlip);
+
+ void ClearAllCaches();
+ void EnableNativeCompositor(bool aEnable);
+ void SetBatchingLookback(uint32_t aCount);
+ void SetBool(wr::BoolParameter, bool value);
+ void SetInt(wr::IntParameter, int32_t value);
+
+ void SetClearColor(const gfx::DeviceColor& aColor);
+ void SetProfilerUI(const nsACString& aUIString);
+
+ void Pause();
+ bool Resume();
+
+ void WakeSceneBuilder();
+ void FlushSceneBuilder();
+
+ void NotifyMemoryPressure();
+ void AccumulateMemoryReport(wr::MemoryReport*);
+
+ wr::WrIdNamespace GetNamespace();
+ layers::WebRenderBackend GetBackendType() { return mBackend; }
+ layers::WebRenderCompositor GetCompositorType() { return mCompositor; }
+ uint32_t GetMaxTextureSize() const { return mMaxTextureSize; }
+ bool GetUseANGLE() const { return mUseANGLE; }
+ bool GetUseDComp() const { return mUseDComp; }
+ bool GetUseTripleBuffering() const { return mUseTripleBuffering; }
+ bool SupportsExternalBufferTextures() const {
+ return mSupportsExternalBufferTextures;
+ }
+ layers::SyncHandle GetSyncHandle() const { return mSyncHandle; }
+
+ void Capture();
+
+ void StartCaptureSequence(const nsACString& aPath, uint32_t aFlags);
+ void StopCaptureSequence();
+
+ void BeginRecording(const TimeStamp& aRecordingStart,
+ wr::PipelineId aRootPipelineId);
+
+ typedef MozPromise<layers::FrameRecording, nsresult, true>
+ EndRecordingPromise;
+
+ RefPtr<EndRecordingPromise> EndRecording();
+
+ layers::RemoteTextureInfoList* GetPendingRemoteTextureInfoList();
+
+ void FlushPendingWrTransactionEventsWithoutWait();
+ void FlushPendingWrTransactionEventsWithWait();
+
+ protected:
+ WebRenderAPI(wr::DocumentHandle* aHandle, wr::WindowId aId,
+ layers::WebRenderBackend aBackend,
+ layers::WebRenderCompositor aCompositor,
+ uint32_t aMaxTextureSize, bool aUseANGLE, bool aUseDComp,
+ bool aUseTripleBuffering, bool aSupportsExternalBufferTextures,
+ layers::SyncHandle aSyncHandle);
+
+ ~WebRenderAPI();
+ // Should be used only for shutdown handling
+ void WaitFlushed();
+
+ void UpdateDebugFlags(uint32_t aFlags);
+ bool CheckIsRemoteTextureReady(layers::RemoteTextureInfoList* aList);
+ void WaitRemoteTextureReady(layers::RemoteTextureInfoList* aList);
+
+ enum class RemoteTextureWaitType : uint8_t {
+ AsyncWait = 0,
+ FlushWithWait = 1,
+ FlushWithoutWait = 2
+ };
+
+ void HandleWrTransactionEvents(RemoteTextureWaitType aType);
+
+ class WrTransactionEvent {
+ public:
+ enum class Tag {
+ Transaction,
+ PendingRemoteTextures,
+ };
+ const Tag mTag;
+
+ struct TransactionWrapper {
+ TransactionWrapper(wr::Transaction* aTxn, bool aUseSceneBuilderThread)
+ : mTxn(aTxn), mUseSceneBuilderThread(aUseSceneBuilderThread) {}
+
+ ~TransactionWrapper() {
+ if (mTxn) {
+ wr_transaction_delete(mTxn);
+ }
+ }
+
+ wr::Transaction* mTxn;
+ const bool mUseSceneBuilderThread;
+ };
+
+ private:
+ WrTransactionEvent(const Tag aTag,
+ UniquePtr<TransactionWrapper>&& aTransaction)
+ : mTag(aTag), mTransaction(std::move(aTransaction)) {
+ MOZ_ASSERT(mTag == Tag::Transaction);
+ }
+ WrTransactionEvent(
+ const Tag aTag,
+ UniquePtr<layers::RemoteTextureInfoList>&& aPendingRemoteTextures)
+ : mTag(aTag),
+ mPendingRemoteTextures(std::move(aPendingRemoteTextures)) {
+ MOZ_ASSERT(mTag == Tag::PendingRemoteTextures);
+ }
+
+ UniquePtr<TransactionWrapper> mTransaction;
+ UniquePtr<layers::RemoteTextureInfoList> mPendingRemoteTextures;
+
+ public:
+ static WrTransactionEvent Transaction(wr::Transaction* aTxn,
+ bool aUseSceneBuilderThread) {
+ auto transaction =
+ MakeUnique<TransactionWrapper>(aTxn, aUseSceneBuilderThread);
+ return WrTransactionEvent(Tag::Transaction, std::move(transaction));
+ }
+
+ static WrTransactionEvent PendingRemoteTextures(
+ UniquePtr<layers::RemoteTextureInfoList>&& aPendingRemoteTextures) {
+ return WrTransactionEvent(Tag::PendingRemoteTextures,
+ std::move(aPendingRemoteTextures));
+ }
+
+ wr::Transaction* Transaction() {
+ if (mTag == Tag::Transaction) {
+ return mTransaction->mTxn;
+ }
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+ return nullptr;
+ }
+
+ bool UseSceneBuilderThread() {
+ if (mTag == Tag::Transaction) {
+ return mTransaction->mUseSceneBuilderThread;
+ }
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+ return true;
+ }
+
+ layers::RemoteTextureInfoList* RemoteTextureInfoList() {
+ if (mTag == Tag::PendingRemoteTextures) {
+ MOZ_ASSERT(mPendingRemoteTextures);
+ return mPendingRemoteTextures.get();
+ }
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+ return nullptr;
+ }
+ };
+
+ wr::DocumentHandle* mDocHandle;
+ wr::WindowId mId;
+ layers::WebRenderBackend mBackend;
+ layers::WebRenderCompositor mCompositor;
+ int32_t mMaxTextureSize;
+ bool mUseANGLE;
+ bool mUseDComp;
+ bool mUseTripleBuffering;
+ bool mSupportsExternalBufferTextures;
+ bool mCaptureSequence;
+ layers::SyncHandle mSyncHandle;
+ bool mRendererDestroyed;
+
+ UniquePtr<layers::RemoteTextureInfoList> mPendingRemoteTextureInfoList;
+ std::queue<WrTransactionEvent> mPendingWrTransactionEvents;
+
+ // We maintain alive the root api to know when to shut the render backend
+ // down, and the root api for the document to know when to delete the
+ // document. mRootApi is null for the api object that owns the channel (and is
+ // responsible for shutting it down), and mRootDocumentApi is null for the api
+ // object owning (and responsible for destroying) a given document. All api
+ // objects in the same window use the same channel, and some api objects write
+ // to the same document (but there is only one owner for each channel and for
+ // each document).
+ RefPtr<wr::WebRenderAPI> mRootApi;
+ RefPtr<wr::WebRenderAPI> mRootDocumentApi;
+
+ friend class DisplayListBuilder;
+ friend class layers::WebRenderBridgeParent;
+};
+
+// This is a RAII class that automatically sends the transaction on
+// destruction. This is useful for code that has multiple exit points and we
+// want to ensure that the stuff accumulated in the transaction gets sent
+// regardless of which exit we take. Note that if the caller explicitly calls
+// mApi->SendTransaction() that's fine too because that empties out the
+// TransactionBuilder and leaves it as a valid empty transaction, so calling
+// SendTransaction on it again ends up being a no-op.
+class MOZ_RAII AutoTransactionSender {
+ public:
+ AutoTransactionSender(WebRenderAPI* aApi, TransactionBuilder* aTxn)
+ : mApi(aApi), mTxn(aTxn) {
+ MOZ_RELEASE_ASSERT(mApi);
+ MOZ_RELEASE_ASSERT(aTxn);
+ }
+
+ ~AutoTransactionSender() { mApi->SendTransaction(*mTxn); }
+
+ private:
+ WebRenderAPI* mApi;
+ TransactionBuilder* mTxn;
+};
+
+/**
+ * A set of optional parameters for stacking context creation.
+ */
+struct MOZ_STACK_CLASS StackingContextParams : public WrStackingContextParams {
+ StackingContextParams()
+ : WrStackingContextParams{
+ WrStackingContextClip::None(),
+ nullptr,
+ nullptr,
+ nullptr,
+ wr::TransformStyle::Flat,
+ wr::WrReferenceFrameKind::Transform,
+ false,
+ false,
+ false,
+ nullptr,
+ /* prim_flags = */ wr::PrimitiveFlags::IS_BACKFACE_VISIBLE,
+ wr::MixBlendMode::Normal,
+ wr::StackingContextFlags{0}} {}
+
+ void SetPreserve3D(bool aPreserve) {
+ transform_style =
+ aPreserve ? wr::TransformStyle::Preserve3D : wr::TransformStyle::Flat;
+ }
+
+ // Fill this in only if this is for the root StackingContextHelper.
+ nsIFrame* mRootReferenceFrame = nullptr;
+ nsTArray<wr::FilterOp> mFilters;
+ nsTArray<wr::WrFilterData> mFilterDatas;
+ wr::LayoutRect mBounds = wr::ToLayoutRect(LayoutDeviceRect());
+ const gfx::Matrix4x4* mBoundTransform = nullptr;
+ const wr::WrTransformInfo* mTransformPtr = nullptr;
+ nsDisplayTransform* mDeferredTransformItem = nullptr;
+ // Whether the stacking context is possibly animated. This alters how
+ // coordinates are transformed/snapped to invalidate less when transforms
+ // change frequently.
+ bool mAnimated = false;
+ // Whether items should be rasterized in a local space that is (mostly)
+ // invariant to transforms, i.e. disabling subpixel AA and screen space pixel
+ // snapping on text runs that would only make sense in screen space.
+ bool mRasterizeLocally = false;
+};
+
+/// This is a simple C++ wrapper around WrState defined in the rust bindings.
+/// We may want to turn this into a direct wrapper on top of
+/// WebRenderFrameBuilder instead, so the interface may change a bit.
+class DisplayListBuilder final {
+ public:
+ explicit DisplayListBuilder(wr::PipelineId aId,
+ layers::WebRenderBackend aBackend);
+ DisplayListBuilder(DisplayListBuilder&&) = default;
+
+ ~DisplayListBuilder();
+
+ void Save();
+ void Restore();
+ void ClearSave();
+
+ usize Dump(usize aIndent, const Maybe<usize>& aStart,
+ const Maybe<usize>& aEnd);
+ void DumpSerializedDisplayList();
+
+ void Begin(layers::DisplayItemCache* aCache = nullptr);
+ void End(wr::BuiltDisplayList& aOutDisplayList);
+ void End(layers::DisplayListData& aOutTransaction);
+
+ Maybe<wr::WrSpatialId> PushStackingContext(
+ const StackingContextParams& aParams, const wr::LayoutRect& aBounds,
+ const wr::RasterSpace& aRasterSpace);
+ void PopStackingContext(bool aIsReferenceFrame);
+
+ wr::WrClipChainId DefineClipChain(const nsTArray<wr::WrClipId>& aClips,
+ bool aParentWithCurrentChain = false);
+
+ wr::WrClipId DefineImageMaskClip(const wr::ImageMask& aMask,
+ const nsTArray<wr::LayoutPoint>&,
+ wr::FillRule);
+ wr::WrClipId DefineRoundedRectClip(Maybe<wr::WrSpatialId> aSpace,
+ const wr::ComplexClipRegion& aComplex);
+ wr::WrClipId DefineRectClip(Maybe<wr::WrSpatialId> aSpace,
+ wr::LayoutRect aClipRect);
+
+ wr::WrSpatialId DefineStickyFrame(
+ const wr::LayoutRect& aContentRect, const float* aTopMargin,
+ const float* aRightMargin, const float* aBottomMargin,
+ const float* aLeftMargin, const StickyOffsetBounds& aVerticalBounds,
+ const StickyOffsetBounds& aHorizontalBounds,
+ const wr::LayoutVector2D& aAppliedOffset, wr::SpatialTreeItemKey aKey);
+
+ Maybe<wr::WrSpatialId> GetScrollIdForDefinedScrollLayer(
+ layers::ScrollableLayerGuid::ViewID aViewId) const;
+ wr::WrSpatialId DefineScrollLayer(
+ const layers::ScrollableLayerGuid::ViewID& aViewId,
+ const Maybe<wr::WrSpatialId>& aParent, const wr::LayoutRect& aContentRect,
+ const wr::LayoutRect& aClipRect, const wr::LayoutVector2D& aScrollOffset,
+ wr::APZScrollGeneration aScrollOffsetGeneration,
+ wr::HasScrollLinkedEffect aHasScrollLinkedEffect,
+ wr::SpatialTreeItemKey aKey);
+
+ void PushRect(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible, bool aForceAntiAliasing,
+ bool aIsCheckerboard, const wr::ColorF& aColor);
+ void PushRectWithAnimation(const wr::LayoutRect& aBounds,
+ const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible, const wr::ColorF& aColor,
+ const WrAnimationProperty* aAnimation);
+ void PushRoundedRect(const wr::LayoutRect& aBounds,
+ const wr::LayoutRect& aClip, bool aIsBackfaceVisible,
+ const wr::ColorF& aColor);
+ void PushHitTest(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible,
+ const layers::ScrollableLayerGuid::ViewID& aScrollId,
+ const gfx::CompositorHitTestInfo& aHitInfo,
+ SideBits aSideBits);
+ void PushClearRect(const wr::LayoutRect& aBounds);
+
+ void PushBackdropFilter(const wr::LayoutRect& aBounds,
+ const wr::ComplexClipRegion& aRegion,
+ const nsTArray<wr::FilterOp>& aFilters,
+ const nsTArray<wr::WrFilterData>& aFilterDatas,
+ bool aIsBackfaceVisible);
+
+ void PushLinearGradient(const wr::LayoutRect& aBounds,
+ const wr::LayoutRect& aClip, bool aIsBackfaceVisible,
+ const wr::LayoutPoint& aStartPoint,
+ const wr::LayoutPoint& aEndPoint,
+ const nsTArray<wr::GradientStop>& aStops,
+ wr::ExtendMode aExtendMode,
+ const wr::LayoutSize aTileSize,
+ const wr::LayoutSize aTileSpacing);
+
+ void PushRadialGradient(const wr::LayoutRect& aBounds,
+ const wr::LayoutRect& aClip, bool aIsBackfaceVisible,
+ const wr::LayoutPoint& aCenter,
+ const wr::LayoutSize& aRadius,
+ const nsTArray<wr::GradientStop>& aStops,
+ wr::ExtendMode aExtendMode,
+ const wr::LayoutSize aTileSize,
+ const wr::LayoutSize aTileSpacing);
+
+ void PushConicGradient(const wr::LayoutRect& aBounds,
+ const wr::LayoutRect& aClip, bool aIsBackfaceVisible,
+ const wr::LayoutPoint& aCenter, const float aAngle,
+ const nsTArray<wr::GradientStop>& aStops,
+ wr::ExtendMode aExtendMode,
+ const wr::LayoutSize aTileSize,
+ const wr::LayoutSize aTileSpacing);
+
+ void PushImage(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible, bool aForceAntiAliasing,
+ wr::ImageRendering aFilter, wr::ImageKey aImage,
+ bool aPremultipliedAlpha = true,
+ const wr::ColorF& aColor = wr::ColorF{1.0f, 1.0f, 1.0f, 1.0f},
+ bool aPreferCompositorSurface = false,
+ bool aSupportsExternalCompositing = false);
+
+ void PushRepeatingImage(
+ const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible, const wr::LayoutSize& aStretchSize,
+ const wr::LayoutSize& aTileSpacing, wr::ImageRendering aFilter,
+ wr::ImageKey aImage, bool aPremultipliedAlpha = true,
+ const wr::ColorF& aColor = wr::ColorF{1.0f, 1.0f, 1.0f, 1.0f});
+
+ void PushYCbCrPlanarImage(
+ const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible, wr::ImageKey aImageChannel0,
+ wr::ImageKey aImageChannel1, wr::ImageKey aImageChannel2,
+ wr::WrColorDepth aColorDepth, wr::WrYuvColorSpace aColorSpace,
+ wr::WrColorRange aColorRange, wr::ImageRendering aFilter,
+ bool aPreferCompositorSurface = false,
+ bool aSupportsExternalCompositing = false);
+
+ void PushNV12Image(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible, wr::ImageKey aImageChannel0,
+ wr::ImageKey aImageChannel1, wr::WrColorDepth aColorDepth,
+ wr::WrYuvColorSpace aColorSpace,
+ wr::WrColorRange aColorRange, wr::ImageRendering aFilter,
+ bool aPreferCompositorSurface = false,
+ bool aSupportsExternalCompositing = false);
+
+ void PushP010Image(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible, wr::ImageKey aImageChannel0,
+ wr::ImageKey aImageChannel1, wr::WrColorDepth aColorDepth,
+ wr::WrYuvColorSpace aColorSpace,
+ wr::WrColorRange aColorRange, wr::ImageRendering aFilter,
+ bool aPreferCompositorSurface = false,
+ bool aSupportsExternalCompositing = false);
+
+ void PushYCbCrInterleavedImage(
+ const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible, wr::ImageKey aImageChannel0,
+ wr::WrColorDepth aColorDepth, wr::WrYuvColorSpace aColorSpace,
+ wr::WrColorRange aColorRange, wr::ImageRendering aFilter,
+ bool aPreferCompositorSurface = false,
+ bool aSupportsExternalCompositing = false);
+
+ void PushIFrame(const LayoutDeviceRect& aDevPxBounds, bool aIsBackfaceVisible,
+ wr::PipelineId aPipeline, bool aIgnoreMissingPipeline);
+
+ // XXX WrBorderSides are passed with Range.
+ // It is just to bypass compiler bug. See Bug 1357734.
+ void PushBorder(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible, const wr::LayoutSideOffsets& aWidths,
+ const Range<const wr::BorderSide>& aSides,
+ const wr::BorderRadius& aRadius,
+ wr::AntialiasBorder = wr::AntialiasBorder::Yes);
+
+ void PushBorderImage(const wr::LayoutRect& aBounds,
+ const wr::LayoutRect& aClip, bool aIsBackfaceVisible,
+ const wr::WrBorderImage& aParams);
+
+ void PushBorderGradient(const wr::LayoutRect& aBounds,
+ const wr::LayoutRect& aClip, bool aIsBackfaceVisible,
+ const wr::LayoutSideOffsets& aWidths,
+ const int32_t aWidth, const int32_t aHeight,
+ bool aFill, const wr::DeviceIntSideOffsets& aSlice,
+ const wr::LayoutPoint& aStartPoint,
+ const wr::LayoutPoint& aEndPoint,
+ const nsTArray<wr::GradientStop>& aStops,
+ wr::ExtendMode aExtendMode);
+
+ void PushBorderRadialGradient(
+ const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible, const wr::LayoutSideOffsets& aWidths, bool aFill,
+ const wr::LayoutPoint& aCenter, const wr::LayoutSize& aRadius,
+ const nsTArray<wr::GradientStop>& aStops, wr::ExtendMode aExtendMode);
+
+ void PushBorderConicGradient(
+ const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible, const wr::LayoutSideOffsets& aWidths, bool aFill,
+ const wr::LayoutPoint& aCenter, const float aAngle,
+ const nsTArray<wr::GradientStop>& aStops, wr::ExtendMode aExtendMode);
+
+ void PushText(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible, const wr::ColorF& aColor,
+ wr::FontInstanceKey aFontKey,
+ Range<const wr::GlyphInstance> aGlyphBuffer,
+ const wr::GlyphOptions* aGlyphOptions = nullptr);
+
+ void PushLine(const wr::LayoutRect& aClip, bool aIsBackfaceVisible,
+ const wr::Line& aLine);
+
+ void PushShadow(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible, const wr::Shadow& aShadow,
+ bool aShouldInflate);
+
+ void PopAllShadows();
+
+ void PushBoxShadow(const wr::LayoutRect& aRect, const wr::LayoutRect& aClip,
+ bool aIsBackfaceVisible, const wr::LayoutRect& aBoxBounds,
+ const wr::LayoutVector2D& aOffset,
+ const wr::ColorF& aColor, const float& aBlurRadius,
+ const float& aSpreadRadius,
+ const wr::BorderRadius& aBorderRadius,
+ const wr::BoxShadowClipMode& aClipMode);
+
+ /**
+ * Notifies the DisplayListBuilder that it can group together WR display items
+ * that are pushed until |CancelGroup()| or |FinishGroup()| call.
+ */
+ void StartGroup(nsPaintedDisplayItem* aItem);
+
+ /**
+ * Cancels grouping of the display items and discards all the display items
+ * pushed between the |StartGroup()| and |CancelGroup()| calls.
+ */
+ void CancelGroup(const bool aDiscard = false);
+
+ /**
+ * Finishes the display item group. The group is stored in WebRender backend,
+ * and can be reused with |ReuseItem()|, if the Gecko display item is reused.
+ */
+ void FinishGroup();
+
+ /**
+ * Try to reuse the previously created WebRender display items for the given
+ * Gecko display item |aItem|.
+ * Returns true if the items were reused, otherwise returns false.
+ */
+ bool ReuseItem(nsPaintedDisplayItem* aItem);
+
+ uint64_t CurrentClipChainId() const {
+ return mCurrentSpaceAndClipChain.clip_chain;
+ }
+
+ const wr::WrSpaceAndClipChain& CurrentSpaceAndClipChain() const {
+ return mCurrentSpaceAndClipChain;
+ }
+
+ const wr::PipelineId& CurrentPipelineId() const { return mPipelineId; }
+ layers::WebRenderBackend GetBackendType() const { return mBackend; }
+
+ // Checks to see if the innermost enclosing fixed pos item has the same
+ // ASR. If so, it returns the scroll target for that fixed-pos item.
+ // Otherwise, it returns Nothing().
+ Maybe<layers::ScrollableLayerGuid::ViewID> GetContainingFixedPosScrollTarget(
+ const ActiveScrolledRoot* aAsr);
+
+ Maybe<SideBits> GetContainingFixedPosSideBits(const ActiveScrolledRoot* aAsr);
+
+ gfxContext* GetTextContext(wr::IpcResourceUpdateQueue& aResources,
+ const layers::StackingContextHelper& aSc,
+ layers::RenderRootStateManager* aManager,
+ nsDisplayItem* aItem, nsRect& aBounds,
+ const gfx::Point& aDeviceOffset);
+
+ // Try to avoid using this when possible.
+ wr::WrState* Raw() { return mWrState; }
+
+ void SetClipChainLeaf(const Maybe<wr::LayoutRect>& aClipRect) {
+ mClipChainLeaf = aClipRect;
+ }
+
+ // Used for opacity flattening. When we flatten away an opacity item,
+ // we push the opacity value onto the builder.
+ // Descendant items should pull the inherited opacity during
+ // their CreateWebRenderCommands implementation. This can only happen if all
+ // descendant items reported supporting this functionality, via
+ // nsDisplayItem::CanApplyOpacity.
+ float GetInheritedOpacity() { return mInheritedOpacity; }
+ void SetInheritedOpacity(float aOpacity) { mInheritedOpacity = aOpacity; }
+ const DisplayItemClipChain* GetInheritedClipChain() {
+ return mInheritedClipChain;
+ }
+ void PushInheritedClipChain(nsDisplayListBuilder* aBuilder,
+ const DisplayItemClipChain* aClipChain);
+ void SetInheritedClipChain(const DisplayItemClipChain* aClipChain) {
+ mInheritedClipChain = aClipChain;
+ }
+
+ layers::DisplayItemCache* GetDisplayItemCache() { return mDisplayItemCache; }
+
+ // A chain of RAII objects, each holding a (ASR, ViewID, SideBits) tuple of
+ // data. The topmost object is pointed to by the mActiveFixedPosTracker
+ // pointer in the wr::DisplayListBuilder.
+ class MOZ_RAII FixedPosScrollTargetTracker final {
+ public:
+ FixedPosScrollTargetTracker(DisplayListBuilder& aBuilder,
+ const ActiveScrolledRoot* aAsr,
+ layers::ScrollableLayerGuid::ViewID aScrollId,
+ SideBits aSideBits);
+ ~FixedPosScrollTargetTracker();
+ Maybe<layers::ScrollableLayerGuid::ViewID> GetScrollTargetForASR(
+ const ActiveScrolledRoot* aAsr);
+ Maybe<SideBits> GetSideBitsForASR(const ActiveScrolledRoot* aAsr);
+
+ private:
+ FixedPosScrollTargetTracker* mParentTracker;
+ DisplayListBuilder& mBuilder;
+ const ActiveScrolledRoot* mAsr;
+ layers::ScrollableLayerGuid::ViewID mScrollId;
+ SideBits mSideBits;
+ };
+
+ protected:
+ wr::LayoutRect MergeClipLeaf(const wr::LayoutRect& aClip) {
+ if (mClipChainLeaf) {
+ return wr::IntersectLayoutRect(*mClipChainLeaf, aClip);
+ }
+ return aClip;
+ }
+
+ // See the implementation of PushShadow for details on these methods.
+ void SuspendClipLeafMerging();
+ void ResumeClipLeafMerging();
+
+ wr::WrState* mWrState;
+
+ // Track each scroll id that we encountered. We use this structure to
+ // ensure that we don't define a particular scroll layer multiple times,
+ // as that results in undefined behaviour in WR.
+ std::unordered_map<layers::ScrollableLayerGuid::ViewID, wr::WrSpatialId>
+ mScrollIds;
+
+ wr::WrSpaceAndClipChain mCurrentSpaceAndClipChain;
+
+ // Contains the current leaf of the clip chain to be merged with the
+ // display item's clip rect when pushing an item. May be set to Nothing() if
+ // there is no clip rect to merge with.
+ Maybe<wr::LayoutRect> mClipChainLeaf;
+
+ // Versions of the above that are on hold while SuspendClipLeafMerging is on
+ // (see the implementation of PushShadow for details).
+ Maybe<wr::WrSpaceAndClipChain> mSuspendedSpaceAndClipChain;
+ Maybe<wr::LayoutRect> mSuspendedClipChainLeaf;
+
+ RefPtr<layout::TextDrawTarget> mCachedTextDT;
+ mozilla::UniquePtr<gfxContext> mCachedContext;
+
+ FixedPosScrollTargetTracker* mActiveFixedPosTracker;
+
+ wr::PipelineId mPipelineId;
+ layers::WebRenderBackend mBackend;
+
+ nsTArray<wr::PipelineId> mRemotePipelineIds;
+
+ layers::DisplayItemCache* mDisplayItemCache;
+ Maybe<uint16_t> mCurrentCacheSlot;
+ float mInheritedOpacity = 1.0f;
+ const DisplayItemClipChain* mInheritedClipChain = nullptr;
+
+ friend class WebRenderAPI;
+ friend class SpaceAndClipChainHelper;
+};
+
+// This is a RAII class that overrides the current Wr's SpatialId and
+// ClipChainId.
+class MOZ_RAII SpaceAndClipChainHelper final {
+ public:
+ SpaceAndClipChainHelper(DisplayListBuilder& aBuilder,
+ wr::WrSpaceAndClipChain aSpaceAndClipChain)
+ : mBuilder(aBuilder),
+ mOldSpaceAndClipChain(aBuilder.mCurrentSpaceAndClipChain) {
+ aBuilder.mCurrentSpaceAndClipChain = aSpaceAndClipChain;
+ }
+ SpaceAndClipChainHelper(DisplayListBuilder& aBuilder,
+ wr::WrSpatialId aSpatialId)
+ : mBuilder(aBuilder),
+ mOldSpaceAndClipChain(aBuilder.mCurrentSpaceAndClipChain) {
+ aBuilder.mCurrentSpaceAndClipChain.space = aSpatialId;
+ }
+ SpaceAndClipChainHelper(DisplayListBuilder& aBuilder,
+ wr::WrClipChainId aClipChainId)
+ : mBuilder(aBuilder),
+ mOldSpaceAndClipChain(aBuilder.mCurrentSpaceAndClipChain) {
+ aBuilder.mCurrentSpaceAndClipChain.clip_chain = aClipChainId.id;
+ }
+
+ ~SpaceAndClipChainHelper() {
+ mBuilder.mCurrentSpaceAndClipChain = mOldSpaceAndClipChain;
+ }
+
+ private:
+ SpaceAndClipChainHelper(const SpaceAndClipChainHelper&) = delete;
+
+ DisplayListBuilder& mBuilder;
+ wr::WrSpaceAndClipChain mOldSpaceAndClipChain;
+};
+
+Maybe<wr::ImageFormat> SurfaceFormatToImageFormat(gfx::SurfaceFormat aFormat);
+
+} // namespace wr
+} // namespace mozilla
+
+#endif
diff --git a/gfx/webrender_bindings/WebRenderTypes.cpp b/gfx/webrender_bindings/WebRenderTypes.cpp
new file mode 100644
index 0000000000..3fa81d2a81
--- /dev/null
+++ b/gfx/webrender_bindings/WebRenderTypes.cpp
@@ -0,0 +1,110 @@
+/* -*- 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 "WebRenderTypes.h"
+
+#include "mozilla/ipc/ByteBuf.h"
+#include "nsStyleConsts.h"
+
+namespace mozilla {
+namespace wr {
+
+WindowId NewWindowId() {
+ static uint64_t sNextId = 1;
+
+ WindowId id;
+ id.mHandle = sNextId++;
+ return id;
+}
+
+BorderStyle ToBorderStyle(StyleBorderStyle aStyle) {
+ switch (aStyle) {
+ case StyleBorderStyle::None:
+ return wr::BorderStyle::None;
+ case StyleBorderStyle::Solid:
+ return wr::BorderStyle::Solid;
+ case StyleBorderStyle::Double:
+ return wr::BorderStyle::Double;
+ case StyleBorderStyle::Dotted:
+ return wr::BorderStyle::Dotted;
+ case StyleBorderStyle::Dashed:
+ return wr::BorderStyle::Dashed;
+ case StyleBorderStyle::Hidden:
+ return wr::BorderStyle::Hidden;
+ case StyleBorderStyle::Groove:
+ return wr::BorderStyle::Groove;
+ case StyleBorderStyle::Ridge:
+ return wr::BorderStyle::Ridge;
+ case StyleBorderStyle::Inset:
+ return wr::BorderStyle::Inset;
+ case StyleBorderStyle::Outset:
+ return wr::BorderStyle::Outset;
+ }
+ return wr::BorderStyle::None;
+}
+
+wr::RepeatMode ToRepeatMode(StyleBorderImageRepeat aRepeat) {
+ switch (aRepeat) {
+ case StyleBorderImageRepeat::Stretch:
+ return wr::RepeatMode::Stretch;
+ case StyleBorderImageRepeat::Repeat:
+ return wr::RepeatMode::Repeat;
+ case StyleBorderImageRepeat::Round:
+ return wr::RepeatMode::Round;
+ case StyleBorderImageRepeat::Space:
+ return wr::RepeatMode::Space;
+ default:
+ MOZ_ASSERT(false);
+ }
+
+ return wr::RepeatMode::Stretch;
+}
+
+ImageRendering ToImageRendering(StyleImageRendering aImageRendering) {
+ switch (aImageRendering) {
+ case StyleImageRendering::Auto:
+ case StyleImageRendering::Smooth:
+ case StyleImageRendering::Optimizequality:
+ return wr::ImageRendering::Auto;
+ case StyleImageRendering::CrispEdges:
+ // FIXME(bug 1728831): Historically we've returned Pixelated here, but
+ // this should arguably pass CrispEdges to WebRender?
+ // return wr::ImageRendering::CrispEdges;
+ [[fallthrough]];
+ case StyleImageRendering::Optimizespeed:
+ case StyleImageRendering::Pixelated:
+ return wr::ImageRendering::Pixelated;
+ }
+ return wr::ImageRendering::Auto;
+}
+
+void Assign_WrVecU8(wr::WrVecU8& aVec, mozilla::ipc::ByteBuf&& aOther) {
+ aVec.data = aOther.mData;
+ aVec.length = aOther.mLen;
+ aVec.capacity = aOther.mCapacity;
+ aOther.mData = nullptr;
+ aOther.mLen = 0;
+ aOther.mCapacity = 0;
+}
+
+WrSpatialId RootScrollNode() { return wr_root_scroll_node_id(); }
+
+WrSpaceAndClipChain RootScrollNodeWithChain() {
+ WrSpaceAndClipChain sacc;
+ sacc.clip_chain = wr::ROOT_CLIP_CHAIN;
+ sacc.space = wr_root_scroll_node_id();
+ return sacc;
+}
+
+WrSpaceAndClipChain InvalidScrollNodeWithChain() {
+ WrSpaceAndClipChain sacc;
+ sacc.clip_chain = std::numeric_limits<uint64_t>::max();
+ sacc.space = wr_root_scroll_node_id();
+ return sacc;
+}
+
+} // namespace wr
+} // namespace mozilla
diff --git a/gfx/webrender_bindings/WebRenderTypes.h b/gfx/webrender_bindings/WebRenderTypes.h
new file mode 100644
index 0000000000..ff2c64aefa
--- /dev/null
+++ b/gfx/webrender_bindings/WebRenderTypes.h
@@ -0,0 +1,925 @@
+/* -*- 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 GFX_WEBRENDERTYPES_H
+#define GFX_WEBRENDERTYPES_H
+
+#include "ImageTypes.h"
+#include "mozilla/webrender/webrender_ffi.h"
+#include "mozilla/EnumSet.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/gfx/Matrix.h"
+#include "mozilla/gfx/Types.h"
+#include "mozilla/gfx/Tools.h"
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/PodOperations.h"
+#include "mozilla/Range.h"
+#include "mozilla/ScrollGeneration.h"
+#include "Units.h"
+#include "nsIWidgetListener.h"
+
+namespace mozilla {
+
+enum class StyleBorderStyle : uint8_t;
+enum class StyleBorderImageRepeat : uint8_t;
+enum class StyleImageRendering : uint8_t;
+
+namespace ipc {
+class ByteBuf;
+} // namespace ipc
+
+namespace wr {
+
+// Using uintptr_t in C++ code for "size" types seems weird, so let's use a
+// better-sounding typedef. The name comes from the fact that we generally
+// have to deal with uintptr_t because that's what rust's usize maps to.
+typedef uintptr_t usize;
+
+typedef wr::WrWindowId WindowId;
+typedef wr::WrRemovedPipeline RemovedPipeline;
+
+class RenderedFrameIdType {};
+typedef layers::BaseTransactionId<RenderedFrameIdType> RenderedFrameId;
+
+typedef mozilla::Maybe<mozilla::wr::IdNamespace> MaybeIdNamespace;
+typedef mozilla::Maybe<mozilla::wr::ImageMask> MaybeImageMask;
+typedef Maybe<ExternalImageId> MaybeExternalImageId;
+
+typedef Maybe<FontInstanceOptions> MaybeFontInstanceOptions;
+typedef Maybe<FontInstancePlatformOptions> MaybeFontInstancePlatformOptions;
+
+struct ExternalImageKeyPair {
+ ImageKey key;
+ ExternalImageId id;
+};
+
+/* Generate a brand new window id and return it. */
+WindowId NewWindowId();
+
+inline bool WindowSizeSanityCheck(int32_t aWidth, int32_t aHeight) {
+ if (aWidth < 0 || aWidth > wr::MAX_RENDER_TASK_SIZE || aHeight < 0 ||
+ aHeight > wr::MAX_RENDER_TASK_SIZE) {
+ return false;
+ }
+ return true;
+}
+
+inline DebugFlags NewDebugFlags(uint32_t aFlags) { return {aFlags}; }
+
+inline Maybe<wr::ImageFormat> SurfaceFormatToImageFormat(
+ gfx::SurfaceFormat aFormat) {
+ switch (aFormat) {
+ case gfx::SurfaceFormat::R8G8B8X8:
+ // WebRender not support RGBX8. Assert here.
+ MOZ_ASSERT(false);
+ return Nothing();
+ case gfx::SurfaceFormat::R8G8B8A8:
+ return Some(wr::ImageFormat::RGBA8);
+ case gfx::SurfaceFormat::B8G8R8X8:
+ // TODO: WebRender will have a BGRA + opaque flag for this but does not
+ // have it yet (cf. issue #732).
+ case gfx::SurfaceFormat::B8G8R8A8:
+ return Some(wr::ImageFormat::BGRA8);
+ case gfx::SurfaceFormat::A8:
+ return Some(wr::ImageFormat::R8);
+ case gfx::SurfaceFormat::A16:
+ return Some(wr::ImageFormat::R16);
+ case gfx::SurfaceFormat::R8G8:
+ return Some(wr::ImageFormat::RG8);
+ case gfx::SurfaceFormat::R16G16:
+ return Some(wr::ImageFormat::RG16);
+ case gfx::SurfaceFormat::UNKNOWN:
+ default:
+ return Nothing();
+ }
+}
+
+inline gfx::SurfaceFormat ImageFormatToSurfaceFormat(ImageFormat aFormat) {
+ switch (aFormat) {
+ case ImageFormat::BGRA8:
+ return gfx::SurfaceFormat::B8G8R8A8;
+ case ImageFormat::R8:
+ return gfx::SurfaceFormat::A8;
+ case ImageFormat::R16:
+ return gfx::SurfaceFormat::A16;
+ default:
+ return gfx::SurfaceFormat::UNKNOWN;
+ }
+}
+
+// This extra piece of data is used to differentiate when spatial nodes that are
+// created by Gecko that have the same mFrame and PerFrameKey. This currently
+// only occurs with sticky display list items that are also zoomable, which
+// results in Gecko creating both a sticky spatial node, and then a property
+// animated reference frame for APZ
+enum class SpatialKeyKind : uint32_t {
+ Transform,
+ Perspective,
+ Scroll,
+ Sticky,
+ ImagePipeline,
+ APZ,
+};
+
+// Construct a unique, persistent spatial key based on the frame tree pointer,
+// per-frame key and a spatial key kind. For now, this covers all the ways Gecko
+// creates spatial nodes. In future, we may need to be more clever with the
+// SpatialKeyKind.
+inline wr::SpatialTreeItemKey SpatialKey(uint64_t aFrame, uint32_t aPerFrameKey,
+ SpatialKeyKind aKind) {
+ return wr::SpatialTreeItemKey{
+ aFrame, uint64_t(aPerFrameKey) | (uint64_t(aKind) << 32)};
+}
+
+struct ImageDescriptor : public wr::WrImageDescriptor {
+ // We need a default constructor for ipdl serialization.
+ ImageDescriptor() {
+ format = (ImageFormat)0;
+ width = 0;
+ height = 0;
+ stride = 0;
+ opacity = OpacityType::HasAlphaChannel;
+ prefer_compositor_surface = false;
+ }
+
+ ImageDescriptor(const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat,
+ bool aPreferCompositorSurface = false) {
+ format = wr::SurfaceFormatToImageFormat(aFormat).valueOr((ImageFormat)0);
+ width = aSize.width;
+ height = aSize.height;
+ stride = 0;
+ opacity = gfx::IsOpaque(aFormat) ? OpacityType::Opaque
+ : OpacityType::HasAlphaChannel;
+ prefer_compositor_surface = aPreferCompositorSurface;
+ }
+
+ ImageDescriptor(const gfx::IntSize& aSize, uint32_t aByteStride,
+ gfx::SurfaceFormat aFormat,
+ bool aPreferCompositorSurface = false) {
+ format = wr::SurfaceFormatToImageFormat(aFormat).valueOr((ImageFormat)0);
+ width = aSize.width;
+ height = aSize.height;
+ stride = aByteStride;
+ opacity = gfx::IsOpaque(aFormat) ? OpacityType::Opaque
+ : OpacityType::HasAlphaChannel;
+ prefer_compositor_surface = aPreferCompositorSurface;
+ }
+
+ ImageDescriptor(const gfx::IntSize& aSize, uint32_t aByteStride,
+ gfx::SurfaceFormat aFormat, OpacityType aOpacity,
+ bool aPreferCompositorSurface = false) {
+ format = wr::SurfaceFormatToImageFormat(aFormat).valueOr((ImageFormat)0);
+ width = aSize.width;
+ height = aSize.height;
+ stride = aByteStride;
+ opacity = aOpacity;
+ prefer_compositor_surface = aPreferCompositorSurface;
+ }
+};
+
+inline uint64_t AsUint64(const NativeSurfaceId& aId) {
+ return static_cast<uint64_t>(aId._0);
+}
+
+// Whenever possible, use wr::WindowId instead of manipulating uint64_t.
+inline uint64_t AsUint64(const WindowId& aId) {
+ return static_cast<uint64_t>(aId.mHandle);
+}
+
+// Whenever possible, use wr::ImageKey instead of manipulating uint64_t.
+inline uint64_t AsUint64(const ImageKey& aId) {
+ return (static_cast<uint64_t>(aId.mNamespace.mHandle) << 32) +
+ static_cast<uint64_t>(aId.mHandle);
+}
+
+inline ImageKey AsImageKey(const uint64_t& aId) {
+ ImageKey imageKey;
+ imageKey.mNamespace.mHandle = aId >> 32;
+ imageKey.mHandle = aId;
+ return imageKey;
+}
+
+// Whenever possible, use wr::FontKey instead of manipulating uint64_t.
+inline uint64_t AsUint64(const FontKey& aId) {
+ return (static_cast<uint64_t>(aId.mNamespace.mHandle) << 32) +
+ static_cast<uint64_t>(aId.mHandle);
+}
+
+inline FontKey AsFontKey(const uint64_t& aId) {
+ FontKey fontKey;
+ fontKey.mNamespace.mHandle = aId >> 32;
+ fontKey.mHandle = aId;
+ return fontKey;
+}
+
+// Whenever possible, use wr::FontInstanceKey instead of manipulating uint64_t.
+inline uint64_t AsUint64(const FontInstanceKey& aId) {
+ return (static_cast<uint64_t>(aId.mNamespace.mHandle) << 32) +
+ static_cast<uint64_t>(aId.mHandle);
+}
+
+inline FontInstanceKey AsFontInstanceKey(const uint64_t& aId) {
+ FontInstanceKey instanceKey;
+ instanceKey.mNamespace.mHandle = aId >> 32;
+ instanceKey.mHandle = aId;
+ return instanceKey;
+}
+
+// Whenever possible, use wr::PipelineId instead of manipulating uint64_t.
+inline uint64_t AsUint64(const PipelineId& aId) {
+ return (static_cast<uint64_t>(aId.mNamespace) << 32) +
+ static_cast<uint64_t>(aId.mHandle);
+}
+
+inline PipelineId AsPipelineId(const uint64_t& aId) {
+ PipelineId pipeline;
+ pipeline.mNamespace = aId >> 32;
+ pipeline.mHandle = aId;
+ return pipeline;
+}
+
+inline mozilla::layers::LayersId AsLayersId(const PipelineId& aId) {
+ return mozilla::layers::LayersId{AsUint64(aId)};
+}
+
+inline PipelineId AsPipelineId(const mozilla::layers::LayersId& aId) {
+ return AsPipelineId(uint64_t(aId));
+}
+
+ImageRendering ToImageRendering(StyleImageRendering);
+
+static inline FontRenderMode ToFontRenderMode(gfx::AntialiasMode aMode,
+ bool aPermitSubpixelAA = true) {
+ switch (aMode) {
+ case gfx::AntialiasMode::NONE:
+ return FontRenderMode::Mono;
+ case gfx::AntialiasMode::GRAY:
+ return FontRenderMode::Alpha;
+ case gfx::AntialiasMode::SUBPIXEL:
+ default:
+ return aPermitSubpixelAA ? FontRenderMode::Subpixel
+ : FontRenderMode::Alpha;
+ }
+}
+
+static inline MixBlendMode ToMixBlendMode(gfx::CompositionOp compositionOp) {
+ switch (compositionOp) {
+ case gfx::CompositionOp::OP_MULTIPLY:
+ return MixBlendMode::Multiply;
+ case gfx::CompositionOp::OP_SCREEN:
+ return MixBlendMode::Screen;
+ case gfx::CompositionOp::OP_OVERLAY:
+ return MixBlendMode::Overlay;
+ case gfx::CompositionOp::OP_DARKEN:
+ return MixBlendMode::Darken;
+ case gfx::CompositionOp::OP_LIGHTEN:
+ return MixBlendMode::Lighten;
+ case gfx::CompositionOp::OP_COLOR_DODGE:
+ return MixBlendMode::ColorDodge;
+ case gfx::CompositionOp::OP_COLOR_BURN:
+ return MixBlendMode::ColorBurn;
+ case gfx::CompositionOp::OP_HARD_LIGHT:
+ return MixBlendMode::HardLight;
+ case gfx::CompositionOp::OP_SOFT_LIGHT:
+ return MixBlendMode::SoftLight;
+ case gfx::CompositionOp::OP_DIFFERENCE:
+ return MixBlendMode::Difference;
+ case gfx::CompositionOp::OP_EXCLUSION:
+ return MixBlendMode::Exclusion;
+ case gfx::CompositionOp::OP_HUE:
+ return MixBlendMode::Hue;
+ case gfx::CompositionOp::OP_SATURATION:
+ return MixBlendMode::Saturation;
+ case gfx::CompositionOp::OP_COLOR:
+ return MixBlendMode::Color;
+ case gfx::CompositionOp::OP_LUMINOSITY:
+ return MixBlendMode::Luminosity;
+ case gfx::CompositionOp::OP_ADD:
+ return MixBlendMode::PlusLighter;
+ default:
+ return MixBlendMode::Normal;
+ }
+}
+
+static inline wr::ColorF ToColorF(const gfx::DeviceColor& color) {
+ wr::ColorF c;
+ c.r = color.r;
+ c.g = color.g;
+ c.b = color.b;
+ c.a = color.a;
+ return c;
+}
+
+static inline wr::ColorU ToColorU(const gfx::DeviceColor& color) {
+ wr::ColorU c;
+ c.r = uint8_t(color.r * 255.0f);
+ c.g = uint8_t(color.g * 255.0f);
+ c.b = uint8_t(color.b * 255.0f);
+ c.a = uint8_t(color.a * 255.0f);
+ return c;
+}
+
+static inline wr::LayoutPoint ToLayoutPoint(
+ const mozilla::LayoutDevicePoint& point) {
+ wr::LayoutPoint p;
+ p.x = point.x;
+ p.y = point.y;
+ return p;
+}
+
+static inline wr::LayoutPoint ToLayoutPoint(
+ const mozilla::LayoutDeviceIntPoint& point) {
+ return ToLayoutPoint(LayoutDevicePoint(point));
+}
+
+static inline wr::WorldPoint ToWorldPoint(const mozilla::ScreenPoint& point) {
+ wr::WorldPoint p;
+ p.x = point.x;
+ p.y = point.y;
+ return p;
+}
+
+static inline wr::LayoutVector2D ToLayoutVector2D(
+ const mozilla::LayoutDevicePoint& point) {
+ wr::LayoutVector2D p;
+ p.x = point.x;
+ p.y = point.y;
+ return p;
+}
+
+static inline wr::LayoutVector2D ToLayoutVector2D(
+ const mozilla::LayoutDeviceIntPoint& point) {
+ return ToLayoutVector2D(LayoutDevicePoint(point));
+}
+
+static inline wr::LayoutRect ToLayoutRect(
+ const mozilla::LayoutDeviceRect& rect) {
+ wr::LayoutRect r;
+ r.min.x = rect.X();
+ r.min.y = rect.Y();
+ r.max.x = rect.X() + rect.Width();
+ r.max.y = rect.Y() + rect.Height();
+ return r;
+}
+
+static inline wr::LayoutRect ToLayoutRect(const gfx::Rect& rect) {
+ wr::LayoutRect r;
+ r.min.x = rect.X();
+ r.min.y = rect.Y();
+ r.max.x = rect.X() + rect.Width();
+ r.max.y = rect.Y() + rect.Height();
+ return r;
+}
+
+static inline wr::DeviceIntRect ToDeviceIntRect(
+ const mozilla::ImageIntRect& rect) {
+ wr::DeviceIntRect r;
+ r.min.x = rect.X();
+ r.min.y = rect.Y();
+ r.max.x = rect.X() + rect.Width();
+ r.max.y = rect.Y() + rect.Height();
+ return r;
+}
+
+// TODO: should be const LayoutDeviceIntRect instead of ImageIntRect
+static inline wr::LayoutIntRect ToLayoutIntRect(
+ const mozilla::ImageIntRect& rect) {
+ wr::LayoutIntRect r;
+ r.min.x = rect.X();
+ r.min.y = rect.Y();
+ r.max.x = rect.X() + rect.Width();
+ r.max.y = rect.Y() + rect.Height();
+ return r;
+}
+
+static inline wr::LayoutRect ToLayoutRect(
+ const mozilla::LayoutDeviceIntRect& rect) {
+ return ToLayoutRect(IntRectToRect(rect));
+}
+
+static inline wr::LayoutRect IntersectLayoutRect(const wr::LayoutRect& aRect,
+ const wr::LayoutRect& aOther) {
+ wr::LayoutRect r;
+ r.min.x = std::max(aRect.min.x, aOther.min.x);
+ r.min.y = std::max(aRect.min.y, aOther.min.y);
+ r.max.x = std::min(aRect.max.x, aOther.max.x);
+ r.max.y = std::min(aRect.max.y, aOther.max.y);
+
+ if (r.max.x < r.min.x || r.max.y < r.min.y) {
+ r.max.x = r.min.x;
+ r.max.y = r.min.y;
+ }
+ return r;
+}
+
+static inline wr::LayoutSize ToLayoutSize(
+ const mozilla::LayoutDeviceSize& size) {
+ wr::LayoutSize ls;
+ ls.width = size.width;
+ ls.height = size.height;
+ return ls;
+}
+
+static inline wr::ComplexClipRegion ToComplexClipRegion(
+ const gfx::RoundedRect& rect) {
+ wr::ComplexClipRegion ret;
+ ret.rect = ToLayoutRect(rect.rect);
+ ret.radii.top_left = ToLayoutSize(LayoutDeviceSize::FromUnknownSize(
+ rect.corners.radii[mozilla::eCornerTopLeft]));
+ ret.radii.top_right = ToLayoutSize(LayoutDeviceSize::FromUnknownSize(
+ rect.corners.radii[mozilla::eCornerTopRight]));
+ ret.radii.bottom_left = ToLayoutSize(LayoutDeviceSize::FromUnknownSize(
+ rect.corners.radii[mozilla::eCornerBottomLeft]));
+ ret.radii.bottom_right = ToLayoutSize(LayoutDeviceSize::FromUnknownSize(
+ rect.corners.radii[mozilla::eCornerBottomRight]));
+ ret.mode = wr::ClipMode::Clip;
+ return ret;
+}
+
+static inline wr::ComplexClipRegion SimpleRadii(const wr::LayoutRect& aRect,
+ float aRadii) {
+ wr::ComplexClipRegion ret;
+ wr::LayoutSize radii{aRadii, aRadii};
+ ret.rect = aRect;
+ ret.radii.top_left = radii;
+ ret.radii.top_right = radii;
+ ret.radii.bottom_left = radii;
+ ret.radii.bottom_right = radii;
+ ret.mode = wr::ClipMode::Clip;
+ return ret;
+}
+
+static inline wr::LayoutSize ToLayoutSize(
+ const mozilla::LayoutDeviceIntSize& size) {
+ return ToLayoutSize(LayoutDeviceSize(size));
+}
+
+template <class S, class T>
+static inline wr::LayoutTransform ToLayoutTransform(
+ const gfx::Matrix4x4Typed<S, T>& m) {
+ wr::LayoutTransform transform;
+ transform.m11 = m._11;
+ transform.m12 = m._12;
+ transform.m13 = m._13;
+ transform.m14 = m._14;
+ transform.m21 = m._21;
+ transform.m22 = m._22;
+ transform.m23 = m._23;
+ transform.m24 = m._24;
+ transform.m31 = m._31;
+ transform.m32 = m._32;
+ transform.m33 = m._33;
+ transform.m34 = m._34;
+ transform.m41 = m._41;
+ transform.m42 = m._42;
+ transform.m43 = m._43;
+ transform.m44 = m._44;
+ return transform;
+}
+
+wr::BorderStyle ToBorderStyle(StyleBorderStyle style);
+
+static inline wr::BorderSide ToBorderSide(const gfx::DeviceColor& color,
+ StyleBorderStyle style) {
+ wr::BorderSide bs;
+ bs.color = ToColorF(color);
+ bs.style = ToBorderStyle(style);
+ return bs;
+}
+
+static inline wr::BorderRadius EmptyBorderRadius() {
+ wr::BorderRadius br;
+ PodZero(&br);
+ return br;
+}
+
+static inline wr::BorderRadius ToBorderRadius(
+ const LayoutDeviceSize& topLeft, const LayoutDeviceSize& topRight,
+ const LayoutDeviceSize& bottomLeft, const LayoutDeviceSize& bottomRight) {
+ wr::BorderRadius br;
+ br.top_left = ToLayoutSize(topLeft);
+ br.top_right = ToLayoutSize(topRight);
+ br.bottom_left = ToLayoutSize(bottomLeft);
+ br.bottom_right = ToLayoutSize(bottomRight);
+ return br;
+}
+
+static inline wr::BorderRadius ToBorderRadius(
+ const gfx::RectCornerRadii& aRadii) {
+ return ToBorderRadius(LayoutDeviceSize::FromUnknownSize(aRadii[0]),
+ LayoutDeviceSize::FromUnknownSize(aRadii[1]),
+ LayoutDeviceSize::FromUnknownSize(aRadii[3]),
+ LayoutDeviceSize::FromUnknownSize(aRadii[2]));
+}
+
+static inline wr::ComplexClipRegion ToComplexClipRegion(
+ const nsRect& aRect, const nscoord* aRadii, int32_t aAppUnitsPerDevPixel) {
+ wr::ComplexClipRegion ret;
+ ret.rect =
+ ToLayoutRect(LayoutDeviceRect::FromAppUnits(aRect, aAppUnitsPerDevPixel));
+ ret.radii = ToBorderRadius(
+ LayoutDeviceSize::FromAppUnits(
+ nsSize(aRadii[eCornerTopLeftX], aRadii[eCornerTopLeftY]),
+ aAppUnitsPerDevPixel),
+ LayoutDeviceSize::FromAppUnits(
+ nsSize(aRadii[eCornerTopRightX], aRadii[eCornerTopRightY]),
+ aAppUnitsPerDevPixel),
+ LayoutDeviceSize::FromAppUnits(
+ nsSize(aRadii[eCornerBottomLeftX], aRadii[eCornerBottomLeftY]),
+ aAppUnitsPerDevPixel),
+ LayoutDeviceSize::FromAppUnits(
+ nsSize(aRadii[eCornerBottomRightX], aRadii[eCornerBottomRightY]),
+ aAppUnitsPerDevPixel));
+ ret.mode = ClipMode::Clip;
+ return ret;
+}
+
+static inline wr::LayoutSideOffsets ToBorderWidths(float top, float right,
+ float bottom, float left) {
+ wr::LayoutSideOffsets bw;
+ bw.top = top;
+ bw.right = right;
+ bw.bottom = bottom;
+ bw.left = left;
+ return bw;
+}
+
+static inline wr::DeviceIntSideOffsets ToDeviceIntSideOffsets(int32_t top,
+ int32_t right,
+ int32_t bottom,
+ int32_t left) {
+ wr::DeviceIntSideOffsets offset;
+ offset.top = top;
+ offset.right = right;
+ offset.bottom = bottom;
+ offset.left = left;
+ return offset;
+}
+
+static inline wr::LayoutSideOffsets ToLayoutSideOffsets(float top, float right,
+ float bottom,
+ float left) {
+ wr::LayoutSideOffsets offset;
+ offset.top = top;
+ offset.right = right;
+ offset.bottom = bottom;
+ offset.left = left;
+ return offset;
+}
+
+wr::RepeatMode ToRepeatMode(StyleBorderImageRepeat);
+
+template <class S, class T>
+static inline wr::WrTransformProperty ToWrTransformProperty(
+ uint64_t id, const gfx::Matrix4x4Typed<S, T>& transform) {
+ wr::WrTransformProperty prop;
+ prop.id = id;
+ prop.value = ToLayoutTransform(transform);
+ return prop;
+}
+
+static inline wr::WrOpacityProperty ToWrOpacityProperty(uint64_t id,
+ const float opacity) {
+ wr::WrOpacityProperty prop;
+ prop.id = id;
+ prop.value = opacity;
+ return prop;
+}
+
+static inline wr::WrColorProperty ToWrColorProperty(
+ uint64_t id, const gfx::DeviceColor& color) {
+ wr::WrColorProperty prop;
+ prop.id = id;
+ prop.value = ToColorF(color);
+ return prop;
+}
+
+// Whenever possible, use wr::ExternalImageId instead of manipulating uint64_t.
+inline uint64_t AsUint64(const ExternalImageId& aId) {
+ return static_cast<uint64_t>(aId._0);
+}
+
+static inline ExternalImageId ToExternalImageId(uint64_t aID) {
+ ExternalImageId Id;
+ Id._0 = aID;
+ return Id;
+}
+
+static inline wr::WrExternalImage RawDataToWrExternalImage(const uint8_t* aBuff,
+ size_t size) {
+ return wr::WrExternalImage{
+ wr::WrExternalImageType::RawData, 0, 0.0f, 0.0f, 0.0f, 0.0f, aBuff, size};
+}
+
+static inline wr::WrExternalImage NativeTextureToWrExternalImage(
+ uint32_t aHandle, float u0, float v0, float u1, float v1) {
+ return wr::WrExternalImage{wr::WrExternalImageType::NativeTexture,
+ aHandle,
+ u0,
+ v0,
+ u1,
+ v1,
+ nullptr,
+ 0};
+}
+
+static inline wr::WrExternalImage InvalidToWrExternalImage() {
+ return wr::WrExternalImage{
+ wr::WrExternalImageType::Invalid, 0, 0, 0, 0, 0, nullptr, 0};
+}
+
+inline wr::ByteSlice RangeToByteSlice(mozilla::Range<uint8_t> aRange) {
+ return wr::ByteSlice{aRange.begin().get(), aRange.length()};
+}
+
+inline mozilla::Range<const uint8_t> ByteSliceToRange(wr::ByteSlice aWrSlice) {
+ return mozilla::Range<const uint8_t>(aWrSlice.buffer, aWrSlice.len);
+}
+
+inline mozilla::Range<uint8_t> MutByteSliceToRange(wr::MutByteSlice aWrSlice) {
+ return mozilla::Range<uint8_t>(aWrSlice.buffer, aWrSlice.len);
+}
+
+void Assign_WrVecU8(wr::WrVecU8& aVec, mozilla::ipc::ByteBuf&& aOther);
+
+template <typename T>
+struct Vec;
+
+template <>
+struct Vec<uint8_t> final {
+ wr::WrVecU8 inner;
+ Vec() { SetEmpty(); }
+ Vec(Vec&) = delete;
+ Vec(Vec&& src) {
+ inner = src.inner;
+ src.SetEmpty();
+ }
+
+ explicit Vec(mozilla::ipc::ByteBuf&& aSrc) {
+ Assign_WrVecU8(inner, std::move(aSrc));
+ }
+
+ Vec& operator=(Vec&& src) {
+ inner = src.inner;
+ src.SetEmpty();
+ return *this;
+ }
+
+ wr::WrVecU8 Extract() {
+ wr::WrVecU8 ret = inner;
+ SetEmpty();
+ return ret;
+ }
+
+ void SetEmpty() {
+ inner.data = (uint8_t*)1;
+ inner.capacity = 0;
+ inner.length = 0;
+ }
+
+ uint8_t* Data() { return inner.data; }
+
+ size_t Length() { return inner.length; }
+
+ size_t Capacity() { return inner.capacity; }
+
+ Range<uint8_t> GetRange() { return Range<uint8_t>(Data(), Length()); }
+
+ void PushBytes(Range<uint8_t> aBytes) {
+ wr_vec_u8_push_bytes(&inner, RangeToByteSlice(aBytes));
+ }
+
+ void Reserve(size_t aLength) { wr_vec_u8_reserve(&inner, aLength); }
+
+ ~Vec() {
+ if (inner.data) {
+ wr_vec_u8_free(inner);
+ }
+ }
+};
+
+struct ByteBuffer {
+ ByteBuffer(size_t aLength, uint8_t* aData)
+ : mLength(aLength), mData(aData), mOwned(false) {}
+
+ // XXX: this is a bit of hack that assumes
+ // the allocators are the same
+ explicit ByteBuffer(VecU8&& vec) {
+ if (vec.inner.capacity) {
+ mLength = vec.inner.length;
+ mData = vec.inner.data;
+ vec.inner.data = nullptr;
+ vec.inner.capacity = 0;
+ mOwned = true;
+ } else {
+ mOwned = false;
+ mData = nullptr;
+ mLength = 0;
+ }
+ }
+
+ ByteBuffer(ByteBuffer&& aFrom)
+ : mLength(aFrom.mLength), mData(aFrom.mData), mOwned(aFrom.mOwned) {
+ aFrom.mLength = 0;
+ aFrom.mData = nullptr;
+ aFrom.mOwned = false;
+ }
+
+ ByteBuffer(ByteBuffer& aFrom)
+ : mLength(aFrom.mLength), mData(aFrom.mData), mOwned(aFrom.mOwned) {
+ aFrom.mLength = 0;
+ aFrom.mData = nullptr;
+ aFrom.mOwned = false;
+ }
+
+ ByteBuffer() : mLength(0), mData(nullptr), mOwned(false) {}
+
+ bool Allocate(size_t aLength) {
+ MOZ_ASSERT(mData == nullptr);
+ mData = (uint8_t*)malloc(aLength);
+ if (!mData) {
+ return false;
+ }
+ mLength = aLength;
+ mOwned = true;
+ return true;
+ }
+
+ ~ByteBuffer() {
+ if (mData && mOwned) {
+ free(mData);
+ }
+ }
+
+ const Range<uint8_t> AsSlice() const {
+ return Range<uint8_t>(mData, mLength);
+ }
+
+ Range<uint8_t> AsSlice() { return Range<uint8_t>(mData, mLength); }
+
+ bool operator==(const ByteBuffer& other) const {
+ return mLength == other.mLength && !(memcmp(mData, other.mData, mLength));
+ }
+
+ size_t mLength;
+ uint8_t* mData;
+ bool mOwned;
+};
+
+struct BuiltDisplayList {
+ wr::VecU8 dl_items;
+ wr::VecU8 dl_cache;
+ wr::VecU8 dl_spatial_tree;
+ wr::BuiltDisplayListDescriptor dl_desc;
+};
+
+// Corresponds to a clip id for a clip chain in webrender. Similar to
+// WrClipId but a separate struct so we don't get them mixed up in C++.
+struct WrClipChainId {
+ uint64_t id;
+
+ bool operator==(const WrClipChainId& other) const { return id == other.id; }
+
+ static WrClipChainId Empty() {
+ WrClipChainId id = {0};
+ return id;
+ }
+};
+
+WrSpatialId RootScrollNode();
+WrSpaceAndClipChain RootScrollNodeWithChain();
+WrSpaceAndClipChain InvalidScrollNodeWithChain();
+
+enum class WebRenderError : int8_t {
+ INITIALIZE = 0,
+ MAKE_CURRENT,
+ RENDER,
+ NEW_SURFACE,
+ BEGIN_DRAW,
+ VIDEO_OVERLAY,
+ EXCESSIVE_RESETS,
+
+ Sentinel /* this must be last for serialization purposes. */
+};
+
+static inline wr::WrYuvColorSpace ToWrYuvColorSpace(
+ gfx::YUVColorSpace aYUVColorSpace) {
+ switch (aYUVColorSpace) {
+ case gfx::YUVColorSpace::BT601:
+ return wr::WrYuvColorSpace::Rec601;
+ case gfx::YUVColorSpace::BT709:
+ return wr::WrYuvColorSpace::Rec709;
+ case gfx::YUVColorSpace::BT2020:
+ return wr::WrYuvColorSpace::Rec2020;
+ case gfx::YUVColorSpace::Identity:
+ return wr::WrYuvColorSpace::Identity;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Tried to convert invalid YUVColorSpace.");
+ }
+ return wr::WrYuvColorSpace::Rec601;
+}
+
+// TODO: Use YUVRangedColorSpace instead of assuming ColorRange::LIMITED.
+static inline wr::YuvRangedColorSpace ToWrYuvRangedColorSpace(
+ gfx::YUVRangedColorSpace aFrom) {
+ switch (aFrom) {
+ case gfx::YUVRangedColorSpace::BT601_Narrow:
+ return wr::YuvRangedColorSpace::Rec601Narrow;
+ case gfx::YUVRangedColorSpace::BT601_Full:
+ return wr::YuvRangedColorSpace::Rec601Full;
+ case gfx::YUVRangedColorSpace::BT709_Narrow:
+ return wr::YuvRangedColorSpace::Rec709Narrow;
+ case gfx::YUVRangedColorSpace::BT709_Full:
+ return wr::YuvRangedColorSpace::Rec709Full;
+ case gfx::YUVRangedColorSpace::BT2020_Narrow:
+ return wr::YuvRangedColorSpace::Rec2020Narrow;
+ case gfx::YUVRangedColorSpace::BT2020_Full:
+ return wr::YuvRangedColorSpace::Rec2020Full;
+ case gfx::YUVRangedColorSpace::GbrIdentity:
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Tried to convert invalid YUVColorSpace.");
+ break;
+ }
+ return wr::YuvRangedColorSpace::GbrIdentity;
+}
+
+static inline wr::WrColorDepth ToWrColorDepth(gfx::ColorDepth aColorDepth) {
+ switch (aColorDepth) {
+ case gfx::ColorDepth::COLOR_8:
+ return wr::WrColorDepth::Color8;
+ case gfx::ColorDepth::COLOR_10:
+ return wr::WrColorDepth::Color10;
+ case gfx::ColorDepth::COLOR_12:
+ return wr::WrColorDepth::Color12;
+ case gfx::ColorDepth::COLOR_16:
+ return wr::WrColorDepth::Color16;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Tried to convert invalid color depth value.");
+ }
+ return wr::WrColorDepth::Color8;
+}
+
+static inline wr::WrColorRange ToWrColorRange(gfx::ColorRange aColorRange) {
+ switch (aColorRange) {
+ case gfx::ColorRange::LIMITED:
+ return wr::WrColorRange::Limited;
+ case gfx::ColorRange::FULL:
+ return wr::WrColorRange::Full;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Tried to convert invalid color range value.");
+ return wr ::WrColorRange::Limited;
+ }
+}
+
+static inline wr::SyntheticItalics DegreesToSyntheticItalics(float aDegrees) {
+ wr::SyntheticItalics synthetic_italics;
+ synthetic_italics.angle =
+ int16_t(std::min(std::max(aDegrees, -89.0f), 89.0f) * 256.0f);
+ return synthetic_italics;
+}
+
+static inline wr::WindowSizeMode ToWrWindowSizeMode(nsSizeMode aSizeMode) {
+ switch (aSizeMode) {
+ case nsSizeMode_Normal:
+ return wr::WindowSizeMode::Normal;
+ case nsSizeMode_Minimized:
+ return wr::WindowSizeMode::Minimized;
+ case nsSizeMode_Maximized:
+ return wr::WindowSizeMode::Maximized;
+ case nsSizeMode_Fullscreen:
+ return wr::WindowSizeMode::Fullscreen;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Tried to convert invalid size mode.");
+ return wr::WindowSizeMode::Invalid;
+ }
+}
+
+static inline wr::APZScrollGeneration ToWrAPZScrollGeneration(
+ const mozilla::APZScrollGeneration& aGeneration) {
+ return wr::APZScrollGeneration(aGeneration.Raw());
+}
+
+static inline wr::HasScrollLinkedEffect ToWrHasScrollLinkedEffect(
+ bool aHasScrollLinkedEffect) {
+ return aHasScrollLinkedEffect ? wr::HasScrollLinkedEffect::Yes
+ : wr::HasScrollLinkedEffect::No;
+}
+
+} // namespace wr
+} // namespace mozilla
+
+namespace std {
+template <>
+struct hash<mozilla::wr::WrSpatialId> {
+ std::size_t operator()(mozilla::wr::WrSpatialId const& aKey) const noexcept {
+ return std::hash<size_t>{}(aKey.id);
+ }
+};
+} // namespace std
+
+#endif /* GFX_WEBRENDERTYPES_H */
diff --git a/gfx/webrender_bindings/cbindgen.toml b/gfx/webrender_bindings/cbindgen.toml
new file mode 100644
index 0000000000..c513ebfd9e
--- /dev/null
+++ b/gfx/webrender_bindings/cbindgen.toml
@@ -0,0 +1,55 @@
+header = """/* 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/. */"""
+autogen_warning = """/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen.
+ * To generate this file:
+ * 1. Get the latest cbindgen using `cargo install --force cbindgen`
+ * a. Alternatively, you can clone `https://github.com/eqrion/cbindgen` and use a tagged release
+ * 2. Run `rustup run nightly cbindgen toolkit/library/rust/ --lockfile Cargo.lock --crate webrender_bindings -o gfx/webrender_bindings/webrender_ffi_generated.h`
+ */"""
+include_version = true
+braces = "SameLine"
+line_length = 100
+tab_width = 2
+language = "C++"
+namespaces = ["mozilla", "wr"]
+
+[export]
+item_types = ["globals", "enums", "structs", "unions", "typedefs", "opaque", "functions", "constants"]
+include = ["POLYGON_CLIP_VERTEX_MAX"]
+
+[parse]
+parse_deps = true
+extra_bindings = ["webrender_api"]
+include = ["log", "euclid", "webrender", "webrender_api"]
+
+[fn]
+args = "Vertical"
+rename_args = "GeckoCase"
+
+[struct]
+associated_constants_in_body = true
+derive_eq = true
+derive_ostream = true
+
+[enum]
+add_sentinel = true
+derive_helper_methods = true
+derive_ostream = true
+
+[macro_expansion]
+bitflags = true
+
+[defines]
+"target_os = windows" = "XP_WIN"
+"target_os = macos" = "XP_MACOSX"
+"target_os = android" = "ANDROID"
+
+[export.rename]
+"ThinVec" = "nsTArray"
+
+[export.body]
+"Box2D" = """
+ inline T width() const { return max.x - min.x; }
+ inline T height() const { return max.y - min.y; }
+""" \ No newline at end of file
diff --git a/gfx/webrender_bindings/moz.build b/gfx/webrender_bindings/moz.build
new file mode 100644
index 0000000000..61e8e4739d
--- /dev/null
+++ b/gfx/webrender_bindings/moz.build
@@ -0,0 +1,131 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+with Files("**"):
+ BUG_COMPONENT = ("Core", "Graphics: WebRender")
+
+EXPORTS.mozilla.webrender += [
+ "RenderBufferTextureHost.h",
+ "RenderCompositor.h",
+ "RenderCompositorEGL.h",
+ "RenderCompositorLayersSWGL.h",
+ "RenderCompositorOGL.h",
+ "RenderCompositorOGLSWGL.h",
+ "RenderCompositorSWGL.h",
+ "RenderEGLImageTextureHost.h",
+ "RendererOGL.h",
+ "RendererScreenshotGrabber.h",
+ "RenderExternalTextureHost.h",
+ "RenderSharedSurfaceTextureHost.h",
+ "RenderTextureHost.h",
+ "RenderTextureHostSWGL.h",
+ "RenderTextureHostWrapper.h",
+ "RenderThread.h",
+ "webrender_ffi.h",
+ "WebRenderAPI.h",
+ "WebRenderTypes.h",
+]
+
+UNIFIED_SOURCES += [
+ "Moz2DImageRenderer.cpp",
+ "RenderBufferTextureHost.cpp",
+ "RenderCompositor.cpp",
+ "RenderCompositorEGL.cpp",
+ "RenderCompositorLayersSWGL.cpp",
+ "RenderCompositorOGL.cpp",
+ "RenderCompositorOGLSWGL.cpp",
+ "RenderCompositorSWGL.cpp",
+ "RenderEGLImageTextureHost.cpp",
+ "RendererOGL.cpp",
+ "RendererScreenshotGrabber.cpp",
+ "RenderExternalTextureHost.cpp",
+ "RenderSharedSurfaceTextureHost.cpp",
+ "RenderTextureHost.cpp",
+ "RenderTextureHostSWGL.cpp",
+ "RenderTextureHostWrapper.cpp",
+ "RenderThread.cpp",
+ "WebRenderAPI.cpp",
+ "WebRenderTypes.cpp",
+]
+
+if CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa":
+ EXPORTS.mozilla.webrender += [
+ "RenderCompositorNative.h",
+ "RenderMacIOSurfaceTextureHost.h",
+ ]
+ UNIFIED_SOURCES += [
+ "RenderCompositorNative.cpp",
+ "RenderMacIOSurfaceTextureHost.cpp",
+ ]
+
+if CONFIG["MOZ_WIDGET_TOOLKIT"] == "android":
+ EXPORTS.mozilla.webrender += [
+ "RenderAndroidHardwareBufferTextureHost.h",
+ "RenderAndroidSurfaceTextureHost.h",
+ ]
+ UNIFIED_SOURCES += [
+ "RenderAndroidHardwareBufferTextureHost.cpp",
+ "RenderAndroidSurfaceTextureHost.cpp",
+ ]
+
+if CONFIG["MOZ_ENABLE_D3D10_LAYER"]:
+ DEFINES["MOZ_ENABLE_D3D10_LAYER"] = True
+ EXPORTS.mozilla.webrender += [
+ "DCLayerTree.h",
+ "RenderCompositorANGLE.h",
+ "RenderCompositorD3D11SWGL.h",
+ "RenderD3D11TextureHost.h",
+ "RenderDcompSurfaceTextureHost.h",
+ ]
+ UNIFIED_SOURCES += [
+ "RenderCompositorD3D11SWGL.cpp",
+ "RenderD3D11TextureHost.cpp",
+ ]
+ SOURCES += [
+ "DCLayerTree.cpp",
+ "RenderCompositorANGLE.cpp",
+ "RenderDcompSurfaceTextureHost.cpp",
+ ]
+
+if CONFIG["MOZ_WAYLAND"]:
+ EXPORTS.mozilla.webrender += [
+ "RenderCompositorNative.h",
+ "RenderDMABUFTextureHost.h",
+ ]
+ SOURCES += [
+ "RenderCompositorNative.cpp",
+ "RenderDMABUFTextureHost.cpp",
+ ]
+
+if CONFIG["MOZ_WIDGET_TOOLKIT"] in ("android", "gtk"):
+ CXXFLAGS += CONFIG["CAIRO_FT_CFLAGS"]
+
+if CONFIG["COMPILE_ENVIRONMENT"]:
+ EXPORTS.mozilla.webrender += [
+ "!webrender_ffi_generated.h",
+ ]
+
+ CbindgenHeader(
+ "webrender_ffi_generated.h",
+ inputs=[
+ "/gfx/webrender_bindings",
+ "/gfx/wr/webrender",
+ "/gfx/wr/webrender_api",
+ ],
+ )
+
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul"
+
+LOCAL_INCLUDES += [
+ "/gfx/cairo/cairo/src",
+]
+if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
+ CXXFLAGS += CONFIG["MOZ_GTK3_CFLAGS"]
+
+CXXFLAGS += ["-Werror=switch"]
diff --git a/gfx/webrender_bindings/rustfmt.toml b/gfx/webrender_bindings/rustfmt.toml
new file mode 100644
index 0000000000..1d15550e6c
--- /dev/null
+++ b/gfx/webrender_bindings/rustfmt.toml
@@ -0,0 +1,16 @@
+ideal_width = 80
+max_width = 120
+
+newline_style = "Unix"
+normalize_comments = false
+
+force_explicit_abi = true
+fn_args_density = "Vertical"
+
+chain_indent = "Visual"
+chain_one_line_max = 90
+
+struct_lit_multiline_style = "ForceMulti"
+
+match_block_trailing_comma = true
+where_trailing_comma = true
diff --git a/gfx/webrender_bindings/src/bindings.rs b/gfx/webrender_bindings/src/bindings.rs
new file mode 100644
index 0000000000..53f59500d0
--- /dev/null
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -0,0 +1,4015 @@
+/* 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/. */
+
+#![allow(clippy::missing_safety_doc)]
+#![allow(clippy::not_unsafe_ptr_arg_deref)]
+
+use gleam::gl;
+use std::cell::RefCell;
+#[cfg(not(target_os = "macos"))]
+use std::ffi::OsString;
+use std::ffi::{CStr, CString};
+use std::io::Cursor;
+use std::marker::PhantomData;
+use std::ops::Range;
+#[cfg(target_os = "android")]
+use std::os::raw::c_int;
+use std::os::raw::{c_char, c_float, c_void};
+#[cfg(not(any(target_os = "macos", target_os = "windows")))]
+use std::os::unix::ffi::OsStringExt;
+#[cfg(target_os = "windows")]
+use std::os::windows::ffi::OsStringExt;
+use std::path::PathBuf;
+use std::rc::Rc;
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::Arc;
+use std::time::Duration;
+use std::{env, mem, ptr, slice};
+use thin_vec::ThinVec;
+
+use euclid::SideOffsets2D;
+use moz2d_renderer::Moz2dBlobImageHandler;
+use nsstring::nsAString;
+use num_cpus;
+use program_cache::{remove_disk_cache, WrProgramCache};
+use rayon;
+use tracy_rs::register_thread_with_profiler;
+use webrender::sw_compositor::SwCompositor;
+use webrender::{
+ api::units::*, api::*, create_webrender_instance, render_api::*, set_profiler_hooks, AsyncPropertySampler,
+ AsyncScreenshotHandle, Compositor, CompositorCapabilities, CompositorConfig, CompositorSurfaceTransform,
+ DebugFlags, Device, MappableCompositor, MappedTileInfo, NativeSurfaceId, NativeSurfaceInfo, NativeTileId,
+ PartialPresentCompositor, PipelineInfo, ProfilerHooks, RecordedFrameHandle, Renderer, RendererStats,
+ SWGLCompositeSurfaceInfo, SceneBuilderHooks, ShaderPrecacheFlags, Shaders, SharedShaders, TextureCacheConfig,
+ UploadMethod, WebRenderOptions, WindowVisibility, ONE_TIME_USAGE_HINT,
+};
+use wr_malloc_size_of::MallocSizeOfOps;
+
+extern "C" {
+ #[cfg(target_os = "android")]
+ fn __android_log_write(prio: c_int, tag: *const c_char, text: *const c_char) -> c_int;
+}
+
+/// The unique id for WR resource identification.
+static NEXT_NAMESPACE_ID: AtomicUsize = AtomicUsize::new(1);
+
+/// Special value handled in this wrapper layer to signify a redundant clip chain.
+pub const ROOT_CLIP_CHAIN: u64 = !0;
+
+fn next_namespace_id() -> IdNamespace {
+ IdNamespace(NEXT_NAMESPACE_ID.fetch_add(1, Ordering::Relaxed) as u32)
+}
+
+/// Whether a border should be antialiased.
+#[repr(C)]
+#[derive(Eq, PartialEq, Copy, Clone)]
+pub enum AntialiasBorder {
+ No = 0,
+ Yes,
+}
+
+/// Used to indicate if an image is opaque, or has an alpha channel.
+#[repr(u8)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum OpacityType {
+ Opaque = 0,
+ HasAlphaChannel = 1,
+}
+
+/// cbindgen:field-names=[mHandle]
+/// cbindgen:derive-lt=true
+/// cbindgen:derive-lte=true
+/// cbindgen:derive-neq=true
+type WrEpoch = Epoch;
+/// cbindgen:field-names=[mHandle]
+/// cbindgen:derive-lt=true
+/// cbindgen:derive-lte=true
+/// cbindgen:derive-neq=true
+pub type WrIdNamespace = IdNamespace;
+
+/// cbindgen:field-names=[mNamespace, mHandle]
+type WrDocumentId = DocumentId;
+/// cbindgen:field-names=[mNamespace, mHandle]
+type WrPipelineId = PipelineId;
+/// cbindgen:field-names=[mNamespace, mHandle]
+/// cbindgen:derive-neq=true
+type WrImageKey = ImageKey;
+/// cbindgen:field-names=[mNamespace, mHandle]
+pub type WrFontKey = FontKey;
+/// cbindgen:field-names=[mNamespace, mHandle]
+pub type WrFontInstanceKey = FontInstanceKey;
+/// cbindgen:field-names=[mNamespace, mHandle]
+type WrYuvColorSpace = YuvColorSpace;
+/// cbindgen:field-names=[mNamespace, mHandle]
+type WrColorDepth = ColorDepth;
+/// cbindgen:field-names=[mNamespace, mHandle]
+type WrColorRange = ColorRange;
+
+#[inline]
+fn clip_chain_id_to_webrender(id: u64, pipeline_id: WrPipelineId) -> ClipChainId {
+ if id == ROOT_CLIP_CHAIN {
+ ClipChainId::INVALID
+ } else {
+ ClipChainId(id, pipeline_id)
+ }
+}
+
+#[repr(C)]
+pub struct WrSpaceAndClipChain {
+ space: WrSpatialId,
+ clip_chain: u64,
+}
+
+impl WrSpaceAndClipChain {
+ fn to_webrender(&self, pipeline_id: WrPipelineId) -> SpaceAndClipInfo {
+ //Warning: special case here to support dummy clip chain
+ SpaceAndClipInfo {
+ spatial_id: self.space.to_webrender(pipeline_id),
+ clip_chain_id: clip_chain_id_to_webrender(self.clip_chain, pipeline_id),
+ }
+ }
+}
+
+#[repr(C)]
+pub enum WrStackingContextClip {
+ None,
+ ClipChain(u64),
+}
+
+impl WrStackingContextClip {
+ fn to_webrender(&self, pipeline_id: WrPipelineId) -> Option<ClipChainId> {
+ match *self {
+ WrStackingContextClip::None => None,
+ WrStackingContextClip::ClipChain(id) => {
+ if id == ROOT_CLIP_CHAIN {
+ None
+ } else {
+ Some(ClipChainId(id, pipeline_id))
+ }
+ },
+ }
+ }
+}
+
+unsafe fn make_slice<'a, T>(ptr: *const T, len: usize) -> &'a [T] {
+ if ptr.is_null() {
+ &[]
+ } else {
+ slice::from_raw_parts(ptr, len)
+ }
+}
+
+unsafe fn make_slice_mut<'a, T>(ptr: *mut T, len: usize) -> &'a mut [T] {
+ if ptr.is_null() {
+ &mut []
+ } else {
+ slice::from_raw_parts_mut(ptr, len)
+ }
+}
+
+pub struct DocumentHandle {
+ api: RenderApi,
+ document_id: DocumentId,
+ // One of the two options below is Some and the other None at all times.
+ // It would be nice to model with an enum, however it is tricky to express
+ // moving a variant's content into another variant without moving the
+ // containing enum.
+ hit_tester_request: Option<HitTesterRequest>,
+ hit_tester: Option<Arc<dyn ApiHitTester>>,
+}
+
+impl DocumentHandle {
+ pub fn new(
+ api: RenderApi,
+ hit_tester: Option<Arc<dyn ApiHitTester>>,
+ size: DeviceIntSize,
+ id: u32,
+ ) -> DocumentHandle {
+ let doc = api.add_document_with_id(size, id);
+ let hit_tester_request = if hit_tester.is_none() {
+ // Request the hit tester early to reduce the likelihood of blocking on the
+ // first hit testing query.
+ Some(api.request_hit_tester(doc))
+ } else {
+ None
+ };
+
+ DocumentHandle {
+ api,
+ document_id: doc,
+ hit_tester_request,
+ hit_tester,
+ }
+ }
+
+ fn ensure_hit_tester(&mut self) -> &Arc<dyn ApiHitTester> {
+ if let Some(ref ht) = self.hit_tester {
+ return ht;
+ }
+ self.hit_tester = Some(self.hit_tester_request.take().unwrap().resolve());
+ self.hit_tester.as_ref().unwrap()
+ }
+}
+
+#[repr(C)]
+pub struct WrVecU8 {
+ data: *mut u8,
+ length: usize,
+ capacity: usize,
+}
+
+impl WrVecU8 {
+ fn into_vec(self) -> Vec<u8> {
+ unsafe { Vec::from_raw_parts(self.data, self.length, self.capacity) }
+ }
+
+ // Equivalent to `into_vec` but clears self instead of consuming the value.
+ fn flush_into_vec(&mut self) -> Vec<u8> {
+ self.convert_into_vec::<u8>()
+ }
+
+ // Like flush_into_vec, but also does an unsafe conversion to the desired type.
+ fn convert_into_vec<T>(&mut self) -> Vec<T> {
+ let vec = unsafe {
+ Vec::from_raw_parts(
+ self.data as *mut T,
+ self.length / mem::size_of::<T>(),
+ self.capacity / mem::size_of::<T>(),
+ )
+ };
+ self.data = ptr::null_mut();
+ self.length = 0;
+ self.capacity = 0;
+ vec
+ }
+
+ fn from_vec(mut v: Vec<u8>) -> WrVecU8 {
+ let w = WrVecU8 {
+ data: v.as_mut_ptr(),
+ length: v.len(),
+ capacity: v.capacity(),
+ };
+ mem::forget(v);
+ w
+ }
+
+ fn reserve(&mut self, len: usize) {
+ let mut vec = self.flush_into_vec();
+ vec.reserve(len);
+ *self = Self::from_vec(vec);
+ }
+
+ fn push_bytes(&mut self, bytes: &[u8]) {
+ let mut vec = self.flush_into_vec();
+ vec.extend_from_slice(bytes);
+ *self = Self::from_vec(vec);
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn wr_vec_u8_push_bytes(v: &mut WrVecU8, bytes: ByteSlice) {
+ v.push_bytes(bytes.as_slice());
+}
+
+#[no_mangle]
+pub extern "C" fn wr_vec_u8_reserve(v: &mut WrVecU8, len: usize) {
+ v.reserve(len);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_vec_u8_free(v: WrVecU8) {
+ v.into_vec();
+}
+
+#[repr(C)]
+pub struct ByteSlice<'a> {
+ buffer: *const u8,
+ len: usize,
+ _phantom: PhantomData<&'a ()>,
+}
+
+impl<'a> ByteSlice<'a> {
+ pub fn new(slice: &'a [u8]) -> ByteSlice<'a> {
+ ByteSlice {
+ buffer: slice.as_ptr(),
+ len: slice.len(),
+ _phantom: PhantomData,
+ }
+ }
+
+ pub fn as_slice(&self) -> &'a [u8] {
+ unsafe { make_slice(self.buffer, self.len) }
+ }
+}
+
+#[repr(C)]
+pub struct MutByteSlice<'a> {
+ buffer: *mut u8,
+ len: usize,
+ _phantom: PhantomData<&'a ()>,
+}
+
+impl<'a> MutByteSlice<'a> {
+ pub fn new(slice: &'a mut [u8]) -> MutByteSlice<'a> {
+ let len = slice.len();
+ MutByteSlice {
+ buffer: slice.as_mut_ptr(),
+ len,
+ _phantom: PhantomData,
+ }
+ }
+
+ pub fn as_mut_slice(&mut self) -> &'a mut [u8] {
+ unsafe { make_slice_mut(self.buffer, self.len) }
+ }
+}
+
+#[repr(C)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub struct WrImageDescriptor {
+ pub format: ImageFormat,
+ pub width: i32,
+ pub height: i32,
+ pub stride: i32,
+ pub opacity: OpacityType,
+ // TODO(gw): Remove this flag (use prim flags instead).
+ pub prefer_compositor_surface: bool,
+}
+
+impl<'a> From<&'a WrImageDescriptor> for ImageDescriptor {
+ fn from(desc: &'a WrImageDescriptor) -> ImageDescriptor {
+ let mut flags = ImageDescriptorFlags::empty();
+
+ if desc.opacity == OpacityType::Opaque {
+ flags |= ImageDescriptorFlags::IS_OPAQUE;
+ }
+
+ ImageDescriptor {
+ size: DeviceIntSize::new(desc.width, desc.height),
+ stride: if desc.stride != 0 { Some(desc.stride) } else { None },
+ format: desc.format,
+ offset: 0,
+ flags,
+ }
+ }
+}
+
+#[repr(u32)]
+#[allow(dead_code)]
+enum WrExternalImageType {
+ RawData,
+ NativeTexture,
+ Invalid,
+}
+
+#[repr(C)]
+struct WrExternalImage {
+ image_type: WrExternalImageType,
+
+ // external texture handle
+ handle: u32,
+ // external texture coordinate
+ u0: f32,
+ v0: f32,
+ u1: f32,
+ v1: f32,
+
+ // external image buffer
+ buff: *const u8,
+ size: usize,
+}
+
+extern "C" {
+ fn wr_renderer_lock_external_image(
+ renderer: *mut c_void,
+ external_image_id: ExternalImageId,
+ channel_index: u8,
+ ) -> WrExternalImage;
+ fn wr_renderer_unlock_external_image(renderer: *mut c_void, external_image_id: ExternalImageId, channel_index: u8);
+}
+
+#[repr(C)]
+#[derive(Copy, Clone, Debug)]
+pub struct WrExternalImageHandler {
+ external_image_obj: *mut c_void,
+}
+
+impl ExternalImageHandler for WrExternalImageHandler {
+ fn lock(&mut self, id: ExternalImageId, channel_index: u8) -> ExternalImage {
+ let image = unsafe { wr_renderer_lock_external_image(self.external_image_obj, id, channel_index) };
+ ExternalImage {
+ uv: TexelRect::new(image.u0, image.v0, image.u1, image.v1),
+ source: match image.image_type {
+ WrExternalImageType::NativeTexture => ExternalImageSource::NativeTexture(image.handle),
+ WrExternalImageType::RawData => {
+ ExternalImageSource::RawData(unsafe { make_slice(image.buff, image.size) })
+ },
+ WrExternalImageType::Invalid => ExternalImageSource::Invalid,
+ },
+ }
+ }
+
+ fn unlock(&mut self, id: ExternalImageId, channel_index: u8) {
+ unsafe {
+ wr_renderer_unlock_external_image(self.external_image_obj, id, channel_index);
+ }
+ }
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+// Used for ComponentTransfer only
+pub struct WrFilterData {
+ funcR_type: ComponentTransferFuncType,
+ R_values: *mut c_float,
+ R_values_count: usize,
+ funcG_type: ComponentTransferFuncType,
+ G_values: *mut c_float,
+ G_values_count: usize,
+ funcB_type: ComponentTransferFuncType,
+ B_values: *mut c_float,
+ B_values_count: usize,
+ funcA_type: ComponentTransferFuncType,
+ A_values: *mut c_float,
+ A_values_count: usize,
+}
+
+#[repr(u32)]
+#[derive(Debug)]
+pub enum WrAnimationType {
+ Transform = 0,
+ Opacity = 1,
+ BackgroundColor = 2,
+}
+
+#[repr(C)]
+pub struct WrAnimationProperty {
+ effect_type: WrAnimationType,
+ id: u64,
+ key: SpatialTreeItemKey,
+}
+
+/// cbindgen:derive-eq=false
+#[repr(C)]
+#[derive(Debug)]
+pub struct WrAnimationPropertyValue<T> {
+ pub id: u64,
+ pub value: T,
+}
+
+pub type WrTransformProperty = WrAnimationPropertyValue<LayoutTransform>;
+pub type WrOpacityProperty = WrAnimationPropertyValue<f32>;
+pub type WrColorProperty = WrAnimationPropertyValue<ColorF>;
+
+/// cbindgen:field-names=[mHandle]
+/// cbindgen:derive-lt=true
+/// cbindgen:derive-lte=true
+#[repr(C)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub struct WrWindowId(u64);
+
+#[repr(C)]
+#[derive(Debug)]
+pub struct WrComputedTransformData {
+ pub scale_from: LayoutSize,
+ pub vertical_flip: bool,
+ pub rotation: WrRotation,
+ pub key: SpatialTreeItemKey,
+}
+
+#[repr(C)]
+pub struct WrTransformInfo {
+ pub transform: LayoutTransform,
+ pub key: SpatialTreeItemKey,
+}
+
+fn get_proc_address(glcontext_ptr: *mut c_void, name: &str) -> *const c_void {
+ extern "C" {
+ fn get_proc_address_from_glcontext(glcontext_ptr: *mut c_void, procname: *const c_char) -> *const c_void;
+ }
+
+ let symbol_name = CString::new(name).unwrap();
+ let symbol = unsafe { get_proc_address_from_glcontext(glcontext_ptr, symbol_name.as_ptr()) };
+ symbol as *const _
+}
+
+#[repr(C)]
+pub enum TelemetryProbe {
+ SceneBuildTime = 0,
+ SceneSwapTime = 1,
+ FrameBuildTime = 2,
+}
+
+extern "C" {
+ fn is_in_compositor_thread() -> bool;
+ fn is_in_render_thread() -> bool;
+ fn is_in_main_thread() -> bool;
+ fn is_glcontext_gles(glcontext_ptr: *mut c_void) -> bool;
+ fn is_glcontext_angle(glcontext_ptr: *mut c_void) -> bool;
+ fn gfx_wr_resource_path_override() -> *const c_char;
+ fn gfx_wr_use_optimized_shaders() -> bool;
+ // TODO: make gfx_critical_error() work.
+ // We still have problem to pass the error message from render/render_backend
+ // thread to main thread now.
+ #[allow(dead_code)]
+ fn gfx_critical_error(msg: *const c_char);
+ fn gfx_critical_note(msg: *const c_char);
+ fn gfx_wr_set_crash_annotation(annotation: CrashAnnotation, value: *const c_char);
+ fn gfx_wr_clear_crash_annotation(annotation: CrashAnnotation);
+}
+
+struct CppNotifier {
+ window_id: WrWindowId,
+}
+
+unsafe impl Send for CppNotifier {}
+
+extern "C" {
+ fn wr_notifier_wake_up(window_id: WrWindowId, composite_needed: bool);
+ fn wr_notifier_new_frame_ready(window_id: WrWindowId, composite_needed: bool, publish_id: FramePublishId);
+ fn wr_notifier_external_event(window_id: WrWindowId, raw_event: usize);
+ fn wr_schedule_render(window_id: WrWindowId, reasons: RenderReasons);
+ // NOTE: This moves away from pipeline_info.
+ fn wr_finished_scene_build(window_id: WrWindowId, pipeline_info: &mut WrPipelineInfo);
+
+ fn wr_transaction_notification_notified(handler: usize, when: Checkpoint);
+}
+
+impl RenderNotifier for CppNotifier {
+ fn clone(&self) -> Box<dyn RenderNotifier> {
+ Box::new(CppNotifier {
+ window_id: self.window_id,
+ })
+ }
+
+ fn wake_up(&self, composite_needed: bool) {
+ unsafe {
+ wr_notifier_wake_up(self.window_id, composite_needed);
+ }
+ }
+
+ fn new_frame_ready(&self, _: DocumentId, _scrolled: bool, composite_needed: bool, publish_id: FramePublishId) {
+ unsafe {
+ wr_notifier_new_frame_ready(self.window_id, composite_needed, publish_id);
+ }
+ }
+
+ fn external_event(&self, event: ExternalEvent) {
+ unsafe {
+ wr_notifier_external_event(self.window_id, event.unwrap());
+ }
+ }
+}
+
+struct MozCrashAnnotator;
+
+unsafe impl Send for MozCrashAnnotator {}
+
+impl CrashAnnotator for MozCrashAnnotator {
+ fn set(&self, annotation: CrashAnnotation, value: &std::ffi::CStr) {
+ unsafe {
+ gfx_wr_set_crash_annotation(annotation, value.as_ptr());
+ }
+ }
+
+ fn clear(&self, annotation: CrashAnnotation) {
+ unsafe {
+ gfx_wr_clear_crash_annotation(annotation);
+ }
+ }
+
+ fn box_clone(&self) -> Box<dyn CrashAnnotator> {
+ Box::new(MozCrashAnnotator)
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn wr_renderer_set_clear_color(renderer: &mut Renderer, color: ColorF) {
+ renderer.set_clear_color(color);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_renderer_set_external_image_handler(
+ renderer: &mut Renderer,
+ external_image_handler: &mut WrExternalImageHandler,
+) {
+ renderer.set_external_image_handler(Box::new(*external_image_handler));
+}
+
+#[no_mangle]
+pub extern "C" fn wr_renderer_update(renderer: &mut Renderer) {
+ renderer.update();
+}
+
+#[no_mangle]
+pub extern "C" fn wr_renderer_set_target_frame_publish_id(renderer: &mut Renderer, publish_id: FramePublishId) {
+ renderer.set_target_frame_publish_id(publish_id);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_renderer_render(
+ renderer: &mut Renderer,
+ width: i32,
+ height: i32,
+ buffer_age: usize,
+ out_stats: &mut RendererStats,
+ out_dirty_rects: &mut ThinVec<DeviceIntRect>,
+) -> bool {
+ match renderer.render(DeviceIntSize::new(width, height), buffer_age) {
+ Ok(results) => {
+ *out_stats = results.stats;
+ out_dirty_rects.extend(results.dirty_rects);
+ true
+ },
+ Err(errors) => {
+ for e in errors {
+ warn!(" Failed to render: {:?}", e);
+ let msg = CString::new(format!("wr_renderer_render: {:?}", e)).unwrap();
+ unsafe {
+ gfx_critical_note(msg.as_ptr());
+ }
+ }
+ false
+ },
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn wr_renderer_force_redraw(renderer: &mut Renderer) {
+ renderer.force_redraw();
+}
+
+#[no_mangle]
+pub extern "C" fn wr_renderer_record_frame(
+ renderer: &mut Renderer,
+ image_format: ImageFormat,
+ out_handle: &mut RecordedFrameHandle,
+ out_width: &mut i32,
+ out_height: &mut i32,
+) -> bool {
+ if let Some((handle, size)) = renderer.record_frame(image_format) {
+ *out_handle = handle;
+ *out_width = size.width;
+ *out_height = size.height;
+
+ true
+ } else {
+ false
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn wr_renderer_map_recorded_frame(
+ renderer: &mut Renderer,
+ handle: RecordedFrameHandle,
+ dst_buffer: *mut u8,
+ dst_buffer_len: usize,
+ dst_stride: usize,
+) -> bool {
+ renderer.map_recorded_frame(
+ handle,
+ unsafe { make_slice_mut(dst_buffer, dst_buffer_len) },
+ dst_stride,
+ )
+}
+
+#[no_mangle]
+pub extern "C" fn wr_renderer_release_composition_recorder_structures(renderer: &mut Renderer) {
+ renderer.release_composition_recorder_structures();
+}
+
+#[no_mangle]
+pub extern "C" fn wr_renderer_get_screenshot_async(
+ renderer: &mut Renderer,
+ window_x: i32,
+ window_y: i32,
+ window_width: i32,
+ window_height: i32,
+ buffer_width: i32,
+ buffer_height: i32,
+ image_format: ImageFormat,
+ screenshot_width: *mut i32,
+ screenshot_height: *mut i32,
+) -> AsyncScreenshotHandle {
+ assert!(!screenshot_width.is_null());
+ assert!(!screenshot_height.is_null());
+
+ let (handle, size) = renderer.get_screenshot_async(
+ DeviceIntRect::from_origin_and_size(
+ DeviceIntPoint::new(window_x, window_y),
+ DeviceIntSize::new(window_width, window_height),
+ ),
+ DeviceIntSize::new(buffer_width, buffer_height),
+ image_format,
+ );
+
+ unsafe {
+ *screenshot_width = size.width;
+ *screenshot_height = size.height;
+ }
+
+ handle
+}
+
+#[no_mangle]
+pub extern "C" fn wr_renderer_map_and_recycle_screenshot(
+ renderer: &mut Renderer,
+ handle: AsyncScreenshotHandle,
+ dst_buffer: *mut u8,
+ dst_buffer_len: usize,
+ dst_stride: usize,
+) -> bool {
+ renderer.map_and_recycle_screenshot(
+ handle,
+ unsafe { make_slice_mut(dst_buffer, dst_buffer_len) },
+ dst_stride,
+ )
+}
+
+#[no_mangle]
+pub extern "C" fn wr_renderer_release_profiler_structures(renderer: &mut Renderer) {
+ renderer.release_profiler_structures();
+}
+
+// Call wr_renderer_render() before calling this function.
+#[no_mangle]
+pub unsafe extern "C" fn wr_renderer_readback(
+ renderer: &mut Renderer,
+ width: i32,
+ height: i32,
+ format: ImageFormat,
+ dst_buffer: *mut u8,
+ buffer_size: usize,
+) {
+ assert!(is_in_render_thread());
+
+ let mut slice = make_slice_mut(dst_buffer, buffer_size);
+ renderer.read_pixels_into(FramebufferIntSize::new(width, height).into(), format, &mut slice);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wr_renderer_set_profiler_ui(renderer: &mut Renderer, ui_str: *const u8, ui_str_len: usize) {
+ let slice = std::slice::from_raw_parts(ui_str, ui_str_len);
+ if let Ok(ui_str) = std::str::from_utf8(slice) {
+ renderer.set_profiler_ui(ui_str);
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wr_renderer_delete(renderer: *mut Renderer) {
+ let renderer = Box::from_raw(renderer);
+ renderer.deinit();
+ // let renderer go out of scope and get dropped
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wr_renderer_accumulate_memory_report(
+ renderer: &mut Renderer,
+ report: &mut MemoryReport,
+ swgl: *mut c_void,
+) {
+ *report += renderer.report_memory(swgl);
+}
+
+// cbindgen doesn't support tuples, so we have a little struct instead, with
+// an Into implementation to convert from the tuple to the struct.
+#[repr(C)]
+pub struct WrPipelineEpoch {
+ pipeline_id: WrPipelineId,
+ document_id: WrDocumentId,
+ epoch: WrEpoch,
+}
+
+impl<'a> From<(&'a (WrPipelineId, WrDocumentId), &'a WrEpoch)> for WrPipelineEpoch {
+ fn from(tuple: (&(WrPipelineId, WrDocumentId), &WrEpoch)) -> WrPipelineEpoch {
+ WrPipelineEpoch {
+ pipeline_id: (tuple.0).0,
+ document_id: (tuple.0).1,
+ epoch: *tuple.1,
+ }
+ }
+}
+
+#[repr(C)]
+pub struct WrPipelineIdAndEpoch {
+ pipeline_id: WrPipelineId,
+ epoch: WrEpoch,
+}
+
+impl<'a> From<(&WrPipelineId, &WrEpoch)> for WrPipelineIdAndEpoch {
+ fn from(tuple: (&WrPipelineId, &WrEpoch)) -> WrPipelineIdAndEpoch {
+ WrPipelineIdAndEpoch {
+ pipeline_id: *tuple.0,
+ epoch: *tuple.1,
+ }
+ }
+}
+
+#[repr(C)]
+pub struct WrRemovedPipeline {
+ pipeline_id: WrPipelineId,
+ document_id: WrDocumentId,
+}
+
+impl<'a> From<&'a (WrPipelineId, WrDocumentId)> for WrRemovedPipeline {
+ fn from(tuple: &(WrPipelineId, WrDocumentId)) -> WrRemovedPipeline {
+ WrRemovedPipeline {
+ pipeline_id: tuple.0,
+ document_id: tuple.1,
+ }
+ }
+}
+
+#[repr(C)]
+pub struct WrPipelineInfo {
+ /// This contains an entry for each pipeline that was rendered, along with
+ /// the epoch at which it was rendered. Rendered pipelines include the root
+ /// pipeline and any other pipelines that were reachable via IFrame display
+ /// items from the root pipeline.
+ epochs: ThinVec<WrPipelineEpoch>,
+ /// This contains an entry for each pipeline that was removed during the
+ /// last transaction. These pipelines would have been explicitly removed by
+ /// calling remove_pipeline on the transaction object; the pipeline showing
+ /// up in this array means that the data structures have been torn down on
+ /// the webrender side, and so any remaining data structures on the caller
+ /// side can now be torn down also.
+ removed_pipelines: ThinVec<WrRemovedPipeline>,
+}
+
+impl WrPipelineInfo {
+ fn new(info: &PipelineInfo) -> Self {
+ WrPipelineInfo {
+ epochs: info.epochs.iter().map(WrPipelineEpoch::from).collect(),
+ removed_pipelines: info.removed_pipelines.iter().map(WrRemovedPipeline::from).collect(),
+ }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wr_renderer_flush_pipeline_info(renderer: &mut Renderer, out: &mut WrPipelineInfo) {
+ let info = renderer.flush_pipeline_info();
+ *out = WrPipelineInfo::new(&info);
+}
+
+extern "C" {
+ pub fn gecko_profiler_thread_is_being_profiled() -> bool;
+}
+
+pub fn gecko_profiler_start_marker(name: &str) {
+ use gecko_profiler::{gecko_profiler_category, MarkerOptions, MarkerTiming, ProfilerTime, Tracing};
+ gecko_profiler::add_marker(
+ name,
+ gecko_profiler_category!(Graphics),
+ MarkerOptions {
+ timing: MarkerTiming::interval_start(ProfilerTime::now()),
+ ..Default::default()
+ },
+ Tracing("Webrender".to_string()),
+ );
+}
+pub fn gecko_profiler_end_marker(name: &str) {
+ use gecko_profiler::{gecko_profiler_category, MarkerOptions, MarkerTiming, ProfilerTime, Tracing};
+ gecko_profiler::add_marker(
+ name,
+ gecko_profiler_category!(Graphics),
+ MarkerOptions {
+ timing: MarkerTiming::interval_end(ProfilerTime::now()),
+ ..Default::default()
+ },
+ Tracing("Webrender".to_string()),
+ );
+}
+
+pub fn gecko_profiler_event_marker(name: &str) {
+ use gecko_profiler::{gecko_profiler_category, Tracing};
+ gecko_profiler::add_marker(
+ name,
+ gecko_profiler_category!(Graphics),
+ Default::default(),
+ Tracing("Webrender".to_string()),
+ );
+}
+
+pub fn gecko_profiler_add_text_marker(name: &str, text: &str, microseconds: f64) {
+ use gecko_profiler::{gecko_profiler_category, MarkerOptions, MarkerTiming, ProfilerTime};
+ if !gecko_profiler::can_accept_markers() {
+ return;
+ }
+
+ let now = ProfilerTime::now();
+ let start = now.clone().subtract_microseconds(microseconds);
+ gecko_profiler::add_text_marker(
+ name,
+ gecko_profiler_category!(Graphics),
+ MarkerOptions {
+ timing: MarkerTiming::interval(start, now),
+ ..Default::default()
+ },
+ text,
+ );
+}
+
+/// Simple implementation of the WR ProfilerHooks trait to allow profile
+/// markers to be seen in the Gecko profiler.
+struct GeckoProfilerHooks;
+
+impl ProfilerHooks for GeckoProfilerHooks {
+ fn register_thread(&self, thread_name: &str) {
+ gecko_profiler::register_thread(thread_name);
+ }
+
+ fn unregister_thread(&self) {
+ gecko_profiler::unregister_thread();
+ }
+
+ fn begin_marker(&self, label: &str) {
+ gecko_profiler_start_marker(label);
+ }
+
+ fn end_marker(&self, label: &str) {
+ gecko_profiler_end_marker(label);
+ }
+
+ fn event_marker(&self, label: &str) {
+ gecko_profiler_event_marker(label);
+ }
+
+ fn add_text_marker(&self, label: &str, text: &str, duration: Duration) {
+ let micros = duration.as_micros() as f64;
+ gecko_profiler_add_text_marker(label, text, micros);
+ }
+
+ fn thread_is_being_profiled(&self) -> bool {
+ unsafe { gecko_profiler_thread_is_being_profiled() }
+ }
+}
+
+static PROFILER_HOOKS: GeckoProfilerHooks = GeckoProfilerHooks {};
+
+#[allow(improper_ctypes)] // this is needed so that rustc doesn't complain about passing the &mut Transaction to an extern function
+extern "C" {
+ // These callbacks are invoked from the scene builder thread (aka the APZ
+ // updater thread)
+ fn apz_register_updater(window_id: WrWindowId);
+ fn apz_pre_scene_swap(window_id: WrWindowId);
+ fn apz_post_scene_swap(window_id: WrWindowId, pipeline_info: &WrPipelineInfo);
+ fn apz_run_updater(window_id: WrWindowId);
+ fn apz_deregister_updater(window_id: WrWindowId);
+
+ // These callbacks are invoked from the render backend thread (aka the APZ
+ // sampler thread)
+ fn apz_register_sampler(window_id: WrWindowId);
+ fn apz_sample_transforms(window_id: WrWindowId, generated_frame_id: *const u64, transaction: &mut Transaction);
+ fn apz_deregister_sampler(window_id: WrWindowId);
+
+ fn omta_register_sampler(window_id: WrWindowId);
+ fn omta_sample(window_id: WrWindowId, transaction: &mut Transaction);
+ fn omta_deregister_sampler(window_id: WrWindowId);
+}
+
+struct APZCallbacks {
+ window_id: WrWindowId,
+}
+
+impl APZCallbacks {
+ pub fn new(window_id: WrWindowId) -> Self {
+ APZCallbacks { window_id }
+ }
+}
+
+impl SceneBuilderHooks for APZCallbacks {
+ fn register(&self) {
+ unsafe { apz_register_updater(self.window_id) }
+ }
+
+ fn pre_scene_build(&self) {
+ gecko_profiler_start_marker("SceneBuilding");
+ }
+
+ fn pre_scene_swap(&self) {
+ unsafe {
+ apz_pre_scene_swap(self.window_id);
+ }
+ }
+
+ fn post_scene_swap(&self, _document_ids: &Vec<DocumentId>, info: PipelineInfo) {
+ let mut info = WrPipelineInfo::new(&info);
+ unsafe {
+ apz_post_scene_swap(self.window_id, &info);
+ }
+
+ // After a scene swap we should schedule a render for the next vsync,
+ // otherwise there's no guarantee that the new scene will get rendered
+ // anytime soon
+ unsafe { wr_finished_scene_build(self.window_id, &mut info) }
+ gecko_profiler_end_marker("SceneBuilding");
+ }
+
+ fn post_resource_update(&self, _document_ids: &Vec<DocumentId>) {
+ unsafe { wr_schedule_render(self.window_id, RenderReasons::POST_RESOURCE_UPDATES_HOOK) }
+ gecko_profiler_end_marker("SceneBuilding");
+ }
+
+ fn post_empty_scene_build(&self) {
+ gecko_profiler_end_marker("SceneBuilding");
+ }
+
+ fn poke(&self) {
+ unsafe { apz_run_updater(self.window_id) }
+ }
+
+ fn deregister(&self) {
+ unsafe { apz_deregister_updater(self.window_id) }
+ }
+}
+
+struct SamplerCallback {
+ window_id: WrWindowId,
+}
+
+impl SamplerCallback {
+ pub fn new(window_id: WrWindowId) -> Self {
+ SamplerCallback { window_id }
+ }
+}
+
+impl AsyncPropertySampler for SamplerCallback {
+ fn register(&self) {
+ unsafe {
+ apz_register_sampler(self.window_id);
+ omta_register_sampler(self.window_id);
+ }
+ }
+
+ fn sample(&self, _document_id: DocumentId, generated_frame_id: Option<u64>) -> Vec<FrameMsg> {
+ let generated_frame_id_value;
+ let generated_frame_id: *const u64 = match generated_frame_id {
+ Some(id) => {
+ generated_frame_id_value = id;
+ &generated_frame_id_value
+ },
+ None => ptr::null_mut(),
+ };
+ let mut transaction = Transaction::new();
+ // Reset the pending properties first because omta_sample and apz_sample_transforms
+ // may be failed to reset them due to null samplers.
+ transaction.reset_dynamic_properties();
+ unsafe {
+ apz_sample_transforms(self.window_id, generated_frame_id, &mut transaction);
+ omta_sample(self.window_id, &mut transaction);
+ };
+ transaction.get_frame_ops()
+ }
+
+ fn deregister(&self) {
+ unsafe {
+ apz_deregister_sampler(self.window_id);
+ omta_deregister_sampler(self.window_id);
+ }
+ }
+}
+
+extern "C" {
+ fn wr_register_thread_local_arena();
+}
+
+pub struct WrThreadPool(Arc<rayon::ThreadPool>);
+
+#[no_mangle]
+pub extern "C" fn wr_thread_pool_new(low_priority: bool) -> *mut WrThreadPool {
+ // Clamp the number of workers between 1 and 4/8. We get diminishing returns
+ // with high worker counts and extra overhead because of rayon and font
+ // management.
+
+ // We clamp to 4 high priority threads because contention and memory usage
+ // make it not worth going higher
+ let max = if low_priority { 8 } else { 4 };
+ let num_threads = num_cpus::get().min(max);
+
+ let priority_tag = if low_priority { "LP" } else { "" };
+
+ let worker = rayon::ThreadPoolBuilder::new()
+ .thread_name(move |idx| format!("WRWorker{}#{}", priority_tag, idx))
+ .num_threads(num_threads)
+ .start_handler(move |idx| {
+ unsafe {
+ wr_register_thread_local_arena();
+ }
+ let name = format!("WRWorker{}#{}", priority_tag, idx);
+ register_thread_with_profiler(name.clone());
+ gecko_profiler::register_thread(&name);
+ })
+ .exit_handler(|_idx| {
+ gecko_profiler::unregister_thread();
+ })
+ .build();
+
+ let workers = Arc::new(worker.unwrap());
+
+ Box::into_raw(Box::new(WrThreadPool(workers)))
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wr_thread_pool_delete(thread_pool: *mut WrThreadPool) {
+ mem::drop(Box::from_raw(thread_pool));
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wr_program_cache_new(
+ prof_path: &nsAString,
+ thread_pool: *mut WrThreadPool,
+) -> *mut WrProgramCache {
+ let workers = &(*thread_pool).0;
+ let program_cache = WrProgramCache::new(prof_path, workers);
+ Box::into_raw(Box::new(program_cache))
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wr_program_cache_delete(program_cache: *mut WrProgramCache) {
+ mem::drop(Box::from_raw(program_cache));
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wr_try_load_startup_shaders_from_disk(program_cache: *mut WrProgramCache) {
+ (*program_cache).try_load_startup_shaders_from_disk();
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn remove_program_binary_disk_cache(prof_path: &nsAString) -> bool {
+ match remove_disk_cache(prof_path) {
+ Ok(_) => true,
+ Err(_) => {
+ error!("Failed to remove program binary disk cache");
+ false
+ },
+ }
+}
+
+// This matches IsEnvSet in gfxEnv.h
+fn env_var_to_bool(key: &'static str) -> bool {
+ env::var(key).ok().map_or(false, |v| !v.is_empty())
+}
+
+// Call MakeCurrent before this.
+fn wr_device_new(gl_context: *mut c_void, pc: Option<&mut WrProgramCache>) -> Device {
+ assert!(unsafe { is_in_render_thread() });
+
+ let gl;
+ if unsafe { is_glcontext_gles(gl_context) } {
+ gl = unsafe { gl::GlesFns::load_with(|symbol| get_proc_address(gl_context, symbol)) };
+ } else {
+ gl = unsafe { gl::GlFns::load_with(|symbol| get_proc_address(gl_context, symbol)) };
+ }
+
+ let version = gl.get_string(gl::VERSION);
+
+ info!("WebRender - OpenGL version new {}", version);
+
+ let upload_method = if unsafe { is_glcontext_angle(gl_context) } {
+ UploadMethod::Immediate
+ } else {
+ UploadMethod::PixelBuffer(ONE_TIME_USAGE_HINT)
+ };
+
+ let resource_override_path = unsafe {
+ let override_charptr = gfx_wr_resource_path_override();
+ if override_charptr.is_null() {
+ None
+ } else {
+ match CStr::from_ptr(override_charptr).to_str() {
+ Ok(override_str) => Some(PathBuf::from(override_str)),
+ _ => None,
+ }
+ }
+ };
+
+ let use_optimized_shaders = unsafe { gfx_wr_use_optimized_shaders() };
+
+ let cached_programs = pc.map(|cached_programs| Rc::clone(cached_programs.rc_get()));
+
+ Device::new(
+ gl,
+ Some(Box::new(MozCrashAnnotator)),
+ resource_override_path,
+ use_optimized_shaders,
+ upload_method,
+ 512 * 512,
+ cached_programs,
+ true,
+ true,
+ None,
+ false,
+ false,
+ )
+}
+
+extern "C" {
+ fn wr_compositor_create_surface(
+ compositor: *mut c_void,
+ id: NativeSurfaceId,
+ virtual_offset: DeviceIntPoint,
+ tile_size: DeviceIntSize,
+ is_opaque: bool,
+ );
+ fn wr_compositor_create_external_surface(compositor: *mut c_void, id: NativeSurfaceId, is_opaque: bool);
+ fn wr_compositor_create_backdrop_surface(compositor: *mut c_void, id: NativeSurfaceId, color: ColorF);
+ fn wr_compositor_destroy_surface(compositor: *mut c_void, id: NativeSurfaceId);
+ fn wr_compositor_create_tile(compositor: *mut c_void, id: NativeSurfaceId, x: i32, y: i32);
+ fn wr_compositor_destroy_tile(compositor: *mut c_void, id: NativeSurfaceId, x: i32, y: i32);
+ fn wr_compositor_attach_external_image(
+ compositor: *mut c_void,
+ id: NativeSurfaceId,
+ external_image: ExternalImageId,
+ );
+ fn wr_compositor_bind(
+ compositor: *mut c_void,
+ id: NativeTileId,
+ offset: &mut DeviceIntPoint,
+ fbo_id: &mut u32,
+ dirty_rect: DeviceIntRect,
+ valid_rect: DeviceIntRect,
+ );
+ fn wr_compositor_unbind(compositor: *mut c_void);
+ fn wr_compositor_begin_frame(compositor: *mut c_void);
+ fn wr_compositor_add_surface(
+ compositor: *mut c_void,
+ id: NativeSurfaceId,
+ transform: &CompositorSurfaceTransform,
+ clip_rect: DeviceIntRect,
+ image_rendering: ImageRendering,
+ );
+ fn wr_compositor_start_compositing(
+ compositor: *mut c_void,
+ clear_color: ColorF,
+ dirty_rects: *const DeviceIntRect,
+ num_dirty_rects: usize,
+ opaque_rects: *const DeviceIntRect,
+ num_opaque_rects: usize,
+ );
+ fn wr_compositor_end_frame(compositor: *mut c_void);
+ fn wr_compositor_enable_native_compositor(compositor: *mut c_void, enable: bool);
+ fn wr_compositor_deinit(compositor: *mut c_void);
+ fn wr_compositor_get_capabilities(compositor: *mut c_void, caps: *mut CompositorCapabilities);
+ fn wr_compositor_get_window_visibility(compositor: *mut c_void, caps: *mut WindowVisibility);
+ fn wr_compositor_map_tile(
+ compositor: *mut c_void,
+ id: NativeTileId,
+ dirty_rect: DeviceIntRect,
+ valid_rect: DeviceIntRect,
+ data: &mut *mut c_void,
+ stride: &mut i32,
+ );
+ fn wr_compositor_unmap_tile(compositor: *mut c_void);
+
+ fn wr_partial_present_compositor_set_buffer_damage_region(
+ compositor: *mut c_void,
+ rects: *const DeviceIntRect,
+ n_rects: usize,
+ );
+}
+
+pub struct WrCompositor(*mut c_void);
+
+impl Compositor for WrCompositor {
+ fn create_surface(
+ &mut self,
+ _device: &mut Device,
+ id: NativeSurfaceId,
+ virtual_offset: DeviceIntPoint,
+ tile_size: DeviceIntSize,
+ is_opaque: bool,
+ ) {
+ unsafe {
+ wr_compositor_create_surface(self.0, id, virtual_offset, tile_size, is_opaque);
+ }
+ }
+
+ fn create_external_surface(&mut self, _device: &mut Device, id: NativeSurfaceId, is_opaque: bool) {
+ unsafe {
+ wr_compositor_create_external_surface(self.0, id, is_opaque);
+ }
+ }
+
+ fn create_backdrop_surface(&mut self, _device: &mut Device, id: NativeSurfaceId, color: ColorF) {
+ unsafe {
+ wr_compositor_create_backdrop_surface(self.0, id, color);
+ }
+ }
+
+ fn destroy_surface(&mut self, _device: &mut Device, id: NativeSurfaceId) {
+ unsafe {
+ wr_compositor_destroy_surface(self.0, id);
+ }
+ }
+
+ fn create_tile(&mut self, _device: &mut Device, id: NativeTileId) {
+ unsafe {
+ wr_compositor_create_tile(self.0, id.surface_id, id.x, id.y);
+ }
+ }
+
+ fn destroy_tile(&mut self, _device: &mut Device, id: NativeTileId) {
+ unsafe {
+ wr_compositor_destroy_tile(self.0, id.surface_id, id.x, id.y);
+ }
+ }
+
+ fn attach_external_image(&mut self, _device: &mut Device, id: NativeSurfaceId, external_image: ExternalImageId) {
+ unsafe {
+ wr_compositor_attach_external_image(self.0, id, external_image);
+ }
+ }
+
+ fn bind(
+ &mut self,
+ _device: &mut Device,
+ id: NativeTileId,
+ dirty_rect: DeviceIntRect,
+ valid_rect: DeviceIntRect,
+ ) -> NativeSurfaceInfo {
+ let mut surface_info = NativeSurfaceInfo {
+ origin: DeviceIntPoint::zero(),
+ fbo_id: 0,
+ };
+
+ unsafe {
+ wr_compositor_bind(
+ self.0,
+ id,
+ &mut surface_info.origin,
+ &mut surface_info.fbo_id,
+ dirty_rect,
+ valid_rect,
+ );
+ }
+
+ surface_info
+ }
+
+ fn unbind(&mut self, _device: &mut Device) {
+ unsafe {
+ wr_compositor_unbind(self.0);
+ }
+ }
+
+ fn begin_frame(&mut self, _device: &mut Device) {
+ unsafe {
+ wr_compositor_begin_frame(self.0);
+ }
+ }
+
+ fn add_surface(
+ &mut self,
+ _device: &mut Device,
+ id: NativeSurfaceId,
+ transform: CompositorSurfaceTransform,
+ clip_rect: DeviceIntRect,
+ image_rendering: ImageRendering,
+ ) {
+ unsafe {
+ wr_compositor_add_surface(self.0, id, &transform, clip_rect, image_rendering);
+ }
+ }
+
+ fn start_compositing(
+ &mut self,
+ _device: &mut Device,
+ clear_color: ColorF,
+ dirty_rects: &[DeviceIntRect],
+ opaque_rects: &[DeviceIntRect],
+ ) {
+ unsafe {
+ wr_compositor_start_compositing(
+ self.0,
+ clear_color,
+ dirty_rects.as_ptr(),
+ dirty_rects.len(),
+ opaque_rects.as_ptr(),
+ opaque_rects.len(),
+ );
+ }
+ }
+
+ fn end_frame(&mut self, _device: &mut Device) {
+ unsafe {
+ wr_compositor_end_frame(self.0);
+ }
+ }
+
+ fn enable_native_compositor(&mut self, _device: &mut Device, enable: bool) {
+ unsafe {
+ wr_compositor_enable_native_compositor(self.0, enable);
+ }
+ }
+
+ fn deinit(&mut self, _device: &mut Device) {
+ unsafe {
+ wr_compositor_deinit(self.0);
+ }
+ }
+
+ fn get_capabilities(&self, _device: &mut Device) -> CompositorCapabilities {
+ unsafe {
+ let mut caps: CompositorCapabilities = Default::default();
+ wr_compositor_get_capabilities(self.0, &mut caps);
+ caps
+ }
+ }
+
+ fn get_window_visibility(&self, _device: &mut Device) -> WindowVisibility {
+ unsafe {
+ let mut visibility: WindowVisibility = Default::default();
+ wr_compositor_get_window_visibility(self.0, &mut visibility);
+ visibility
+ }
+ }
+}
+
+extern "C" {
+ fn wr_swgl_lock_composite_surface(
+ ctx: *mut c_void,
+ external_image_id: ExternalImageId,
+ composite_info: *mut SWGLCompositeSurfaceInfo,
+ ) -> bool;
+ fn wr_swgl_unlock_composite_surface(ctx: *mut c_void, external_image_id: ExternalImageId);
+}
+
+impl MappableCompositor for WrCompositor {
+ /// Map a tile's underlying buffer so it can be used as the backing for
+ /// a SWGL framebuffer. This is intended to be a replacement for 'bind'
+ /// in any compositors that intend to directly interoperate with SWGL
+ /// while supporting some form of native layers.
+ fn map_tile(
+ &mut self,
+ _device: &mut Device,
+ id: NativeTileId,
+ dirty_rect: DeviceIntRect,
+ valid_rect: DeviceIntRect,
+ ) -> Option<MappedTileInfo> {
+ let mut tile_info = MappedTileInfo {
+ data: ptr::null_mut(),
+ stride: 0,
+ };
+
+ unsafe {
+ wr_compositor_map_tile(
+ self.0,
+ id,
+ dirty_rect,
+ valid_rect,
+ &mut tile_info.data,
+ &mut tile_info.stride,
+ );
+ }
+
+ if !tile_info.data.is_null() && tile_info.stride != 0 {
+ Some(tile_info)
+ } else {
+ None
+ }
+ }
+
+ /// Unmap a tile that was was previously mapped via map_tile to signal
+ /// that SWGL is done rendering to the buffer.
+ fn unmap_tile(&mut self, _device: &mut Device) {
+ unsafe {
+ wr_compositor_unmap_tile(self.0);
+ }
+ }
+
+ fn lock_composite_surface(
+ &mut self,
+ _device: &mut Device,
+ ctx: *mut c_void,
+ external_image_id: ExternalImageId,
+ composite_info: *mut SWGLCompositeSurfaceInfo,
+ ) -> bool {
+ unsafe { wr_swgl_lock_composite_surface(ctx, external_image_id, composite_info) }
+ }
+ fn unlock_composite_surface(&mut self, _device: &mut Device, ctx: *mut c_void, external_image_id: ExternalImageId) {
+ unsafe { wr_swgl_unlock_composite_surface(ctx, external_image_id) }
+ }
+}
+
+pub struct WrPartialPresentCompositor(*mut c_void);
+
+impl PartialPresentCompositor for WrPartialPresentCompositor {
+ fn set_buffer_damage_region(&mut self, rects: &[DeviceIntRect]) {
+ unsafe {
+ wr_partial_present_compositor_set_buffer_damage_region(self.0, rects.as_ptr(), rects.len());
+ }
+ }
+}
+
+/// A wrapper around a strong reference to a Shaders object.
+pub struct WrShaders(SharedShaders);
+
+// Call MakeCurrent before this.
+#[no_mangle]
+pub extern "C" fn wr_window_new(
+ window_id: WrWindowId,
+ window_width: i32,
+ window_height: i32,
+ is_main_window: bool,
+ support_low_priority_transactions: bool,
+ support_low_priority_threadpool: bool,
+ allow_texture_swizzling: bool,
+ allow_scissored_cache_clears: bool,
+ swgl_context: *mut c_void,
+ gl_context: *mut c_void,
+ surface_origin_is_top_left: bool,
+ program_cache: Option<&mut WrProgramCache>,
+ shaders: Option<&mut WrShaders>,
+ thread_pool: *mut WrThreadPool,
+ thread_pool_low_priority: *mut WrThreadPool,
+ size_of_op: VoidPtrToSizeFn,
+ enclosing_size_of_op: VoidPtrToSizeFn,
+ document_id: u32,
+ compositor: *mut c_void,
+ use_native_compositor: bool,
+ use_partial_present: bool,
+ max_partial_present_rects: usize,
+ draw_previous_partial_present_regions: bool,
+ out_handle: &mut *mut DocumentHandle,
+ out_renderer: &mut *mut Renderer,
+ out_max_texture_size: *mut i32,
+ out_err: &mut *mut c_char,
+ enable_gpu_markers: bool,
+ panic_on_gl_error: bool,
+ picture_tile_width: i32,
+ picture_tile_height: i32,
+ reject_software_rasterizer: bool,
+ low_quality_pinch_zoom: bool,
+ max_shared_surface_size: i32,
+) -> bool {
+ assert!(unsafe { is_in_render_thread() });
+
+ // Ensure the WR profiler callbacks are hooked up to the Gecko profiler.
+ set_profiler_hooks(Some(&PROFILER_HOOKS));
+
+ let software = !swgl_context.is_null();
+ let (gl, sw_gl) = if software {
+ let ctx = swgl::Context::from(swgl_context);
+ ctx.make_current();
+ (Rc::new(ctx) as Rc<dyn gl::Gl>, Some(ctx))
+ } else {
+ let gl = unsafe {
+ if gl_context.is_null() {
+ panic!("Native GL context required when not using SWGL!");
+ } else if is_glcontext_gles(gl_context) {
+ gl::GlesFns::load_with(|symbol| get_proc_address(gl_context, symbol))
+ } else {
+ gl::GlFns::load_with(|symbol| get_proc_address(gl_context, symbol))
+ }
+ };
+ (gl, None)
+ };
+
+ let version = gl.get_string(gl::VERSION);
+
+ info!("WebRender - OpenGL version new {}", version);
+
+ let workers = unsafe { Arc::clone(&(*thread_pool).0) };
+ let workers_low_priority = unsafe {
+ if support_low_priority_threadpool {
+ Arc::clone(&(*thread_pool_low_priority).0)
+ } else {
+ Arc::clone(&(*thread_pool).0)
+ }
+ };
+
+ let upload_method = if !gl_context.is_null() && unsafe { is_glcontext_angle(gl_context) } {
+ UploadMethod::Immediate
+ } else {
+ UploadMethod::PixelBuffer(ONE_TIME_USAGE_HINT)
+ };
+
+ let precache_flags = if env_var_to_bool("MOZ_WR_PRECACHE_SHADERS") {
+ ShaderPrecacheFlags::FULL_COMPILE
+ } else {
+ ShaderPrecacheFlags::empty()
+ };
+
+ let cached_programs = program_cache.map(|program_cache| Rc::clone(&program_cache.rc_get()));
+
+ let color = if cfg!(target_os = "android") {
+ // The color is for avoiding black flash before receiving display list.
+ ColorF::new(1.0, 1.0, 1.0, 1.0)
+ } else {
+ ColorF::new(0.0, 0.0, 0.0, 0.0)
+ };
+
+ let compositor_config = if software {
+ CompositorConfig::Native {
+ compositor: Box::new(SwCompositor::new(
+ sw_gl.unwrap(),
+ Box::new(WrCompositor(compositor)),
+ use_native_compositor,
+ )),
+ }
+ } else if use_native_compositor {
+ CompositorConfig::Native {
+ compositor: Box::new(WrCompositor(compositor)),
+ }
+ } else {
+ CompositorConfig::Draw {
+ max_partial_present_rects,
+ draw_previous_partial_present_regions,
+ partial_present: if use_partial_present {
+ Some(Box::new(WrPartialPresentCompositor(compositor)))
+ } else {
+ None
+ },
+ }
+ };
+
+ let picture_tile_size = if picture_tile_width > 0 && picture_tile_height > 0 {
+ Some(DeviceIntSize::new(picture_tile_width, picture_tile_height))
+ } else {
+ None
+ };
+
+ let texture_cache_config = if is_main_window {
+ TextureCacheConfig::DEFAULT
+ } else {
+ TextureCacheConfig {
+ color8_linear_texture_size: 512,
+ color8_nearest_texture_size: 512,
+ color8_glyph_texture_size: 512,
+ alpha8_texture_size: 512,
+ alpha8_glyph_texture_size: 512,
+ alpha16_texture_size: 512,
+ }
+ };
+
+ let opts = WebRenderOptions {
+ enable_aa: true,
+ enable_subpixel_aa: cfg!(not(target_os = "android")),
+ support_low_priority_transactions,
+ allow_texture_swizzling,
+ blob_image_handler: Some(Box::new(Moz2dBlobImageHandler::new(
+ workers.clone(),
+ workers_low_priority,
+ ))),
+ crash_annotator: Some(Box::new(MozCrashAnnotator)),
+ workers: Some(workers),
+ size_of_op: Some(size_of_op),
+ enclosing_size_of_op: Some(enclosing_size_of_op),
+ cached_programs,
+ resource_override_path: unsafe {
+ let override_charptr = gfx_wr_resource_path_override();
+ if override_charptr.is_null() {
+ None
+ } else {
+ match CStr::from_ptr(override_charptr).to_str() {
+ Ok(override_str) => Some(PathBuf::from(override_str)),
+ _ => None,
+ }
+ }
+ },
+ use_optimized_shaders: unsafe { gfx_wr_use_optimized_shaders() },
+ renderer_id: Some(window_id.0),
+ upload_method,
+ scene_builder_hooks: Some(Box::new(APZCallbacks::new(window_id))),
+ sampler: Some(Box::new(SamplerCallback::new(window_id))),
+ max_internal_texture_size: Some(8192), // We want to tile if larger than this
+ clear_color: color,
+ precache_flags,
+ namespace_alloc_by_client: true,
+ // Font namespace must be allocated by the client
+ shared_font_namespace: Some(next_namespace_id()),
+ // SWGL doesn't support the GL_ALWAYS depth comparison function used by
+ // `clear_caches_with_quads`, but scissored clears work well.
+ clear_caches_with_quads: !software && !allow_scissored_cache_clears,
+ // SWGL supports KHR_blend_equation_advanced safely, but we haven't yet
+ // tested other HW platforms determine if it is safe to allow them.
+ allow_advanced_blend_equation: software,
+ surface_origin_is_top_left,
+ compositor_config,
+ enable_gpu_markers,
+ panic_on_gl_error,
+ picture_tile_size,
+ texture_cache_config,
+ reject_software_rasterizer,
+ low_quality_pinch_zoom,
+ max_shared_surface_size,
+ ..Default::default()
+ };
+
+ let window_size = DeviceIntSize::new(window_width, window_height);
+ let notifier = Box::new(CppNotifier { window_id });
+ let (renderer, sender) = match create_webrender_instance(gl, notifier, opts, shaders.map(|sh| &sh.0)) {
+ Ok((renderer, sender)) => (renderer, sender),
+ Err(e) => {
+ warn!(" Failed to create a Renderer: {:?}", e);
+ let msg = CString::new(format!("wr_window_new: {:?}", e)).unwrap();
+ unsafe {
+ gfx_critical_note(msg.as_ptr());
+ }
+ *out_err = msg.into_raw();
+ return false;
+ },
+ };
+
+ unsafe {
+ *out_max_texture_size = renderer.get_max_texture_size();
+ }
+ *out_handle = Box::into_raw(Box::new(DocumentHandle::new(
+ sender.create_api_by_client(next_namespace_id()),
+ None,
+ window_size,
+ document_id,
+ )));
+ *out_renderer = Box::into_raw(Box::new(renderer));
+
+ true
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wr_api_free_error_msg(msg: *mut c_char) {
+ if !msg.is_null() {
+ drop(CString::from_raw(msg));
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wr_api_delete_document(dh: &mut DocumentHandle) {
+ dh.api.delete_document(dh.document_id);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_api_clone(dh: &mut DocumentHandle, out_handle: &mut *mut DocumentHandle) {
+ assert!(unsafe { is_in_compositor_thread() });
+
+ let hit_tester = dh.ensure_hit_tester().clone();
+
+ let handle = DocumentHandle {
+ api: dh.api.create_sender().create_api_by_client(next_namespace_id()),
+ document_id: dh.document_id,
+ hit_tester: Some(hit_tester),
+ hit_tester_request: None,
+ };
+ *out_handle = Box::into_raw(Box::new(handle));
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wr_api_delete(dh: *mut DocumentHandle) {
+ let _ = Box::from_raw(dh);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wr_api_stop_render_backend(dh: &mut DocumentHandle) {
+ dh.api.stop_render_backend();
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wr_api_shut_down(dh: &mut DocumentHandle) {
+ dh.api.shut_down(true);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wr_api_notify_memory_pressure(dh: &mut DocumentHandle) {
+ dh.api.notify_memory_pressure();
+}
+
+#[no_mangle]
+pub extern "C" fn wr_api_set_debug_flags(dh: &mut DocumentHandle, flags: DebugFlags) {
+ dh.api.set_debug_flags(flags);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_api_set_bool(dh: &mut DocumentHandle, param_name: BoolParameter, val: bool) {
+ dh.api.set_parameter(Parameter::Bool(param_name, val));
+}
+
+#[no_mangle]
+pub extern "C" fn wr_api_set_int(dh: &mut DocumentHandle, param_name: IntParameter, val: i32) {
+ dh.api.set_parameter(Parameter::Int(param_name, val));
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wr_api_accumulate_memory_report(
+ dh: &mut DocumentHandle,
+ report: &mut MemoryReport,
+ // we manually expand VoidPtrToSizeFn here because cbindgen otherwise fails to fold the Option<fn()>
+ // https://github.com/eqrion/cbindgen/issues/552
+ size_of_op: unsafe extern "C" fn(ptr: *const c_void) -> usize,
+ enclosing_size_of_op: Option<unsafe extern "C" fn(ptr: *const c_void) -> usize>,
+) {
+ let ops = MallocSizeOfOps::new(size_of_op, enclosing_size_of_op);
+ *report += dh.api.report_memory(ops);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wr_api_clear_all_caches(dh: &mut DocumentHandle) {
+ dh.api.send_debug_cmd(DebugCommand::ClearCaches(ClearCache::all()));
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wr_api_enable_native_compositor(dh: &mut DocumentHandle, enable: bool) {
+ dh.api.send_debug_cmd(DebugCommand::EnableNativeCompositor(enable));
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wr_api_set_batching_lookback(dh: &mut DocumentHandle, count: u32) {
+ dh.api.send_debug_cmd(DebugCommand::SetBatchingLookback(count));
+}
+
+fn make_transaction(do_async: bool) -> Transaction {
+ let mut transaction = Transaction::new();
+ // Ensure that we either use async scene building or not based on the
+ // gecko pref, regardless of what the default is. We can remove this once
+ // the scene builder thread is enabled everywhere and working well.
+ if do_async {
+ transaction.use_scene_builder_thread();
+ } else {
+ transaction.skip_scene_builder();
+ }
+ transaction
+}
+
+#[no_mangle]
+pub extern "C" fn wr_transaction_new(do_async: bool) -> *mut Transaction {
+ Box::into_raw(Box::new(make_transaction(do_async)))
+}
+
+#[no_mangle]
+pub extern "C" fn wr_transaction_delete(txn: *mut Transaction) {
+ unsafe {
+ let _ = Box::from_raw(txn);
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn wr_transaction_set_low_priority(txn: &mut Transaction, low_priority: bool) {
+ txn.set_low_priority(low_priority);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_transaction_is_empty(txn: &Transaction) -> bool {
+ txn.is_empty()
+}
+
+#[no_mangle]
+pub extern "C" fn wr_transaction_resource_updates_is_empty(txn: &Transaction) -> bool {
+ txn.resource_updates.is_empty()
+}
+
+#[no_mangle]
+pub extern "C" fn wr_transaction_is_rendered_frame_invalidated(txn: &Transaction) -> bool {
+ txn.invalidate_rendered_frame
+}
+
+#[no_mangle]
+pub extern "C" fn wr_transaction_notify(txn: &mut Transaction, when: Checkpoint, event: usize) {
+ struct GeckoNotification(usize);
+ impl NotificationHandler for GeckoNotification {
+ fn notify(&self, when: Checkpoint) {
+ unsafe {
+ wr_transaction_notification_notified(self.0, when);
+ }
+ }
+ }
+
+ let handler = Box::new(GeckoNotification(event));
+ txn.notify(NotificationRequest::new(when, handler));
+}
+
+#[no_mangle]
+pub extern "C" fn wr_transaction_update_epoch(txn: &mut Transaction, pipeline_id: WrPipelineId, epoch: WrEpoch) {
+ txn.update_epoch(pipeline_id, epoch);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_transaction_set_root_pipeline(txn: &mut Transaction, pipeline_id: WrPipelineId) {
+ txn.set_root_pipeline(pipeline_id);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_transaction_remove_pipeline(txn: &mut Transaction, pipeline_id: WrPipelineId) {
+ txn.remove_pipeline(pipeline_id);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_transaction_set_display_list(
+ txn: &mut Transaction,
+ epoch: WrEpoch,
+ pipeline_id: WrPipelineId,
+ dl_descriptor: BuiltDisplayListDescriptor,
+ dl_items_data: &mut WrVecU8,
+ dl_cache_data: &mut WrVecU8,
+ dl_spatial_tree_data: &mut WrVecU8,
+) {
+ let payload = DisplayListPayload {
+ items_data: dl_items_data.flush_into_vec(),
+ cache_data: dl_cache_data.flush_into_vec(),
+ spatial_tree: dl_spatial_tree_data.flush_into_vec(),
+ };
+
+ let dl = BuiltDisplayList::from_data(payload, dl_descriptor);
+
+ txn.set_display_list(epoch, (pipeline_id, dl));
+}
+
+#[no_mangle]
+pub extern "C" fn wr_transaction_set_document_view(txn: &mut Transaction, doc_rect: &DeviceIntRect) {
+ txn.set_document_view(*doc_rect);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_transaction_generate_frame(txn: &mut Transaction, id: u64, reasons: RenderReasons) {
+ txn.generate_frame(id, reasons);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_transaction_invalidate_rendered_frame(txn: &mut Transaction, reasons: RenderReasons) {
+ txn.invalidate_rendered_frame(reasons);
+}
+
+fn wr_animation_properties_into_vec<T>(
+ animation_array: *const WrAnimationPropertyValue<T>,
+ array_count: usize,
+ vec: &mut Vec<PropertyValue<T>>,
+) where
+ T: Copy,
+{
+ if array_count > 0 {
+ debug_assert!(
+ vec.capacity() - vec.len() >= array_count,
+ "The Vec should have fufficient free capacity"
+ );
+ let slice = unsafe { make_slice(animation_array, array_count) };
+
+ for element in slice.iter() {
+ let prop = PropertyValue {
+ key: PropertyBindingKey::new(element.id),
+ value: element.value,
+ };
+
+ vec.push(prop);
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn wr_transaction_append_dynamic_properties(
+ txn: &mut Transaction,
+ opacity_array: *const WrOpacityProperty,
+ opacity_count: usize,
+ transform_array: *const WrTransformProperty,
+ transform_count: usize,
+ color_array: *const WrColorProperty,
+ color_count: usize,
+) {
+ if opacity_count == 0 && transform_count == 0 && color_count == 0 {
+ return;
+ }
+
+ let mut properties = DynamicProperties {
+ transforms: Vec::with_capacity(transform_count),
+ floats: Vec::with_capacity(opacity_count),
+ colors: Vec::with_capacity(color_count),
+ };
+
+ wr_animation_properties_into_vec(transform_array, transform_count, &mut properties.transforms);
+
+ wr_animation_properties_into_vec(opacity_array, opacity_count, &mut properties.floats);
+
+ wr_animation_properties_into_vec(color_array, color_count, &mut properties.colors);
+
+ txn.append_dynamic_properties(properties);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_transaction_append_transform_properties(
+ txn: &mut Transaction,
+ transform_array: *const WrTransformProperty,
+ transform_count: usize,
+) {
+ if transform_count == 0 {
+ return;
+ }
+
+ let mut transforms = Vec::with_capacity(transform_count);
+ wr_animation_properties_into_vec(transform_array, transform_count, &mut transforms);
+
+ txn.append_dynamic_transform_properties(transforms);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_transaction_scroll_layer(
+ txn: &mut Transaction,
+ pipeline_id: WrPipelineId,
+ scroll_id: u64,
+ sampled_scroll_offsets: &ThinVec<SampledScrollOffset>,
+) {
+ let scroll_id = ExternalScrollId(scroll_id, pipeline_id);
+ txn.set_scroll_offsets(scroll_id, sampled_scroll_offsets.to_vec());
+}
+
+#[no_mangle]
+pub extern "C" fn wr_transaction_set_is_transform_async_zooming(
+ txn: &mut Transaction,
+ animation_id: u64,
+ is_zooming: bool,
+) {
+ txn.set_is_transform_async_zooming(is_zooming, PropertyBindingId::new(animation_id));
+}
+
+#[no_mangle]
+pub extern "C" fn wr_transaction_set_quality_settings(txn: &mut Transaction, force_subpixel_aa_where_possible: bool) {
+ txn.set_quality_settings(QualitySettings {
+ force_subpixel_aa_where_possible,
+ });
+}
+
+#[no_mangle]
+pub extern "C" fn wr_resource_updates_add_image(
+ txn: &mut Transaction,
+ image_key: WrImageKey,
+ descriptor: &WrImageDescriptor,
+ bytes: &mut WrVecU8,
+) {
+ txn.add_image(
+ image_key,
+ descriptor.into(),
+ ImageData::new(bytes.flush_into_vec()),
+ None,
+ );
+}
+
+#[no_mangle]
+pub extern "C" fn wr_resource_updates_add_blob_image(
+ txn: &mut Transaction,
+ image_key: BlobImageKey,
+ descriptor: &WrImageDescriptor,
+ tile_size: u16,
+ bytes: &mut WrVecU8,
+ visible_rect: DeviceIntRect,
+) {
+ txn.add_blob_image(
+ image_key,
+ descriptor.into(),
+ Arc::new(bytes.flush_into_vec()),
+ visible_rect,
+ if descriptor.format == ImageFormat::BGRA8 {
+ Some(tile_size)
+ } else {
+ None
+ },
+ );
+}
+
+#[no_mangle]
+pub extern "C" fn wr_resource_updates_add_external_image(
+ txn: &mut Transaction,
+ image_key: WrImageKey,
+ descriptor: &WrImageDescriptor,
+ external_image_id: ExternalImageId,
+ image_type: &ExternalImageType,
+ channel_index: u8,
+) {
+ txn.add_image(
+ image_key,
+ descriptor.into(),
+ ImageData::External(ExternalImageData {
+ id: external_image_id,
+ channel_index,
+ image_type: *image_type,
+ }),
+ None,
+ );
+}
+
+#[no_mangle]
+pub extern "C" fn wr_resource_updates_update_image(
+ txn: &mut Transaction,
+ key: WrImageKey,
+ descriptor: &WrImageDescriptor,
+ bytes: &mut WrVecU8,
+) {
+ txn.update_image(
+ key,
+ descriptor.into(),
+ ImageData::new(bytes.flush_into_vec()),
+ &DirtyRect::All,
+ );
+}
+
+#[no_mangle]
+pub extern "C" fn wr_resource_updates_set_blob_image_visible_area(
+ txn: &mut Transaction,
+ key: BlobImageKey,
+ area: &DeviceIntRect,
+) {
+ txn.set_blob_image_visible_area(key, *area);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_resource_updates_update_external_image(
+ txn: &mut Transaction,
+ key: WrImageKey,
+ descriptor: &WrImageDescriptor,
+ external_image_id: ExternalImageId,
+ image_type: &ExternalImageType,
+ channel_index: u8,
+) {
+ txn.update_image(
+ key,
+ descriptor.into(),
+ ImageData::External(ExternalImageData {
+ id: external_image_id,
+ channel_index,
+ image_type: *image_type,
+ }),
+ &DirtyRect::All,
+ );
+}
+
+#[no_mangle]
+pub extern "C" fn wr_resource_updates_update_external_image_with_dirty_rect(
+ txn: &mut Transaction,
+ key: WrImageKey,
+ descriptor: &WrImageDescriptor,
+ external_image_id: ExternalImageId,
+ image_type: &ExternalImageType,
+ channel_index: u8,
+ dirty_rect: DeviceIntRect,
+) {
+ txn.update_image(
+ key,
+ descriptor.into(),
+ ImageData::External(ExternalImageData {
+ id: external_image_id,
+ channel_index,
+ image_type: *image_type,
+ }),
+ &DirtyRect::Partial(dirty_rect),
+ );
+}
+
+#[no_mangle]
+pub extern "C" fn wr_resource_updates_update_blob_image(
+ txn: &mut Transaction,
+ image_key: BlobImageKey,
+ descriptor: &WrImageDescriptor,
+ bytes: &mut WrVecU8,
+ visible_rect: DeviceIntRect,
+ dirty_rect: LayoutIntRect,
+) {
+ txn.update_blob_image(
+ image_key,
+ descriptor.into(),
+ Arc::new(bytes.flush_into_vec()),
+ visible_rect,
+ &DirtyRect::Partial(dirty_rect),
+ );
+}
+
+#[no_mangle]
+pub extern "C" fn wr_resource_updates_delete_image(txn: &mut Transaction, key: WrImageKey) {
+ txn.delete_image(key);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_resource_updates_delete_blob_image(txn: &mut Transaction, key: BlobImageKey) {
+ txn.delete_blob_image(key);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_api_send_transaction(dh: &mut DocumentHandle, transaction: &mut Transaction, is_async: bool) {
+ if transaction.is_empty() {
+ return;
+ }
+ let new_txn = make_transaction(is_async);
+ let txn = mem::replace(transaction, new_txn);
+ dh.api.send_transaction(dh.document_id, txn);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wr_transaction_clear_display_list(
+ txn: &mut Transaction,
+ epoch: WrEpoch,
+ pipeline_id: WrPipelineId,
+) {
+ let mut frame_builder = WebRenderFrameBuilder::new(pipeline_id);
+ frame_builder.dl_builder.begin();
+
+ txn.set_display_list(epoch, frame_builder.dl_builder.end());
+}
+
+#[no_mangle]
+pub extern "C" fn wr_api_send_external_event(dh: &mut DocumentHandle, evt: usize) {
+ assert!(unsafe { !is_in_render_thread() });
+
+ dh.api.send_external_event(ExternalEvent::from_raw(evt));
+}
+
+#[no_mangle]
+pub extern "C" fn wr_resource_updates_add_raw_font(
+ txn: &mut Transaction,
+ key: WrFontKey,
+ bytes: &mut WrVecU8,
+ index: u32,
+) {
+ txn.add_raw_font(key, bytes.flush_into_vec(), index);
+}
+
+fn generate_capture_path(path: *const c_char) -> Option<PathBuf> {
+ use std::fs::{create_dir_all, File};
+ use std::io::Write;
+
+ let cstr = unsafe { CStr::from_ptr(path) };
+ let local_dir = PathBuf::from(&*cstr.to_string_lossy());
+
+ // On Android we need to write into a particular folder on external
+ // storage so that (a) it can be written without requiring permissions
+ // and (b) it can be pulled off via `adb pull`. This env var is set
+ // in GeckoLoader.java.
+ // When running in Firefox CI, the MOZ_UPLOAD_DIR variable is set to a path
+ // that taskcluster will export artifacts from, so let's put it there.
+ let mut path = if let Ok(storage_path) = env::var("PUBLIC_STORAGE") {
+ PathBuf::from(storage_path).join(local_dir)
+ } else if let Ok(storage_path) = env::var("MOZ_UPLOAD_DIR") {
+ PathBuf::from(storage_path).join(local_dir)
+ } else if let Some(storage_path) = dirs::home_dir() {
+ storage_path.join(local_dir)
+ } else {
+ local_dir
+ };
+
+ // Increment the extension until we find a fresh path
+ while path.is_dir() {
+ let count: u32 = path
+ .extension()
+ .and_then(|x| x.to_str())
+ .and_then(|x| x.parse().ok())
+ .unwrap_or(0);
+ path.set_extension((count + 1).to_string());
+ }
+
+ // Use warn! so that it gets emitted to logcat on android as well
+ let border = "--------------------------\n";
+ warn!("{} Capturing WR state to: {:?}\n{}", &border, &path, &border);
+
+ let _ = create_dir_all(&path);
+ match File::create(path.join("wr.txt")) {
+ Ok(mut file) => {
+ // The Gecko HG revision is available at compile time
+ if let Some(moz_revision) = option_env!("GECKO_HEAD_REV") {
+ writeln!(file, "mozilla-central {}", moz_revision).unwrap();
+ }
+ Some(path)
+ },
+ Err(e) => {
+ warn!("Unable to create path '{:?}' for capture: {:?}", path, e);
+ None
+ },
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn wr_api_capture(dh: &mut DocumentHandle, path: *const c_char, bits_raw: u32) {
+ if let Some(path) = generate_capture_path(path) {
+ let bits = CaptureBits::from_bits(bits_raw as _).unwrap();
+ dh.api.save_capture(path, bits);
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn wr_api_start_capture_sequence(dh: &mut DocumentHandle, path: *const c_char, bits_raw: u32) {
+ if let Some(path) = generate_capture_path(path) {
+ let bits = CaptureBits::from_bits(bits_raw as _).unwrap();
+ dh.api.start_capture_sequence(path, bits);
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn wr_api_stop_capture_sequence(dh: &mut DocumentHandle) {
+ let border = "--------------------------\n";
+ warn!("{} Stop capturing WR state\n{}", &border, &border);
+ dh.api.stop_capture_sequence();
+}
+
+#[cfg(target_os = "windows")]
+fn read_font_descriptor(bytes: &mut WrVecU8, index: u32) -> NativeFontHandle {
+ let wchars = bytes.convert_into_vec::<u16>();
+ NativeFontHandle {
+ path: PathBuf::from(OsString::from_wide(&wchars)),
+ index,
+ }
+}
+
+#[cfg(target_os = "macos")]
+fn read_font_descriptor(bytes: &mut WrVecU8, _index: u32) -> NativeFontHandle {
+ let chars = bytes.flush_into_vec();
+ NativeFontHandle {
+ name: String::from_utf8(chars).unwrap(),
+ }
+}
+
+#[cfg(not(any(target_os = "macos", target_os = "windows")))]
+fn read_font_descriptor(bytes: &mut WrVecU8, index: u32) -> NativeFontHandle {
+ let chars = bytes.flush_into_vec();
+ NativeFontHandle {
+ path: PathBuf::from(OsString::from_vec(chars)),
+ index,
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn wr_resource_updates_add_font_descriptor(
+ txn: &mut Transaction,
+ key: WrFontKey,
+ bytes: &mut WrVecU8,
+ index: u32,
+) {
+ let native_font_handle = read_font_descriptor(bytes, index);
+ txn.add_native_font(key, native_font_handle);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_resource_updates_delete_font(txn: &mut Transaction, key: WrFontKey) {
+ txn.delete_font(key);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_resource_updates_add_font_instance(
+ txn: &mut Transaction,
+ key: WrFontInstanceKey,
+ font_key: WrFontKey,
+ glyph_size: f32,
+ options: *const FontInstanceOptions,
+ platform_options: *const FontInstancePlatformOptions,
+ variations: &mut WrVecU8,
+) {
+ txn.add_font_instance(
+ key,
+ font_key,
+ glyph_size,
+ unsafe { options.as_ref().cloned() },
+ unsafe { platform_options.as_ref().cloned() },
+ variations.convert_into_vec::<FontVariation>(),
+ );
+}
+
+#[no_mangle]
+pub extern "C" fn wr_resource_updates_delete_font_instance(txn: &mut Transaction, key: WrFontInstanceKey) {
+ txn.delete_font_instance(key);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_resource_updates_clear(txn: &mut Transaction) {
+ txn.resource_updates.clear();
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wr_api_get_namespace(dh: &mut DocumentHandle) -> WrIdNamespace {
+ dh.api.get_namespace_id()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wr_api_wake_scene_builder(dh: &mut DocumentHandle) {
+ dh.api.wake_scene_builder();
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wr_api_flush_scene_builder(dh: &mut DocumentHandle) {
+ dh.api.flush_scene_builder();
+}
+
+// RenderThread WIP notes:
+// In order to separate the compositor thread (or ipc receiver) and the render
+// thread, some of the logic below needs to be rewritten. In particular
+// the WrWindowState and Notifier implementations aren't designed to work with
+// a separate render thread.
+// As part of that I am moving the bindings closer to WebRender's API boundary,
+// and moving more of the logic in C++ land.
+// This work is tracked by bug 1328602.
+//
+// See RenderThread.h for some notes about how the pieces fit together.
+
+pub struct WebRenderFrameBuilder {
+ pub root_pipeline_id: WrPipelineId,
+ pub dl_builder: DisplayListBuilder,
+}
+
+impl WebRenderFrameBuilder {
+ pub fn new(root_pipeline_id: WrPipelineId) -> WebRenderFrameBuilder {
+ WebRenderFrameBuilder {
+ root_pipeline_id,
+ dl_builder: DisplayListBuilder::new(root_pipeline_id),
+ }
+ }
+}
+
+pub struct WrState {
+ pipeline_id: WrPipelineId,
+ frame_builder: WebRenderFrameBuilder,
+}
+
+#[no_mangle]
+pub extern "C" fn wr_state_new(pipeline_id: WrPipelineId) -> *mut WrState {
+ assert!(unsafe { !is_in_render_thread() });
+
+ let state = Box::new(WrState {
+ pipeline_id,
+ frame_builder: WebRenderFrameBuilder::new(pipeline_id),
+ });
+
+ Box::into_raw(state)
+}
+
+#[no_mangle]
+pub extern "C" fn wr_state_delete(state: *mut WrState) {
+ assert!(unsafe { !is_in_render_thread() });
+
+ unsafe {
+ mem::drop(Box::from_raw(state));
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_save(state: &mut WrState) {
+ state.frame_builder.dl_builder.save();
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_restore(state: &mut WrState) {
+ state.frame_builder.dl_builder.restore();
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_clear_save(state: &mut WrState) {
+ state.frame_builder.dl_builder.clear_save();
+}
+
+#[repr(u8)]
+#[derive(PartialEq, Eq, Debug)]
+pub enum WrReferenceFrameKind {
+ Transform,
+ Perspective,
+}
+
+#[repr(u8)]
+#[derive(PartialEq, Eq, Debug)]
+pub enum WrRotation {
+ Degree0,
+ Degree90,
+ Degree180,
+ Degree270,
+}
+
+/// IMPORTANT: If you add fields to this struct, you need to also add initializers
+/// for those fields in WebRenderAPI.h.
+#[repr(C)]
+pub struct WrStackingContextParams {
+ pub clip: WrStackingContextClip,
+ pub animation: *const WrAnimationProperty,
+ pub opacity: *const f32,
+ pub computed_transform: *const WrComputedTransformData,
+ pub transform_style: TransformStyle,
+ pub reference_frame_kind: WrReferenceFrameKind,
+ pub is_2d_scale_translation: bool,
+ pub should_snap: bool,
+ pub paired_with_perspective: bool,
+ pub scrolling_relative_to: *const u64,
+ pub prim_flags: PrimitiveFlags,
+ pub mix_blend_mode: MixBlendMode,
+ pub flags: StackingContextFlags,
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_push_stacking_context(
+ state: &mut WrState,
+ bounds: LayoutRect,
+ spatial_id: WrSpatialId,
+ params: &WrStackingContextParams,
+ transform: *const WrTransformInfo,
+ filters: *const FilterOp,
+ filter_count: usize,
+ filter_datas: *const WrFilterData,
+ filter_datas_count: usize,
+ glyph_raster_space: RasterSpace,
+) -> WrSpatialId {
+ debug_assert!(unsafe { !is_in_render_thread() });
+
+ let c_filters = unsafe { make_slice(filters, filter_count) };
+ let mut filters: Vec<FilterOp> = c_filters.iter().copied().collect();
+
+ let c_filter_datas = unsafe { make_slice(filter_datas, filter_datas_count) };
+ let r_filter_datas: Vec<FilterData> = c_filter_datas
+ .iter()
+ .map(|c_filter_data| FilterData {
+ func_r_type: c_filter_data.funcR_type,
+ r_values: unsafe { make_slice(c_filter_data.R_values, c_filter_data.R_values_count).to_vec() },
+ func_g_type: c_filter_data.funcG_type,
+ g_values: unsafe { make_slice(c_filter_data.G_values, c_filter_data.G_values_count).to_vec() },
+ func_b_type: c_filter_data.funcB_type,
+ b_values: unsafe { make_slice(c_filter_data.B_values, c_filter_data.B_values_count).to_vec() },
+ func_a_type: c_filter_data.funcA_type,
+ a_values: unsafe { make_slice(c_filter_data.A_values, c_filter_data.A_values_count).to_vec() },
+ })
+ .collect();
+
+ let transform_ref = unsafe { transform.as_ref() };
+ let mut transform_binding = transform_ref.map(|info| (PropertyBinding::Value(info.transform), info.key));
+
+ let computed_ref = unsafe { params.computed_transform.as_ref() };
+ let opacity_ref = unsafe { params.opacity.as_ref() };
+ let mut has_opacity_animation = false;
+ let anim = unsafe { params.animation.as_ref() };
+ if let Some(anim) = anim {
+ debug_assert!(anim.id > 0);
+ match anim.effect_type {
+ WrAnimationType::Opacity => {
+ filters.push(FilterOp::Opacity(
+ PropertyBinding::Binding(
+ PropertyBindingKey::new(anim.id),
+ // We have to set the static opacity value as
+ // the value for the case where the animation is
+ // in not in-effect (e.g. in the delay phase
+ // with no corresponding fill mode).
+ opacity_ref.cloned().unwrap_or(1.0),
+ ),
+ 1.0,
+ ));
+ has_opacity_animation = true;
+ },
+ WrAnimationType::Transform => {
+ transform_binding = Some((
+ PropertyBinding::Binding(
+ PropertyBindingKey::new(anim.id),
+ // Same as above opacity case.
+ transform_ref
+ .map(|info| info.transform)
+ .unwrap_or_else(LayoutTransform::identity),
+ ),
+ anim.key,
+ ));
+ },
+ _ => unreachable!("{:?} should not create a stacking context", anim.effect_type),
+ }
+ }
+
+ if let Some(opacity) = opacity_ref {
+ if !has_opacity_animation && *opacity < 1.0 {
+ filters.push(FilterOp::Opacity(PropertyBinding::Value(*opacity), *opacity));
+ }
+ }
+
+ let mut wr_spatial_id = spatial_id.to_webrender(state.pipeline_id);
+ let wr_clip_id = params.clip.to_webrender(state.pipeline_id);
+
+ let mut origin = bounds.min;
+
+ // Note: 0 has special meaning in WR land, standing for ROOT_REFERENCE_FRAME.
+ // However, it is never returned by `push_reference_frame`, and we need to return
+ // an option here across FFI, so we take that 0 value for the None semantics.
+ // This is resolved into proper `Maybe<WrSpatialId>` inside `WebRenderAPI::PushStackingContext`.
+ let mut result = WrSpatialId { id: 0 };
+ if let Some(transform_binding) = transform_binding {
+ let scrolling_relative_to = match unsafe { params.scrolling_relative_to.as_ref() } {
+ Some(scroll_id) => {
+ debug_assert_eq!(params.reference_frame_kind, WrReferenceFrameKind::Perspective);
+ Some(ExternalScrollId(*scroll_id, state.pipeline_id))
+ },
+ None => None,
+ };
+
+ let reference_frame_kind = match params.reference_frame_kind {
+ WrReferenceFrameKind::Transform => ReferenceFrameKind::Transform {
+ is_2d_scale_translation: params.is_2d_scale_translation,
+ should_snap: params.should_snap,
+ paired_with_perspective: params.paired_with_perspective,
+ },
+ WrReferenceFrameKind::Perspective => ReferenceFrameKind::Perspective { scrolling_relative_to },
+ };
+ wr_spatial_id = state.frame_builder.dl_builder.push_reference_frame(
+ origin,
+ wr_spatial_id,
+ params.transform_style,
+ transform_binding.0,
+ reference_frame_kind,
+ transform_binding.1,
+ );
+
+ origin = LayoutPoint::zero();
+ result.id = wr_spatial_id.0;
+ assert_ne!(wr_spatial_id.0, 0);
+ } else if let Some(data) = computed_ref {
+ let rotation = match data.rotation {
+ WrRotation::Degree0 => Rotation::Degree0,
+ WrRotation::Degree90 => Rotation::Degree90,
+ WrRotation::Degree180 => Rotation::Degree180,
+ WrRotation::Degree270 => Rotation::Degree270,
+ };
+ wr_spatial_id = state.frame_builder.dl_builder.push_computed_frame(
+ origin,
+ wr_spatial_id,
+ Some(data.scale_from),
+ data.vertical_flip,
+ rotation,
+ data.key,
+ );
+
+ origin = LayoutPoint::zero();
+ result.id = wr_spatial_id.0;
+ assert_ne!(wr_spatial_id.0, 0);
+ }
+
+ state.frame_builder.dl_builder.push_stacking_context(
+ origin,
+ wr_spatial_id,
+ params.prim_flags,
+ wr_clip_id,
+ params.transform_style,
+ params.mix_blend_mode,
+ &filters,
+ &r_filter_datas,
+ &[],
+ glyph_raster_space,
+ params.flags,
+ );
+
+ result
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_pop_stacking_context(state: &mut WrState, is_reference_frame: bool) {
+ debug_assert!(unsafe { !is_in_render_thread() });
+ state.frame_builder.dl_builder.pop_stacking_context();
+ if is_reference_frame {
+ state.frame_builder.dl_builder.pop_reference_frame();
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_define_clipchain(
+ state: &mut WrState,
+ parent_clipchain_id: *const u64,
+ clips: *const WrClipId,
+ clips_count: usize,
+) -> u64 {
+ debug_assert!(unsafe { is_in_main_thread() });
+ let parent = unsafe { parent_clipchain_id.as_ref() }.map(|id| ClipChainId(*id, state.pipeline_id));
+
+ let pipeline_id = state.pipeline_id;
+ let clips = unsafe { make_slice(clips, clips_count) }
+ .iter()
+ .map(|clip_id| clip_id.to_webrender(pipeline_id));
+
+ let clipchain_id = state.frame_builder.dl_builder.define_clip_chain(parent, clips);
+ assert!(clipchain_id.1 == state.pipeline_id);
+ clipchain_id.0
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_define_image_mask_clip_with_parent_clip_chain(
+ state: &mut WrState,
+ space: WrSpatialId,
+ mask: ImageMask,
+ points: *const LayoutPoint,
+ point_count: usize,
+ fill_rule: FillRule,
+) -> WrClipId {
+ debug_assert!(unsafe { is_in_main_thread() });
+
+ let c_points = unsafe { make_slice(points, point_count) };
+ let points: Vec<LayoutPoint> = c_points.iter().copied().collect();
+
+ let clip_id = state.frame_builder.dl_builder.define_clip_image_mask(
+ space.to_webrender(state.pipeline_id),
+ mask,
+ &points,
+ fill_rule,
+ );
+ WrClipId::from_webrender(clip_id)
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_define_rounded_rect_clip(
+ state: &mut WrState,
+ space: WrSpatialId,
+ complex: ComplexClipRegion,
+) -> WrClipId {
+ debug_assert!(unsafe { is_in_main_thread() });
+
+ let clip_id = state
+ .frame_builder
+ .dl_builder
+ .define_clip_rounded_rect(space.to_webrender(state.pipeline_id), complex);
+ WrClipId::from_webrender(clip_id)
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_define_rect_clip(state: &mut WrState, space: WrSpatialId, clip_rect: LayoutRect) -> WrClipId {
+ debug_assert!(unsafe { is_in_main_thread() });
+
+ let clip_id = state
+ .frame_builder
+ .dl_builder
+ .define_clip_rect(space.to_webrender(state.pipeline_id), clip_rect);
+ WrClipId::from_webrender(clip_id)
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_define_sticky_frame(
+ state: &mut WrState,
+ parent_spatial_id: WrSpatialId,
+ content_rect: LayoutRect,
+ top_margin: *const f32,
+ right_margin: *const f32,
+ bottom_margin: *const f32,
+ left_margin: *const f32,
+ vertical_bounds: StickyOffsetBounds,
+ horizontal_bounds: StickyOffsetBounds,
+ applied_offset: LayoutVector2D,
+ key: SpatialTreeItemKey,
+) -> WrSpatialId {
+ assert!(unsafe { is_in_main_thread() });
+ let spatial_id = state.frame_builder.dl_builder.define_sticky_frame(
+ parent_spatial_id.to_webrender(state.pipeline_id),
+ content_rect,
+ SideOffsets2D::new(
+ unsafe { top_margin.as_ref() }.cloned(),
+ unsafe { right_margin.as_ref() }.cloned(),
+ unsafe { bottom_margin.as_ref() }.cloned(),
+ unsafe { left_margin.as_ref() }.cloned(),
+ ),
+ vertical_bounds,
+ horizontal_bounds,
+ applied_offset,
+ key,
+ );
+
+ WrSpatialId { id: spatial_id.0 }
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_define_scroll_layer(
+ state: &mut WrState,
+ external_scroll_id: u64,
+ parent: &WrSpatialId,
+ content_rect: LayoutRect,
+ clip_rect: LayoutRect,
+ scroll_offset: LayoutVector2D,
+ scroll_offset_generation: APZScrollGeneration,
+ has_scroll_linked_effect: HasScrollLinkedEffect,
+ key: SpatialTreeItemKey,
+) -> WrSpatialId {
+ assert!(unsafe { is_in_main_thread() });
+
+ let space_and_clip = state.frame_builder.dl_builder.define_scroll_frame(
+ parent.to_webrender(state.pipeline_id),
+ ExternalScrollId(external_scroll_id, state.pipeline_id),
+ content_rect,
+ clip_rect,
+ scroll_offset,
+ scroll_offset_generation,
+ has_scroll_linked_effect,
+ key,
+ );
+
+ WrSpatialId::from_webrender(space_and_clip)
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_push_iframe(
+ state: &mut WrState,
+ rect: LayoutRect,
+ clip: LayoutRect,
+ _is_backface_visible: bool,
+ parent: &WrSpaceAndClipChain,
+ pipeline_id: WrPipelineId,
+ ignore_missing_pipeline: bool,
+) {
+ debug_assert!(unsafe { is_in_main_thread() });
+
+ state.frame_builder.dl_builder.push_iframe(
+ rect,
+ clip,
+ &parent.to_webrender(state.pipeline_id),
+ pipeline_id,
+ ignore_missing_pipeline,
+ );
+}
+
+// A helper fn to construct a PrimitiveFlags
+fn prim_flags(is_backface_visible: bool, prefer_compositor_surface: bool) -> PrimitiveFlags {
+ let mut flags = PrimitiveFlags::empty();
+
+ if is_backface_visible {
+ flags |= PrimitiveFlags::IS_BACKFACE_VISIBLE;
+ }
+
+ if prefer_compositor_surface {
+ flags |= PrimitiveFlags::PREFER_COMPOSITOR_SURFACE;
+ }
+
+ flags
+}
+
+fn prim_flags2(
+ is_backface_visible: bool,
+ prefer_compositor_surface: bool,
+ supports_external_compositing: bool,
+) -> PrimitiveFlags {
+ let mut flags = PrimitiveFlags::empty();
+
+ if supports_external_compositing {
+ flags |= PrimitiveFlags::SUPPORTS_EXTERNAL_COMPOSITOR_SURFACE;
+ }
+
+ flags | prim_flags(is_backface_visible, prefer_compositor_surface)
+}
+
+fn common_item_properties_for_rect(
+ state: &mut WrState,
+ clip_rect: LayoutRect,
+ is_backface_visible: bool,
+ parent: &WrSpaceAndClipChain,
+) -> CommonItemProperties {
+ let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+ CommonItemProperties {
+ // NB: the damp-e10s talos-test will frequently crash on startup if we
+ // early-return here for empty rects. I couldn't figure out why, but
+ // it's pretty harmless to feed these through, so, uh, we do?
+ clip_rect,
+ clip_chain_id: space_and_clip.clip_chain_id,
+ spatial_id: space_and_clip.spatial_id,
+ flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_push_rect(
+ state: &mut WrState,
+ rect: LayoutRect,
+ clip: LayoutRect,
+ is_backface_visible: bool,
+ force_antialiasing: bool,
+ is_checkerboard: bool,
+ parent: &WrSpaceAndClipChain,
+ color: ColorF,
+) {
+ debug_assert!(unsafe { !is_in_render_thread() });
+
+ let mut prim_info = common_item_properties_for_rect(state, clip, is_backface_visible, parent);
+ if force_antialiasing {
+ prim_info.flags |= PrimitiveFlags::ANTIALISED;
+ }
+ if is_checkerboard {
+ prim_info.flags |= PrimitiveFlags::CHECKERBOARD_BACKGROUND;
+ }
+
+ state.frame_builder.dl_builder.push_rect(&prim_info, rect, color);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_push_rect_with_animation(
+ state: &mut WrState,
+ rect: LayoutRect,
+ clip: LayoutRect,
+ is_backface_visible: bool,
+ parent: &WrSpaceAndClipChain,
+ color: ColorF,
+ animation: *const WrAnimationProperty,
+) {
+ debug_assert!(unsafe { !is_in_render_thread() });
+
+ let prim_info = common_item_properties_for_rect(state, clip, is_backface_visible, parent);
+
+ let anim = unsafe { animation.as_ref() };
+ if let Some(anim) = anim {
+ debug_assert!(anim.id > 0);
+ match anim.effect_type {
+ WrAnimationType::BackgroundColor => state.frame_builder.dl_builder.push_rect_with_animation(
+ &prim_info,
+ rect,
+ PropertyBinding::Binding(PropertyBindingKey::new(anim.id), color),
+ ),
+ _ => unreachable!("Didn't expect {:?} animation", anim.effect_type),
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_push_backdrop_filter(
+ state: &mut WrState,
+ rect: LayoutRect,
+ clip: LayoutRect,
+ is_backface_visible: bool,
+ parent: &WrSpaceAndClipChain,
+ filters: *const FilterOp,
+ filter_count: usize,
+ filter_datas: *const WrFilterData,
+ filter_datas_count: usize,
+) {
+ debug_assert!(unsafe { !is_in_render_thread() });
+
+ let c_filters = unsafe { make_slice(filters, filter_count) };
+ let filters: Vec<FilterOp> = c_filters.iter().copied().collect();
+
+ let c_filter_datas = unsafe { make_slice(filter_datas, filter_datas_count) };
+ let filter_datas: Vec<FilterData> = c_filter_datas
+ .iter()
+ .map(|c_filter_data| FilterData {
+ func_r_type: c_filter_data.funcR_type,
+ r_values: unsafe { make_slice(c_filter_data.R_values, c_filter_data.R_values_count).to_vec() },
+ func_g_type: c_filter_data.funcG_type,
+ g_values: unsafe { make_slice(c_filter_data.G_values, c_filter_data.G_values_count).to_vec() },
+ func_b_type: c_filter_data.funcB_type,
+ b_values: unsafe { make_slice(c_filter_data.B_values, c_filter_data.B_values_count).to_vec() },
+ func_a_type: c_filter_data.funcA_type,
+ a_values: unsafe { make_slice(c_filter_data.A_values, c_filter_data.A_values_count).to_vec() },
+ })
+ .collect();
+
+ let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+ let clip_rect = clip.intersection(&rect);
+ if clip_rect.is_none() {
+ return;
+ }
+
+ let prim_info = CommonItemProperties {
+ clip_rect: clip_rect.unwrap(),
+ clip_chain_id: space_and_clip.clip_chain_id,
+ spatial_id: space_and_clip.spatial_id,
+ flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
+ };
+
+ state
+ .frame_builder
+ .dl_builder
+ .push_backdrop_filter(&prim_info, &filters, &filter_datas, &[]);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_push_clear_rect(
+ state: &mut WrState,
+ rect: LayoutRect,
+ clip_rect: LayoutRect,
+ parent: &WrSpaceAndClipChain,
+) {
+ debug_assert!(unsafe { !is_in_render_thread() });
+
+ let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+ let prim_info = CommonItemProperties {
+ clip_rect,
+ clip_chain_id: space_and_clip.clip_chain_id,
+ spatial_id: space_and_clip.spatial_id,
+ flags: prim_flags(true, /* prefer_compositor_surface */ false),
+ };
+
+ state.frame_builder.dl_builder.push_clear_rect(&prim_info, rect);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_push_hit_test(
+ state: &mut WrState,
+ rect: LayoutRect,
+ clip: LayoutRect,
+ is_backface_visible: bool,
+ parent: &WrSpaceAndClipChain,
+ scroll_id: u64,
+ hit_info: u16,
+) {
+ debug_assert!(unsafe { !is_in_render_thread() });
+
+ let clip_rect = clip.intersection(&rect);
+ if clip_rect.is_none() {
+ return;
+ }
+ let tag = (scroll_id, hit_info);
+
+ let spatial_id = parent.space.to_webrender(state.pipeline_id);
+
+ let clip_chain_id = if parent.clip_chain == ROOT_CLIP_CHAIN {
+ ClipChainId::INVALID
+ } else {
+ ClipChainId(parent.clip_chain, state.pipeline_id)
+ };
+
+ state.frame_builder.dl_builder.push_hit_test(
+ clip_rect.unwrap(),
+ clip_chain_id,
+ spatial_id,
+ prim_flags(is_backface_visible, false),
+ tag,
+ );
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_push_image(
+ state: &mut WrState,
+ bounds: LayoutRect,
+ clip: LayoutRect,
+ is_backface_visible: bool,
+ force_antialiasing: bool,
+ parent: &WrSpaceAndClipChain,
+ image_rendering: ImageRendering,
+ key: WrImageKey,
+ premultiplied_alpha: bool,
+ color: ColorF,
+ prefer_compositor_surface: bool,
+ supports_external_compositing: bool,
+) {
+ debug_assert!(unsafe { is_in_main_thread() || is_in_compositor_thread() });
+
+ let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+ let mut flags = prim_flags2(
+ is_backface_visible,
+ prefer_compositor_surface,
+ supports_external_compositing,
+ );
+
+ if force_antialiasing {
+ flags |= PrimitiveFlags::ANTIALISED;
+ }
+
+ let prim_info = CommonItemProperties {
+ clip_rect: clip,
+ clip_chain_id: space_and_clip.clip_chain_id,
+ spatial_id: space_and_clip.spatial_id,
+ flags,
+ };
+
+ let alpha_type = if premultiplied_alpha {
+ AlphaType::PremultipliedAlpha
+ } else {
+ AlphaType::Alpha
+ };
+
+ state
+ .frame_builder
+ .dl_builder
+ .push_image(&prim_info, bounds, image_rendering, alpha_type, key, color);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_push_repeating_image(
+ state: &mut WrState,
+ bounds: LayoutRect,
+ clip: LayoutRect,
+ is_backface_visible: bool,
+ parent: &WrSpaceAndClipChain,
+ stretch_size: LayoutSize,
+ tile_spacing: LayoutSize,
+ image_rendering: ImageRendering,
+ key: WrImageKey,
+ premultiplied_alpha: bool,
+ color: ColorF,
+) {
+ debug_assert!(unsafe { is_in_main_thread() || is_in_compositor_thread() });
+
+ let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+ let prim_info = CommonItemProperties {
+ clip_rect: clip,
+ clip_chain_id: space_and_clip.clip_chain_id,
+ spatial_id: space_and_clip.spatial_id,
+ flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
+ };
+
+ let alpha_type = if premultiplied_alpha {
+ AlphaType::PremultipliedAlpha
+ } else {
+ AlphaType::Alpha
+ };
+
+ state.frame_builder.dl_builder.push_repeating_image(
+ &prim_info,
+ bounds,
+ stretch_size,
+ tile_spacing,
+ image_rendering,
+ alpha_type,
+ key,
+ color,
+ );
+}
+
+/// Push a 3 planar yuv image.
+#[no_mangle]
+pub extern "C" fn wr_dp_push_yuv_planar_image(
+ state: &mut WrState,
+ bounds: LayoutRect,
+ clip: LayoutRect,
+ is_backface_visible: bool,
+ parent: &WrSpaceAndClipChain,
+ image_key_0: WrImageKey,
+ image_key_1: WrImageKey,
+ image_key_2: WrImageKey,
+ color_depth: WrColorDepth,
+ color_space: WrYuvColorSpace,
+ color_range: WrColorRange,
+ image_rendering: ImageRendering,
+ prefer_compositor_surface: bool,
+ supports_external_compositing: bool,
+) {
+ debug_assert!(unsafe { is_in_main_thread() || is_in_compositor_thread() });
+
+ let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+ let prim_info = CommonItemProperties {
+ clip_rect: clip,
+ clip_chain_id: space_and_clip.clip_chain_id,
+ spatial_id: space_and_clip.spatial_id,
+ flags: prim_flags2(
+ is_backface_visible,
+ prefer_compositor_surface,
+ supports_external_compositing,
+ ),
+ };
+
+ state.frame_builder.dl_builder.push_yuv_image(
+ &prim_info,
+ bounds,
+ YuvData::PlanarYCbCr(image_key_0, image_key_1, image_key_2),
+ color_depth,
+ color_space,
+ color_range,
+ image_rendering,
+ );
+}
+
+/// Push a 2 planar NV12 image.
+#[no_mangle]
+pub extern "C" fn wr_dp_push_yuv_NV12_image(
+ state: &mut WrState,
+ bounds: LayoutRect,
+ clip: LayoutRect,
+ is_backface_visible: bool,
+ parent: &WrSpaceAndClipChain,
+ image_key_0: WrImageKey,
+ image_key_1: WrImageKey,
+ color_depth: WrColorDepth,
+ color_space: WrYuvColorSpace,
+ color_range: WrColorRange,
+ image_rendering: ImageRendering,
+ prefer_compositor_surface: bool,
+ supports_external_compositing: bool,
+) {
+ debug_assert!(unsafe { is_in_main_thread() || is_in_compositor_thread() });
+
+ let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+ let prim_info = CommonItemProperties {
+ clip_rect: clip,
+ clip_chain_id: space_and_clip.clip_chain_id,
+ spatial_id: space_and_clip.spatial_id,
+ flags: prim_flags2(
+ is_backface_visible,
+ prefer_compositor_surface,
+ supports_external_compositing,
+ ),
+ };
+
+ state.frame_builder.dl_builder.push_yuv_image(
+ &prim_info,
+ bounds,
+ YuvData::NV12(image_key_0, image_key_1),
+ color_depth,
+ color_space,
+ color_range,
+ image_rendering,
+ );
+}
+
+/// Push a 2 planar P010 image.
+#[no_mangle]
+pub extern "C" fn wr_dp_push_yuv_P010_image(
+ state: &mut WrState,
+ bounds: LayoutRect,
+ clip: LayoutRect,
+ is_backface_visible: bool,
+ parent: &WrSpaceAndClipChain,
+ image_key_0: WrImageKey,
+ image_key_1: WrImageKey,
+ color_depth: WrColorDepth,
+ color_space: WrYuvColorSpace,
+ color_range: WrColorRange,
+ image_rendering: ImageRendering,
+ prefer_compositor_surface: bool,
+ supports_external_compositing: bool,
+) {
+ debug_assert!(unsafe { is_in_main_thread() || is_in_compositor_thread() });
+
+ let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+ let prim_info = CommonItemProperties {
+ clip_rect: clip,
+ clip_chain_id: space_and_clip.clip_chain_id,
+ spatial_id: space_and_clip.spatial_id,
+ flags: prim_flags2(
+ is_backface_visible,
+ prefer_compositor_surface,
+ supports_external_compositing,
+ ),
+ };
+
+ state.frame_builder.dl_builder.push_yuv_image(
+ &prim_info,
+ bounds,
+ YuvData::P010(image_key_0, image_key_1),
+ color_depth,
+ color_space,
+ color_range,
+ image_rendering,
+ );
+}
+
+/// Push a yuv interleaved image.
+#[no_mangle]
+pub extern "C" fn wr_dp_push_yuv_interleaved_image(
+ state: &mut WrState,
+ bounds: LayoutRect,
+ clip: LayoutRect,
+ is_backface_visible: bool,
+ parent: &WrSpaceAndClipChain,
+ image_key_0: WrImageKey,
+ color_depth: WrColorDepth,
+ color_space: WrYuvColorSpace,
+ color_range: WrColorRange,
+ image_rendering: ImageRendering,
+ prefer_compositor_surface: bool,
+ supports_external_compositing: bool,
+) {
+ debug_assert!(unsafe { is_in_main_thread() || is_in_compositor_thread() });
+
+ let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+ let prim_info = CommonItemProperties {
+ clip_rect: clip,
+ clip_chain_id: space_and_clip.clip_chain_id,
+ spatial_id: space_and_clip.spatial_id,
+ flags: prim_flags2(
+ is_backface_visible,
+ prefer_compositor_surface,
+ supports_external_compositing,
+ ),
+ };
+
+ state.frame_builder.dl_builder.push_yuv_image(
+ &prim_info,
+ bounds,
+ YuvData::InterleavedYCbCr(image_key_0),
+ color_depth,
+ color_space,
+ color_range,
+ image_rendering,
+ );
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_push_text(
+ state: &mut WrState,
+ bounds: LayoutRect,
+ clip: LayoutRect,
+ is_backface_visible: bool,
+ parent: &WrSpaceAndClipChain,
+ color: ColorF,
+ font_key: WrFontInstanceKey,
+ glyphs: *const GlyphInstance,
+ glyph_count: u32,
+ glyph_options: *const GlyphOptions,
+) {
+ debug_assert!(unsafe { is_in_main_thread() });
+
+ let glyph_slice = unsafe { make_slice(glyphs, glyph_count as usize) };
+
+ let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+ let prim_info = CommonItemProperties {
+ clip_rect: clip,
+ spatial_id: space_and_clip.spatial_id,
+ clip_chain_id: space_and_clip.clip_chain_id,
+ flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
+ };
+
+ state
+ .frame_builder
+ .dl_builder
+ .push_text(&prim_info, bounds, &glyph_slice, font_key, color, unsafe {
+ glyph_options.as_ref().cloned()
+ });
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_push_shadow(
+ state: &mut WrState,
+ _bounds: LayoutRect,
+ _clip: LayoutRect,
+ _is_backface_visible: bool,
+ parent: &WrSpaceAndClipChain,
+ shadow: Shadow,
+ should_inflate: bool,
+) {
+ debug_assert!(unsafe { is_in_main_thread() });
+
+ state
+ .frame_builder
+ .dl_builder
+ .push_shadow(&parent.to_webrender(state.pipeline_id), shadow, should_inflate);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_pop_all_shadows(state: &mut WrState) {
+ debug_assert!(unsafe { is_in_main_thread() });
+
+ state.frame_builder.dl_builder.pop_all_shadows();
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_push_line(
+ state: &mut WrState,
+ clip: &LayoutRect,
+ is_backface_visible: bool,
+ parent: &WrSpaceAndClipChain,
+ bounds: &LayoutRect,
+ wavy_line_thickness: f32,
+ orientation: LineOrientation,
+ color: &ColorF,
+ style: LineStyle,
+) {
+ debug_assert!(unsafe { is_in_main_thread() });
+
+ let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+ let prim_info = CommonItemProperties {
+ clip_rect: *clip,
+ clip_chain_id: space_and_clip.clip_chain_id,
+ spatial_id: space_and_clip.spatial_id,
+ flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
+ };
+
+ state
+ .frame_builder
+ .dl_builder
+ .push_line(&prim_info, bounds, wavy_line_thickness, orientation, color, style);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_push_border(
+ state: &mut WrState,
+ rect: LayoutRect,
+ clip: LayoutRect,
+ is_backface_visible: bool,
+ parent: &WrSpaceAndClipChain,
+ do_aa: AntialiasBorder,
+ widths: LayoutSideOffsets,
+ top: BorderSide,
+ right: BorderSide,
+ bottom: BorderSide,
+ left: BorderSide,
+ radius: BorderRadius,
+) {
+ debug_assert!(unsafe { is_in_main_thread() });
+
+ let border_details = BorderDetails::Normal(NormalBorder {
+ left,
+ right,
+ top,
+ bottom,
+ radius,
+ do_aa: do_aa == AntialiasBorder::Yes,
+ });
+
+ let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+ let prim_info = CommonItemProperties {
+ clip_rect: clip,
+ clip_chain_id: space_and_clip.clip_chain_id,
+ spatial_id: space_and_clip.spatial_id,
+ flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
+ };
+
+ state
+ .frame_builder
+ .dl_builder
+ .push_border(&prim_info, rect, widths, border_details);
+}
+
+#[repr(C)]
+pub struct WrBorderImage {
+ widths: LayoutSideOffsets,
+ image: WrImageKey,
+ image_rendering: ImageRendering,
+ width: i32,
+ height: i32,
+ fill: bool,
+ slice: DeviceIntSideOffsets,
+ repeat_horizontal: RepeatMode,
+ repeat_vertical: RepeatMode,
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_push_border_image(
+ state: &mut WrState,
+ rect: LayoutRect,
+ clip: LayoutRect,
+ is_backface_visible: bool,
+ parent: &WrSpaceAndClipChain,
+ params: &WrBorderImage,
+) {
+ debug_assert!(unsafe { is_in_main_thread() });
+ let border_details = BorderDetails::NinePatch(NinePatchBorder {
+ source: NinePatchBorderSource::Image(params.image, params.image_rendering),
+ width: params.width,
+ height: params.height,
+ slice: params.slice,
+ fill: params.fill,
+ repeat_horizontal: params.repeat_horizontal,
+ repeat_vertical: params.repeat_vertical,
+ });
+ let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+ let prim_info = CommonItemProperties {
+ clip_rect: clip,
+ clip_chain_id: space_and_clip.clip_chain_id,
+ spatial_id: space_and_clip.spatial_id,
+ flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
+ };
+
+ state
+ .frame_builder
+ .dl_builder
+ .push_border(&prim_info, rect, params.widths, border_details);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_push_border_gradient(
+ state: &mut WrState,
+ rect: LayoutRect,
+ clip: LayoutRect,
+ is_backface_visible: bool,
+ parent: &WrSpaceAndClipChain,
+ widths: LayoutSideOffsets,
+ width: i32,
+ height: i32,
+ fill: bool,
+ slice: DeviceIntSideOffsets,
+ start_point: LayoutPoint,
+ end_point: LayoutPoint,
+ stops: *const GradientStop,
+ stops_count: usize,
+ extend_mode: ExtendMode,
+) {
+ debug_assert!(unsafe { is_in_main_thread() });
+
+ let stops_slice = unsafe { make_slice(stops, stops_count) };
+ let stops_vector = stops_slice.to_owned();
+
+ let gradient = state
+ .frame_builder
+ .dl_builder
+ .create_gradient(start_point, end_point, stops_vector, extend_mode);
+
+ let border_details = BorderDetails::NinePatch(NinePatchBorder {
+ source: NinePatchBorderSource::Gradient(gradient),
+ width,
+ height,
+ slice,
+ fill,
+ repeat_horizontal: RepeatMode::Stretch,
+ repeat_vertical: RepeatMode::Stretch,
+ });
+
+ let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+ let prim_info = CommonItemProperties {
+ clip_rect: clip,
+ clip_chain_id: space_and_clip.clip_chain_id,
+ spatial_id: space_and_clip.spatial_id,
+ flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
+ };
+
+ state
+ .frame_builder
+ .dl_builder
+ .push_border(&prim_info, rect, widths, border_details);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_push_border_radial_gradient(
+ state: &mut WrState,
+ rect: LayoutRect,
+ clip: LayoutRect,
+ is_backface_visible: bool,
+ parent: &WrSpaceAndClipChain,
+ widths: LayoutSideOffsets,
+ fill: bool,
+ center: LayoutPoint,
+ radius: LayoutSize,
+ stops: *const GradientStop,
+ stops_count: usize,
+ extend_mode: ExtendMode,
+) {
+ debug_assert!(unsafe { is_in_main_thread() });
+
+ let stops_slice = unsafe { make_slice(stops, stops_count) };
+ let stops_vector = stops_slice.to_owned();
+
+ let slice = SideOffsets2D::new(
+ widths.top as i32,
+ widths.right as i32,
+ widths.bottom as i32,
+ widths.left as i32,
+ );
+
+ let gradient = state
+ .frame_builder
+ .dl_builder
+ .create_radial_gradient(center, radius, stops_vector, extend_mode);
+
+ let border_details = BorderDetails::NinePatch(NinePatchBorder {
+ source: NinePatchBorderSource::RadialGradient(gradient),
+ width: rect.width() as i32,
+ height: rect.height() as i32,
+ slice,
+ fill,
+ repeat_horizontal: RepeatMode::Stretch,
+ repeat_vertical: RepeatMode::Stretch,
+ });
+
+ let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+ let prim_info = CommonItemProperties {
+ clip_rect: clip,
+ clip_chain_id: space_and_clip.clip_chain_id,
+ spatial_id: space_and_clip.spatial_id,
+ flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
+ };
+
+ state
+ .frame_builder
+ .dl_builder
+ .push_border(&prim_info, rect, widths, border_details);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_push_border_conic_gradient(
+ state: &mut WrState,
+ rect: LayoutRect,
+ clip: LayoutRect,
+ is_backface_visible: bool,
+ parent: &WrSpaceAndClipChain,
+ widths: LayoutSideOffsets,
+ fill: bool,
+ center: LayoutPoint,
+ angle: f32,
+ stops: *const GradientStop,
+ stops_count: usize,
+ extend_mode: ExtendMode,
+) {
+ debug_assert!(unsafe { is_in_main_thread() });
+
+ let stops_slice = unsafe { make_slice(stops, stops_count) };
+ let stops_vector = stops_slice.to_owned();
+
+ let slice = SideOffsets2D::new(
+ widths.top as i32,
+ widths.right as i32,
+ widths.bottom as i32,
+ widths.left as i32,
+ );
+
+ let gradient = state
+ .frame_builder
+ .dl_builder
+ .create_conic_gradient(center, angle, stops_vector, extend_mode);
+
+ let border_details = BorderDetails::NinePatch(NinePatchBorder {
+ source: NinePatchBorderSource::ConicGradient(gradient),
+ width: rect.width() as i32,
+ height: rect.height() as i32,
+ slice,
+ fill,
+ repeat_horizontal: RepeatMode::Stretch,
+ repeat_vertical: RepeatMode::Stretch,
+ });
+
+ let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+ let prim_info = CommonItemProperties {
+ clip_rect: clip,
+ clip_chain_id: space_and_clip.clip_chain_id,
+ spatial_id: space_and_clip.spatial_id,
+ flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
+ };
+
+ state
+ .frame_builder
+ .dl_builder
+ .push_border(&prim_info, rect, widths, border_details);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_push_linear_gradient(
+ state: &mut WrState,
+ rect: LayoutRect,
+ clip: LayoutRect,
+ is_backface_visible: bool,
+ parent: &WrSpaceAndClipChain,
+ start_point: LayoutPoint,
+ end_point: LayoutPoint,
+ stops: *const GradientStop,
+ stops_count: usize,
+ extend_mode: ExtendMode,
+ tile_size: LayoutSize,
+ tile_spacing: LayoutSize,
+) {
+ debug_assert!(unsafe { is_in_main_thread() });
+
+ let stops_slice = unsafe { make_slice(stops, stops_count) };
+ let stops_vector = stops_slice.to_owned();
+
+ let gradient = state
+ .frame_builder
+ .dl_builder
+ .create_gradient(start_point, end_point, stops_vector, extend_mode);
+
+ let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+ let prim_info = CommonItemProperties {
+ clip_rect: clip,
+ clip_chain_id: space_and_clip.clip_chain_id,
+ spatial_id: space_and_clip.spatial_id,
+ flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
+ };
+
+ state
+ .frame_builder
+ .dl_builder
+ .push_gradient(&prim_info, rect, gradient, tile_size, tile_spacing);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_push_radial_gradient(
+ state: &mut WrState,
+ rect: LayoutRect,
+ clip: LayoutRect,
+ is_backface_visible: bool,
+ parent: &WrSpaceAndClipChain,
+ center: LayoutPoint,
+ radius: LayoutSize,
+ stops: *const GradientStop,
+ stops_count: usize,
+ extend_mode: ExtendMode,
+ tile_size: LayoutSize,
+ tile_spacing: LayoutSize,
+) {
+ debug_assert!(unsafe { is_in_main_thread() });
+
+ let stops_slice = unsafe { make_slice(stops, stops_count) };
+ let stops_vector = stops_slice.to_owned();
+
+ let gradient = state
+ .frame_builder
+ .dl_builder
+ .create_radial_gradient(center, radius, stops_vector, extend_mode);
+
+ let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+ let prim_info = CommonItemProperties {
+ clip_rect: clip,
+ clip_chain_id: space_and_clip.clip_chain_id,
+ spatial_id: space_and_clip.spatial_id,
+ flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
+ };
+
+ state
+ .frame_builder
+ .dl_builder
+ .push_radial_gradient(&prim_info, rect, gradient, tile_size, tile_spacing);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_push_conic_gradient(
+ state: &mut WrState,
+ rect: LayoutRect,
+ clip: LayoutRect,
+ is_backface_visible: bool,
+ parent: &WrSpaceAndClipChain,
+ center: LayoutPoint,
+ angle: f32,
+ stops: *const GradientStop,
+ stops_count: usize,
+ extend_mode: ExtendMode,
+ tile_size: LayoutSize,
+ tile_spacing: LayoutSize,
+) {
+ debug_assert!(unsafe { is_in_main_thread() });
+
+ let stops_slice = unsafe { make_slice(stops, stops_count) };
+ let stops_vector = stops_slice.to_owned();
+
+ let gradient = state
+ .frame_builder
+ .dl_builder
+ .create_conic_gradient(center, angle, stops_vector, extend_mode);
+
+ let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+ let prim_info = CommonItemProperties {
+ clip_rect: clip,
+ clip_chain_id: space_and_clip.clip_chain_id,
+ spatial_id: space_and_clip.spatial_id,
+ flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
+ };
+
+ state
+ .frame_builder
+ .dl_builder
+ .push_conic_gradient(&prim_info, rect, gradient, tile_size, tile_spacing);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_push_box_shadow(
+ state: &mut WrState,
+ _rect: LayoutRect,
+ clip: LayoutRect,
+ is_backface_visible: bool,
+ parent: &WrSpaceAndClipChain,
+ box_bounds: LayoutRect,
+ offset: LayoutVector2D,
+ color: ColorF,
+ blur_radius: f32,
+ spread_radius: f32,
+ border_radius: BorderRadius,
+ clip_mode: BoxShadowClipMode,
+) {
+ debug_assert!(unsafe { is_in_main_thread() });
+
+ let space_and_clip = parent.to_webrender(state.pipeline_id);
+
+ let prim_info = CommonItemProperties {
+ clip_rect: clip,
+ clip_chain_id: space_and_clip.clip_chain_id,
+ spatial_id: space_and_clip.spatial_id,
+ flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
+ };
+
+ state.frame_builder.dl_builder.push_box_shadow(
+ &prim_info,
+ box_bounds,
+ offset,
+ color,
+ blur_radius,
+ spread_radius,
+ border_radius,
+ clip_mode,
+ );
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_start_item_group(state: &mut WrState) {
+ state.frame_builder.dl_builder.start_item_group();
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_cancel_item_group(state: &mut WrState, discard: bool) {
+ state.frame_builder.dl_builder.cancel_item_group(discard);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_finish_item_group(state: &mut WrState, key: ItemKey) -> bool {
+ state.frame_builder.dl_builder.finish_item_group(key)
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_push_reuse_items(state: &mut WrState, key: ItemKey) {
+ state.frame_builder.dl_builder.push_reuse_items(key);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_set_cache_size(state: &mut WrState, cache_size: usize) {
+ state.frame_builder.dl_builder.set_cache_size(cache_size);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dump_display_list(
+ state: &mut WrState,
+ indent: usize,
+ start: *const usize,
+ end: *const usize,
+) -> usize {
+ let start = unsafe { start.as_ref().cloned() };
+ let end = unsafe { end.as_ref().cloned() };
+ let range = Range { start, end };
+ let mut sink = Cursor::new(Vec::new());
+ let index = state
+ .frame_builder
+ .dl_builder
+ .emit_display_list(indent, range, &mut sink);
+
+ // For Android, dump to logcat instead of stderr. This is the same as
+ // what printf_stderr does on the C++ side.
+
+ #[cfg(target_os = "android")]
+ unsafe {
+ let gecko = CString::new("Gecko").unwrap();
+ let sink = CString::new(sink.into_inner()).unwrap();
+ __android_log_write(4 /* info */, gecko.as_ptr(), sink.as_ptr());
+ }
+
+ #[cfg(not(target_os = "android"))]
+ eprint!("{}", String::from_utf8(sink.into_inner()).unwrap());
+
+ index
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dump_serialized_display_list(state: &mut WrState) {
+ state.frame_builder.dl_builder.dump_serialized_display_list();
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wr_api_begin_builder(state: &mut WrState) {
+ state.frame_builder.dl_builder.begin();
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wr_api_end_builder(
+ state: &mut WrState,
+ dl_descriptor: &mut BuiltDisplayListDescriptor,
+ dl_items_data: &mut WrVecU8,
+ dl_cache_data: &mut WrVecU8,
+ dl_spatial_tree: &mut WrVecU8,
+) {
+ let (_, dl) = state.frame_builder.dl_builder.end();
+ let (payload, descriptor) = dl.into_data();
+ *dl_items_data = WrVecU8::from_vec(payload.items_data);
+ *dl_cache_data = WrVecU8::from_vec(payload.cache_data);
+ *dl_spatial_tree = WrVecU8::from_vec(payload.spatial_tree);
+ *dl_descriptor = descriptor;
+}
+
+#[repr(C)]
+pub struct HitResult {
+ pipeline_id: WrPipelineId,
+ scroll_id: u64,
+ animation_id: u64,
+ hit_info: u16,
+}
+
+#[no_mangle]
+pub extern "C" fn wr_api_hit_test(dh: &mut DocumentHandle, point: WorldPoint, out_results: &mut ThinVec<HitResult>) {
+ let result = dh.ensure_hit_tester().hit_test(point);
+ for item in &result.items {
+ out_results.push(HitResult {
+ pipeline_id: item.pipeline,
+ scroll_id: item.tag.0,
+ animation_id: item.animation_id,
+ hit_info: item.tag.1,
+ });
+ }
+}
+
+pub type VecU8 = Vec<u8>;
+pub type ArcVecU8 = Arc<VecU8>;
+
+#[no_mangle]
+pub extern "C" fn wr_add_ref_arc(arc: &ArcVecU8) -> *const VecU8 {
+ Arc::into_raw(arc.clone())
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wr_dec_ref_arc(arc: *const VecU8) {
+ Arc::from_raw(arc);
+}
+
+// TODO: nical
+// Update for the new blob image interface changes.
+//
+extern "C" {
+ // TODO: figure out the API for tiled blob images.
+ pub fn wr_moz2d_render_cb(
+ blob: ByteSlice,
+ format: ImageFormat,
+ render_rect: &LayoutIntRect,
+ visible_rect: &DeviceIntRect,
+ tile_size: u16,
+ tile_offset: &TileOffset,
+ dirty_rect: Option<&LayoutIntRect>,
+ output: MutByteSlice,
+ ) -> bool;
+}
+
+#[no_mangle]
+pub extern "C" fn wr_root_scroll_node_id() -> WrSpatialId {
+ // The PipelineId doesn't matter here, since we just want the numeric part of the id
+ // produced for any given root reference frame.
+ WrSpatialId {
+ id: SpatialId::root_scroll_node(PipelineId(0, 0)).0,
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn wr_root_clip_id() -> WrClipId {
+ // The PipelineId doesn't matter here, since we just want the numeric part of the id
+ // produced for any given root reference frame.
+ WrClipId::from_webrender(ClipId::root(PipelineId(0, 0)))
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct WrClipId {
+ id: usize,
+}
+
+impl WrClipId {
+ fn to_webrender(&self, pipeline_id: WrPipelineId) -> ClipId {
+ ClipId(self.id, pipeline_id)
+ }
+
+ fn from_webrender(clip_id: ClipId) -> Self {
+ WrClipId { id: clip_id.0 }
+ }
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct WrSpatialId {
+ id: usize,
+}
+
+impl WrSpatialId {
+ fn to_webrender(&self, pipeline_id: WrPipelineId) -> SpatialId {
+ SpatialId::new(self.id, pipeline_id)
+ }
+
+ fn from_webrender(id: SpatialId) -> Self {
+ WrSpatialId { id: id.0 }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wr_device_delete(device: *mut Device) {
+ mem::drop(Box::from_raw(device));
+}
+
+// Call MakeCurrent before this.
+#[no_mangle]
+pub extern "C" fn wr_shaders_new(
+ gl_context: *mut c_void,
+ program_cache: Option<&mut WrProgramCache>,
+ precache_shaders: bool,
+) -> *mut WrShaders {
+ let mut device = wr_device_new(gl_context, program_cache);
+
+ let precache_flags = if precache_shaders || env_var_to_bool("MOZ_WR_PRECACHE_SHADERS") {
+ ShaderPrecacheFlags::FULL_COMPILE
+ } else {
+ ShaderPrecacheFlags::ASYNC_COMPILE
+ };
+
+ let opts = WebRenderOptions {
+ precache_flags,
+ ..Default::default()
+ };
+
+ let gl_type = device.gl().get_type();
+ device.begin_frame();
+
+ let shaders = Rc::new(RefCell::new(match Shaders::new(&mut device, gl_type, &opts) {
+ Ok(shaders) => shaders,
+ Err(e) => {
+ warn!(" Failed to create a Shaders: {:?}", e);
+ let msg = CString::new(format!("wr_shaders_new: {:?}", e)).unwrap();
+ unsafe {
+ gfx_critical_note(msg.as_ptr());
+ }
+ return ptr::null_mut();
+ },
+ }));
+
+ let shaders = WrShaders(shaders);
+
+ device.end_frame();
+ Box::into_raw(Box::new(shaders))
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wr_shaders_delete(shaders: *mut WrShaders, gl_context: *mut c_void) {
+ let mut device = wr_device_new(gl_context, None);
+ let shaders = Box::from_raw(shaders);
+ if let Ok(shaders) = Rc::try_unwrap(shaders.0) {
+ shaders.into_inner().deinit(&mut device);
+ }
+ // let shaders go out of scope and get dropped
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn wr_program_cache_report_memory(
+ cache: *const WrProgramCache,
+ size_of_op: VoidPtrToSizeFn,
+) -> usize {
+ (*cache).program_cache.report_memory(size_of_op)
+}
diff --git a/gfx/webrender_bindings/src/lib.rs b/gfx/webrender_bindings/src/lib.rs
new file mode 100644
index 0000000000..9e94302e0f
--- /dev/null
+++ b/gfx/webrender_bindings/src/lib.rs
@@ -0,0 +1,43 @@
+/* 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/. */
+
+#![deny(warnings)]
+
+extern crate app_units;
+extern crate bincode;
+extern crate euclid;
+extern crate fxhash;
+extern crate gecko_profiler;
+extern crate gleam;
+extern crate nsstring;
+extern crate num_cpus;
+extern crate rayon;
+extern crate swgl;
+extern crate thin_vec;
+extern crate tracy_rs;
+extern crate uuid;
+extern crate webrender;
+extern crate wr_malloc_size_of;
+
+#[macro_use]
+extern crate log;
+
+#[cfg(target_os = "windows")]
+extern crate dwrote;
+#[cfg(target_os = "windows")]
+extern crate winapi;
+
+#[cfg(target_os = "macos")]
+extern crate core_foundation;
+#[cfg(target_os = "macos")]
+extern crate core_graphics;
+#[cfg(target_os = "macos")]
+extern crate foreign_types;
+
+mod program_cache;
+
+#[allow(non_snake_case)]
+pub mod bindings;
+pub mod moz2d_renderer;
+mod swgl_bindings;
diff --git a/gfx/webrender_bindings/src/moz2d_renderer.rs b/gfx/webrender_bindings/src/moz2d_renderer.rs
new file mode 100644
index 0000000000..4c7a32c912
--- /dev/null
+++ b/gfx/webrender_bindings/src/moz2d_renderer.rs
@@ -0,0 +1,871 @@
+/* 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/. */
+#![deny(missing_docs)]
+
+//! Provides the webrender-side implementation of gecko blob images.
+//!
+//! Pretty much this is just a shim that calls back into Moz2DImageRenderer, but
+//! it also handles merging "partial" blob images (see `merge_blob_images`) and
+//! registering fonts found in the blob (see `prepare_request`).
+
+use bindings::{
+ gecko_profiler_end_marker, gecko_profiler_start_marker, wr_moz2d_render_cb, ArcVecU8, ByteSlice, MutByteSlice,
+};
+use gecko_profiler::gecko_profiler_label;
+use rayon::prelude::*;
+use rayon::ThreadPool;
+use webrender::api::units::{BlobDirtyRect, BlobToDeviceTranslation, DeviceIntRect};
+use webrender::api::*;
+
+use euclid::point2;
+use std;
+use std::collections::btree_map::BTreeMap;
+use std::collections::hash_map;
+use std::collections::hash_map::HashMap;
+use std::collections::Bound::Included;
+use std::i32;
+use std::mem;
+use std::os::raw::c_void;
+use std::ptr;
+use std::sync::Arc;
+
+#[cfg(target_os = "windows")]
+use dwrote;
+
+#[cfg(target_os = "macos")]
+use core_foundation::string::CFString;
+#[cfg(target_os = "macos")]
+use core_graphics::font::CGFont;
+#[cfg(target_os = "macos")]
+use foreign_types::ForeignType;
+
+#[cfg(not(any(target_os = "macos", target_os = "windows")))]
+use std::ffi::CString;
+#[cfg(not(any(target_os = "macos", target_os = "windows")))]
+use std::os::unix::ffi::OsStrExt;
+
+/// Local print-debugging utility
+macro_rules! dlog {
+ ($($e:expr),*) => { {$(let _ = $e;)*} }
+ //($($t:tt)*) => { println!($($t)*) }
+}
+
+/// Debug prints a blob's item bounds, indicating whether the bounds are dirty or not.
+fn dump_bounds(blob: &[u8], dirty_rect: DeviceIntRect) {
+ let mut index = BlobReader::new(blob);
+ while index.reader.has_more() {
+ let e = index.read_entry();
+ dlog!(
+ " {:?} {}",
+ e.bounds,
+ if dirty_rect.contains_box(&e.bounds) { "*" } else { "" }
+ );
+ }
+}
+
+/// Debug prints a blob's metadata.
+fn dump_index(blob: &[u8]) {
+ let mut index = BlobReader::new(blob);
+ // we might get an empty result here because sub groups are not tightly bound
+ // and we'll sometimes have display items that end up with empty bounds in
+ // the blob image.
+ while index.reader.has_more() {
+ let e = index.read_entry();
+ dlog!("result bounds: {} {} {:?}", e.end, e.extra_end, e.bounds);
+ }
+}
+
+/// Handles the interpretation and rasterization of gecko-based (moz2d) WR blob images.
+pub struct Moz2dBlobImageHandler {
+ workers: Arc<ThreadPool>,
+ workers_low_priority: Arc<ThreadPool>,
+ blob_commands: HashMap<BlobImageKey, BlobCommand>,
+ enable_multithreading: bool,
+}
+
+/// Transmute some bytes into a value.
+///
+/// FIXME: kill this with fire and/or do a super robust security audit
+unsafe fn convert_from_bytes<T: Copy>(slice: &[u8]) -> T {
+ assert!(mem::size_of::<T>() <= slice.len());
+ ptr::read_unaligned(slice.as_ptr() as *const T)
+}
+
+/// Transmute a value into some bytes.
+fn convert_to_bytes<T>(x: &T) -> &[u8] {
+ unsafe {
+ let ip: *const T = x;
+ let bp: *const u8 = ip as *const _;
+ ::std::slice::from_raw_parts(bp, mem::size_of::<T>())
+ }
+}
+
+/// A simple helper for deserializing a bunch of transmuted POD data from bytes.
+struct BufReader<'a> {
+ /// The buffer to read from.
+ buf: &'a [u8],
+ /// Where we currently are reading from.
+ pos: usize,
+}
+
+impl<'a> BufReader<'a> {
+ /// Creates a reader over the given input.
+ fn new(buf: &'a [u8]) -> BufReader<'a> {
+ BufReader { buf, pos: 0 }
+ }
+
+ /// Transmute-deserializes a value of type T from the stream.
+ ///
+ /// !!! SUPER DANGEROUS !!!
+ ///
+ /// To limit the scope of this unsafety, please don't call this directly.
+ /// Make a helper method for each whitelisted type.
+ unsafe fn read<T: Copy>(&mut self) -> T {
+ let ret = convert_from_bytes(&self.buf[self.pos..]);
+ self.pos += mem::size_of::<T>();
+ ret
+ }
+
+ /// Deserializes a BlobFont.
+ fn read_blob_font(&mut self) -> BlobFont {
+ unsafe { self.read::<BlobFont>() }
+ }
+
+ /// Deserializes a usize.
+ fn read_usize(&mut self) -> usize {
+ unsafe { self.read::<usize>() }
+ }
+
+ /// Deserializes a rectangle.
+ fn read_box(&mut self) -> DeviceIntRect {
+ unsafe { self.read::<DeviceIntRect>() }
+ }
+
+ /// Returns whether the buffer has more data to deserialize.
+ fn has_more(&self) -> bool {
+ self.pos < self.buf.len()
+ }
+}
+
+/// Reads the metadata of a blob image.
+///
+/// Blob stream format:
+/// { data[..], index[..], offset in the stream of the index array }
+///
+/// An 'item' has 'data' and 'extra_data'
+/// - In our case the 'data' is the stream produced by DrawTargetRecording
+/// and the 'extra_data' includes things like webrender font keys
+///
+/// The index is an array of entries of the following form:
+/// { end, extra_end, bounds }
+///
+/// - end is the offset of the end of an item's data
+/// an item's data goes from the begining of the stream or
+/// the begining of the last item til end
+/// - extra_end is the offset of the end of an item's extra data
+/// an item's extra data goes from 'end' until 'extra_end'
+/// - bounds is a set of 4 ints { min.x, min.y, max.x, max.y }
+///
+/// The offsets in the index should be monotonically increasing.
+///
+/// Design rationale:
+/// - the index is smaller so we append it to the end of the data array
+/// during construction. This makes it more likely that we'll fit inside
+/// the data Vec
+/// - we use indices/offsets instead of sizes to avoid having to deal with any
+/// arithmetic that might overflow.
+struct BlobReader<'a> {
+ /// The buffer of the blob.
+ reader: BufReader<'a>,
+ /// Where the buffer head is.
+ begin: usize,
+}
+
+#[derive(PartialEq, Debug, Eq, Clone, Copy)]
+struct IntPoint {
+ x: i32,
+ y: i32,
+}
+
+/// The metadata for each display item in a blob image (doesn't match the serialized layout).
+///
+/// See BlobReader above for detailed docs of the blob image format.
+struct Entry {
+ /// The bounds of the display item.
+ bounds: DeviceIntRect,
+ /// Where the item's recorded drawing commands start.
+ begin: usize,
+ /// Where the item's recorded drawing commands end, and its extra data starts.
+ end: usize,
+ /// Where the item's extra data ends, and the next item's `begin`.
+ extra_end: usize,
+}
+
+impl<'a> BlobReader<'a> {
+ /// Creates a new BlobReader for the given buffer.
+ fn new(buf: &'a [u8]) -> BlobReader<'a> {
+ // The offset of the index is at the end of the buffer.
+ let index_offset_pos = buf.len() - mem::size_of::<usize>();
+ assert!(index_offset_pos < buf.len());
+ let index_offset = unsafe { convert_from_bytes::<usize>(&buf[index_offset_pos..]) };
+
+ BlobReader {
+ reader: BufReader::new(&buf[index_offset..index_offset_pos]),
+ begin: 0,
+ }
+ }
+
+ /// Reads the next display item's metadata.
+ fn read_entry(&mut self) -> Entry {
+ let end = self.reader.read_usize();
+ let extra_end = self.reader.read_usize();
+ let bounds = self.reader.read_box();
+ let ret = Entry {
+ begin: self.begin,
+ end,
+ extra_end,
+ bounds,
+ };
+ self.begin = extra_end;
+ ret
+ }
+}
+
+/// Writes new blob images.
+///
+/// In our case this is the result of merging an old one and a new one
+struct BlobWriter {
+ /// The buffer that the data and extra data for the items is accumulated.
+ data: Vec<u8>,
+ /// The buffer that the metadata for the items is accumulated.
+ index: Vec<u8>,
+}
+
+impl BlobWriter {
+ /// Creates an empty BlobWriter.
+ fn new() -> BlobWriter {
+ BlobWriter {
+ data: Vec::new(),
+ index: Vec::new(),
+ }
+ }
+
+ /// Writes a display item to the blob.
+ fn new_entry(&mut self, extra_size: usize, bounds: DeviceIntRect, data: &[u8]) {
+ self.data.extend_from_slice(data);
+ // Write 'end' to the index: the offset where the regular data ends and the extra data starts.
+ self.index
+ .extend_from_slice(convert_to_bytes(&(self.data.len() - extra_size)));
+ // Write 'extra_end' to the index: the offset where the extra data ends.
+ self.index.extend_from_slice(convert_to_bytes(&self.data.len()));
+ // XXX: we can aggregate these writes
+ // Write the bounds to the index.
+ self.index.extend_from_slice(convert_to_bytes(&bounds.min.x));
+ self.index.extend_from_slice(convert_to_bytes(&bounds.min.y));
+ self.index.extend_from_slice(convert_to_bytes(&bounds.max.x));
+ self.index.extend_from_slice(convert_to_bytes(&bounds.max.y));
+ }
+
+ /// Completes the blob image, producing a single buffer containing it.
+ fn finish(mut self) -> Vec<u8> {
+ // Append the index to the end of the buffer
+ // and then append the offset to the beginning of the index.
+ let index_begin = self.data.len();
+ self.data.extend_from_slice(&self.index);
+ self.data.extend_from_slice(convert_to_bytes(&index_begin));
+ self.data
+ }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+struct CacheKey {
+ x1: i32,
+ y1: i32,
+ x2: i32,
+ y2: i32,
+ cache_order: u32,
+}
+
+impl CacheKey {
+ pub fn new(bounds: DeviceIntRect, cache_order: u32) -> Self {
+ CacheKey {
+ x1: bounds.min.x,
+ y1: bounds.min.y,
+ x2: bounds.max.x,
+ y2: bounds.max.y,
+ cache_order,
+ }
+ }
+}
+
+/// Provides an API for looking up the display items in a blob image by bounds, yielding items
+/// with equal bounds in their original relative ordering.
+///
+/// This is used to implement `merge_blobs_images`.
+///
+/// We use a BTree as a kind of multi-map, by appending an integer "cache_order" to the key.
+/// This lets us use multiple items with matching bounds in the map and allows
+/// us to fetch and remove them while retaining the ordering of the original list.
+struct CachedReader<'a> {
+ /// Wrapped reader.
+ reader: BlobReader<'a>,
+ /// Cached entries that have been read but not yet requested by our consumer.
+ cache: BTreeMap<CacheKey, Entry>,
+ /// The current number of internally read display items, used to preserve list order.
+ cache_index_counter: u32,
+}
+
+impl<'a> CachedReader<'a> {
+ /// Creates a new CachedReader.
+ pub fn new(buf: &'a [u8]) -> CachedReader {
+ CachedReader {
+ reader: BlobReader::new(buf),
+ cache: BTreeMap::new(),
+ cache_index_counter: 0,
+ }
+ }
+
+ /// Tries to find the given bounds in the cache of internally read items, removing it if found.
+ fn take_entry_with_bounds_from_cache(&mut self, bounds: &DeviceIntRect) -> Option<Entry> {
+ if self.cache.is_empty() {
+ return None;
+ }
+
+ let key_to_delete = match self
+ .cache
+ .range((
+ Included(CacheKey::new(*bounds, 0u32)),
+ Included(CacheKey::new(*bounds, std::u32::MAX)),
+ ))
+ .next()
+ {
+ Some((&key, _)) => key,
+ None => return None,
+ };
+
+ Some(
+ self.cache
+ .remove(&key_to_delete)
+ .expect("We just got this key from range, it needs to be present"),
+ )
+ }
+
+ /// Yields the next item in the blob image with the given bounds.
+ ///
+ /// If the given bounds aren't found in the blob, this panics. `merge_blob_images` should
+ /// avoid this by construction if the blob images are well-formed.
+ pub fn next_entry_with_bounds(&mut self, bounds: &DeviceIntRect, ignore_rect: &DeviceIntRect) -> Entry {
+ if let Some(entry) = self.take_entry_with_bounds_from_cache(bounds) {
+ return entry;
+ }
+
+ loop {
+ // This will panic if we run through the whole list without finding our bounds.
+ let old = self.reader.read_entry();
+ if old.bounds == *bounds {
+ return old;
+ } else if !ignore_rect.contains_box(&old.bounds) {
+ self.cache
+ .insert(CacheKey::new(old.bounds, self.cache_index_counter), old);
+ self.cache_index_counter += 1;
+ }
+ }
+ }
+}
+
+/// Merges a new partial blob image into an existing complete one.
+///
+/// A blob image represents a recording of the drawing commands needed to render
+/// (part of) a display list. A partial blob image is a diff between the old display
+/// list and a new one. It contains an entry for every display item in the new list, but
+/// the actual drawing commands are missing for any item that isn't strictly contained
+/// in the dirty rect. This is possible because not being contained in the dirty
+/// rect implies that the item is unchanged between the old and new list, so we can
+/// just grab the drawing commands from the old list.
+///
+/// The dirty rect strictly contains the bounds of every item that has been inserted
+/// into or deleted from the old list to create the new list. (For simplicity
+/// you may think of any other update as deleting and reinserting the item).
+///
+/// Partial blobs are based on gecko's "retained display list" system, and
+/// in particular rely on one key property: if two items have overlapping bounds
+/// and *aren't* contained in the dirty rect, then their relative order in both
+/// the old and new list will not change. This lets us uniquely identify a display
+/// item using only its bounds and relative order in the list.
+///
+/// That is, the first non-dirty item in the new list with bounds (10, 15, 100, 100)
+/// is *also* the first non-dirty item in the old list with those bounds.
+///
+/// Note that *every* item contained inside the dirty rect will be fully recorded in
+/// the new list, even if it is actually unchanged from the old list.
+///
+/// All of this together gives us a fairly simple merging algorithm: all we need
+/// to do is walk through the new (partial) list, determine which of the two lists
+/// has the recording for that item, and copy the recording into the result.
+///
+/// If an item is contained in the dirty rect, then the new list contains the
+/// correct recording for that item, so we always copy it from there. Otherwise, we find
+/// the first not-yet-copied item with those bounds in the old list and copy that.
+/// Any items found in the old list but not the new one can be safely assumed to
+/// have been deleted.
+fn merge_blob_images(
+ old_buf: &[u8],
+ new_buf: &[u8],
+ dirty_rect: DeviceIntRect,
+ old_visible_rect: DeviceIntRect,
+ new_visible_rect: DeviceIntRect,
+) -> Vec<u8> {
+ let mut result = BlobWriter::new();
+ dlog!("dirty rect: {:?}", dirty_rect);
+ dlog!("old:");
+ dump_bounds(old_buf, dirty_rect);
+ dlog!("new:");
+ dump_bounds(new_buf, dirty_rect);
+ dlog!("old visibile rect: {:?}", old_visible_rect);
+ dlog!("new visibile rect: {:?}", new_visible_rect);
+
+ let mut old_reader = CachedReader::new(old_buf);
+ let mut new_reader = BlobReader::new(new_buf);
+ let preserved_rect = old_visible_rect.intersection_unchecked(&new_visible_rect);
+
+ // Loop over both new and old entries merging them.
+ // Both new and old must have the same number of entries that
+ // overlap but are not contained by the dirty rect, and they
+ // must be in the same order.
+ while new_reader.reader.has_more() {
+ let new = new_reader.read_entry();
+ dlog!("bounds: {} {} {:?}", new.end, new.extra_end, new.bounds);
+ let preserved_bounds = new.bounds.intersection_unchecked(&preserved_rect);
+ if dirty_rect.contains_box(&preserved_bounds) {
+ result.new_entry(new.extra_end - new.end, new.bounds, &new_buf[new.begin..new.extra_end]);
+ } else {
+ let old = old_reader.next_entry_with_bounds(&new.bounds, &dirty_rect);
+ result.new_entry(old.extra_end - old.end, new.bounds, &old_buf[old.begin..old.extra_end])
+ }
+ }
+
+ // XXX: future work: ensure that items that have been deleted but aren't in the blob's visible
+ // rect don't affect the dirty rect -- this allows us to scroll content out of view while only
+ // updating the areas where items have been scrolled *into* view. This is very important for
+ // the performance of blobs that are larger than the viewport. When this is done this
+ // assertion will need to be modified to factor in the visible rect, or removed.
+
+ // Ensure all remaining items will be discarded
+ while old_reader.reader.reader.has_more() {
+ let old = old_reader.reader.read_entry();
+ dlog!("new bounds: {} {} {:?}", old.end, old.extra_end, old.bounds);
+ //assert!(dirty_rect.contains_box(&old.bounds));
+ }
+
+ //assert!(old_reader.cache.is_empty());
+
+ let result = result.finish();
+ dump_index(&result);
+ result
+}
+
+/// A font used by a blob image.
+#[repr(C)]
+#[derive(Copy, Clone)]
+struct BlobFont {
+ /// The font key.
+ font_instance_key: FontInstanceKey,
+ /// A pointer to the scaled font.
+ scaled_font_ptr: u64,
+}
+
+/// A blob image and extra data provided by webrender on how to rasterize it.
+#[derive(Clone)]
+struct BlobCommand {
+ /// The blob.
+ data: Arc<BlobImageData>,
+ /// What part of the blob should be rasterized (visible_rect's top-left corresponds to
+ /// (0,0) in the blob's rasterization)
+ visible_rect: DeviceIntRect,
+ /// The size of the tiles to use in rasterization.
+ tile_size: TileSize,
+}
+
+struct Job {
+ request: BlobImageRequest,
+ descriptor: BlobImageDescriptor,
+ commands: Arc<BlobImageData>,
+ dirty_rect: BlobDirtyRect,
+ visible_rect: DeviceIntRect,
+ tile_size: TileSize,
+}
+
+/// Rasterizes gecko blob images.
+struct Moz2dBlobRasterizer {
+ /// Pool of rasterizers.
+ workers: Arc<ThreadPool>,
+ /// Pool of low priority rasterizers.
+ workers_low_priority: Arc<ThreadPool>,
+ /// Blobs to rasterize.
+ blob_commands: HashMap<BlobImageKey, BlobCommand>,
+ ///
+ enable_multithreading: bool,
+}
+
+struct GeckoProfilerMarker {
+ name: &'static str,
+}
+
+impl GeckoProfilerMarker {
+ pub fn new(name: &'static str) -> GeckoProfilerMarker {
+ gecko_profiler_start_marker(name);
+ GeckoProfilerMarker { name }
+ }
+}
+
+impl Drop for GeckoProfilerMarker {
+ fn drop(&mut self) {
+ gecko_profiler_end_marker(self.name);
+ }
+}
+
+impl AsyncBlobImageRasterizer for Moz2dBlobRasterizer {
+ fn rasterize(
+ &mut self,
+ requests: &[BlobImageParams],
+ low_priority: bool,
+ ) -> Vec<(BlobImageRequest, BlobImageResult)> {
+ // All we do here is spin up our workers to callback into gecko to replay the drawing commands.
+ gecko_profiler_label!(Graphics, Rasterization);
+ let _marker = GeckoProfilerMarker::new("BlobRasterization");
+
+ let requests: Vec<Job> = requests
+ .iter()
+ .map(|params| {
+ let command = &self.blob_commands[&params.request.key];
+ let blob = Arc::clone(&command.data);
+ assert!(!params.descriptor.rect.is_empty());
+
+ Job {
+ request: params.request,
+ descriptor: params.descriptor,
+ commands: blob,
+ visible_rect: command.visible_rect,
+ dirty_rect: params.dirty_rect,
+ tile_size: command.tile_size,
+ }
+ })
+ .collect();
+
+ // If we don't have a lot of blobs it is probably not worth the initial cost
+ // of installing work on rayon's thread pool so we do it serially on this thread.
+ let should_parallelize = if !self.enable_multithreading {
+ false
+ } else if low_priority {
+ requests.len() > 2
+ } else {
+ // For high priority requests we don't "risk" the potential priority inversion of
+ // dispatching to a thread pool full of low priority jobs unless it is really
+ // appealing.
+ requests.len() > 4
+ };
+
+ if should_parallelize {
+ // Parallel version synchronously installs a job on the thread pool which will
+ // try to do the work in parallel.
+ // This thread is blocked until the thread pool is done doing the work.
+ let lambda = || requests.into_par_iter().map(rasterize_blob).collect();
+ if low_priority {
+ //TODO --bpe runtime flag to A/B test these two
+ self.workers_low_priority.install(lambda)
+ //self.workers.install(lambda)
+ } else {
+ self.workers.install(lambda)
+ }
+ } else {
+ requests.into_iter().map(rasterize_blob).collect()
+ }
+ }
+}
+
+// a cross platform wrapper that creates an autorelease pool
+// on macOS
+fn autoreleasepool<T, F: FnOnce() -> T>(f: F) -> T {
+ #[cfg(target_os = "macos")]
+ {
+ objc::rc::autoreleasepool(f)
+ }
+ #[cfg(not(target_os = "macos"))]
+ {
+ f()
+ }
+}
+
+fn rasterize_blob(job: Job) -> (BlobImageRequest, BlobImageResult) {
+ gecko_profiler_label!(Graphics, Rasterization);
+ let descriptor = job.descriptor;
+ let buf_size = (descriptor.rect.area() * descriptor.format.bytes_per_pixel()) as usize;
+
+ let mut output = vec![0u8; buf_size];
+
+ let dirty_rect = match job.dirty_rect {
+ DirtyRect::Partial(rect) => Some(rect),
+ DirtyRect::All => None,
+ };
+ assert!(!descriptor.rect.is_empty());
+
+ let result = autoreleasepool(|| {
+ unsafe {
+ if wr_moz2d_render_cb(
+ ByteSlice::new(&job.commands[..]),
+ descriptor.format,
+ &descriptor.rect,
+ &job.visible_rect,
+ job.tile_size,
+ &job.request.tile,
+ dirty_rect.as_ref(),
+ MutByteSlice::new(output.as_mut_slice()),
+ ) {
+ // We want the dirty rect local to the tile rather than the whole image.
+ // TODO(nical): move that up and avoid recomupting the tile bounds in the callback
+ let dirty_rect = job.dirty_rect.to_subrect_of(&descriptor.rect);
+ let tx: BlobToDeviceTranslation = (-descriptor.rect.min.to_vector()).into();
+ let rasterized_rect = tx.transform_box(&dirty_rect);
+
+ Ok(RasterizedBlobImage {
+ rasterized_rect,
+ data: Arc::new(output),
+ })
+ } else {
+ panic!("Moz2D replay problem");
+ }
+ }
+ });
+
+ (job.request, result)
+}
+
+impl BlobImageHandler for Moz2dBlobImageHandler {
+ fn create_similar(&self) -> Box<dyn BlobImageHandler> {
+ Box::new(Self::new(
+ Arc::clone(&self.workers),
+ Arc::clone(&self.workers_low_priority),
+ ))
+ }
+
+ fn add(&mut self, key: BlobImageKey, data: Arc<BlobImageData>, visible_rect: &DeviceIntRect, tile_size: TileSize) {
+ {
+ let index = BlobReader::new(&data);
+ assert!(index.reader.has_more());
+ }
+ self.blob_commands.insert(
+ key,
+ BlobCommand {
+ data: Arc::clone(&data),
+ visible_rect: *visible_rect,
+ tile_size,
+ },
+ );
+ }
+
+ fn update(
+ &mut self,
+ key: BlobImageKey,
+ data: Arc<BlobImageData>,
+ visible_rect: &DeviceIntRect,
+ dirty_rect: &BlobDirtyRect,
+ ) {
+ match self.blob_commands.entry(key) {
+ hash_map::Entry::Occupied(mut e) => {
+ let command = e.get_mut();
+ let dirty_rect = if let DirtyRect::Partial(rect) = *dirty_rect {
+ rect.cast_unit()
+ } else {
+ DeviceIntRect {
+ min: point2(i32::MIN, i32::MIN),
+ max: point2(i32::MAX, i32::MAX),
+ }
+ };
+ command.data = Arc::new(merge_blob_images(
+ &command.data,
+ &data,
+ dirty_rect,
+ command.visible_rect,
+ *visible_rect,
+ ));
+ command.visible_rect = *visible_rect;
+ },
+ _ => {
+ panic!("missing image key");
+ },
+ }
+ }
+
+ fn delete(&mut self, key: BlobImageKey) {
+ self.blob_commands.remove(&key);
+ }
+
+ fn create_blob_rasterizer(&mut self) -> Box<dyn AsyncBlobImageRasterizer> {
+ Box::new(Moz2dBlobRasterizer {
+ workers: Arc::clone(&self.workers),
+ workers_low_priority: Arc::clone(&self.workers_low_priority),
+ blob_commands: self.blob_commands.clone(),
+ enable_multithreading: self.enable_multithreading,
+ })
+ }
+
+ fn delete_font(&mut self, font: FontKey) {
+ unsafe {
+ DeleteFontData(font);
+ }
+ }
+
+ fn delete_font_instance(&mut self, key: FontInstanceKey) {
+ unsafe {
+ DeleteBlobFont(key);
+ }
+ }
+
+ fn clear_namespace(&mut self, namespace: IdNamespace) {
+ unsafe {
+ ClearBlobImageResources(namespace);
+ }
+ }
+
+ fn prepare_resources(&mut self, resources: &dyn BlobImageResources, requests: &[BlobImageParams]) {
+ for params in requests {
+ let commands = &self.blob_commands[&params.request.key];
+ let blob = Arc::clone(&commands.data);
+ self.prepare_request(&blob, resources);
+ }
+ }
+
+ fn enable_multithreading(&mut self, enable: bool) {
+ self.enable_multithreading = enable;
+ }
+}
+
+use bindings::{WrFontInstanceKey, WrFontKey, WrIdNamespace};
+
+#[allow(improper_ctypes)] // this is needed so that rustc doesn't complain about passing the &Arc<Vec> to an extern function
+extern "C" {
+ fn HasFontData(key: WrFontKey) -> bool;
+ fn AddFontData(key: WrFontKey, data: *const u8, size: usize, index: u32, vec: &ArcVecU8);
+ fn AddNativeFontHandle(key: WrFontKey, handle: *mut c_void, index: u32);
+ fn DeleteFontData(key: WrFontKey);
+ fn AddBlobFont(
+ instance_key: WrFontInstanceKey,
+ font_key: WrFontKey,
+ size: f32,
+ options: Option<&FontInstanceOptions>,
+ platform_options: Option<&FontInstancePlatformOptions>,
+ variations: *const FontVariation,
+ num_variations: usize,
+ );
+ fn DeleteBlobFont(key: WrFontInstanceKey);
+ fn ClearBlobImageResources(namespace: WrIdNamespace);
+
+}
+
+impl Moz2dBlobImageHandler {
+ /// Create a new BlobImageHandler with the given thread pool.
+ pub fn new(workers: Arc<ThreadPool>, workers_low_priority: Arc<ThreadPool>) -> Self {
+ Moz2dBlobImageHandler {
+ blob_commands: HashMap::new(),
+ workers,
+ workers_low_priority,
+ enable_multithreading: true,
+ }
+ }
+
+ /// Does early preprocessing of a blob's resources.
+ ///
+ /// Currently just sets up fonts found in the blob.
+ fn prepare_request(&self, blob: &[u8], resources: &dyn BlobImageResources) {
+ #[cfg(target_os = "windows")]
+ fn process_native_font_handle(key: FontKey, handle: &NativeFontHandle) {
+ let file = dwrote::FontFile::new_from_path(&handle.path).unwrap();
+ let face = file
+ .create_face(handle.index, dwrote::DWRITE_FONT_SIMULATIONS_NONE)
+ .unwrap();
+ unsafe { AddNativeFontHandle(key, face.as_ptr() as *mut c_void, 0) };
+ }
+
+ #[cfg(target_os = "macos")]
+ fn process_native_font_handle(key: FontKey, handle: &NativeFontHandle) {
+ let font = match CGFont::from_name(&CFString::new(&handle.name)) {
+ Ok(font) => font,
+ Err(_) => {
+ // If for some reason we failed to load a font descriptor, then our
+ // only options are to either abort or substitute a fallback font.
+ // It is preferable to use a fallback font instead so that rendering
+ // can at least still proceed in some fashion without erroring.
+ // Lucida Grande is the fallback font in Gecko, so use that here.
+ CGFont::from_name(&CFString::from_static_string("Lucida Grande"))
+ .expect("Failed reading font descriptor and could not load fallback font")
+ },
+ };
+ unsafe { AddNativeFontHandle(key, font.as_ptr() as *mut c_void, 0) };
+ }
+
+ #[cfg(not(any(target_os = "macos", target_os = "windows")))]
+ fn process_native_font_handle(key: FontKey, handle: &NativeFontHandle) {
+ let cstr = CString::new(handle.path.as_os_str().as_bytes()).unwrap();
+ unsafe { AddNativeFontHandle(key, cstr.as_ptr() as *mut c_void, handle.index) };
+ }
+
+ fn process_fonts(
+ mut extra_data: BufReader,
+ resources: &dyn BlobImageResources,
+ unscaled_fonts: &mut Vec<FontKey>,
+ scaled_fonts: &mut Vec<FontInstanceKey>,
+ ) {
+ let font_count = extra_data.read_usize();
+ for _ in 0..font_count {
+ let font = extra_data.read_blob_font();
+ if scaled_fonts.contains(&font.font_instance_key) {
+ continue;
+ }
+ scaled_fonts.push(font.font_instance_key);
+ if let Some(instance) = resources.get_font_instance_data(font.font_instance_key) {
+ if !unscaled_fonts.contains(&instance.font_key) {
+ unscaled_fonts.push(instance.font_key);
+ if !unsafe { HasFontData(instance.font_key) } {
+ let template = resources.get_font_data(instance.font_key).unwrap();
+ match template {
+ FontTemplate::Raw(ref data, ref index) => unsafe {
+ AddFontData(instance.font_key, data.as_ptr(), data.len(), *index, data);
+ },
+ FontTemplate::Native(ref handle) => {
+ process_native_font_handle(instance.font_key, handle);
+ },
+ }
+ }
+ }
+ unsafe {
+ AddBlobFont(
+ font.font_instance_key,
+ instance.font_key,
+ instance.size,
+ instance.options.as_ref(),
+ instance.platform_options.as_ref(),
+ instance.variations.as_ptr(),
+ instance.variations.len(),
+ );
+ }
+ }
+ }
+ }
+
+ {
+ let mut index = BlobReader::new(blob);
+ let mut unscaled_fonts = Vec::new();
+ let mut scaled_fonts = Vec::new();
+ while index.reader.pos < index.reader.buf.len() {
+ let e = index.read_entry();
+ process_fonts(
+ BufReader::new(&blob[e.end..e.extra_end]),
+ resources,
+ &mut unscaled_fonts,
+ &mut scaled_fonts,
+ );
+ }
+ }
+ }
+}
diff --git a/gfx/webrender_bindings/src/program_cache.rs b/gfx/webrender_bindings/src/program_cache.rs
new file mode 100644
index 0000000000..5db61fc8b5
--- /dev/null
+++ b/gfx/webrender_bindings/src/program_cache.rs
@@ -0,0 +1,355 @@
+/* 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/. */
+
+use std::cell::RefCell;
+use std::ffi::OsString;
+use std::fs::{create_dir_all, read_dir, read_to_string, File};
+use std::io::{Error, ErrorKind};
+use std::io::{Read, Write};
+use std::path::{Path, PathBuf};
+use std::rc::Rc;
+use std::sync::Arc;
+
+use bincode;
+use fxhash;
+use nsstring::nsAString;
+use rayon::ThreadPool;
+use webrender::{ProgramBinary, ProgramCache, ProgramCacheObserver, ProgramSourceDigest};
+
+const MAX_LOAD_TIME_MS: u64 = 400;
+
+fn deserialize_program_binary(path: &Path) -> Result<Arc<ProgramBinary>, Error> {
+ let mut buf = vec![];
+ let mut file = File::open(path)?;
+ file.read_to_end(&mut buf)?;
+
+ if buf.len() <= 8 + 4 {
+ return Err(Error::new(ErrorKind::InvalidData, "File size is too small"));
+ }
+ let magic = &buf[0..4];
+ let hash = &buf[4..8 + 4];
+ let data = &buf[8 + 4..];
+
+ // Check if magic + version are correct.
+ let mv: u32 = bincode::deserialize(&magic).unwrap();
+ if mv != MAGIC_AND_VERSION {
+ return Err(Error::new(
+ ErrorKind::InvalidData,
+ "File data is invalid (magic+version)",
+ ));
+ }
+
+ // Check if hash is correct
+ let hash: u64 = bincode::deserialize(&hash).unwrap();
+ let hash_data = fxhash::hash64(&data);
+ if hash != hash_data {
+ return Err(Error::new(ErrorKind::InvalidData, "File data is invalid (hash)"));
+ }
+
+ // Deserialize ProgramBinary
+ let binary = match bincode::deserialize(&data) {
+ Ok(binary) => binary,
+ Err(_) => {
+ return Err(Error::new(
+ ErrorKind::InvalidData,
+ "Failed to deserialize ProgramBinary",
+ ))
+ },
+ };
+
+ Ok(Arc::new(binary))
+}
+
+#[cfg(target_os = "windows")]
+fn get_cache_path_from_prof_path(prof_path: &nsAString) -> Option<PathBuf> {
+ if prof_path.is_empty() {
+ // Empty means that we do not use disk cache.
+ return None;
+ }
+
+ use std::os::windows::prelude::*;
+
+ let prof_path = OsString::from_wide(prof_path.as_ref());
+ let mut cache_path = PathBuf::from(&prof_path);
+ cache_path.push("shader-cache");
+
+ Some(cache_path)
+}
+
+#[cfg(not(target_os = "windows"))]
+fn get_cache_path_from_prof_path(prof_path: &nsAString) -> Option<PathBuf> {
+ if prof_path.is_empty() {
+ // Empty means that we do not use disk cache.
+ return None;
+ }
+
+ let utf8 = String::from_utf16(prof_path.as_ref()).unwrap();
+ let prof_path = OsString::from(utf8);
+ let mut cache_path = PathBuf::from(&prof_path);
+ cache_path.push("shader-cache");
+
+ Some(cache_path)
+}
+
+struct WrProgramBinaryDiskCache {
+ cache_path: PathBuf,
+ workers: Arc<ThreadPool>,
+ cached_shader_filenames: Vec<OsString>,
+}
+
+// Magic number + version. Increment the version when the binary format changes.
+const MAGIC: u32 = 0xB154AD30; // BI-SHADE + version.
+const VERSION: u32 = 2;
+const MAGIC_AND_VERSION: u32 = MAGIC + VERSION;
+
+const WHITELIST_FILENAME: &str = "startup_shaders";
+const WHITELIST_SEPARATOR: &str = "\n";
+
+/// Helper to convert a closure returning a `Result` to one that returns void.
+/// This allows the enclosed code to use the question-mark operator in a
+/// context where the calling function doesn't expect a `Result`.
+#[allow(unused_must_use)]
+fn result_to_void<F: FnOnce() -> Result<(), ()>>(f: F) {
+ f();
+}
+
+impl WrProgramBinaryDiskCache {
+ #[allow(dead_code)]
+ fn new(cache_path: PathBuf, workers: &Arc<ThreadPool>) -> Self {
+ WrProgramBinaryDiskCache {
+ cache_path,
+ workers: Arc::clone(workers),
+ cached_shader_filenames: Vec::new(),
+ }
+ }
+
+ /// Saves the specified binaries to the on-disk shader cache.
+ fn save_shaders_to_disk(&mut self, entries: Vec<Arc<ProgramBinary>>) {
+ info!("Saving binaries to on-disk shader cache");
+
+ // Write the entries to disk on a worker thread.
+ for entry in entries {
+ let file_name = entry.source_digest().to_string();
+ let file_path = self.cache_path.join(&file_name);
+
+ self.workers.spawn(move || {
+ result_to_void(move || {
+ info!("Writing shader: {}", file_name);
+
+ use std::time::Instant;
+ let start = Instant::now();
+
+ let data: Vec<u8> =
+ bincode::serialize(&*entry).map_err(|e| error!("shader-cache: Failed to serialize: {}", e))?;
+
+ let mut file =
+ File::create(&file_path).map_err(|e| error!("shader-cache: Failed to create file: {}", e))?;
+
+ // Write magic + version.
+ let mv = MAGIC_AND_VERSION;
+ let mv = bincode::serialize(&mv).unwrap();
+ assert!(mv.len() == 4);
+ file.write_all(&mv)
+ .map_err(|e| error!("shader-cache: Failed to write magic + version: {}", e))?;
+
+ // Write hash
+ let hash = fxhash::hash64(&data);
+ let hash = bincode::serialize(&hash).unwrap();
+ assert!(hash.len() == 8);
+ file.write_all(&hash)
+ .map_err(|e| error!("shader-cache: Failed to write hash: {}", e))?;
+
+ // Write serialized data
+ file.write_all(&data)
+ .map_err(|e| error!("shader-cache: Failed to write program binary: {}", e))?;
+
+ info!("Wrote shader {} in {:?}", file_name, start.elapsed());
+ Ok(())
+ })
+ });
+ }
+ }
+
+ /// Writes the whitelist containing the set of startup shaders to disk.
+ fn set_startup_shaders(&mut self, entries: Vec<Arc<ProgramBinary>>) {
+ let whitelist = entries
+ .iter()
+ .map(|e| e.source_digest().to_string())
+ .collect::<Vec<String>>()
+ .join(WHITELIST_SEPARATOR);
+
+ let mut whitelist_path = self.cache_path.clone();
+ whitelist_path.push(WHITELIST_FILENAME);
+ self.workers.spawn(move || {
+ result_to_void(move || {
+ info!("Writing startup shader whitelist");
+ File::create(&whitelist_path)
+ .and_then(|mut file| file.write_all(whitelist.as_bytes()))
+ .map_err(|e| error!("shader-cache: Failed to write startup whitelist: {}", e))?;
+ Ok(())
+ })
+ });
+ }
+
+ pub fn try_load_shader_from_disk(&mut self, filename: &str, program_cache: &Rc<ProgramCache>) {
+ if let Some(index) = self.cached_shader_filenames.iter().position(|e| e == filename) {
+ let mut path = self.cache_path.clone();
+ path.push(filename);
+
+ self.cached_shader_filenames.swap_remove(index);
+
+ info!("Loading shader: {}", filename);
+
+ match deserialize_program_binary(&path) {
+ Ok(program) => {
+ program_cache.load_program_binary(program);
+ },
+ Err(err) => {
+ error!("shader-cache: Failed to deserialize program binary: {}", err);
+ },
+ };
+ } else {
+ info!("shader-cache: Program binary not found in disk cache");
+ }
+ }
+
+ pub fn try_load_startup_shaders_from_disk(&mut self, program_cache: &Rc<ProgramCache>) {
+ use std::time::Instant;
+ let start = Instant::now();
+
+ // Load and parse the whitelist if it exists
+ let mut whitelist_path = self.cache_path.clone();
+ whitelist_path.push(WHITELIST_FILENAME);
+ let whitelist = match read_to_string(&whitelist_path) {
+ Ok(whitelist) => whitelist
+ .split(WHITELIST_SEPARATOR)
+ .map(|s| s.to_string())
+ .collect::<Vec<String>>(),
+ Err(err) => {
+ info!("shader-cache: Could not read startup whitelist: {}", err);
+ Vec::new()
+ },
+ };
+ info!("Loaded startup shader whitelist in {:?}", start.elapsed());
+
+ self.cached_shader_filenames = read_dir(&self.cache_path)
+ .map_err(|err| {
+ error!(
+ "shader-cache: Error reading directory whilst loading startup shaders: {}",
+ err
+ )
+ })
+ .into_iter()
+ .flatten()
+ .filter_map(Result::ok)
+ .map(|entry| entry.file_name())
+ .filter(|filename| filename != WHITELIST_FILENAME)
+ .collect::<Vec<OsString>>();
+
+ // Load whitelisted program binaries if they exist
+ for entry in &whitelist {
+ self.try_load_shader_from_disk(&entry, program_cache);
+
+ let elapsed = start.elapsed();
+ info!("Loaded shader in {:?}", elapsed);
+ let elapsed_ms = (elapsed.as_secs() * 1_000) + elapsed.subsec_millis() as u64;
+
+ if elapsed_ms > MAX_LOAD_TIME_MS {
+ // Loading the startup shaders is taking too long, so bail out now.
+ // Additionally clear the list of remaining shaders cached on disk,
+ // so that we do not attempt to load any on demand during rendering.
+ error!("shader-cache: Timed out before finishing loads");
+ self.cached_shader_filenames.clear();
+ break;
+ }
+ }
+ }
+}
+
+pub struct WrProgramCacheObserver {
+ disk_cache: Rc<RefCell<WrProgramBinaryDiskCache>>,
+}
+
+impl WrProgramCacheObserver {
+ #[allow(dead_code)]
+ fn new(disk_cache: Rc<RefCell<WrProgramBinaryDiskCache>>) -> Self {
+ WrProgramCacheObserver { disk_cache }
+ }
+}
+
+impl ProgramCacheObserver for WrProgramCacheObserver {
+ fn save_shaders_to_disk(&self, entries: Vec<Arc<ProgramBinary>>) {
+ self.disk_cache.borrow_mut().save_shaders_to_disk(entries);
+ }
+
+ fn set_startup_shaders(&self, entries: Vec<Arc<ProgramBinary>>) {
+ self.disk_cache.borrow_mut().set_startup_shaders(entries);
+ }
+
+ fn try_load_shader_from_disk(&self, digest: &ProgramSourceDigest, program_cache: &Rc<ProgramCache>) {
+ let filename = digest.to_string();
+ self.disk_cache
+ .borrow_mut()
+ .try_load_shader_from_disk(&filename, program_cache);
+ }
+
+ fn notify_program_binary_failed(&self, _program_binary: &Arc<ProgramBinary>) {
+ error!("shader-cache: Failed program_binary");
+ }
+}
+
+pub struct WrProgramCache {
+ pub program_cache: Rc<ProgramCache>,
+ disk_cache: Option<Rc<RefCell<WrProgramBinaryDiskCache>>>,
+}
+
+impl WrProgramCache {
+ pub fn new(prof_path: &nsAString, workers: &Arc<ThreadPool>) -> Self {
+ let cache_path = get_cache_path_from_prof_path(prof_path);
+ let use_disk_cache = cache_path.as_ref().map_or(false, |p| create_dir_all(p).is_ok());
+ let (disk_cache, program_cache_observer) = if use_disk_cache {
+ let cache = Rc::new(RefCell::new(WrProgramBinaryDiskCache::new(
+ cache_path.unwrap(),
+ workers,
+ )));
+ let obs = Box::new(WrProgramCacheObserver::new(Rc::clone(&cache))) as Box<dyn ProgramCacheObserver>;
+ (Some(cache), Some(obs))
+ } else {
+ (None, None)
+ };
+ let program_cache = ProgramCache::new(program_cache_observer);
+
+ WrProgramCache {
+ program_cache,
+ disk_cache,
+ }
+ }
+
+ pub fn rc_get(&self) -> &Rc<ProgramCache> {
+ &self.program_cache
+ }
+
+ pub fn try_load_startup_shaders_from_disk(&self) {
+ if let Some(ref disk_cache) = self.disk_cache {
+ disk_cache
+ .borrow_mut()
+ .try_load_startup_shaders_from_disk(&self.program_cache);
+ } else {
+ error!("shader-cache: Shader disk cache is not supported");
+ }
+ }
+}
+
+pub fn remove_disk_cache(prof_path: &nsAString) -> Result<(), Error> {
+ use std::time::Instant;
+
+ if let Some(cache_path) = get_cache_path_from_prof_path(prof_path) {
+ if cache_path.exists() {
+ let start = Instant::now();
+ remove_dir_all::remove_dir_all(&cache_path)?;
+ info!("removed all disk cache shaders in {:?}", start.elapsed());
+ }
+ }
+ Ok(())
+}
diff --git a/gfx/webrender_bindings/src/swgl_bindings.rs b/gfx/webrender_bindings/src/swgl_bindings.rs
new file mode 100644
index 0000000000..b1e7684a69
--- /dev/null
+++ b/gfx/webrender_bindings/src/swgl_bindings.rs
@@ -0,0 +1,101 @@
+/* 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/. */
+
+use gleam::gl::Gl;
+
+use std::os::raw::c_void;
+
+#[no_mangle]
+pub extern "C" fn wr_swgl_create_context() -> *mut c_void {
+ swgl::Context::create().into()
+}
+
+#[no_mangle]
+pub extern "C" fn wr_swgl_reference_context(ctx: *mut c_void) {
+ swgl::Context::from(ctx).reference();
+}
+
+#[no_mangle]
+pub extern "C" fn wr_swgl_destroy_context(ctx: *mut c_void) {
+ swgl::Context::from(ctx).destroy();
+}
+
+#[no_mangle]
+pub extern "C" fn wr_swgl_make_current(ctx: *mut c_void) {
+ swgl::Context::from(ctx).make_current();
+}
+
+#[no_mangle]
+pub extern "C" fn wr_swgl_init_default_framebuffer(
+ ctx: *mut c_void,
+ x: i32,
+ y: i32,
+ width: i32,
+ height: i32,
+ stride: i32,
+ buf: *mut c_void,
+) {
+ swgl::Context::from(ctx).init_default_framebuffer(x, y, width, height, stride, buf);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_swgl_resolve_framebuffer(ctx: *mut c_void, fbo: u32) {
+ swgl::Context::from(ctx).resolve_framebuffer(fbo);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_swgl_gen_texture(ctx: *mut c_void) -> u32 {
+ swgl::Context::from(ctx).gen_textures(1)[0]
+}
+
+#[no_mangle]
+pub extern "C" fn wr_swgl_delete_texture(ctx: *mut c_void, tex: u32) {
+ swgl::Context::from(ctx).delete_textures(&[tex]);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_swgl_set_texture_parameter(ctx: *mut c_void, tex: u32, pname: u32, param: i32) {
+ swgl::Context::from(ctx).set_texture_parameter(tex, pname, param);
+}
+
+#[no_mangle]
+pub extern "C" fn wr_swgl_set_texture_buffer(
+ ctx: *mut c_void,
+ tex: u32,
+ internal_format: u32,
+ width: i32,
+ height: i32,
+ stride: i32,
+ buf: *mut c_void,
+ min_width: i32,
+ min_height: i32,
+) {
+ swgl::Context::from(ctx).set_texture_buffer(
+ tex,
+ internal_format,
+ width,
+ height,
+ stride,
+ buf,
+ min_width,
+ min_height,
+ );
+}
+
+#[no_mangle]
+#[allow(clippy::many_single_char_names)]
+pub extern "C" fn wr_swgl_clear_color_rect(
+ ctx: *mut c_void,
+ fbo: u32,
+ x: i32,
+ y: i32,
+ width: i32,
+ height: i32,
+ r: f32,
+ g: f32,
+ b: f32,
+ a: f32,
+) {
+ swgl::Context::from(ctx).clear_color_rect(fbo, x, y, width, height, r, g, b, a);
+}
diff --git a/gfx/webrender_bindings/webrender_ffi.h b/gfx/webrender_bindings/webrender_ffi.h
new file mode 100644
index 0000000000..e9c8be1ef4
--- /dev/null
+++ b/gfx/webrender_bindings/webrender_ffi.h
@@ -0,0 +1,123 @@
+/* -*- 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 WR_h
+#define WR_h
+
+#include "mozilla/gfx/Types.h"
+#include "nsTArray.h"
+
+extern "C" {
+
+// ----
+// Functions invoked from Rust code
+// ----
+
+bool is_in_compositor_thread();
+bool is_in_main_thread();
+bool is_in_render_thread();
+bool is_glcontext_gles(void* glcontext_ptr);
+bool is_glcontext_angle(void* glcontext_ptr);
+bool gfx_use_wrench();
+const char* gfx_wr_resource_path_override();
+bool gfx_wr_use_optimized_shaders();
+void gfx_critical_note(const char* msg);
+void gfx_critical_error(const char* msg);
+void gecko_printf_stderr_output(const char* msg);
+void* get_proc_address_from_glcontext(void* glcontext_ptr,
+ const char* procname);
+
+bool gecko_profiler_thread_is_being_profiled();
+
+// IMPORTANT: Keep this synchronized with enumerate_interners in
+// gfx/wr/webrender_api
+#define WEBRENDER_FOR_EACH_INTERNER(macro) \
+ macro(clip); \
+ macro(prim); \
+ macro(normal_border); \
+ macro(image_border); \
+ macro(image); \
+ macro(yuv_image); \
+ macro(line_decoration); \
+ macro(linear_grad); \
+ macro(radial_grad); \
+ macro(conic_grad); \
+ macro(picture); \
+ macro(text_run); \
+ macro(filterdata); \
+ macro(backdrop_capture); \
+ macro(backdrop_render); \
+ macro(polyon);
+
+// Prelude of types necessary before including webrender_ffi_generated.h
+namespace mozilla {
+namespace wr {
+
+// Because this struct is macro-generated on the Rust side, cbindgen can't see
+// it. Work around that by re-declaring it here.
+#define DECLARE_MEMBER(id) uintptr_t id;
+struct InternerSubReport {
+ WEBRENDER_FOR_EACH_INTERNER(DECLARE_MEMBER)
+};
+
+#undef DECLARE_MEMBER
+
+struct Transaction;
+struct WrWindowId;
+struct DocumentId;
+struct WrPipelineInfo;
+
+struct WrPipelineIdAndEpoch;
+using WrPipelineIdEpochs = nsTArray<WrPipelineIdAndEpoch>;
+
+} // namespace wr
+} // namespace mozilla
+
+void apz_register_updater(mozilla::wr::WrWindowId aWindowId);
+void apz_pre_scene_swap(mozilla::wr::WrWindowId aWindowId);
+void apz_post_scene_swap(mozilla::wr::WrWindowId aWindowId,
+ const mozilla::wr::WrPipelineInfo* aInfo);
+void apz_run_updater(mozilla::wr::WrWindowId aWindowId);
+void apz_deregister_updater(mozilla::wr::WrWindowId aWindowId);
+
+void apz_register_sampler(mozilla::wr::WrWindowId aWindowId);
+void apz_sample_transforms(mozilla::wr::WrWindowId aWindowId,
+ const uint64_t* aGeneratedFrameId,
+ mozilla::wr::Transaction* aTransaction);
+void apz_deregister_sampler(mozilla::wr::WrWindowId aWindowId);
+
+void omta_register_sampler(mozilla::wr::WrWindowId aWindowId);
+void omta_sample(mozilla::wr::WrWindowId aWindowId,
+ mozilla::wr::Transaction* aTransaction);
+void omta_deregister_sampler(mozilla::wr::WrWindowId aWindowId);
+} // extern "C"
+
+// Work-around Solaris define which conflcits with WR color constant, see
+// bug 1773491.
+#pragma push_macro("TRANSPARENT")
+#undef TRANSPARENT
+
+#include "webrender_ffi_generated.h"
+
+#pragma pop_macro("TRANSPARENT")
+
+template struct mozilla::wr::Point2D<int32_t, mozilla::wr::DevicePixel>;
+template struct mozilla::wr::Point2D<int, mozilla::wr::WorldPixel>;
+template struct mozilla::wr::Point2D<float, mozilla::wr::WorldPixel>;
+template struct mozilla::wr::Box2D<int32_t, mozilla::wr::DevicePixel>;
+template struct mozilla::wr::Box2D<int, mozilla::wr::LayoutPixel>;
+
+namespace mozilla {
+namespace wr {
+
+// Cast a blob image key into a regular image for use in
+// a display item.
+inline ImageKey AsImageKey(BlobImageKey aKey) { return aKey._0; }
+
+} // namespace wr
+} // namespace mozilla
+
+#endif // WR_h