Source code
Revision control
Copy as Markdown
Other Tools
/* -*- 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
#include "mozilla/layers/WebRenderBridgeParent.h"
#include "mozmemory.h"
#include "CompositableHost.h"
#include "gfxEnv.h"
#include "gfxPlatform.h"
#include "gfxOTSUtils.h"
#include "GeckoProfiler.h"
#include "GLContext.h"
#include "GLContextProvider.h"
#include "GLLibraryLoader.h"
#include "nsExceptionHandler.h"
#include "mozilla/Range.h"
#include "mozilla/EnumeratedRange.h"
#include "mozilla/StaticPrefs_gfx.h"
#include "mozilla/StaticPrefs_webgl.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/gfx/GPUParent.h"
#include "mozilla/glean/GleanMetrics.h"
#include "mozilla/layers/AnimationHelper.h"
#include "mozilla/layers/APZSampler.h"
#include "mozilla/layers/APZUpdater.h"
#include "mozilla/layers/Compositor.h"
#include "mozilla/layers/CompositorBridgeParent.h"
#include "mozilla/layers/CompositorAnimationStorage.h"
#include "mozilla/layers/CompositorThread.h"
#include "mozilla/layers/CompositorVsyncScheduler.h"
#include "mozilla/layers/ImageBridgeParent.h"
#include "mozilla/layers/ImageDataSerializer.h"
#include "mozilla/layers/IpcResourceUpdateQueue.h"
#include "mozilla/layers/OMTASampler.h"
#include "mozilla/layers/RemoteTextureMap.h"
#include "mozilla/layers/SharedSurfacesParent.h"
#include "mozilla/layers/TextureHost.h"
#include "mozilla/layers/AsyncImagePipelineManager.h"
#include "mozilla/layers/UiCompositorControllerParent.h"
#include "mozilla/layers/WebRenderImageHost.h"
#include "mozilla/layers/WebRenderTextureHost.h"
#include "mozilla/ProfilerMarkerTypes.h"
#include "mozilla/Telemetry.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Unused.h"
#include "mozilla/webrender/RenderTextureHostSWGL.h"
#include "mozilla/webrender/RenderThread.h"
#include "mozilla/widget/CompositorWidget.h"
#ifdef XP_WIN
# include "mozilla/gfx/DeviceManagerDx.h"
# include "mozilla/widget/WinCompositorWidget.h"
#endif
#if defined(MOZ_WIDGET_GTK)
# include "mozilla/widget/GtkCompositorWidget.h"
#endif
bool is_in_main_thread() { return NS_IsMainThread(); }
bool is_in_compositor_thread() {
return mozilla::layers::CompositorThreadHolder::IsInCompositorThread();
}
bool is_in_render_thread() {
return mozilla::wr::RenderThread::IsInRenderThread();
}
bool gecko_profiler_thread_is_being_profiled() {
return profiler_thread_is_being_profiled(ThreadProfilingFeatures::Any);
}
bool is_glcontext_gles(void* const glcontext_ptr) {
MOZ_RELEASE_ASSERT(glcontext_ptr);
return reinterpret_cast<mozilla::gl::GLContext*>(glcontext_ptr)->IsGLES();
}
bool is_glcontext_angle(void* glcontext_ptr) {
MOZ_ASSERT(glcontext_ptr);
mozilla::gl::GLContext* glcontext =
reinterpret_cast<mozilla::gl::GLContext*>(glcontext_ptr);
if (!glcontext) {
return false;
}
return glcontext->IsANGLE();
}
const char* gfx_wr_resource_path_override() {
return gfxPlatform::WebRenderResourcePathOverride();
}
bool gfx_wr_use_optimized_shaders() {
return mozilla::gfx::gfxVars::UseWebRenderOptimizedShaders();
}
void gfx_critical_note(const char* msg) { gfxCriticalNote << msg; }
void gfx_critical_error(const char* msg) { gfxCriticalError() << msg; }
void gecko_printf_stderr_output(const char* msg) { printf_stderr("%s\n", msg); }
void* get_proc_address_from_glcontext(void* glcontext_ptr,
const char* procname) {
mozilla::gl::GLContext* glcontext =
reinterpret_cast<mozilla::gl::GLContext*>(glcontext_ptr);
MOZ_ASSERT(glcontext);
if (!glcontext) {
return nullptr;
}
const auto& loader = glcontext->GetSymbolLoader();
MOZ_ASSERT(loader);
const auto ret = loader->GetProcAddress(procname);
return reinterpret_cast<void*>(ret);
}
static CrashReporter::Annotation FromWrCrashAnnotation(
mozilla::wr::CrashAnnotation aAnnotation) {
switch (aAnnotation) {
case mozilla::wr::CrashAnnotation::CompileShader:
return CrashReporter::Annotation::GraphicsCompileShader;
case mozilla::wr::CrashAnnotation::DrawShader:
return CrashReporter::Annotation::GraphicsDrawShader;
default:
MOZ_ASSERT_UNREACHABLE("Unhandled annotation!");
return CrashReporter::Annotation::Count;
}
}
extern "C" {
void gfx_wr_set_crash_annotation(mozilla::wr::CrashAnnotation aAnnotation,
const char* aValue) {
MOZ_ASSERT(aValue);
auto annotation = FromWrCrashAnnotation(aAnnotation);
if (annotation == CrashReporter::Annotation::Count) {
return;
}
CrashReporter::RecordAnnotationCString(annotation, aValue);
}
void gfx_wr_clear_crash_annotation(mozilla::wr::CrashAnnotation aAnnotation) {
auto annotation = FromWrCrashAnnotation(aAnnotation);
if (annotation == CrashReporter::Annotation::Count) {
return;
}
CrashReporter::UnrecordAnnotation(annotation);
}
}
namespace mozilla::layers {
using namespace mozilla::gfx;
static bool sAllocAsjustmentTaskCancelled = false;
static bool sIncreasedDirtyPageThreshold = false;
void ResetDirtyPageModifier();
void ScheduleResetMaxDirtyPageModifier() {
NS_DelayedDispatchToCurrentThread(
NewRunnableFunction("ResetDirtyPageModifier", &ResetDirtyPageModifier),
100 // In ms.
);
}
void NeedIncreasedMaxDirtyPageModifier() {
if (sIncreasedDirtyPageThreshold) {
sAllocAsjustmentTaskCancelled = true;
return;
}
moz_set_max_dirty_page_modifier(3);
sIncreasedDirtyPageThreshold = true;
ScheduleResetMaxDirtyPageModifier();
}
void ResetDirtyPageModifier() {
if (!sIncreasedDirtyPageThreshold) {
return;
}
if (sAllocAsjustmentTaskCancelled) {
sAllocAsjustmentTaskCancelled = false;
ScheduleResetMaxDirtyPageModifier();
return;
}
moz_set_max_dirty_page_modifier(0);
wr::RenderThread* renderThread = wr::RenderThread::Get();
if (renderThread) {
renderThread->NotifyIdle();
}
#if defined(MOZ_MEMORY)
jemalloc_free_excess_dirty_pages();
#endif
sIncreasedDirtyPageThreshold = false;
}
LazyLogModule gWebRenderBridgeParentLog("WebRenderBridgeParent");
#define LOG(...) \
MOZ_LOG(gWebRenderBridgeParentLog, LogLevel::Debug, (__VA_ARGS__))
class ScheduleObserveLayersUpdate : public wr::NotificationHandler {
public:
ScheduleObserveLayersUpdate(RefPtr<CompositorBridgeParentBase> aBridge,
LayersId aLayersId, bool aIsActive)
: mBridge(std::move(aBridge)),
mLayersId(aLayersId),
mIsActive(aIsActive) {}
void Notify(wr::Checkpoint) override {
CompositorThread()->Dispatch(NewRunnableMethod<LayersId, int>(
"ObserveLayersUpdate", mBridge,
&CompositorBridgeParentBase::ObserveLayersUpdate, mLayersId,
mIsActive));
}
protected:
RefPtr<CompositorBridgeParentBase> mBridge;
LayersId mLayersId;
bool mIsActive;
};
class SceneBuiltNotification : public wr::NotificationHandler {
public:
SceneBuiltNotification(WebRenderBridgeParent* aParent, wr::Epoch aEpoch,
TimeStamp aTxnStartTime)
: mParent(aParent), mEpoch(aEpoch), mTxnStartTime(aTxnStartTime) {}
void Notify(wr::Checkpoint) override {
auto startTime = this->mTxnStartTime;
RefPtr<WebRenderBridgeParent> parent = mParent;
wr::Epoch epoch = mEpoch;
CompositorThread()->Dispatch(NS_NewRunnableFunction(
"SceneBuiltNotificationRunnable", [parent, epoch, startTime]() {
auto endTime = TimeStamp::Now();
if (profiler_thread_is_being_profiled_for_markers()) {
PROFILER_MARKER("CONTENT_FULL_PAINT_TIME", GRAPHICS,
MarkerTiming::Interval(startTime, endTime),
ContentBuildMarker);
}
mozilla::glean::gfx_content::full_paint_time.AccumulateRawDuration(
endTime - startTime);
parent->NotifySceneBuiltForEpoch(epoch, endTime);
}));
}
protected:
RefPtr<WebRenderBridgeParent> mParent;
wr::Epoch mEpoch;
TimeStamp mTxnStartTime;
};
class WebRenderBridgeParent::ScheduleSharedSurfaceRelease final
: public wr::NotificationHandler {
public:
explicit ScheduleSharedSurfaceRelease(WebRenderBridgeParent* aWrBridge)
: mWrBridge(aWrBridge), mSurfaces(20) {}
~ScheduleSharedSurfaceRelease() override {
if (!mSurfaces.IsEmpty()) {
MOZ_ASSERT_UNREACHABLE("Unreleased surfaces!");
gfxCriticalNote << "ScheduleSharedSurfaceRelease destroyed non-empty";
NotifyInternal(/* aFromCheckpoint */ false);
}
}
void Add(const wr::ImageKey& aKey, const wr::ExternalImageId& aId) {
mSurfaces.AppendElement(wr::ExternalImageKeyPair{aKey, aId});
}
void Notify(wr::Checkpoint) override {
NotifyInternal(/* aFromCheckpoint */ true);
}
private:
void NotifyInternal(bool aFromCheckpoint) {
CompositorThread()->Dispatch(
NewRunnableMethod<nsTArray<wr::ExternalImageKeyPair>, bool>(
"ObserveSharedSurfaceRelease", mWrBridge,
&WebRenderBridgeParent::ObserveSharedSurfaceRelease,
std::move(mSurfaces), aFromCheckpoint));
}
RefPtr<WebRenderBridgeParent> mWrBridge;
nsTArray<wr::ExternalImageKeyPair> mSurfaces;
};
class MOZ_STACK_CLASS AutoWebRenderBridgeParentAsyncMessageSender final {
public:
explicit AutoWebRenderBridgeParentAsyncMessageSender(
WebRenderBridgeParent* aWebRenderBridgeParent,
nsTArray<OpDestroy>* aDestroyActors = nullptr)
: mWebRenderBridgeParent(aWebRenderBridgeParent),
mActorsToDestroy(aDestroyActors) {
mWebRenderBridgeParent->SetAboutToSendAsyncMessages();
}
~AutoWebRenderBridgeParentAsyncMessageSender() {
mWebRenderBridgeParent->SendPendingAsyncMessages();
if (mActorsToDestroy) {
// Destroy the actors after sending the async messages because the latter
// may contain references to some actors.
for (const auto& op : *mActorsToDestroy) {
mWebRenderBridgeParent->DestroyActor(op);
}
}
}
private:
WebRenderBridgeParent* mWebRenderBridgeParent;
nsTArray<OpDestroy>* mActorsToDestroy;
};
WebRenderBridgeParent::WebRenderBridgeParent(
CompositorBridgeParentBase* aCompositorBridge,
const wr::PipelineId& aPipelineId, widget::CompositorWidget* aWidget,
CompositorVsyncScheduler* aScheduler, RefPtr<wr::WebRenderAPI>&& aApi,
RefPtr<AsyncImagePipelineManager>&& aImageMgr, TimeDuration aVsyncRate)
: mCompositorBridge(aCompositorBridge),
mPipelineId(aPipelineId),
mWidget(aWidget),
mApi(aApi),
mAsyncImageManager(aImageMgr),
mCompositorScheduler(aScheduler),
mVsyncRate(aVsyncRate),
mWrEpoch{0},
mIdNamespace(aApi->GetNamespace()),
#if defined(MOZ_WIDGET_ANDROID)
mScreenPixelsTarget(nullptr),
#endif
mBlobTileSize(256),
mSkippedCompositeReasons(wr::RenderReasons::NONE),
mDestroyed(false),
mIsFirstPaint(true),
mPendingScrollPayloads("WebRenderBridgeParent::mPendingScrollPayloads") {
MOZ_ASSERT(mAsyncImageManager);
LOG("WebRenderBridgeParent::WebRenderBridgeParent() PipelineId %" PRIx64
" Id %" PRIx64 " root %d",
wr::AsUint64(mPipelineId), wr::AsUint64(mApi->GetId()),
IsRootWebRenderBridgeParent());
mAsyncImageManager->AddPipeline(mPipelineId, this);
if (IsRootWebRenderBridgeParent()) {
MOZ_ASSERT(!mCompositorScheduler);
mCompositorScheduler = new CompositorVsyncScheduler(this, mWidget);
UpdateDebugFlags();
UpdateQualitySettings();
UpdateProfilerUI();
UpdateParameters();
// Start with the cached bool parameter bits inverted so that we update them
// all.
mBoolParameterBits = ~gfxVars::WebRenderBoolParameters();
UpdateBoolParameters();
}
mRemoteTextureTxnScheduler =
RemoteTextureTxnScheduler::Create(aCompositorBridge);
}
WebRenderBridgeParent::WebRenderBridgeParent(const wr::PipelineId& aPipelineId,
nsCString&& aError)
: mCompositorBridge(nullptr),
mPipelineId(aPipelineId),
mWrEpoch{0},
mIdNamespace{0},
mInitError(aError),
mDestroyed(true),
mIsFirstPaint(false),
mPendingScrollPayloads("WebRenderBridgeParent::mPendingScrollPayloads") {
LOG("WebRenderBridgeParent::WebRenderBridgeParent() PipelineId %" PRIx64 "",
wr::AsUint64(mPipelineId));
}
WebRenderBridgeParent::~WebRenderBridgeParent() {
LOG("WebRenderBridgeParent::WebRenderBridgeParent() PipelineId %" PRIx64 "",
wr::AsUint64(mPipelineId));
}
/* static */
WebRenderBridgeParent* WebRenderBridgeParent::CreateDestroyed(
const wr::PipelineId& aPipelineId, nsCString&& aError) {
return new WebRenderBridgeParent(aPipelineId, std::move(aError));
}
mozilla::ipc::IPCResult WebRenderBridgeParent::RecvEnsureConnected(
TextureFactoryIdentifier* aTextureFactoryIdentifier,
MaybeIdNamespace* aMaybeIdNamespace, nsCString* aError) {
if (mDestroyed) {
*aTextureFactoryIdentifier =
TextureFactoryIdentifier(LayersBackend::LAYERS_NONE);
*aMaybeIdNamespace = Nothing();
if (mInitError.IsEmpty()) {
// Got destroyed after we initialized but before the handshake finished?
aError->AssignLiteral("FEATURE_FAILURE_WEBRENDER_INITIALIZE_RACE");
} else {
*aError = std::move(mInitError);
}
return IPC_OK();
}
MOZ_ASSERT(mIdNamespace.mHandle != 0);
*aTextureFactoryIdentifier = GetTextureFactoryIdentifier();
*aMaybeIdNamespace = Some(mIdNamespace);
return IPC_OK();
}
mozilla::ipc::IPCResult WebRenderBridgeParent::RecvShutdown() {
return HandleShutdown();
}
mozilla::ipc::IPCResult WebRenderBridgeParent::RecvShutdownSync() {
return HandleShutdown();
}
mozilla::ipc::IPCResult WebRenderBridgeParent::HandleShutdown() {
Destroy();
IProtocol* mgr = Manager();
if (!Send__delete__(this)) {
return IPC_FAIL_NO_REASON(mgr);
}
return IPC_OK();
}
void WebRenderBridgeParent::Destroy() {
if (mDestroyed) {
return;
}
LOG("WebRenderBridgeParent::Destroy() PipelineId %" PRIx64 " Id %" PRIx64
" root %d",
wr::AsUint64(mPipelineId), wr::AsUint64(mApi->GetId()),
IsRootWebRenderBridgeParent());
mDestroyed = true;
if (mRemoteTextureTxnScheduler) {
mRemoteTextureTxnScheduler = nullptr;
}
if (mWebRenderBridgeRef) {
// Break mutual reference
mWebRenderBridgeRef->Clear();
mWebRenderBridgeRef = nullptr;
}
for (const auto& entry : mCompositables) {
entry.second->OnReleased();
}
mCompositables.clear();
ClearResources();
}
struct WROTSAlloc {
wr::Vec<uint8_t> mVec;
void* Grow(void* aPtr, size_t aLength) {
if (aLength > mVec.Capacity()) {
mVec.Reserve(aLength - mVec.Capacity());
}
return mVec.inner.data;
}
wr::Vec<uint8_t> ShrinkToFit(void* aPtr, size_t aLength) {
wr::Vec<uint8_t> result(std::move(mVec));
result.inner.length = aLength;
return result;
}
void Free(void* aPtr) {}
};
static bool ReadRawFont(const OpAddRawFont& aOp, wr::ShmSegmentsReader& aReader,
wr::TransactionBuilder& aUpdates) {
wr::Vec<uint8_t> sourceBytes;
Maybe<Range<uint8_t>> ptr =
aReader.GetReadPointerOrCopy(aOp.bytes(), sourceBytes);
if (ptr.isNothing()) {
gfxCriticalNote << "No read pointer from reader for sanitizing font "
<< aOp.key().mHandle;
return false;
}
Range<uint8_t>& source = ptr.ref();
// Attempt to sanitize the font before passing it along for updating.
// Ensure that we're not strict here about font types, since any font that
// failed generating a descriptor might end up here as raw font data.
size_t lengthHint = gfxOTSContext::GuessSanitizedFontSize(
source.begin().get(), source.length(), false);
if (!lengthHint) {
gfxCriticalNote << "Could not determine font type for sanitizing font "
<< aOp.key().mHandle;
return false;
}
gfxOTSExpandingMemoryStream<WROTSAlloc> output(lengthHint);
gfxOTSContext otsContext;
if (!otsContext.Process(&output, source.begin().get(), source.length())) {
gfxCriticalNote << "Failed sanitizing font " << aOp.key().mHandle;
return false;
}
wr::Vec<uint8_t> bytes = output.forget();
aUpdates.AddRawFont(aOp.key(), bytes, aOp.fontIndex());
return true;
}
bool WebRenderBridgeParent::UpdateResources(
const nsTArray<OpUpdateResource>& aResourceUpdates,
const nsTArray<RefCountedShmem>& aSmallShmems,
const nsTArray<ipc::Shmem>& aLargeShmems,
wr::TransactionBuilder& aUpdates) {
wr::ShmSegmentsReader reader(aSmallShmems, aLargeShmems);
UniquePtr<ScheduleSharedSurfaceRelease> scheduleRelease;
while (GPUParent::MaybeFlushMemory()) {
// If the GPU process has memory pressure, preemptively unmap some of our
// shared memory images. If we are in the parent process, the expiration
// tracker itself will listen for the memory pressure event.
if (!SharedSurfacesParent::AgeAndExpireOneGeneration()) {
break;
}
}
bool success = true;
for (const auto& cmd : aResourceUpdates) {
switch (cmd.type()) {
case OpUpdateResource::TOpAddImage: {
const auto& op = cmd.get_OpAddImage();
if (!MatchesNamespace(op.key())) {
MOZ_ASSERT_UNREACHABLE("Stale image key (add)!");
break;
}
wr::Vec<uint8_t> bytes;
if (reader.Read(op.bytes(), bytes)) {
aUpdates.AddImage(op.key(), op.descriptor(), bytes);
} else {
gfxCriticalNote << "TOpAddImage failed";
success = false;
}
break;
}
case OpUpdateResource::TOpUpdateImage: {
const auto& op = cmd.get_OpUpdateImage();
if (!MatchesNamespace(op.key())) {
MOZ_ASSERT_UNREACHABLE("Stale image key (update)!");
break;
}
wr::Vec<uint8_t> bytes;
if (reader.Read(op.bytes(), bytes)) {
aUpdates.UpdateImageBuffer(op.key(), op.descriptor(), bytes);
} else {
gfxCriticalNote << "TOpUpdateImage failed";
success = false;
}
break;
}
case OpUpdateResource::TOpAddBlobImage: {
const auto& op = cmd.get_OpAddBlobImage();
if (!MatchesNamespace(op.key())) {
MOZ_ASSERT_UNREACHABLE("Stale blob image key (add)!");
break;
}
wr::Vec<uint8_t> bytes;
if (reader.Read(op.bytes(), bytes)) {
aUpdates.AddBlobImage(op.key(), op.descriptor(), mBlobTileSize, bytes,
wr::ToDeviceIntRect(op.visibleRect()));
} else {
gfxCriticalNote << "TOpAddBlobImage failed";
success = false;
}
break;
}
case OpUpdateResource::TOpUpdateBlobImage: {
const auto& op = cmd.get_OpUpdateBlobImage();
if (!MatchesNamespace(op.key())) {
MOZ_ASSERT_UNREACHABLE("Stale blob image key (update)!");
break;
}
wr::Vec<uint8_t> bytes;
if (reader.Read(op.bytes(), bytes)) {
aUpdates.UpdateBlobImage(op.key(), op.descriptor(), bytes,
wr::ToDeviceIntRect(op.visibleRect()),
wr::ToLayoutIntRect(op.dirtyRect()));
} else {
gfxCriticalNote << "TOpUpdateBlobImage failed";
success = false;
}
break;
}
case OpUpdateResource::TOpSetBlobImageVisibleArea: {
const auto& op = cmd.get_OpSetBlobImageVisibleArea();
if (!MatchesNamespace(op.key())) {
MOZ_ASSERT_UNREACHABLE("Stale blob image key (visible)!");
break;
}
aUpdates.SetBlobImageVisibleArea(op.key(),
wr::ToDeviceIntRect(op.area()));
break;
}
case OpUpdateResource::TOpAddSharedExternalImage: {
const auto& op = cmd.get_OpAddSharedExternalImage();
// gfxCriticalNote is called on error
if (!AddSharedExternalImage(op.externalImageId(), op.key(), aUpdates)) {
success = false;
}
break;
}
case OpUpdateResource::TOpPushExternalImageForTexture: {
const auto& op = cmd.get_OpPushExternalImageForTexture();
CompositableTextureHostRef texture;
texture = TextureHost::AsTextureHost(op.texture().AsParent());
// gfxCriticalNote is called on error
if (!PushExternalImageForTexture(op.externalImageId(), op.key(),
texture, op.isUpdate(), aUpdates)) {
success = false;
}
break;
}
case OpUpdateResource::TOpUpdateSharedExternalImage: {
const auto& op = cmd.get_OpUpdateSharedExternalImage();
// gfxCriticalNote is called on error
if (!UpdateSharedExternalImage(op.externalImageId(), op.key(),
op.dirtyRect(), aUpdates,
scheduleRelease)) {
success = false;
}
break;
}
case OpUpdateResource::TOpAddRawFont: {
if (!ReadRawFont(cmd.get_OpAddRawFont(), reader, aUpdates)) {
success = false;
}
break;
}
case OpUpdateResource::TOpAddFontDescriptor: {
const auto& op = cmd.get_OpAddFontDescriptor();
if (!MatchesNamespace(op.key())) {
MOZ_ASSERT_UNREACHABLE("Stale font key (add descriptor)!");
break;
}
wr::Vec<uint8_t> bytes;
if (reader.Read(op.bytes(), bytes)) {
aUpdates.AddFontDescriptor(op.key(), bytes, op.fontIndex());
} else {
gfxCriticalNote << "TOpAddFontDescriptor failed";
success = false;
}
break;
}
case OpUpdateResource::TOpAddFontInstance: {
const auto& op = cmd.get_OpAddFontInstance();
if (!MatchesNamespace(op.instanceKey()) ||
!MatchesNamespace(op.fontKey())) {
MOZ_ASSERT_UNREACHABLE("Stale font key (add instance)!");
break;
}
wr::Vec<uint8_t> variations;
if (reader.Read(op.variations(), variations)) {
aUpdates.AddFontInstance(op.instanceKey(), op.fontKey(),
op.glyphSize(), op.options().ptrOr(nullptr),
op.platformOptions().ptrOr(nullptr),
variations);
} else {
gfxCriticalNote << "TOpAddFontInstance failed";
success = false;
}
break;
}
case OpUpdateResource::TOpDeleteImage: {
const auto& op = cmd.get_OpDeleteImage();
if (!MatchesNamespace(op.key())) {
// TODO(aosmond): We should also assert here, but the callers are less
// careful about checking when cleaning up their old keys. We should
// perform an audit on image key usage.
break;
}
DeleteImage(op.key(), aUpdates);
break;
}
case OpUpdateResource::TOpDeleteBlobImage: {
const auto& op = cmd.get_OpDeleteBlobImage();
if (!MatchesNamespace(op.key())) {
MOZ_ASSERT_UNREACHABLE("Stale blob image key (delete)!");
break;
}
aUpdates.DeleteBlobImage(op.key());
break;
}
case OpUpdateResource::TOpDeleteFont: {
const auto& op = cmd.get_OpDeleteFont();
if (!MatchesNamespace(op.key())) {
MOZ_ASSERT_UNREACHABLE("Stale font key (delete)!");
break;
}
aUpdates.DeleteFont(op.key());
break;
}
case OpUpdateResource::TOpDeleteFontInstance: {
const auto& op = cmd.get_OpDeleteFontInstance();
if (!MatchesNamespace(op.key())) {
MOZ_ASSERT_UNREACHABLE("Stale font instance key (delete)!");
break;
}
aUpdates.DeleteFontInstance(op.key());
break;
}
case OpUpdateResource::T__None:
break;
}
}
if (scheduleRelease) {
// When software WR is enabled, shared surfaces are read during rendering
// rather than copied to the texture cache.
wr::Checkpoint when = mApi->GetBackendType() == WebRenderBackend::SOFTWARE
? wr::Checkpoint::FrameRendered
: wr::Checkpoint::FrameTexturesUpdated;
aUpdates.Notify(when, std::move(scheduleRelease));
}
MOZ_ASSERT(success);
return success;
}
bool WebRenderBridgeParent::AddSharedExternalImage(
wr::ExternalImageId aExtId, wr::ImageKey aKey,
wr::TransactionBuilder& aResources) {
if (!MatchesNamespace(aKey)) {
MOZ_ASSERT_UNREACHABLE("Stale shared external image key (add)!");
return true;
}
auto key = wr::AsUint64(aKey);
auto it = mSharedSurfaceIds.find(key);
if (it != mSharedSurfaceIds.end()) {
gfxCriticalNote << "Readding known shared surface: " << key;
return false;
}
RefPtr<DataSourceSurface> dSurf = SharedSurfacesParent::Acquire(aExtId);
if (!dSurf) {
gfxCriticalNote
<< "DataSourceSurface of SharedSurfaces does not exist for extId:"
<< wr::AsUint64(aExtId);
return false;
}
mSharedSurfaceIds.insert(std::make_pair(key, aExtId));
// Prefer raw buffers, unless our backend requires native textures.
IntSize surfaceSize = dSurf->GetSize();
TextureHost::NativeTexturePolicy policy =
TextureHost::BackendNativeTexturePolicy(mApi->GetBackendType(),
surfaceSize);
auto imageType =
policy == TextureHost::NativeTexturePolicy::REQUIRE
? wr::ExternalImageType::TextureHandle(wr::ImageBufferKind::Texture2D)
: wr::ExternalImageType::Buffer();
wr::ImageDescriptor descriptor(surfaceSize, dSurf->Stride(),
dSurf->GetFormat());
aResources.AddExternalImage(aKey, descriptor, aExtId, imageType, 0);
return true;
}
bool WebRenderBridgeParent::PushExternalImageForTexture(
wr::ExternalImageId aExtId, wr::ImageKey aKey, TextureHost* aTexture,
bool aIsUpdate, wr::TransactionBuilder& aResources) {
if (!MatchesNamespace(aKey)) {
MOZ_ASSERT_UNREACHABLE("Stale texture external image key!");
return true;
}
if (!aTexture) {
gfxCriticalNote << "TextureHost does not exist for extId:"
<< wr::AsUint64(aExtId);
return false;
}
auto op = aIsUpdate ? TextureHost::UPDATE_IMAGE : TextureHost::ADD_IMAGE;
WebRenderTextureHost* wrTexture = aTexture->AsWebRenderTextureHost();
if (wrTexture) {
Range<wr::ImageKey> keys(&aKey, 1);
wrTexture->PushResourceUpdates(aResources, op, keys,
wrTexture->GetExternalImageKey());
auto it = mTextureHosts.find(wr::AsUint64(aKey));
MOZ_ASSERT((it == mTextureHosts.end() && !aIsUpdate) ||
(it != mTextureHosts.end() && aIsUpdate));
if (it != mTextureHosts.end()) {
// Release Texture if it exists.
ReleaseTextureOfImage(aKey);
}
mTextureHosts.emplace(wr::AsUint64(aKey),
CompositableTextureHostRef(aTexture));
return true;
}
RefPtr<DataSourceSurface> dSurf = aTexture->GetAsSurface();
if (!dSurf) {
gfxCriticalNote
<< "TextureHost does not return DataSourceSurface for extId:"
<< wr::AsUint64(aExtId);
return false;
}
DataSourceSurface::MappedSurface map;
if (!dSurf->Map(gfx::DataSourceSurface::MapType::READ, &map)) {
gfxCriticalNote << "DataSourceSurface failed to map for Image for extId:"
<< wr::AsUint64(aExtId);
return false;
}
IntSize size = dSurf->GetSize();
wr::ImageDescriptor descriptor(size, map.mStride, dSurf->GetFormat());
wr::Vec<uint8_t> data;
data.PushBytes(Range<uint8_t>(map.mData, size.height * map.mStride));
if (op == TextureHost::UPDATE_IMAGE) {
aResources.UpdateImageBuffer(aKey, descriptor, data);
} else {
aResources.AddImage(aKey, descriptor, data);
}
dSurf->Unmap();
return true;
}
bool WebRenderBridgeParent::UpdateSharedExternalImage(
wr::ExternalImageId aExtId, wr::ImageKey aKey,
const ImageIntRect& aDirtyRect, wr::TransactionBuilder& aResources,
UniquePtr<ScheduleSharedSurfaceRelease>& aScheduleRelease) {
if (!MatchesNamespace(aKey)) {
MOZ_ASSERT_UNREACHABLE("Stale shared external image key (update)!");
return true;
}
auto key = wr::AsUint64(aKey);
auto it = mSharedSurfaceIds.find(key);
if (it == mSharedSurfaceIds.end()) {
gfxCriticalNote << "Updating unknown shared surface: " << key;
return false;
}
RefPtr<DataSourceSurface> dSurf;
if (it->second == aExtId) {
dSurf = SharedSurfacesParent::Get(aExtId);
} else {
dSurf = SharedSurfacesParent::Acquire(aExtId);
}
if (!dSurf) {
gfxCriticalNote << "Shared surface does not exist for extId:"
<< wr::AsUint64(aExtId);
return false;
}
if (!(it->second == aExtId)) {
// We already have a mapping for this image key, so ensure we release the
// previous external image ID. This can happen when an image is animated,
// and it is changing the external image that the animation points to.
if (!aScheduleRelease) {
aScheduleRelease = MakeUnique<ScheduleSharedSurfaceRelease>(this);
}
aScheduleRelease->Add(aKey, it->second);
it->second = aExtId;
}
// Prefer raw buffers, unless our backend requires native textures.
IntSize surfaceSize = dSurf->GetSize();
TextureHost::NativeTexturePolicy policy =
TextureHost::BackendNativeTexturePolicy(mApi->GetBackendType(),
surfaceSize);
auto imageType =
policy == TextureHost::NativeTexturePolicy::REQUIRE
? wr::ExternalImageType::TextureHandle(wr::ImageBufferKind::Texture2D)
: wr::ExternalImageType::Buffer();
wr::ImageDescriptor descriptor(surfaceSize, dSurf->Stride(),
dSurf->GetFormat());
aResources.UpdateExternalImageWithDirtyRect(
aKey, descriptor, aExtId, imageType, wr::ToDeviceIntRect(aDirtyRect), 0,
/* aNormalizedUvs */ false);
return true;
}
void WebRenderBridgeParent::ObserveSharedSurfaceRelease(
const nsTArray<wr::ExternalImageKeyPair>& aPairs,
const bool& aFromCheckpoint) {
if (!mDestroyed) {
Unused << SendWrReleasedImages(aPairs);
}
if (!aFromCheckpoint && mAsyncImageManager) {
// We failed to receive a checkpoint notification, so we are releasing these
// surfaces blind. Let's wait until the next epoch to complete releasing.
for (const auto& pair : aPairs) {
mAsyncImageManager->HoldExternalImage(mPipelineId, mWrEpoch, pair.id);
}
return;
}
// We hit the checkpoint, so we know we can safely release the surfaces now.
for (const auto& pair : aPairs) {
SharedSurfacesParent::Release(pair.id);
}
}
mozilla::ipc::IPCResult WebRenderBridgeParent::RecvUpdateResources(
const wr::IdNamespace& aIdNamespace,
nsTArray<OpUpdateResource>&& aResourceUpdates,
nsTArray<RefCountedShmem>&& aSmallShmems,
nsTArray<ipc::Shmem>&& aLargeShmems) {
const bool isValidMessage = aIdNamespace == mIdNamespace;
if (mDestroyed || !isValidMessage) {
wr::IpcResourceUpdateQueue::ReleaseShmems(this, aSmallShmems);
wr::IpcResourceUpdateQueue::ReleaseShmems(this, aLargeShmems);
return IPC_OK();
}
LOG("WebRenderBridgeParent::RecvUpdateResources() PipelineId %" PRIx64
" Id %" PRIx64 " root %d",
wr::AsUint64(mPipelineId), wr::AsUint64(mApi->GetId()),
IsRootWebRenderBridgeParent());
wr::TransactionBuilder txn(mApi);
txn.SetLowPriority(!IsRootWebRenderBridgeParent());
Unused << GetNextWrEpoch();
bool success =
UpdateResources(aResourceUpdates, aSmallShmems, aLargeShmems, txn);
wr::IpcResourceUpdateQueue::ReleaseShmems(this, aSmallShmems);
wr::IpcResourceUpdateQueue::ReleaseShmems(this, aLargeShmems);
// Even when txn.IsResourceUpdatesEmpty() is true, there could be resource
// updates. It is handled by WebRenderTextureHostWrapper. In this case
// txn.IsRenderedFrameInvalidated() becomes true.
if (!txn.IsResourceUpdatesEmpty() || txn.IsRenderedFrameInvalidated()) {
// There are resource updates, then we update Epoch of transaction.
txn.UpdateEpoch(mPipelineId, mWrEpoch);
mAsyncImageManager->SetWillGenerateFrame();
ScheduleGenerateFrame(wr::RenderReasons::RESOURCE_UPDATE);
} else {
// If TransactionBuilder does not have resource updates nor display list,
// ScheduleGenerateFrame is not triggered via SceneBuilder and there is no
// need to update WrEpoch.
RollbackWrEpoch();
}
mApi->SendTransaction(txn);
if (!success) {
return IPC_FAIL(this, "Invalid WebRender resource data shmem or address.");
}
return IPC_OK();
}
mozilla::ipc::IPCResult WebRenderBridgeParent::RecvDeleteCompositorAnimations(
nsTArray<uint64_t>&& aIds) {
if (mDestroyed) {
return IPC_OK();
}
LOG("WebRenderBridgeParent::RecvDeleteCompositorAnimations() PipelineId "
"%" PRIx64 " Id %" PRIx64 " root %d",
wr::AsUint64(mPipelineId), wr::AsUint64(mApi->GetId()),
IsRootWebRenderBridgeParent());
// Once mWrEpoch has been rendered, we can delete these compositor animations
mCompositorAnimationsToDelete.push(
CompositorAnimationIdsForEpoch(mWrEpoch, std::move(aIds)));
return IPC_OK();
}
void WebRenderBridgeParent::RemoveEpochDataPriorTo(
const wr::Epoch& aRenderedEpoch) {
if (RefPtr<OMTASampler> sampler = GetOMTASampler()) {
sampler->RemoveEpochDataPriorTo(mCompositorAnimationsToDelete,
mActiveAnimations, aRenderedEpoch);
}
}
bool WebRenderBridgeParent::IsRootWebRenderBridgeParent() const {
return !!mWidget;