/* -*- 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<widget::CompositorWidget>&& 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<RenderCompositor> 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<RenderThread> thread = &aRenderThread;
auto renderer = MakeUnique<RendererOGL>(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<widget::CompositorWidget> 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<uint8_t>& 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<wr::WrOpacityProperty>& aOpacityArray,
const nsTArray<wr::WrTransformProperty>& 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::WrLogLevelFilter::Error);
}
/*static*/ void WebRenderAPI::ShutdownExternalLogHandler() {
mozilla::wr::wr_shutdown_external_log_handler();
}
/*static*/ already_AddRefed<WebRenderAPI> WebRenderAPI::Create(
layers::CompositorBridgeParentBase* aBridge,
RefPtr<widget::CompositorWidget>&& 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<NewRenderer>(&docHandle, aBridge, &maxTextureSize, &useANGLE,
Move(aWidget), &task, aSize, &syncHandle);
RenderThread::Get()->RunEvent(id, Move(event));
task.Wait();
if (!docHandle) {
return nullptr;
}
return RefPtr<WebRenderAPI>(new WebRenderAPI(docHandle, id, maxTextureSize,
useANGLE, syncHandle))
.forget();
}
already_AddRefed<WebRenderAPI> WebRenderAPI::Clone() {
wr::DocumentHandle* docHandle = nullptr;
wr_api_clone(mDocHandle, &docHandle);
RefPtr<WebRenderAPI> renderApi =
new WebRenderAPI(docHandle, mId, mMaxTextureSize, mUseANGLE, mSyncHandle);
renderApi->mRootApi = this; // Hold root api
renderApi->mRootDocumentApi = this;
return renderApi.forget();
}
already_AddRefed<WebRenderAPI> 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<WebRenderAPI> 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<RemoveRenderer>(&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, /* aReadback */ true);
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<Readback>(&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<PauseEvent>(&task);
// This event will be passed from wr_backend thread to renderer thread. That
// implies that all frame data have been processed when the renderer runs this
// event.
RunOnRenderThread(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<ResumeEvent>(&task, &result);
// This event will be passed from wr_backend thread to renderer thread. That
// implies that all frame data have been processed when the renderer runs this
// event.
RunOnRenderThread(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<WaitFlushedEvent>(&task);
// This event will be passed from wr_backend thread to renderer thread. That
// implies that all frame data have been processed when the renderer runs this
// event.
RunOnRenderThread(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);
}
void TransactionBuilder::Clear() {
wr_resource_updates_clear(mResourceUpdates);
}
void TransactionBuilder::AddImage(ImageKey key,
const ImageDescriptor& aDescriptor,
wr::Vec<uint8_t>& aBytes) {
wr_resource_updates_add_image(mResourceUpdates, key, &aDescriptor,
&aBytes.inner);
}
void TransactionBuilder::AddBlobImage(ImageKey key,
const ImageDescriptor& aDescriptor,
wr::Vec<uint8_t>& 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<uint8_t>& aBytes) {
wr_resource_updates_update_image(mResourceUpdates, aKey, &aDescriptor,
&aBytes.inner);
}
void TransactionBuilder::UpdateBlobImage(ImageKey aKey,
const ImageDescriptor& aDescriptor,
wr::Vec<uint8_t>& 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<uint8_t>& aBytes,
uint32_t aIndex) {
wr_resource_updates_add_raw_font(mResourceUpdates, aKey, &aBytes.inner,
aIndex);
}
void TransactionBuilder::AddFontDescriptor(wr::FontKey aKey,
wr::Vec<uint8_t>& 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<uint8_t>& 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<FrameStartTime>(aTime);
RunOnRenderThread(Move(event));
}
void WebRenderAPI::RunOnRenderThread(UniquePtr<RendererEvent> aEvent) {
auto event = reinterpret_cast<uintptr_t>(aEvent.release());
wr_api_send_external_event(mDocHandle, event);
}
DisplayListBuilder::DisplayListBuilder(PipelineId aId,
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<wr::WrFilterOp>& 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<wr::WrScrollId>& aAncestorScrollId,
const Maybe<wr::WrClipId>& aAncestorClipId, const wr::LayoutRect& aClipRect,
const nsTArray<wr::ComplexClipRegion>* aComplex,
const wr::WrImageMask* aMask) {
const size_t* ancestorScrollId = nullptr;
if (aAncestorScrollId) {
ancestorScrollId = &(aAncestorScrollId.ref().id);
}
const size_t* ancestorClipId = nullptr;
if (aAncestorClipId) {
ancestorClipId = &(aAncestorClipId.ref().id);
}
size_t clip_id =
wr_dp_define_clip(mWrState, ancestorScrollId, ancestorClipId, aClipRect,
aComplex ? aComplex->Elements() : nullptr,
aComplex ? aComplex->Length() : 0, aMask);
WRDL_LOG(
"DefineClip id=%zu 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=%zu\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<wr::WrClipId>());
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<wr::WrClipId>()});
it.first->second.push_back(aClipId);
WRDL_LOG("Pushing override %p -> %zu\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 -> %zu\n", mWrState, i, it->second.back().id);
it->second.pop_back();
if (it->second.empty()) {
mCacheOverride.erase(it);
}
}
}
Maybe<wr::WrClipId> 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) {
size_t id = wr_dp_define_sticky_frame(
mWrState, aContentRect, aTopMargin, aRightMargin, aBottomMargin,
aLeftMargin, aVerticalBounds, aHorizontalBounds, aAppliedOffset);
WRDL_LOG("DefineSticky id=%zu 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=%zu\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<wr::WrScrollId> 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<wr::WrScrollId>& aAncestorScrollId,
const Maybe<wr::WrClipId>& 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.
size_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=%zu\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<wr::WrScrollId>());
WRDL_LOG("PopScrollLayer id=%zu\n", mWrState,
mClipStack.back().as<wr::WrScrollId>().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=%zu 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<wr::WrScrollId>());
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<wr::GradientStop>& 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<wr::GradientStop>& 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<const wr::BorderSide>& 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<float>& 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<wr::GradientStop>& aStops, wr::ExtendMode aExtendMode,
const wr::SideOffsets2D<float>& 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<wr::GradientStop>& aStops, wr::ExtendMode aExtendMode,
const wr::SideOffsets2D<float>& 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<const wr::GlyphInstance> 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<wr::WrClipId> DisplayListBuilder::TopmostClipId() {
for (auto it = mClipStack.crbegin(); it != mClipStack.crend(); it++) {
if (it->is<wr::WrClipId>()) {
return Some(it->as<wr::WrClipId>());
}
}
return Nothing();
}
wr::WrScrollId DisplayListBuilder::TopmostScrollId() {
for (auto it = mClipStack.crbegin(); it != mClipStack.crend(); it++) {
if (it->is<wr::WrScrollId>()) {
return it->as<wr::WrScrollId>();
}
}
return wr::WrScrollId{0};
}
bool DisplayListBuilder::TopmostIsClip() {
if (mClipStack.empty()) {
return false;
}
return mClipStack.back().is<wr::WrClipId>();
}
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<uint16_t>(aHitInfo));
}
void DisplayListBuilder::ClearHitTestInfo() { wr_clear_item_tag(mWrState); }
} // namespace wr
} // namespace mozilla