mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-13 05:15:45 +00:00
Bug 1475139 part 11 - Add CrossProcessPaint implementation. r=mattwoodrow
This commit adds a CrossProcessPaint class which can be used to paint a cross process document tree. This API is async, as we cannot block on child processes, and initially geared towards servicing a JS API and not internal consumers. The API can only be used in the chrome process for security reasons. The class is implemented as a recursive resolver, requesting a root paint, gathering dependent frames to be painted, then requesting paints from those tabs. Once all paints have been completed, the dependency tree is rasterized in a bottom up fashion. Future improvements can be made here. Currently, the rasterization is performed on the main thread which could cause jank. We also transmit recordings directly over IPDl, and no effort is made to minimize the recordings from child layer trees. Differential Revision: https://phabricator.services.mozilla.com/D6790 --HG-- extra : rebase_source : b213de269b33486552ddc0be17207f9fb3f78c9c
This commit is contained in:
parent
454819897e
commit
84bbf4f7d0
@ -32,6 +32,7 @@ include "mozilla/GfxMessageUtils.h";
|
||||
include "mozilla/layers/LayersMessageUtils.h";
|
||||
|
||||
using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
|
||||
using moveonly mozilla::gfx::PaintFragment from "mozilla/gfx/CrossProcessPaint.h";
|
||||
using mozilla::gfx::Matrix from "mozilla/gfx/Matrix.h";
|
||||
using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h";
|
||||
using mozilla::LayoutDeviceIntPoint from "Units.h";
|
||||
@ -766,6 +767,9 @@ child:
|
||||
*/
|
||||
async RenderLayers(bool aEnabled, bool aForceRepaint, LayersObserverEpoch aEpoch);
|
||||
|
||||
async RequestRootPaint(IntRect aRect, float aScale, nscolor aBackgroundColor) returns (PaintFragment retval);
|
||||
async RequestSubPaint(float aScale, nscolor aBackgroundColor) returns (PaintFragment retval);
|
||||
child:
|
||||
/**
|
||||
* Notify the child that it shouldn't paint the offscreen displayport.
|
||||
* This is useful to speed up interactive operations over async
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "mozilla/dom/MessageManagerBinding.h"
|
||||
#include "mozilla/dom/MouseEventBinding.h"
|
||||
#include "mozilla/dom/PaymentRequestChild.h"
|
||||
#include "mozilla/gfx/CrossProcessPaint.h"
|
||||
#include "mozilla/IMEStateManager.h"
|
||||
#include "mozilla/ipc/URIUtils.h"
|
||||
#include "mozilla/layers/APZChild.h"
|
||||
@ -2667,6 +2668,31 @@ TabChild::RecvRenderLayers(const bool& aEnabled, const bool& aForceRepaint, cons
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TabChild::RecvRequestRootPaint(const IntRect& aRect, const float& aScale, const nscolor& aBackgroundColor, RequestRootPaintResolver&& aResolve)
|
||||
{
|
||||
nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
|
||||
if (!docShell) {
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
aResolve(gfx::PaintFragment::Record(docShell, aRect, aScale, aBackgroundColor));
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TabChild::RecvRequestSubPaint(const float& aScale, const nscolor& aBackgroundColor, RequestSubPaintResolver&& aResolve)
|
||||
{
|
||||
nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
|
||||
if (!docShell) {
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
gfx::IntRect rect = gfx::RoundedIn(gfx::Rect(0.0f, 0.0f, mUnscaledInnerSize.width, mUnscaledInnerSize.height));
|
||||
aResolve(gfx::PaintFragment::Record(docShell, rect, aScale, aBackgroundColor));
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TabChild::RecvNavigateByKey(const bool& aForward, const bool& aForDocumentNavigation)
|
||||
{
|
||||
|
@ -724,6 +724,10 @@ protected:
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvRenderLayers(const bool& aEnabled, const bool& aForce, const layers::LayersObserverEpoch& aEpoch) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvRequestRootPaint(const IntRect& aRect, const float& aScale, const nscolor& aBackgroundColor, RequestRootPaintResolver&& aResolve) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvRequestSubPaint(const float& aScale, const nscolor& aBackgroundColor, RequestSubPaintResolver&& aResolve) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvNavigateByKey(const bool& aForward,
|
||||
const bool& aForDocumentNavigation) override;
|
||||
|
||||
|
@ -3098,6 +3098,38 @@ TabParent::LayerTreeUpdate(const LayersObserverEpoch& aEpoch, bool aActive)
|
||||
mFrameElement->DispatchEvent(*event);
|
||||
}
|
||||
|
||||
void
|
||||
TabParent::RequestRootPaint(gfx::CrossProcessPaint* aPaint, IntRect aRect, float aScale, nscolor aBackgroundColor)
|
||||
{
|
||||
auto promise = SendRequestRootPaint(aRect, aScale, aBackgroundColor);
|
||||
|
||||
RefPtr<gfx::CrossProcessPaint> paint(aPaint);
|
||||
TabId tabId(GetTabId());
|
||||
promise->Then(GetMainThreadSerialEventTarget(), __func__,
|
||||
[paint, tabId] (PaintFragment&& aFragment) {
|
||||
paint->ReceiveFragment(tabId, std::move(aFragment));
|
||||
},
|
||||
[paint, tabId] (ResponseRejectReason aReason) {
|
||||
paint->LostFragment(tabId);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
TabParent::RequestSubPaint(gfx::CrossProcessPaint* aPaint, float aScale, nscolor aBackgroundColor)
|
||||
{
|
||||
auto promise = SendRequestSubPaint(aScale, aBackgroundColor);
|
||||
|
||||
RefPtr<gfx::CrossProcessPaint> paint(aPaint);
|
||||
TabId tabId(GetTabId());
|
||||
promise->Then(GetMainThreadSerialEventTarget(), __func__,
|
||||
[paint, tabId] (PaintFragment&& aFragment) {
|
||||
paint->ReceiveFragment(tabId, std::move(aFragment));
|
||||
},
|
||||
[paint, tabId] (ResponseRejectReason aReason) {
|
||||
paint->LostFragment(tabId);
|
||||
});
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TabParent::RecvPaintWhileInterruptingJSNoOp(const LayersObserverEpoch& aEpoch)
|
||||
{
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "mozilla/dom/TabContext.h"
|
||||
#include "mozilla/EventForwards.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/gfx/CrossProcessPaint.h"
|
||||
#include "mozilla/layers/CompositorBridgeParent.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/Move.h"
|
||||
@ -562,6 +563,9 @@ public:
|
||||
|
||||
void LayerTreeUpdate(const LayersObserverEpoch& aEpoch, bool aActive);
|
||||
|
||||
void RequestRootPaint(gfx::CrossProcessPaint* aPaint, IntRect aRect, float aScale, nscolor aBackgroundColor);
|
||||
void RequestSubPaint(gfx::CrossProcessPaint* aPaint, float aScale, nscolor aBackgroundColor);
|
||||
|
||||
virtual mozilla::ipc::IPCResult
|
||||
RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers,
|
||||
const uint32_t& aAction,
|
||||
|
427
gfx/ipc/CrossProcessPaint.cpp
Normal file
427
gfx/ipc/CrossProcessPaint.cpp
Normal file
@ -0,0 +1,427 @@
|
||||
/* -*- 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 "CrossProcessPaint.h"
|
||||
|
||||
#include "mozilla/dom/ContentProcessManager.h"
|
||||
#include "mozilla/dom/ImageBitmap.h"
|
||||
#include "mozilla/dom/TabParent.h"
|
||||
#include "mozilla/gfx/DrawEventRecorder.h"
|
||||
#include "mozilla/gfx/InlineTranslator.h"
|
||||
#include "mozilla/PresShell.h"
|
||||
|
||||
#include "gfxPlatform.h"
|
||||
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsGlobalWindowInner.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsPresContext.h"
|
||||
|
||||
#define ENABLE_PAINT_LOG 0
|
||||
// #define ENABLE_PAINT_LOG 1
|
||||
|
||||
#if ENABLE_PAINT_LOG
|
||||
# define PF_LOG(...) printf_stderr("PaintFragment: " __VA_ARGS__)
|
||||
# define CPP_LOG(...) printf_stderr("CrossProcessPaint: " __VA_ARGS__)
|
||||
#else
|
||||
# define PF_LOG(...)
|
||||
# define CPP_LOG(...)
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
using namespace mozilla::ipc;
|
||||
|
||||
/// The minimum scale we allow tabs to be rasterized at.
|
||||
static const float kMinPaintScale = 0.05;
|
||||
|
||||
/* static */ PaintFragment
|
||||
PaintFragment::Record(nsIDocShell* aDocShell,
|
||||
const IntRect& aRect,
|
||||
float aScale,
|
||||
nscolor aBackgroundColor)
|
||||
{
|
||||
IntSize surfaceSize = aRect.Size();
|
||||
surfaceSize.width *= aScale;
|
||||
surfaceSize.height *= aScale;
|
||||
|
||||
CPP_LOG("Recording "
|
||||
"[docshell=%p, "
|
||||
"rect=(%d, %d) x (%d, %d), "
|
||||
"scale=%f, "
|
||||
"color=(%u, %u, %u, %u)]\n",
|
||||
aDocShell,
|
||||
aRect.x, aRect.y, aRect.width, aRect.height,
|
||||
aScale,
|
||||
NS_GET_R(aBackgroundColor),
|
||||
NS_GET_G(aBackgroundColor),
|
||||
NS_GET_B(aBackgroundColor),
|
||||
NS_GET_A(aBackgroundColor));
|
||||
|
||||
// Check for invalid sizes
|
||||
if (surfaceSize.width <= 0 || surfaceSize.height <= 0 ||
|
||||
!Factory::CheckSurfaceSize(surfaceSize)) {
|
||||
PF_LOG("Invalid surface size of (%d x %d).\n",
|
||||
surfaceSize.width,
|
||||
surfaceSize.height);
|
||||
return PaintFragment{};
|
||||
}
|
||||
|
||||
// Flush any pending notifications
|
||||
nsContentUtils::FlushLayoutForTree(aDocShell->GetWindow());
|
||||
|
||||
// Grab the presentation shell to render
|
||||
RefPtr<nsPresContext> presContext;
|
||||
if (aDocShell) {
|
||||
aDocShell->GetPresContext(getter_AddRefs(presContext));
|
||||
}
|
||||
if (!presContext) {
|
||||
PF_LOG("Couldn't find PresContext.\n");
|
||||
return PaintFragment{};
|
||||
}
|
||||
|
||||
// Initialize the recorder
|
||||
SurfaceFormat format = SurfaceFormat::B8G8R8A8;
|
||||
RefPtr<DrawTarget> referenceDt =
|
||||
Factory::CreateDrawTarget(gfxPlatform::GetPlatform()->GetSoftwareBackend(),
|
||||
IntSize(1, 1),
|
||||
format);
|
||||
|
||||
// TODO: This may OOM crash if the content is complex enough
|
||||
RefPtr<DrawEventRecorderMemory> recorder =
|
||||
MakeAndAddRef<DrawEventRecorderMemory>(nullptr);
|
||||
RefPtr<DrawTarget> dt =
|
||||
Factory::CreateRecordingDrawTarget(recorder, referenceDt, surfaceSize);
|
||||
|
||||
// Perform the actual rendering
|
||||
{
|
||||
nsRect r(nsPresContext::CSSPixelsToAppUnits(aRect.x),
|
||||
nsPresContext::CSSPixelsToAppUnits(aRect.y),
|
||||
nsPresContext::CSSPixelsToAppUnits(aRect.width),
|
||||
nsPresContext::CSSPixelsToAppUnits(aRect.height));
|
||||
|
||||
RefPtr<gfxContext> thebes = gfxContext::CreateOrNull(dt);
|
||||
thebes->SetMatrix(Matrix::Scaling(aScale, aScale));
|
||||
nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
|
||||
Unused << shell->RenderDocument(r, 0, aBackgroundColor, thebes);
|
||||
}
|
||||
|
||||
ByteBuf recording = ByteBuf((uint8_t*)recorder->mOutputStream.mData,
|
||||
recorder->mOutputStream.mLength,
|
||||
recorder->mOutputStream.mCapacity);
|
||||
recorder->mOutputStream.mData = nullptr;
|
||||
recorder->mOutputStream.mLength = 0;
|
||||
recorder->mOutputStream.mCapacity = 0;
|
||||
|
||||
return PaintFragment{
|
||||
surfaceSize,
|
||||
std::move(recording),
|
||||
std::move(recorder->TakeDependentSurfaces()),
|
||||
};
|
||||
}
|
||||
|
||||
bool
|
||||
PaintFragment::IsEmpty() const
|
||||
{
|
||||
return !mRecording.mData || mRecording.mLen == 0 || mSize == IntSize(0, 0);
|
||||
}
|
||||
|
||||
PaintFragment::PaintFragment(IntSize aSize,
|
||||
ByteBuf&& aRecording,
|
||||
nsTHashtable<nsUint64HashKey>&& aDependencies)
|
||||
: mSize(aSize)
|
||||
, mRecording(std::move(aRecording))
|
||||
, mDependencies(std::move(aDependencies))
|
||||
{
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
CrossProcessPaint::StartLocal(nsIDocShell* aRoot,
|
||||
const IntRect& aRect,
|
||||
float aScale,
|
||||
nscolor aBackgroundColor,
|
||||
dom::Promise* aPromise)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
|
||||
aScale = std::max(aScale, kMinPaintScale);
|
||||
|
||||
CPP_LOG("Starting local paint. "
|
||||
"[docshell=%p, "
|
||||
"rect=(%d, %d) x (%d, %d), "
|
||||
"scale=%f, "
|
||||
"color=(%u, %u, %u, %u)]\n",
|
||||
aRoot,
|
||||
aRect.x, aRect.y, aRect.width, aRect.height,
|
||||
aScale,
|
||||
NS_GET_R(aBackgroundColor),
|
||||
NS_GET_G(aBackgroundColor),
|
||||
NS_GET_B(aBackgroundColor),
|
||||
NS_GET_A(aBackgroundColor));
|
||||
|
||||
RefPtr<CrossProcessPaint> resolver = new CrossProcessPaint(aPromise,
|
||||
aScale,
|
||||
aBackgroundColor,
|
||||
dom::TabId(0));
|
||||
resolver->ReceiveFragment(dom::TabId(0),
|
||||
PaintFragment::Record(aRoot,
|
||||
aRect,
|
||||
aScale,
|
||||
aBackgroundColor));
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
CrossProcessPaint::StartRemote(dom::TabId aRoot,
|
||||
const IntRect& aRect,
|
||||
float aScale,
|
||||
nscolor aBackgroundColor,
|
||||
dom::Promise* aPromise)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
|
||||
aScale = std::max(aScale, kMinPaintScale);
|
||||
|
||||
CPP_LOG("Starting remote paint. "
|
||||
"[tab=%llu, "
|
||||
"rect=(%d, %d) x (%d, %d), "
|
||||
"scale=%f, "
|
||||
"color=(%u, %u, %u, %u)]\n",
|
||||
(uint64_t)aRoot,
|
||||
aRect.x, aRect.y, aRect.width, aRect.height,
|
||||
aScale,
|
||||
NS_GET_R(aBackgroundColor),
|
||||
NS_GET_G(aBackgroundColor),
|
||||
NS_GET_B(aBackgroundColor),
|
||||
NS_GET_A(aBackgroundColor));
|
||||
|
||||
RefPtr<CrossProcessPaint> resolver = new CrossProcessPaint(aPromise,
|
||||
aScale,
|
||||
aBackgroundColor,
|
||||
aRoot);
|
||||
resolver->QueueRootPaint(aRoot, aRect, aScale, aBackgroundColor);
|
||||
}
|
||||
|
||||
CrossProcessPaint::CrossProcessPaint(dom::Promise* aPromise,
|
||||
float aScale,
|
||||
nscolor aBackgroundColor,
|
||||
dom::TabId aRootId)
|
||||
: mPromise{aPromise}
|
||||
, mRootId{aRootId}
|
||||
, mScale{aScale}
|
||||
, mBackgroundColor{aBackgroundColor}
|
||||
, mPendingFragments{1}
|
||||
{
|
||||
}
|
||||
|
||||
CrossProcessPaint::~CrossProcessPaint()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
CrossProcessPaint::ReceiveFragment(dom::TabId aId, PaintFragment&& aFragment)
|
||||
{
|
||||
if (IsCleared()) {
|
||||
CPP_LOG("Ignoring fragment from %llu.\n", (uint64_t)aId);
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mPendingFragments > 0);
|
||||
MOZ_ASSERT(!mReceivedFragments.GetValue(aId));
|
||||
MOZ_ASSERT(!aFragment.IsEmpty());
|
||||
|
||||
// Double check our invariants to protect against a compromised content
|
||||
// process
|
||||
if (mPendingFragments == 0 ||
|
||||
mReceivedFragments.GetValue(aId) ||
|
||||
aFragment.IsEmpty()) {
|
||||
CPP_LOG("Dropping invalid fragment from %llu.\n", (uint64_t)aId);
|
||||
LostFragment(aId);
|
||||
return;
|
||||
}
|
||||
|
||||
CPP_LOG("Receiving fragment from %llu.\n", (uint64_t)aId);
|
||||
|
||||
// Queue paints for child tabs
|
||||
for (auto iter = aFragment.mDependencies.Iter(); !iter.Done(); iter.Next()) {
|
||||
auto dependency = iter.Get()->GetKey();
|
||||
QueueSubPaint(dom::TabId(dependency));
|
||||
}
|
||||
|
||||
mReceivedFragments.Put(aId, std::move(aFragment));
|
||||
mPendingFragments -= 1;
|
||||
|
||||
// Resolve this paint if we have received all pending fragments
|
||||
MaybeResolve();
|
||||
}
|
||||
|
||||
void
|
||||
CrossProcessPaint::LostFragment(dom::TabId aId)
|
||||
{
|
||||
if (IsCleared()) {
|
||||
CPP_LOG("Ignoring lost fragment from %llu.\n", (uint64_t)aId);
|
||||
return;
|
||||
}
|
||||
|
||||
mPromise->MaybeReject(NS_ERROR_FAILURE);
|
||||
Clear();
|
||||
}
|
||||
|
||||
void
|
||||
CrossProcessPaint::QueueRootPaint(dom::TabId aId,
|
||||
const IntRect& aRect,
|
||||
float aScale,
|
||||
nscolor aBackgroundColor)
|
||||
{
|
||||
MOZ_ASSERT(!mReceivedFragments.GetValue(aId));
|
||||
MOZ_ASSERT(mPendingFragments == 1);
|
||||
|
||||
CPP_LOG("Queueing root paint for %llu.\n", (uint64_t)aId);
|
||||
|
||||
dom::ContentProcessManager* cpm = dom::ContentProcessManager::GetSingleton();
|
||||
|
||||
dom::ContentParentId cpId = cpm->GetTabProcessId(aId);
|
||||
RefPtr<dom::TabParent> tab = cpm->GetTabParentByProcessAndTabId(cpId, aId);
|
||||
tab->RequestRootPaint(this, aRect, aScale, aBackgroundColor);
|
||||
|
||||
// This will always be the first paint, so the constructor will already have
|
||||
// incremented one pending fragment
|
||||
}
|
||||
|
||||
void
|
||||
CrossProcessPaint::QueueSubPaint(dom::TabId aId)
|
||||
{
|
||||
MOZ_ASSERT(!mReceivedFragments.GetValue((uint64_t)aId));
|
||||
|
||||
CPP_LOG("Queueing sub paint for %llu.\n", (uint64_t)aId);
|
||||
|
||||
dom::ContentProcessManager* cpm = dom::ContentProcessManager::GetSingleton();
|
||||
|
||||
dom::ContentParentId cpId = cpm->GetTabProcessId(aId);
|
||||
RefPtr<dom::TabParent> tab = cpm->GetTabParentByProcessAndTabId(cpId, aId);
|
||||
tab->RequestSubPaint(this, mScale, mBackgroundColor);
|
||||
|
||||
mPendingFragments += 1;
|
||||
}
|
||||
|
||||
void
|
||||
CrossProcessPaint::Clear()
|
||||
{
|
||||
mPromise = nullptr;
|
||||
mPendingFragments = 0;
|
||||
mReceivedFragments.Clear();
|
||||
}
|
||||
|
||||
bool
|
||||
CrossProcessPaint::IsCleared() const
|
||||
{
|
||||
return !mPromise;
|
||||
}
|
||||
|
||||
void
|
||||
CrossProcessPaint::MaybeResolve()
|
||||
{
|
||||
// Don't do anything if we aren't ready, experienced an error, or already
|
||||
// resolved this paint
|
||||
if (IsCleared() || mPendingFragments > 0) {
|
||||
CPP_LOG("Not ready to resolve yet, have %u fragments left.\n",
|
||||
mPendingFragments);
|
||||
return;
|
||||
}
|
||||
|
||||
CPP_LOG("Starting to resolve fragments.\n");
|
||||
|
||||
// Resolve the paint fragments from the bottom up
|
||||
ResolvedSurfaceMap resolved;
|
||||
if (!ResolveInternal(mRootId, &resolved)) {
|
||||
CPP_LOG("Couldn't resolve.\n");
|
||||
|
||||
mPromise->MaybeReject(NS_ERROR_FAILURE);
|
||||
Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// Grab the result from the resolved table
|
||||
RefPtr<SourceSurface> root = resolved.Get(mRootId);
|
||||
CPP_LOG("Resolved all fragments.\n");
|
||||
|
||||
ErrorResult rv;
|
||||
RefPtr<dom::ImageBitmap> bitmap =
|
||||
dom::ImageBitmap::CreateFromSourceSurface(mPromise->GetParentObject(),
|
||||
root,
|
||||
rv);
|
||||
|
||||
if (!rv.Failed()) {
|
||||
CPP_LOG("Success, fulfilling promise.\n");
|
||||
mPromise->MaybeResolve(bitmap);
|
||||
} else {
|
||||
CPP_LOG("Couldn't create ImageBitmap for SourceSurface.\n");
|
||||
mPromise->MaybeReject(rv);
|
||||
}
|
||||
Clear();
|
||||
}
|
||||
|
||||
bool
|
||||
CrossProcessPaint::ResolveInternal(dom::TabId aId,
|
||||
ResolvedSurfaceMap* aResolved)
|
||||
{
|
||||
// We should not have resolved this paint already
|
||||
MOZ_ASSERT(!aResolved->GetWeak(aId));
|
||||
|
||||
CPP_LOG("Resolving fragment %llu.\n", (uint64_t)aId);
|
||||
|
||||
Maybe<PaintFragment> fragment = mReceivedFragments.GetAndRemove(aId);
|
||||
|
||||
// Rasterize all the dependencies first so that we can resolve this fragment
|
||||
for (auto iter = fragment->mDependencies.Iter(); !iter.Done(); iter.Next()) {
|
||||
auto dependency = iter.Get()->GetKey();
|
||||
if (!ResolveInternal(dom::TabId(dependency), aResolved)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the destination draw target
|
||||
RefPtr<DrawTarget> drawTarget =
|
||||
gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(fragment->mSize,
|
||||
SurfaceFormat::B8G8R8A8);
|
||||
if (!drawTarget || !drawTarget->IsValid()) {
|
||||
CPP_LOG("Couldn't create (%d x %d) surface for fragment %llu.\n",
|
||||
fragment->mSize.width,
|
||||
fragment->mSize.height,
|
||||
(uint64_t)aId);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Translate the recording using our child tabs
|
||||
{
|
||||
InlineTranslator translator(drawTarget, nullptr);
|
||||
translator.SetExternalSurfaces(aResolved);
|
||||
if (!translator.TranslateRecording((char*)fragment->mRecording.mData,
|
||||
fragment->mRecording.mLen)) {
|
||||
CPP_LOG("Couldn't translate recording for fragment %llu.\n",
|
||||
(uint64_t)aId);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<SourceSurface> snapshot = drawTarget->Snapshot();
|
||||
if (!snapshot) {
|
||||
CPP_LOG("Couldn't get snapshot for fragment %llu.\n",
|
||||
(uint64_t)aId);
|
||||
return false;
|
||||
}
|
||||
|
||||
// We are done with the resolved images of our dependencies, let's remove
|
||||
// them
|
||||
for (auto iter = fragment->mDependencies.Iter(); !iter.Done(); iter.Next()) {
|
||||
auto dependency = iter.Get()->GetKey();
|
||||
aResolved->Remove(dependency);
|
||||
}
|
||||
|
||||
aResolved->Put(aId, snapshot);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
170
gfx/ipc/CrossProcessPaint.h
Normal file
170
gfx/ipc/CrossProcessPaint.h
Normal file
@ -0,0 +1,170 @@
|
||||
/* -*- 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/. */
|
||||
#ifndef _include_mozilla_gfx_ipc_CrossProcessPaint_h_
|
||||
#define _include_mozilla_gfx_ipc_CrossProcessPaint_h_
|
||||
|
||||
#include "nsISupportsImpl.h"
|
||||
|
||||
#include "mozilla/dom/ipc/IdType.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/ipc/ByteBuf.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsTHashtable.h"
|
||||
|
||||
namespace IPC {
|
||||
template<typename T> struct ParamTraits;
|
||||
} // namespace IPC
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
class CrossProcessPaint;
|
||||
|
||||
/**
|
||||
* A fragment of a paint of a cross process document tree.
|
||||
*/
|
||||
class PaintFragment
|
||||
{
|
||||
public:
|
||||
/// Initializes an empty PaintFragment
|
||||
PaintFragment() = default;
|
||||
|
||||
/**
|
||||
* Creates a paint fragment by recording the draw commands and dependent tabs
|
||||
* for an nsIDocShell.
|
||||
*
|
||||
* @param aDocShell The document shell to record.
|
||||
* @param aRect The rectangle relative to the viewport to use.
|
||||
* @param aScale The coordinate scale to use. The size of the resolved
|
||||
* surface will be `aRect.Size() * aScale`, with aScale clamped to
|
||||
* at least kMinPaintScale.
|
||||
* @param aBackgroundColor The background color to use.
|
||||
*
|
||||
* @return A paint fragment. The paint fragment may be `empty` if rendering
|
||||
* was unable to be accomplished for some reason.
|
||||
*/
|
||||
static PaintFragment Record(nsIDocShell* aDocShell,
|
||||
const IntRect& aRect,
|
||||
float aScale,
|
||||
nscolor aBackgroundColor);
|
||||
|
||||
/// Returns whether this paint fragment contains a valid recording.
|
||||
bool IsEmpty() const;
|
||||
|
||||
PaintFragment(PaintFragment&&) = default;
|
||||
PaintFragment& operator=(PaintFragment&&) = default;
|
||||
|
||||
protected:
|
||||
friend struct IPC::ParamTraits<PaintFragment>;
|
||||
friend CrossProcessPaint;
|
||||
|
||||
typedef mozilla::ipc::ByteBuf ByteBuf;
|
||||
|
||||
PaintFragment(IntSize, ByteBuf&&, nsTHashtable<nsUint64HashKey>&&);
|
||||
|
||||
IntSize mSize;
|
||||
ByteBuf mRecording;
|
||||
nsTHashtable<nsUint64HashKey> mDependencies;
|
||||
};
|
||||
|
||||
/**
|
||||
* An object for painting a cross process document tree.
|
||||
*/
|
||||
class CrossProcessPaint
|
||||
{
|
||||
NS_INLINE_DECL_REFCOUNTING(CrossProcessPaint);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Begin an asynchronous paint of a cross process document tree starting at
|
||||
* a local document shell. The local document will be painted, then async
|
||||
* paints will be queued for remote subframes. Once all subframes have been
|
||||
* recorded, the final image will be resolved, and the promise will be
|
||||
* resolved with a dom::ImageBitmap.
|
||||
*
|
||||
* @param aDocShell The document shell to paint.
|
||||
* @param aRect The rectangle relative to the viewport to use.
|
||||
* @param aScale The coordinate scale to use. The size of the resolved
|
||||
* surface will be `aRect.Size() * aScale`, with aScale clamped to
|
||||
* at least kMinPaintScale. See the implementation for the current
|
||||
* minimum value.
|
||||
* @param aBackgroundColor The background color to use.
|
||||
* @param aPromise The promise to resolve with a dom::ImageBitmap.
|
||||
*/
|
||||
static void StartLocal(nsIDocShell* aRoot,
|
||||
const IntRect& aRect,
|
||||
float aScale,
|
||||
nscolor aBackgroundColor,
|
||||
dom::Promise* aPromise);
|
||||
|
||||
/**
|
||||
* Begin an asynchronous paint of a cross process document tree starting at
|
||||
* a remote tab. An async paint for the remote tab will be queued, then async
|
||||
* paints will be recursively queued for remote subframes. Once all subframes
|
||||
* have been recorded, the final image will be resolved, and the promise will
|
||||
* be resolved with a dom::ImageBitmap.
|
||||
*
|
||||
* @param aDocShell The document shell to paint.
|
||||
* @param aRect The rectangle relative to the viewport to use.
|
||||
* @param aScale The coordinate scale to use. The size of the resolved
|
||||
* surface will be `aRect.Size() * aScale`, with aScale clamped to
|
||||
* at least kMinPaintScale. See the implementation for the current
|
||||
* minimum value.
|
||||
* @param aBackgroundColor The background color to use.
|
||||
* @param aPromise The promise to resolve with a dom::ImageBitmap.
|
||||
*/
|
||||
static void StartRemote(dom::TabId aRoot,
|
||||
const IntRect& aRect,
|
||||
float aScale,
|
||||
nscolor aBackgroundColor,
|
||||
dom::Promise* aPromise);
|
||||
|
||||
void ReceiveFragment(dom::TabId aId, PaintFragment&& aFragment);
|
||||
void LostFragment(dom::TabId aId);
|
||||
private:
|
||||
typedef nsRefPtrHashtable<nsUint64HashKey, SourceSurface> ResolvedSurfaceMap;
|
||||
typedef nsDataHashtable<nsUint64HashKey, PaintFragment> ReceivedFragmentMap;
|
||||
|
||||
CrossProcessPaint(dom::Promise* aPromise,
|
||||
float aScale,
|
||||
nscolor aBackgroundColor,
|
||||
dom::TabId aRootId);
|
||||
~CrossProcessPaint();
|
||||
|
||||
void QueueRootPaint(dom::TabId aId,
|
||||
const IntRect& aRect,
|
||||
float aScale,
|
||||
nscolor aBackgroundColor);
|
||||
void QueueSubPaint(dom::TabId aId);
|
||||
|
||||
/// Clear the state of this paint so that it cannot be resolved or receive
|
||||
/// any paint fragments.
|
||||
void Clear();
|
||||
|
||||
/// Returns if this paint has been cleared.
|
||||
bool IsCleared() const;
|
||||
|
||||
/// Resolves the paint fragments if we have none pending and resolves the
|
||||
/// promise.
|
||||
void MaybeResolve();
|
||||
bool ResolveInternal(dom::TabId aId,
|
||||
ResolvedSurfaceMap* aResolved);
|
||||
|
||||
RefPtr<dom::Promise> mPromise;
|
||||
dom::TabId mRootId;
|
||||
float mScale;
|
||||
nscolor mBackgroundColor;
|
||||
uint32_t mPendingFragments;
|
||||
ReceivedFragmentMap mReceivedFragments;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // _include_mozilla_gfx_ipc_CrossProcessPaint_h_
|
@ -20,6 +20,7 @@
|
||||
#include "gfxTelemetry.h"
|
||||
#include "gfxTypes.h"
|
||||
#include "ipc/IPCMessageUtils.h"
|
||||
#include "mozilla/gfx/CrossProcessPaint.h"
|
||||
#include "mozilla/gfx/Matrix.h"
|
||||
#include "nsRect.h"
|
||||
#include "nsRegion.h"
|
||||
@ -1279,6 +1280,23 @@ struct ParamTraits<mozilla::Array<T, Length>>
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ParamTraits<mozilla::gfx::PaintFragment>
|
||||
{
|
||||
typedef mozilla::gfx::PaintFragment paramType;
|
||||
static void Write(Message* aMsg, paramType& aParam) {
|
||||
WriteParam(aMsg, aParam.mSize);
|
||||
WriteParam(aMsg, aParam.mRecording);
|
||||
WriteParam(aMsg, aParam.mDependencies);
|
||||
}
|
||||
|
||||
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) {
|
||||
return ReadParam(aMsg, aIter, &aResult->mSize) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mRecording) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mDependencies);
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace IPC */
|
||||
|
||||
#endif /* __GFXMESSAGEUTILS_H__ */
|
||||
|
@ -13,6 +13,7 @@ EXPORTS.mozilla += [
|
||||
]
|
||||
|
||||
EXPORTS.mozilla.gfx += [
|
||||
'CrossProcessPaint.h',
|
||||
'GPUChild.h',
|
||||
'GPUParent.h',
|
||||
'GPUProcessHost.h',
|
||||
@ -49,6 +50,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
||||
UNIFIED_SOURCES += [
|
||||
'CompositorSession.cpp',
|
||||
'CompositorWidgetVsyncObserver.cpp',
|
||||
'CrossProcessPaint.cpp',
|
||||
'D3DMessageUtils.cpp',
|
||||
'GPUChild.cpp',
|
||||
'GPUProcessHost.cpp',
|
||||
|
Loading…
Reference in New Issue
Block a user