/* -*- 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 "DisplayItemClipChain.h" #include "LayersLogging.h" #include "mozilla/webrender/RendererOGL.h" #include "mozilla/gfx/gfxVars.h" #include "mozilla/layers/CompositorThread.h" #include "mozilla/webrender/RenderCompositor.h" #include "mozilla/widget/CompositorWidget.h" #include "mozilla/layers/SynchronousTask.h" #define WRDL_LOG(...) //#define WRDL_LOG(...) printf_stderr("WRDL(%p): " __VA_ARGS__) //#define WRDL_LOG(...) if (XRE_IsContentProcess()) printf_stderr("WRDL(%p): " __VA_ARGS__) namespace mozilla { namespace wr { using layers::Stringify; class NewRenderer : public RendererEvent { public: NewRenderer(wr::DocumentHandle** aDocHandle, layers::CompositorBridgeParentBase* aBridge, uint32_t* aMaxTextureSize, bool* aUseANGLE, RefPtr&& aWidget, layers::SynchronousTask* aTask, LayoutDeviceIntSize aSize, layers::SyncHandle* aHandle) : mDocHandle(aDocHandle) , mMaxTextureSize(aMaxTextureSize) , mUseANGLE(aUseANGLE) , mBridge(aBridge) , mCompositorWidget(Move(aWidget)) , mTask(aTask) , mSize(aSize) , mSyncHandle(aHandle) { MOZ_COUNT_CTOR(NewRenderer); } ~NewRenderer() { MOZ_COUNT_DTOR(NewRenderer); } virtual void Run(RenderThread& aRenderThread, WindowId aWindowId) override { layers::AutoCompleteTask complete(mTask); UniquePtr compositor = RenderCompositor::Create(Move(mCompositorWidget)); if (!compositor) { // RenderCompositor::Create puts a message into gfxCriticalNote if it is nullptr return; } *mUseANGLE = compositor->UseANGLE(); wr::Renderer* wrRenderer = nullptr; if (!wr_window_new(aWindowId, mSize.width, mSize.height, compositor->gl(), aRenderThread.ThreadPool().Raw(), mDocHandle, &wrRenderer, mMaxTextureSize)) { // wr_window_new puts a message into gfxCriticalNote if it returns false return; } MOZ_ASSERT(wrRenderer); RefPtr thread = &aRenderThread; auto renderer = MakeUnique(Move(thread), Move(compositor), aWindowId, wrRenderer, mBridge); if (wrRenderer && renderer) { wr::WrExternalImageHandler handler = renderer->GetExternalImageHandler(); wr_renderer_set_external_image_handler(wrRenderer, &handler); if (gfx::gfxVars::UseWebRenderProgramBinary()) { wr_renderer_update_program_cache(wrRenderer, aRenderThread.ProgramCache()->Raw()); } } if (renderer) { layers::SyncObjectHost* syncObj = renderer->GetSyncObject(); if (syncObj) { *mSyncHandle = syncObj->GetSyncHandle(); } } aRenderThread.AddRenderer(aWindowId, Move(renderer)); } private: wr::DocumentHandle** mDocHandle; uint32_t* mMaxTextureSize; bool* mUseANGLE; layers::CompositorBridgeParentBase* mBridge; RefPtr mCompositorWidget; layers::SynchronousTask* mTask; LayoutDeviceIntSize mSize; layers::SyncHandle* mSyncHandle; }; class RemoveRenderer : public RendererEvent { public: explicit RemoveRenderer(layers::SynchronousTask* aTask) : mTask(aTask) { MOZ_COUNT_CTOR(RemoveRenderer); } ~RemoveRenderer() { MOZ_COUNT_DTOR(RemoveRenderer); } virtual void Run(RenderThread& aRenderThread, WindowId aWindowId) override { aRenderThread.RemoveRenderer(aWindowId); layers::AutoCompleteTask complete(mTask); } private: layers::SynchronousTask* mTask; }; TransactionBuilder::TransactionBuilder() { mTxn = wr_transaction_new(); mResourceUpdates = wr_resource_updates_new(); } TransactionBuilder::~TransactionBuilder() { wr_transaction_delete(mTxn); wr_resource_updates_delete(mResourceUpdates); } 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(gfx::Color aBgColor, Epoch aEpoch, mozilla::LayerSize aViewportSize, wr::WrPipelineId pipeline_id, const wr::LayoutSize& content_size, wr::BuiltDisplayListDescriptor dl_descriptor, wr::Vec& dl_data) { wr_transaction_set_display_list(mTxn, aEpoch, ToColorF(aBgColor), aViewportSize.width, aViewportSize.height, pipeline_id, content_size, dl_descriptor, &dl_data.inner); } void TransactionBuilder::ClearDisplayList(Epoch aEpoch, wr::WrPipelineId aPipelineId) { wr_transaction_clear_display_list(mTxn, aEpoch, aPipelineId); } void TransactionBuilder::GenerateFrame() { wr_transaction_generate_frame(mTxn); } void TransactionBuilder::UpdateDynamicProperties(const nsTArray& aOpacityArray, const nsTArray& aTransformArray) { wr_transaction_update_dynamic_properties(mTxn, aOpacityArray.IsEmpty() ? nullptr : aOpacityArray.Elements(), aOpacityArray.Length(), aTransformArray.IsEmpty() ? nullptr : aTransformArray.Elements(), aTransformArray.Length()); } bool TransactionBuilder::IsEmpty() const { return wr_transaction_is_empty(mTxn); } void TransactionBuilder::SetWindowParameters(const LayoutDeviceIntSize& aWindowSize, const LayoutDeviceIntRect& aDocumentRect) { wr::DeviceUintSize wrWindowSize; wrWindowSize.width = aWindowSize.width; wrWindowSize.height = aWindowSize.height; wr::DeviceUintRect wrDocRect; wrDocRect.origin.x = aDocumentRect.x; wrDocRect.origin.y = aDocumentRect.y; wrDocRect.size.width = aDocumentRect.width; wrDocRect.size.height = aDocumentRect.height; wr_transaction_set_window_parameters(mTxn, &wrWindowSize, &wrDocRect); } void TransactionBuilder::UpdateScrollPosition(const wr::WrPipelineId& aPipelineId, const layers::FrameMetrics::ViewID& aScrollId, const wr::LayoutPoint& aScrollPosition) { wr_transaction_scroll_layer(mTxn, aPipelineId, aScrollId, aScrollPosition); } /*static*/ void WebRenderAPI::InitExternalLogHandler() { // Redirect the webrender's log to gecko's log system. // The current log level is "error". mozilla::wr::wr_init_external_log_handler(wr::LogLevelFilter::Error); } /*static*/ void WebRenderAPI::ShutdownExternalLogHandler() { mozilla::wr::wr_shutdown_external_log_handler(); } /*static*/ already_AddRefed WebRenderAPI::Create(layers::CompositorBridgeParentBase* aBridge, RefPtr&& aWidget, LayoutDeviceIntSize aSize) { MOZ_ASSERT(aBridge); MOZ_ASSERT(aWidget); static uint64_t sNextId = 1; auto id = NewWindowId(sNextId++); wr::DocumentHandle* docHandle = nullptr; uint32_t maxTextureSize = 0; bool useANGLE = false; layers::SyncHandle syncHandle = 0; // Dispatch a synchronous task because the DocumentHandle object needs to be created // on the render thread. If need be we could delay waiting on this task until // the next time we need to access the DocumentHandle object. layers::SynchronousTask task("Create Renderer"); auto event = MakeUnique(&docHandle, aBridge, &maxTextureSize, &useANGLE, Move(aWidget), &task, aSize, &syncHandle); RenderThread::Get()->RunEvent(id, Move(event)); task.Wait(); if (!docHandle) { return nullptr; } return RefPtr(new WebRenderAPI(docHandle, id, maxTextureSize, useANGLE, syncHandle)).forget(); } already_AddRefed WebRenderAPI::Clone() { wr::DocumentHandle* docHandle = nullptr; wr_api_clone(mDocHandle, &docHandle); RefPtr renderApi = new WebRenderAPI(docHandle, mId, mMaxTextureSize, mUseANGLE, mSyncHandle); renderApi->mRootApi = this; // Hold root api renderApi->mRootDocumentApi = this; return renderApi.forget(); } already_AddRefed WebRenderAPI::CreateDocument(LayoutDeviceIntSize aSize, int8_t aLayerIndex) { wr::DeviceUintSize wrSize; wrSize.width = aSize.width; wrSize.height = aSize.height; wr::DocumentHandle* newDoc; wr_api_create_document(mDocHandle, &newDoc, wrSize, aLayerIndex); RefPtr api(new WebRenderAPI(newDoc, mId, mMaxTextureSize, mUseANGLE, mSyncHandle)); api->mRootApi = this; return api.forget(); } wr::WrIdNamespace WebRenderAPI::GetNamespace() { return wr_api_get_namespace(mDocHandle); } extern void ClearBlobImageResources(WrIdNamespace aNamespace); WebRenderAPI::~WebRenderAPI() { if (!mRootDocumentApi) { wr_api_delete_document(mDocHandle); } if (!mRootApi) { RenderThread::Get()->SetDestroyed(GetId()); layers::SynchronousTask task("Destroy WebRenderAPI"); auto event = MakeUnique(&task); RunOnRenderThread(Move(event)); task.Wait(); wr_api_shut_down(mDocHandle); } // wr_api_get_namespace cannot be marked destructor-safe because it has a // return value, and we can't call it if MOZ_BUILD_WEBRENDER is not defined // because it's not destructor-safe. So let's just ifdef around it. This is // basically a hack to get around compile-time warnings, this code never runs // unless MOZ_BUILD_WEBRENDER is defined anyway. #ifdef MOZ_BUILD_WEBRENDER wr::WrIdNamespace ns = GetNamespace(); #else wr::WrIdNamespace ns{0}; #endif // Clean up any resources the blob image renderer is holding onto that // can no longer be used once this WR API instance goes away. ClearBlobImageResources(ns); wr_api_delete(mDocHandle); } void WebRenderAPI::SendTransaction(TransactionBuilder& aTxn) { wr_transaction_update_resources(aTxn.Raw(), aTxn.RawUpdates()); wr_api_send_transaction(mDocHandle, aTxn.Raw()); } bool WebRenderAPI::HitTest(const wr::WorldPoint& aPoint, wr::WrPipelineId& aOutPipelineId, layers::FrameMetrics::ViewID& aOutScrollId, gfx::CompositorHitTestInfo& aOutHitInfo) { static_assert(sizeof(gfx::CompositorHitTestInfo) == sizeof(uint16_t), "CompositorHitTestInfo should be u16-sized"); return wr_api_hit_test(mDocHandle, aPoint, &aOutPipelineId, &aOutScrollId, (uint16_t*)&aOutHitInfo); } void WebRenderAPI::Readback(gfx::IntSize size, uint8_t *buffer, uint32_t buffer_size) { class Readback : public RendererEvent { public: explicit Readback(layers::SynchronousTask* aTask, gfx::IntSize aSize, uint8_t *aBuffer, uint32_t aBufferSize) : mTask(aTask) , mSize(aSize) , mBuffer(aBuffer) , mBufferSize(aBufferSize) { MOZ_COUNT_CTOR(Readback); } ~Readback() { MOZ_COUNT_DTOR(Readback); } virtual void Run(RenderThread& aRenderThread, WindowId aWindowId) override { aRenderThread.UpdateAndRender(aWindowId); wr_renderer_readback(aRenderThread.GetRenderer(aWindowId)->GetRenderer(), mSize.width, mSize.height, mBuffer, mBufferSize); layers::AutoCompleteTask complete(mTask); } layers::SynchronousTask* mTask; gfx::IntSize mSize; uint8_t *mBuffer; uint32_t mBufferSize; }; layers::SynchronousTask task("Readback"); auto event = MakeUnique(&task, size, buffer, buffer_size); // 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(Move(event)); task.Wait(); } void WebRenderAPI::Pause() { class PauseEvent : public RendererEvent { public: explicit PauseEvent(layers::SynchronousTask* aTask) : mTask(aTask) { MOZ_COUNT_CTOR(PauseEvent); } ~PauseEvent() { MOZ_COUNT_DTOR(PauseEvent); } virtual 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(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); } ~ResumeEvent() { MOZ_COUNT_DTOR(ResumeEvent); } virtual 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(Move(event)); task.Wait(); return result; } void WebRenderAPI::WaitFlushed() { class WaitFlushedEvent : public RendererEvent { public: explicit WaitFlushedEvent(layers::SynchronousTask* aTask) : mTask(aTask) { MOZ_COUNT_CTOR(WaitFlushedEvent); } ~WaitFlushedEvent() { MOZ_COUNT_DTOR(WaitFlushedEvent); } virtual 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(Move(event)); task.Wait(); } void WebRenderAPI::Capture() { uint8_t bits = 3; //TODO: get from JavaScript const char* path = "wr-capture"; //TODO: get from JavaScript const char* border = "--------------------------\n"; printf("%s Capturing WR state to: %s\n%s", border, path, border); wr_api_capture(mDocHandle, path, bits); RenderThread::Get()->IncPendingFrameCount(GetId()); } void TransactionBuilder::Clear() { wr_resource_updates_clear(mResourceUpdates); } void TransactionBuilder::AddImage(ImageKey key, const ImageDescriptor& aDescriptor, wr::Vec& aBytes) { wr_resource_updates_add_image(mResourceUpdates, key, &aDescriptor, &aBytes.inner); } void TransactionBuilder::AddBlobImage(ImageKey key, const ImageDescriptor& aDescriptor, wr::Vec& aBytes) { wr_resource_updates_add_blob_image(mResourceUpdates, key, &aDescriptor, &aBytes.inner); } void TransactionBuilder::AddExternalImage(ImageKey key, const ImageDescriptor& aDescriptor, ExternalImageId aExtID, wr::WrExternalImageBufferType aBufferType, uint8_t aChannelIndex) { wr_resource_updates_add_external_image(mResourceUpdates, key, &aDescriptor, aExtID, aBufferType, aChannelIndex); } void TransactionBuilder::AddExternalImageBuffer(ImageKey aKey, const ImageDescriptor& aDescriptor, ExternalImageId aHandle) { auto channelIndex = 0; AddExternalImage(aKey, aDescriptor, aHandle, wr::WrExternalImageBufferType::ExternalBuffer, channelIndex); } void TransactionBuilder::UpdateImageBuffer(ImageKey aKey, const ImageDescriptor& aDescriptor, wr::Vec& aBytes) { wr_resource_updates_update_image(mResourceUpdates, aKey, &aDescriptor, &aBytes.inner); } void TransactionBuilder::UpdateBlobImage(ImageKey aKey, const ImageDescriptor& aDescriptor, wr::Vec& aBytes, const wr::DeviceUintRect& aDirtyRect) { wr_resource_updates_update_blob_image(mResourceUpdates, aKey, &aDescriptor, &aBytes.inner, aDirtyRect); } void TransactionBuilder::UpdateExternalImage(ImageKey aKey, const ImageDescriptor& aDescriptor, ExternalImageId aExtID, wr::WrExternalImageBufferType aBufferType, uint8_t aChannelIndex) { wr_resource_updates_update_external_image(mResourceUpdates, aKey, &aDescriptor, aExtID, aBufferType, aChannelIndex); } void TransactionBuilder::DeleteImage(ImageKey aKey) { wr_resource_updates_delete_image(mResourceUpdates, aKey); } void TransactionBuilder::AddRawFont(wr::FontKey aKey, wr::Vec& aBytes, uint32_t aIndex) { wr_resource_updates_add_raw_font(mResourceUpdates, aKey, &aBytes.inner, aIndex); } void TransactionBuilder::AddFontDescriptor(wr::FontKey aKey, wr::Vec& aBytes, uint32_t aIndex) { wr_resource_updates_add_font_descriptor(mResourceUpdates, aKey, &aBytes.inner, aIndex); } void TransactionBuilder::DeleteFont(wr::FontKey aKey) { wr_resource_updates_delete_font(mResourceUpdates, 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(mResourceUpdates, aKey, aFontKey, aGlyphSize, aOptions, aPlatformOptions, &aVariations.inner); } void TransactionBuilder::DeleteFontInstance(wr::FontInstanceKey aKey) { wr_resource_updates_delete_font_instance(mResourceUpdates, aKey); } class FrameStartTime : public RendererEvent { public: explicit FrameStartTime(const TimeStamp& aTime) : mTime(aTime) { MOZ_COUNT_CTOR(FrameStartTime); } ~FrameStartTime() { MOZ_COUNT_DTOR(FrameStartTime); } virtual 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(Move(event)); } void WebRenderAPI::RunOnRenderThread(UniquePtr aEvent) { auto event = reinterpret_cast(aEvent.release()); wr_api_send_external_event(mDocHandle, event); } DisplayListBuilder::DisplayListBuilder(PipelineId aId, const wr::LayoutSize& aContentSize, size_t aCapacity) { MOZ_COUNT_CTOR(DisplayListBuilder); mWrState = wr_state_new(aId, aContentSize, aCapacity); } 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); } void DisplayListBuilder::Dump() { wr_dump_display_list(mWrState); } void DisplayListBuilder::Finalize(wr::LayoutSize& aOutContentSize, BuiltDisplayList& aOutDisplayList) { wr_api_finalize_builder(mWrState, &aOutContentSize, &aOutDisplayList.dl_desc, &aOutDisplayList.dl.inner); } void DisplayListBuilder::PushStackingContext(const wr::LayoutRect& aBounds, const WrAnimationProperty* aAnimation, const float* aOpacity, const gfx::Matrix4x4* aTransform, wr::TransformStyle aTransformStyle, const gfx::Matrix4x4* aPerspective, const wr::MixBlendMode& aMixBlendMode, const nsTArray& aFilters, bool aIsBackfaceVisible) { wr::LayoutTransform matrix; if (aTransform) { matrix = ToLayoutTransform(*aTransform); } const wr::LayoutTransform* maybeTransform = aTransform ? &matrix : nullptr; wr::LayoutTransform perspective; if (aPerspective) { perspective = ToLayoutTransform(*aPerspective); } const wr::LayoutTransform* maybePerspective = aPerspective ? &perspective : nullptr; WRDL_LOG("PushStackingContext b=%s t=%s\n", mWrState, Stringify(aBounds).c_str(), aTransform ? Stringify(*aTransform).c_str() : "none"); wr_dp_push_stacking_context(mWrState, aBounds, aAnimation, aOpacity, maybeTransform, aTransformStyle, maybePerspective, aMixBlendMode, aFilters.Elements(), aFilters.Length(), aIsBackfaceVisible); } void DisplayListBuilder::PopStackingContext() { WRDL_LOG("PopStackingContext\n", mWrState); wr_dp_pop_stacking_context(mWrState); } wr::WrClipId DisplayListBuilder::DefineClip(const Maybe& aAncestorScrollId, const Maybe& aAncestorClipId, const wr::LayoutRect& aClipRect, const nsTArray* aComplex, const wr::WrImageMask* aMask) { const uint64_t* ancestorScrollId = nullptr; if (aAncestorScrollId) { ancestorScrollId = &(aAncestorScrollId.ref().id); } const uint64_t* ancestorClipId = nullptr; if (aAncestorClipId) { ancestorClipId = &(aAncestorClipId.ref().id); } uint64_t clip_id = wr_dp_define_clip(mWrState, ancestorScrollId, ancestorClipId, aClipRect, aComplex ? aComplex->Elements() : nullptr, aComplex ? aComplex->Length() : 0, aMask); WRDL_LOG("DefineClip id=%" PRIu64 " as=%s ac=%s r=%s m=%p b=%s complex=%zu\n", mWrState, clip_id, aAncestorScrollId ? Stringify(aAncestorScrollId.ref().id).c_str() : "(nil)", aAncestorClipId ? Stringify(aAncestorClipId.ref().id).c_str() : "(nil)", Stringify(aClipRect).c_str(), aMask, aMask ? Stringify(aMask->rect).c_str() : "none", aComplex ? aComplex->Length() : 0); return wr::WrClipId { clip_id }; } void DisplayListBuilder::PushClip(const wr::WrClipId& aClipId, const DisplayItemClipChain* aParent) { wr_dp_push_clip(mWrState, aClipId.id); WRDL_LOG("PushClip id=%" PRIu64 "\n", mWrState, aClipId.id); if (!aParent) { mClipStack.push_back(wr::ScrollOrClipId(aClipId)); } else { PushCacheOverride(aParent, aClipId); } } void DisplayListBuilder::PopClip(const DisplayItemClipChain* aParent) { WRDL_LOG("PopClip\n", mWrState); if (!aParent) { MOZ_ASSERT(mClipStack.back().is()); mClipStack.pop_back(); } else { PopCacheOverride(aParent); } wr_dp_pop_clip(mWrState); } void DisplayListBuilder::PushCacheOverride(const DisplayItemClipChain* aParent, const wr::WrClipId& aClipId) { // We need to walk the entire clip chain from aParent up and install aClipId // as an override for all of them. This is so that nested display items end up // with the correct parent clip regardless of which outside clip their clip // chains are attached to. Example: // nsDisplayStickyPosition with clipChain D -> C -> B -> A -> nullptr // nsDisplayItem with clipChain F -> E -> B -> A -> nullptr // In this case the sticky clip that is generated by the sticky display item // is defined as a child of D, which is a child of C and so on. When we go // to define E for the nested display item, we want to make sure that it // is defined as a child of sticky clip, regardless of which of {D, C, B, A} // it has as a parent. {D, C, B, A} are what I refer to as the "outside clips" // because they are "outside" the sticky clip, and if we define E as a child // of any of those clips directly, then we end up losing the sticky clip from // the WR clip chain of the nested item. This is why we install cache // overrides for all of them so that when we walk the nested item's clip chain // from E to B we discover that really we want to use the sticky clip as E's // parent. for (const DisplayItemClipChain* i = aParent; i; i = i->mParent) { auto it = mCacheOverride.insert({ i, std::vector() }); it.first->second.push_back(aClipId); WRDL_LOG("Pushing override %p -> %" PRIu64 "\n", mWrState, i, aClipId.id); } } void DisplayListBuilder::PopCacheOverride(const DisplayItemClipChain* aParent) { for (const DisplayItemClipChain* i = aParent; i; i = i->mParent) { auto it = mCacheOverride.find(i); MOZ_ASSERT(it != mCacheOverride.end()); MOZ_ASSERT(!(it->second.empty())); WRDL_LOG("Popping override %p -> %" PRIu64 "\n", mWrState, i, it->second.back().id); it->second.pop_back(); if (it->second.empty()) { mCacheOverride.erase(it); } } } Maybe DisplayListBuilder::GetCacheOverride(const DisplayItemClipChain* aParent) { auto it = mCacheOverride.find(aParent); return it == mCacheOverride.end() ? Nothing() : Some(it->second.back()); } wr::WrStickyId 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) { uint64_t id = wr_dp_define_sticky_frame(mWrState, aContentRect, aTopMargin, aRightMargin, aBottomMargin, aLeftMargin, aVerticalBounds, aHorizontalBounds, aAppliedOffset); WRDL_LOG("DefineSticky id=%" PRIu64 " c=%s t=%s r=%s b=%s l=%s v=%s h=%s a=%s\n", mWrState, id, Stringify(aContentRect).c_str(), aTopMargin ? Stringify(*aTopMargin).c_str() : "none", aRightMargin ? Stringify(*aRightMargin).c_str() : "none", aBottomMargin ? Stringify(*aBottomMargin).c_str() : "none", aLeftMargin ? Stringify(*aLeftMargin).c_str() : "none", Stringify(aVerticalBounds).c_str(), Stringify(aHorizontalBounds).c_str(), Stringify(aAppliedOffset).c_str()); return wr::WrStickyId { id }; } void DisplayListBuilder::PushStickyFrame(const wr::WrStickyId& aStickyId, const DisplayItemClipChain* aParent) { wr_dp_push_clip(mWrState, aStickyId.id); WRDL_LOG("PushSticky id=%" PRIu64 "\n", mWrState, aStickyId.id); // inside WR, a sticky id is just a regular clip id. so we can do some // handwaving here and turn the WrStickyId into a WrclipId and treat it // like any other out-of-band clip. wr::WrClipId stickyIdAsClipId; stickyIdAsClipId.id = aStickyId.id; PushCacheOverride(aParent, stickyIdAsClipId); } void DisplayListBuilder::PopStickyFrame(const DisplayItemClipChain* aParent) { WRDL_LOG("PopSticky\n", mWrState); PopCacheOverride(aParent); wr_dp_pop_clip(mWrState); } Maybe DisplayListBuilder::GetScrollIdForDefinedScrollLayer(layers::FrameMetrics::ViewID aViewId) const { if (aViewId == layers::FrameMetrics::NULL_SCROLL_ID) { return Some(wr::WrScrollId { 0 }); } auto it = mScrollIds.find(aViewId); if (it == mScrollIds.end()) { return Nothing(); } return Some(it->second); } wr::WrScrollId DisplayListBuilder::DefineScrollLayer(const layers::FrameMetrics::ViewID& aViewId, const Maybe& aAncestorScrollId, const Maybe& aAncestorClipId, const wr::LayoutRect& aContentRect, const wr::LayoutRect& aClipRect) { WRDL_LOG("DefineScrollLayer id=%" PRIu64 " as=%s ac=%s co=%s cl=%s\n", mWrState, aViewId, aAncestorScrollId ? Stringify(aAncestorScrollId.ref().id).c_str() : "(nil)", aAncestorClipId ? Stringify(aAncestorClipId.ref().id).c_str() : "(nil)", Stringify(aContentRect).c_str(), Stringify(aClipRect).c_str()); auto it = mScrollIds.find(aViewId); if (it != mScrollIds.end()) { return it->second; } // We haven't defined aViewId before, so let's define it now. uint64_t numericScrollId = wr_dp_define_scroll_layer( mWrState, aViewId, aAncestorScrollId ? &(aAncestorScrollId->id) : nullptr, aAncestorClipId ? &(aAncestorClipId->id) : nullptr, aContentRect, aClipRect); auto wrScrollId = wr::WrScrollId { numericScrollId }; mScrollIds[aViewId] = wrScrollId; return wrScrollId; } void DisplayListBuilder::PushScrollLayer(const wr::WrScrollId& aScrollId) { WRDL_LOG("PushScrollLayer id=%" PRIu64 "\n", mWrState, aScrollId.id); wr_dp_push_scroll_layer(mWrState, aScrollId.id); mClipStack.push_back(wr::ScrollOrClipId(aScrollId)); } void DisplayListBuilder::PopScrollLayer() { MOZ_ASSERT(mClipStack.back().is()); WRDL_LOG("PopScrollLayer id=%" PRIu64 "\n", mWrState, mClipStack.back().as().id); mClipStack.pop_back(); wr_dp_pop_scroll_layer(mWrState); } void DisplayListBuilder::PushClipAndScrollInfo(const wr::WrScrollId& aScrollId, const wr::WrClipId* aClipId) { WRDL_LOG("PushClipAndScroll s=%" PRIu64 " c=%s\n", mWrState, aScrollId.id, aClipId ? Stringify(aClipId->id).c_str() : "none"); wr_dp_push_clip_and_scroll_info(mWrState, aScrollId.id, aClipId ? &(aClipId->id) : nullptr); mClipStack.push_back(wr::ScrollOrClipId(aScrollId)); } void DisplayListBuilder::PopClipAndScrollInfo() { MOZ_ASSERT(mClipStack.back().is()); WRDL_LOG("PopClipAndScroll\n", mWrState); mClipStack.pop_back(); wr_dp_pop_clip_and_scroll_info(mWrState); } void DisplayListBuilder::PushRect(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, bool aIsBackfaceVisible, const wr::ColorF& aColor) { WRDL_LOG("PushRect b=%s cl=%s c=%s\n", mWrState, Stringify(aBounds).c_str(), Stringify(aClip).c_str(), Stringify(aColor).c_str()); wr_dp_push_rect(mWrState, aBounds, aClip, aIsBackfaceVisible, aColor); } void DisplayListBuilder::PushClearRect(const wr::LayoutRect& aBounds) { WRDL_LOG("PushClearRect b=%s\n", mWrState, Stringify(aBounds).c_str()); wr_dp_push_clear_rect(mWrState, aBounds); } 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, aClip, aIsBackfaceVisible, 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, aClip, aIsBackfaceVisible, aCenter, aRadius, aStops.Elements(), aStops.Length(), aExtendMode, aTileSize, aTileSpacing); } void DisplayListBuilder::PushImage(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, bool aIsBackfaceVisible, wr::ImageRendering aFilter, wr::ImageKey aImage, bool aPremultipliedAlpha) { wr::LayoutSize size; size.width = aBounds.size.width; size.height = aBounds.size.height; PushImage(aBounds, aClip, aIsBackfaceVisible, size, size, aFilter, aImage, aPremultipliedAlpha); } void DisplayListBuilder::PushImage(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) { WRDL_LOG("PushImage b=%s cl=%s s=%s t=%s\n", mWrState, Stringify(aBounds).c_str(), Stringify(aClip).c_str(), Stringify(aStretchSize).c_str(), Stringify(aTileSpacing).c_str()); wr_dp_push_image(mWrState, aBounds, aClip, aIsBackfaceVisible, aStretchSize, aTileSpacing, aFilter, aImage, aPremultipliedAlpha); } void DisplayListBuilder::PushYCbCrPlanarImage(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, bool aIsBackfaceVisible, wr::ImageKey aImageChannel0, wr::ImageKey aImageChannel1, wr::ImageKey aImageChannel2, wr::WrYuvColorSpace aColorSpace, wr::ImageRendering aRendering) { wr_dp_push_yuv_planar_image(mWrState, aBounds, aClip, aIsBackfaceVisible, aImageChannel0, aImageChannel1, aImageChannel2, aColorSpace, aRendering); } void DisplayListBuilder::PushNV12Image(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, bool aIsBackfaceVisible, wr::ImageKey aImageChannel0, wr::ImageKey aImageChannel1, wr::WrYuvColorSpace aColorSpace, wr::ImageRendering aRendering) { wr_dp_push_yuv_NV12_image(mWrState, aBounds, aClip, aIsBackfaceVisible, aImageChannel0, aImageChannel1, aColorSpace, aRendering); } void DisplayListBuilder::PushYCbCrInterleavedImage(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, bool aIsBackfaceVisible, wr::ImageKey aImageChannel0, wr::WrYuvColorSpace aColorSpace, wr::ImageRendering aRendering) { wr_dp_push_yuv_interleaved_image(mWrState, aBounds, aClip, aIsBackfaceVisible, aImageChannel0, aColorSpace, aRendering); } void DisplayListBuilder::PushIFrame(const wr::LayoutRect& aBounds, bool aIsBackfaceVisible, PipelineId aPipeline) { wr_dp_push_iframe(mWrState, aBounds, aIsBackfaceVisible, aPipeline); } void DisplayListBuilder::PushBorder(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, bool aIsBackfaceVisible, const wr::BorderWidths& aWidths, const Range& aSides, const wr::BorderRadius& aRadius) { MOZ_ASSERT(aSides.length() == 4); if (aSides.length() != 4) { return; } wr_dp_push_border(mWrState, aBounds, aClip, aIsBackfaceVisible, 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::BorderWidths& aWidths, wr::ImageKey aImage, const wr::NinePatchDescriptor& aPatch, const wr::SideOffsets2D& aOutset, const wr::RepeatMode& aRepeatHorizontal, const wr::RepeatMode& aRepeatVertical) { wr_dp_push_border_image(mWrState, aBounds, aClip, aIsBackfaceVisible, aWidths, aImage, aPatch, aOutset, aRepeatHorizontal, aRepeatVertical); } void DisplayListBuilder::PushBorderGradient(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, bool aIsBackfaceVisible, const wr::BorderWidths& aWidths, const wr::LayoutPoint& aStartPoint, const wr::LayoutPoint& aEndPoint, const nsTArray& aStops, wr::ExtendMode aExtendMode, const wr::SideOffsets2D& aOutset) { wr_dp_push_border_gradient(mWrState, aBounds, aClip, aIsBackfaceVisible, aWidths, aStartPoint, aEndPoint, aStops.Elements(), aStops.Length(), aExtendMode, aOutset); } void DisplayListBuilder::PushBorderRadialGradient(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, bool aIsBackfaceVisible, const wr::BorderWidths& aWidths, const wr::LayoutPoint& aCenter, const wr::LayoutSize& aRadius, const nsTArray& aStops, wr::ExtendMode aExtendMode, const wr::SideOffsets2D& aOutset) { wr_dp_push_border_radial_gradient( mWrState, aBounds, aClip, aIsBackfaceVisible, aWidths, aCenter, aRadius, aStops.Elements(), aStops.Length(), aExtendMode, aOutset); } 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, aClip, aIsBackfaceVisible, aColor, aFontKey, &aGlyphBuffer[0], aGlyphBuffer.length(), aGlyphOptions); } void DisplayListBuilder::PushLine(const wr::LayoutRect& aClip, bool aIsBackfaceVisible, const wr::Line& aLine) { wr_dp_push_line(mWrState, &aClip, aIsBackfaceVisible, &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) { wr_dp_push_shadow(mWrState, aRect, aClip, aIsBackfaceVisible, aShadow); } void DisplayListBuilder::PopAllShadows() { wr_dp_pop_all_shadows(mWrState); } 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, aClip, aIsBackfaceVisible, aBoxBounds, aOffset, aColor, aBlurRadius, aSpreadRadius, aBorderRadius, aClipMode); } Maybe DisplayListBuilder::TopmostClipId() { for (auto it = mClipStack.crbegin(); it != mClipStack.crend(); it++) { if (it->is()) { return Some(it->as()); } } return Nothing(); } Maybe DisplayListBuilder::TopmostScrollId() { for (auto it = mClipStack.crbegin(); it != mClipStack.crend(); it++) { if (it->is()) { return Some(it->as()); } } return Nothing(); } bool DisplayListBuilder::TopmostIsClip() { if (mClipStack.empty()) { return false; } return mClipStack.back().is(); } void DisplayListBuilder::SetHitTestInfo(const layers::FrameMetrics::ViewID& aScrollId, gfx::CompositorHitTestInfo aHitInfo) { static_assert(sizeof(gfx::CompositorHitTestInfo) == sizeof(uint16_t), "CompositorHitTestInfo should be u16-sized"); wr_set_item_tag(mWrState, aScrollId, static_cast(aHitInfo)); } void DisplayListBuilder::ClearHitTestInfo() { wr_clear_item_tag(mWrState); } } // namespace wr } // namespace mozilla