summaryrefslogtreecommitdiffstats
path: root/gfx/webrender_bindings/WebRenderAPI.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /gfx/webrender_bindings/WebRenderAPI.cpp
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/webrender_bindings/WebRenderAPI.cpp')
-rw-r--r--gfx/webrender_bindings/WebRenderAPI.cpp1867
1 files changed, 1867 insertions, 0 deletions
diff --git a/gfx/webrender_bindings/WebRenderAPI.cpp b/gfx/webrender_bindings/WebRenderAPI.cpp
new file mode 100644
index 0000000000..09a024a897
--- /dev/null
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -0,0 +1,1867 @@
+/* -*- 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/Logging.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"
+
+static mozilla::LazyLogModule sWrDLLog("wr.dl");
+#define WRDL_LOG(...) \
+ MOZ_LOG(sWrDLLog, LogLevel::Debug, ("WRDL(%p): " __VA_ARGS__))
+
+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(),
+ StaticPrefs::gfx_webrender_enable_subpixel_aa_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()),
+ mOwnsData(true) {
+ mTxn = wr_transaction_new(mUseSceneBuilderThread);
+}
+
+TransactionBuilder::TransactionBuilder(WebRenderAPI* aApi, Transaction* aTxn,
+ bool aUseSceneBuilderThread,
+ bool aOwnsData)
+ : mTxn(aTxn),
+ mUseSceneBuilderThread(aUseSceneBuilderThread),
+ mApiBackend(aApi->GetBackendType()),
+ mOwnsData(aOwnsData) {}
+
+TransactionBuilder::~TransactionBuilder() {
+ if (mOwnsData) {
+ 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::ExternalScrollId& aScrollId,
+ const nsTArray<wr::SampledScrollOffset>& aSampledOffsets) {
+ wr_transaction_scroll_layer(mTxn, aScrollId, &aSampledOffsets);
+}
+
+void TransactionWrapper::UpdateIsTransformAsyncZooming(uint64_t aAnimationId,
+ bool aIsZooming) {
+ wr_transaction_set_is_transform_async_zooming(mTxn, aAnimationId, aIsZooming);
+}
+
+void TransactionWrapper::AddMinimapData(const wr::ExternalScrollId& aScrollId,
+ const MinimapData& aMinimapData) {
+ wr_transaction_add_minimap_data(mTxn, aScrollId, aMinimapData);
+}
+
+/*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 = {};
+
+ // 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, this, 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,
+ wr::WebRenderAPI* aRootApi, wr::WebRenderAPI* aRootDocumentApi)
+ : mDocHandle(aHandle),
+ mId(aId),
+ mBackend(aBackend),
+ mCompositor(aCompositor),
+ mMaxTextureSize(aMaxTextureSize),
+ mUseANGLE(aUseANGLE),
+ mUseDComp(aUseDComp),
+ mUseTripleBuffering(aUseTripleBuffering),
+ mSupportsExternalBufferTextures(aSupportsExternalBufferTextures),
+ mCaptureSequence(false),
+ mSyncHandle(aSyncHandle),
+ mRendererDestroyed(false),
+ mRootApi(aRootApi),
+ mRootDocumentApi(aRootDocumentApi) {}
+
+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;
+}
+
+wr::WebRenderAPI* WebRenderAPI::GetRootAPI() {
+ if (mRootApi) {
+ return mRootApi;
+ }
+ return this;
+}
+
+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 (mPendingAsyncImagePipelineOps &&
+ !mPendingAsyncImagePipelineOps->mList.empty()) {
+ mPendingWrTransactionEvents.emplace(
+ WrTransactionEvent::PendingAsyncImagePipelineOps(
+ std::move(mPendingAsyncImagePipelineOps), this, aTxn.Raw(),
+ aTxn.UseSceneBuilderThread()));
+ }
+
+ if (!mPendingWrTransactionEvents.empty()) {
+ mPendingWrTransactionEvents.emplace(WrTransactionEvent::Transaction(
+ this, 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 (!mPendingRemoteTextureInfoList) {
+ mPendingRemoteTextureInfoList = MakeUnique<layers::RemoteTextureInfoList>();
+ }
+ return mPendingRemoteTextureInfoList.get();
+}
+
+layers::AsyncImagePipelineOps* WebRenderAPI::GetPendingAsyncImagePipelineOps(
+ TransactionBuilder& aTxn) {
+ if (!mRootApi) {
+ // root api does not support async wait RemoteTexture.
+ return nullptr;
+ }
+
+ if (!mPendingAsyncImagePipelineOps ||
+ mPendingAsyncImagePipelineOps->mTransaction != aTxn.Raw()) {
+ if (mPendingAsyncImagePipelineOps &&
+ !mPendingAsyncImagePipelineOps->mList.empty()) {
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+ gfxCriticalNoteOnce << "Invalid AsyncImagePipelineOps";
+ }
+ mPendingAsyncImagePipelineOps =
+ MakeUnique<layers::AsyncImagePipelineOps>(aTxn.Raw());
+ } else {
+ MOZ_RELEASE_ASSERT(mPendingAsyncImagePipelineOps->mTransaction ==
+ aTxn.Raw());
+ }
+
+ return mPendingAsyncImagePipelineOps.get();
+}
+
+bool WebRenderAPI::CheckIsRemoteTextureReady(
+ layers::RemoteTextureInfoList* aList, const TimeStamp& aTimeStamp) {
+ MOZ_ASSERT(layers::CompositorThreadHolder::IsInCompositorThread());
+ MOZ_ASSERT(aList);
+
+ 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());
+ };
+
+ const auto maxWaitDurationMs = 10000;
+ const auto now = TimeStamp::Now();
+ const auto waitDurationMs =
+ static_cast<uint32_t>((now - aTimeStamp).ToMilliseconds());
+
+ const auto isTimeout = waitDurationMs > maxWaitDurationMs;
+ if (isTimeout) {
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+ gfxCriticalNote << "RemoteTexture ready timeout";
+ }
+
+ bool isReady = true;
+ while (!aList->mList.empty() && isReady) {
+ auto& front = aList->mList.front();
+ isReady &= layers::RemoteTextureMap::Get()->CheckRemoteTextureReady(
+ front, callback);
+ if (isTimeout) {
+ isReady = true;
+ }
+ if (isReady) {
+ aList->mList.pop();
+ }
+ }
+
+ return isReady;
+}
+
+void WebRenderAPI::WaitRemoteTextureReady(
+ layers::RemoteTextureInfoList* aList) {
+ MOZ_ASSERT(layers::CompositorThreadHolder::IsInCompositorThread());
+ MOZ_ASSERT(aList);
+
+ 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.RawTransaction(),
+ front.UseSceneBuilderThread());
+ break;
+ case WrTransactionEvent::Tag::PendingRemoteTextures: {
+ bool isReady = true;
+ if (aType == RemoteTextureWaitType::AsyncWait) {
+ isReady = CheckIsRemoteTextureReady(front.RemoteTextureInfoList(),
+ front.mTimeStamp);
+ } 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 && (aType != RemoteTextureWaitType::FlushWithoutWait)) {
+ return;
+ }
+ break;
+ }
+ case WrTransactionEvent::Tag::PendingAsyncImagePipelineOps: {
+ auto* list = front.AsyncImagePipelineOps();
+ TransactionBuilder& txn = *front.GetTransactionBuilder();
+
+ list->HandleOps(txn);
+ 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() {
+ if (!mOwnsData) {
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+ return nullptr;
+ }
+ 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();
+}
+
+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);
+ 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());
+ if (MOZ_LOG_TEST(sWrDLLog, LogLevel::Debug)) {
+ nsCString message;
+ message.AppendPrintf("DefineClipChain id=%" PRIu64
+ " clipCount=%zu clipIds=[",
+ clipchainId, aClips.Length());
+ for (const auto& clip : aClips) {
+ message.AppendPrintf("%" PRIuPTR ",", clip.id);
+ }
+ message.Append("]");
+ WRDL_LOG("%s", mWrState, message.get());
+ }
+ 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) {
+ // 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