/* -*- 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&& 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 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 thread = &aRenderThread; auto renderer = MakeUnique(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 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& dl_items_data, wr::Vec& dl_cache_data, wr::Vec& 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& aOpacityArray, const nsTArray& aTransformArray, const nsTArray& 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& aTransformArray) { wr_transaction_append_transform_properties( mTxn, aTransformArray.IsEmpty() ? nullptr : aTransformArray.Elements(), aTransformArray.Length()); } void TransactionWrapper::UpdateScrollPosition( const wr::ExternalScrollId& aScrollId, const nsTArray& 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::Create( layers::CompositorBridgeParent* aBridge, RefPtr&& 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( &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( new WebRenderAPI(docHandle, aWindowId, backend, compositor, maxTextureSize, useANGLE, useDComp, useTripleBuffering, supportsExternalBufferTextures, syncHandle)) .forget(); } already_AddRefed WebRenderAPI::Clone() { wr::DocumentHandle* docHandle = nullptr; wr_api_clone(mDocHandle, &docHandle); RefPtr 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(&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(); } 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(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 self = this; auto callback = [self](const layers::RemoteTextureInfo&) { RefPtr runnable = NewRunnableMethod( "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((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 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 wrResults; wr_api_hit_test(mDocHandle, aPoint, &wrResults); std::vector geckoResults; for (wr::HitResult wrResult : wrResults) { WrHitResult geckoResult; geckoResult.mLayersId = wr::AsLayersId(wrResult.pipeline_id); geckoResult.mScrollId = static_cast(wrResult.scroll_id); geckoResult.mHitInfo.deserialize(wrResult.hit_info & 0x0fff); geckoResult.mSideBits = static_cast(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& buffer, bool* aNeedsYFlip) { class Readback : public RendererEvent { public: explicit Readback(layers::SynchronousTask* aTask, TimeStamp aStartTime, gfx::IntSize aSize, const gfx::SurfaceFormat& aFormat, const Range& 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& mBuffer; bool* mNeedsYFlip; }; // Disable debug flags during readback. See bug 1436020. UpdateDebugFlags(0); layers::SynchronousTask task("Readback"); auto event = MakeUnique(&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(&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(&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(&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(aRecordingStart, aRootPipelineId); RunOnRenderThread(std::move(event)); } RefPtr 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 recording = aRenderThread.EndRecordingForWindow(aWindowId); if (recording) { mPromise.Resolve(recording.extract(), __func__); } else { mPromise.Reject(NS_ERROR_UNEXPECTED, __func__); } } RefPtr GetPromise() { return mPromise.Ensure(__func__); } private: MozPromiseHolder mPromise; }; auto event = MakeUnique(); 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 aEvent) { wr_transaction_notify(mTxn, aWhen, reinterpret_cast(aEvent.release())); } void TransactionBuilder::AddImage(ImageKey key, const ImageDescriptor& aDescriptor, wr::Vec& aBytes) { wr_resource_updates_add_image(mTxn, key, &aDescriptor, &aBytes.inner); } void TransactionBuilder::AddBlobImage(BlobImageKey key, const ImageDescriptor& aDescriptor, uint16_t aTileSize, wr::Vec& 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& aBytes) { wr_resource_updates_update_image(mTxn, aKey, &aDescriptor, &aBytes.inner); } void TransactionBuilder::UpdateBlobImage(BlobImageKey aKey, const ImageDescriptor& aDescriptor, wr::Vec& 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& aBytes, uint32_t aIndex) { wr_resource_updates_add_raw_font(mTxn, aKey, &aBytes.inner, aIndex); } void TransactionBuilder::AddFontDescriptor(wr::FontKey aKey, wr::Vec& 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& 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(aTime); RunOnRenderThread(std::move(event)); } void WebRenderAPI::RunOnRenderThread(UniquePtr aEvent) { auto event = reinterpret_cast(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& aStart, const Maybe& 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 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& 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& 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 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 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 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& 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(aHitInfo.serialize()) | (static_cast(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& aFilters, const nsTArray& 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& 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& 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& 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& 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& 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& 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& 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 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 slot = mDisplayItemCache->CanReuseItem(aItem, CurrentSpaceAndClipChain()); if (slot) { mDisplayItemCache->Stats().AddReused(); wr_dp_push_reuse_items(mWrState, slot.ref()); return true; } return false; } Maybe DisplayListBuilder::GetContainingFixedPosScrollTarget( const ActiveScrolledRoot* aAsr) { return mActiveFixedPosTracker ? mActiveFixedPosTracker->GetScrollTargetForASR(aAsr) : Nothing(); } Maybe 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 DisplayListBuilder::FixedPosScrollTargetTracker::GetScrollTargetForASR( const ActiveScrolledRoot* aAsr) { return aAsr == mAsr ? Some(mScrollId) : Nothing(); } Maybe 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(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(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