gecko-dev/image/SourceSurfaceBlobImage.cpp

264 lines
9.1 KiB
C++

/* -*- 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 "SourceSurfaceBlobImage.h"
#include "AutoRestoreSVGState.h"
#include "ImageRegion.h"
#include "SVGDrawingParameters.h"
#include "SVGDrawingCallback.h"
#include "SVGDocumentWrapper.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/layers/IpcResourceUpdateQueue.h"
#include "mozilla/layers/WebRenderBridgeChild.h"
#include "mozilla/layers/WebRenderDrawEventRecorder.h"
using namespace mozilla::gfx;
using namespace mozilla::layers;
namespace mozilla::image {
SourceSurfaceBlobImage::SourceSurfaceBlobImage(
image::SVGDocumentWrapper* aSVGDocumentWrapper,
const Maybe<SVGImageContext>& aSVGContext,
const Maybe<ImageIntRegion>& aRegion, const IntSize& aSize,
uint32_t aWhichFrame, uint32_t aImageFlags)
: mSVGDocumentWrapper(aSVGDocumentWrapper),
mSVGContext(aSVGContext),
mRegion(aRegion),
mSize(aSize),
mWhichFrame(aWhichFrame),
mImageFlags(aImageFlags) {
MOZ_ASSERT(mSVGDocumentWrapper);
MOZ_ASSERT(aWhichFrame <= imgIContainer::FRAME_MAX_VALUE);
MOZ_ASSERT(aImageFlags & imgIContainer::FLAG_RECORD_BLOB);
}
SourceSurfaceBlobImage::~SourceSurfaceBlobImage() {
if (NS_IsMainThread()) {
DestroyKeys(mKeys);
return;
}
NS_ReleaseOnMainThread("SourceSurfaceBlobImage::mSVGDocumentWrapper",
mSVGDocumentWrapper.forget());
NS_DispatchToMainThread(
NS_NewRunnableFunction("SourceSurfaceBlobImage::DestroyKeys",
[keys = std::move(mKeys)] { DestroyKeys(keys); }));
}
/* static */ void SourceSurfaceBlobImage::DestroyKeys(
const AutoTArray<BlobImageKeyData, 1>& aKeys) {
for (const auto& entry : aKeys) {
if (!entry.mManager->IsDestroyed()) {
entry.mManager->GetRenderRootStateManager()->AddBlobImageKeyForDiscard(
entry.mBlobKey);
}
}
}
Maybe<wr::BlobImageKey> SourceSurfaceBlobImage::UpdateKey(
WebRenderLayerManager* aManager, wr::IpcResourceUpdateQueue& aResources) {
MOZ_ASSERT(NS_IsMainThread());
Maybe<wr::BlobImageKey> key;
auto i = mKeys.Length();
while (i > 0) {
--i;
BlobImageKeyData& entry = mKeys[i];
if (entry.mManager->IsDestroyed()) {
mKeys.RemoveElementAt(i);
} else if (entry.mManager == aManager) {
WebRenderBridgeChild* wrBridge = aManager->WrBridge();
MOZ_ASSERT(wrBridge);
bool ownsKey = wrBridge->MatchesNamespace(entry.mBlobKey);
if (ownsKey && !entry.mDirty) {
key.emplace(entry.mBlobKey);
continue;
}
// Even if the manager is the same, its underlying WebRenderBridgeChild
// can change state. Either our namespace differs, and our old key has
// already been discarded, or the blob has changed. Either way, we need
// to rerecord it.
auto newEntry = RecordDrawing(aManager, aResources,
ownsKey ? Some(entry.mBlobKey) : Nothing());
if (!newEntry) {
if (ownsKey) {
aManager->GetRenderRootStateManager()->AddBlobImageKeyForDiscard(
entry.mBlobKey);
}
mKeys.RemoveElementAt(i);
continue;
}
key.emplace(newEntry.ref().mBlobKey);
entry = std::move(newEntry.ref());
MOZ_ASSERT(!entry.mDirty);
}
}
// We didn't find an entry. Attempt to record the blob with a new key.
if (!key) {
auto newEntry = RecordDrawing(aManager, aResources, Nothing());
if (newEntry) {
key.emplace(newEntry.ref().mBlobKey);
mKeys.AppendElement(std::move(newEntry.ref()));
}
}
return key;
}
void SourceSurfaceBlobImage::MarkDirty() {
MOZ_ASSERT(NS_IsMainThread());
auto i = mKeys.Length();
while (i > 0) {
--i;
BlobImageKeyData& entry = mKeys[i];
if (entry.mManager->IsDestroyed()) {
mKeys.RemoveElementAt(i);
} else {
entry.mDirty = true;
}
}
}
Maybe<BlobImageKeyData> SourceSurfaceBlobImage::RecordDrawing(
WebRenderLayerManager* aManager, wr::IpcResourceUpdateQueue& aResources,
Maybe<wr::BlobImageKey> aBlobKey) {
MOZ_ASSERT(!aManager->IsDestroyed());
if (mSVGDocumentWrapper->IsDrawing()) {
return Nothing();
}
// This is either our first pass, or we have a stale key requiring us to
// re-record the SVG image draw commands.
auto* rootManager = aManager->GetRenderRootStateManager();
auto* wrBridge = aManager->WrBridge();
IntRect imageRect =
mRegion ? mRegion->Rect() : IntRect(IntPoint(0, 0), mSize);
IntRect imageRectOrigin = imageRect - imageRect.TopLeft();
std::vector<RefPtr<ScaledFont>> fonts;
bool validFonts = true;
RefPtr<WebRenderDrawEventRecorder> recorder =
MakeAndAddRef<WebRenderDrawEventRecorder>(
[&](MemStream& aStream,
std::vector<RefPtr<ScaledFont>>& aScaledFonts) {
auto count = aScaledFonts.size();
aStream.write((const char*)&count, sizeof(count));
for (auto& scaled : aScaledFonts) {
Maybe<wr::FontInstanceKey> key =
wrBridge->GetFontKeyForScaledFont(scaled, &aResources);
if (key.isNothing()) {
validFonts = false;
break;
}
BlobFont font = {key.value(), scaled};
aStream.write((const char*)&font, sizeof(font));
}
fonts = std::move(aScaledFonts);
});
RefPtr<DrawTarget> dummyDt = Factory::CreateDrawTarget(
BackendType::SKIA, IntSize(1, 1), SurfaceFormat::OS_RGBA);
RefPtr<DrawTarget> dt =
Factory::CreateRecordingDrawTarget(recorder, dummyDt, imageRectOrigin);
if (!dt || !dt->IsValid()) {
return Nothing();
}
{
bool contextPaint = mSVGContext && mSVGContext->GetContextPaint();
float animTime = (mWhichFrame == imgIContainer::FRAME_FIRST)
? 0.0f
: mSVGDocumentWrapper->GetCurrentTimeAsFloat();
auto region =
mRegion
? mRegion->ToImageRegion()
: ImageRegion::Create(gfxRect(imageRect.x, imageRect.y,
imageRect.width, imageRect.height));
SVGDrawingParameters params(nullptr, mSize, mSize, region,
SamplingFilter::POINT, mSVGContext, animTime,
mImageFlags, 1.0);
AutoRestoreSVGState autoRestore(params, mSVGDocumentWrapper, contextPaint);
RefPtr<gfxDrawingCallback> cb = new SVGDrawingCallback(
mSVGDocumentWrapper, params.viewportSize, params.size, params.flags);
RefPtr<gfxDrawable> svgDrawable = new gfxCallbackDrawable(cb, params.size);
mSVGDocumentWrapper->UpdateViewportBounds(params.viewportSize);
mSVGDocumentWrapper->FlushImageTransformInvalidation();
// Draw using the drawable the caller provided.
RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
MOZ_ASSERT(ctx); // Already checked the draw target above.
ctx->SetMatrix(
ctx->CurrentMatrix().PreTranslate(-imageRect.x, -imageRect.y));
gfxUtils::DrawPixelSnapped(ctx, svgDrawable, SizeDouble(mSize), region,
SurfaceFormat::OS_RGBA, SamplingFilter::POINT,
mImageFlags);
}
recorder->FlushItem(imageRectOrigin);
recorder->Finish();
if (!validFonts) {
gfxCriticalNote << "Failed serializing fonts for blob vector image";
return Nothing();
}
Range<uint8_t> bytes((uint8_t*)recorder->mOutputStream.mData,
recorder->mOutputStream.mLength);
wr::BlobImageKey key = aBlobKey
? aBlobKey.value()
: wr::BlobImageKey{wrBridge->GetNextImageKey()};
wr::ImageDescriptor descriptor(imageRect.Size(), 0, SurfaceFormat::OS_RGBA,
wr::OpacityType::HasAlphaChannel);
auto visibleRect = ImageIntRect::FromUnknownRect(imageRectOrigin);
if (aBlobKey) {
if (!aResources.UpdateBlobImage(key, descriptor, bytes, visibleRect,
visibleRect)) {
return Nothing();
}
} else if (!aResources.AddBlobImage(key, descriptor, bytes, visibleRect)) {
return Nothing();
}
std::vector<RefPtr<SourceSurface>> externalSurfaces;
recorder->TakeExternalSurfaces(externalSurfaces);
for (auto& surface : externalSurfaces) {
// While we don't use the image key with the surface, because the blob image
// renderer doesn't have easy access to the resource set, we still want to
// ensure one is generated. That will ensure the surface remains alive until
// at least the last epoch which the blob image could be used in.
wr::ImageKey key = {};
DebugOnly<nsresult> rv =
SharedSurfacesChild::Share(surface, rootManager, aResources, key);
MOZ_ASSERT(rv.value != NS_ERROR_NOT_IMPLEMENTED);
}
return Some(BlobImageKeyData(aManager, key, std::move(fonts),
std::move(externalSurfaces)));
}
} // namespace mozilla::image