summaryrefslogtreecommitdiffstats
path: root/image/BlobSurfaceProvider.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--image/BlobSurfaceProvider.cpp310
1 files changed, 310 insertions, 0 deletions
diff --git a/image/BlobSurfaceProvider.cpp b/image/BlobSurfaceProvider.cpp
new file mode 100644
index 0000000000..b50ed5bf25
--- /dev/null
+++ b/image/BlobSurfaceProvider.cpp
@@ -0,0 +1,310 @@
+/* -*- 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 "BlobSurfaceProvider.h"
+#include "AutoRestoreSVGState.h"
+#include "ImageRegion.h"
+#include "SVGDocumentWrapper.h"
+#include "mozilla/PresShell.h"
+#include "mozilla/dom/Document.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 {
+
+BlobSurfaceProvider::BlobSurfaceProvider(
+ const ImageKey aImageKey, const SurfaceKey& aSurfaceKey,
+ image::SVGDocumentWrapper* aSVGDocumentWrapper, uint32_t aImageFlags)
+ : ISurfaceProvider(aImageKey, aSurfaceKey,
+ AvailabilityState::StartAvailable()),
+ mSVGDocumentWrapper(aSVGDocumentWrapper),
+ mImageFlags(aImageFlags) {
+ MOZ_ASSERT(mSVGDocumentWrapper);
+ MOZ_ASSERT(aImageFlags & imgIContainer::FLAG_RECORD_BLOB);
+}
+
+BlobSurfaceProvider::~BlobSurfaceProvider() {
+ 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 BlobSurfaceProvider::DestroyKeys(
+ const AutoTArray<BlobImageKeyData, 1>& aKeys) {
+ for (const auto& entry : aKeys) {
+ if (entry.mManager->IsDestroyed()) {
+ continue;
+ }
+
+ WebRenderBridgeChild* wrBridge = entry.mManager->WrBridge();
+ if (!wrBridge || !wrBridge->MatchesNamespace(entry.mBlobKey)) {
+ continue;
+ }
+
+ entry.mManager->GetRenderRootStateManager()->AddBlobImageKeyForDiscard(
+ entry.mBlobKey);
+ }
+}
+
+nsresult BlobSurfaceProvider::UpdateKey(
+ layers::RenderRootStateManager* aManager,
+ wr::IpcResourceUpdateQueue& aResources, wr::ImageKey& aKey) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ layers::WebRenderLayerManager* manager = aManager->LayerManager();
+ MOZ_ASSERT(manager);
+
+ 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 == manager) {
+ WebRenderBridgeChild* wrBridge = manager->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(manager, aResources,
+ ownsKey ? Some(entry.mBlobKey) : Nothing());
+ if (!newEntry) {
+ if (ownsKey) {
+ aManager->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(manager, aResources, Nothing());
+ if (newEntry) {
+ key.emplace(newEntry.ref().mBlobKey);
+ mKeys.AppendElement(std::move(newEntry.ref()));
+ }
+ }
+
+ if (key) {
+ aKey = wr::AsImageKey(key.value());
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+void BlobSurfaceProvider::InvalidateRecording() {
+ 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> BlobSurfaceProvider::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();
+
+ const auto& size = GetSurfaceKey().Size();
+ const auto& region = GetSurfaceKey().Region();
+ const auto& svgContext = GetSurfaceKey().SVGContext();
+
+ IntRect imageRect = region ? region->Rect() : IntRect(IntPoint(0, 0), size);
+ 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 =
+ gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
+ RefPtr<DrawTarget> dt =
+ Factory::CreateRecordingDrawTarget(recorder, dummyDt, imageRectOrigin);
+
+ if (!dt || !dt->IsValid()) {
+ return Nothing();
+ }
+
+ bool contextPaint = svgContext.GetContextPaint();
+
+ float animTime = (GetSurfaceKey().Playback() == PlaybackType::eStatic)
+ ? 0.0f
+ : mSVGDocumentWrapper->GetCurrentTimeAsFloat();
+
+ IntSize viewportSize = size;
+ if (auto cssViewportSize = svgContext.GetViewportSize()) {
+ // XXX losing unit
+ viewportSize.SizeTo(cssViewportSize->width, cssViewportSize->height);
+ }
+
+ {
+ // Get (& sanity-check) the helper-doc's presShell
+ RefPtr<PresShell> presShell = mSVGDocumentWrapper->GetPresShell();
+ MOZ_ASSERT(presShell, "GetPresShell returned null for an SVG image?");
+
+ nsPresContext* presContext = presShell->GetPresContext();
+ MOZ_ASSERT(presContext, "pres shell w/out pres context");
+
+ auto* doc = presShell->GetDocument();
+ [[maybe_unused]] nsIURI* uri = doc ? doc->GetDocumentURI() : nullptr;
+ AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(
+ "SVG Image recording", GRAPHICS,
+ nsPrintfCString("(%d,%d) %dx%d from %dx%d %s", imageRect.x, imageRect.y,
+ imageRect.width, imageRect.height, size.width,
+ size.height,
+ uri ? uri->GetSpecOrDefault().get() : "N/A"));
+
+ AutoRestoreSVGState autoRestore(svgContext, animTime, mSVGDocumentWrapper,
+ contextPaint);
+
+ mSVGDocumentWrapper->UpdateViewportBounds(viewportSize);
+ mSVGDocumentWrapper->FlushImageTransformInvalidation();
+
+ gfxContext ctx(dt);
+
+ nsRect svgRect;
+ auto auPerDevPixel = presContext->AppUnitsPerDevPixel();
+ if (size != viewportSize) {
+ auto scaleX = double(size.width) / viewportSize.width;
+ auto scaleY = double(size.height) / viewportSize.height;
+ ctx.SetMatrix(Matrix::Scaling(float(scaleX), float(scaleY)));
+
+ auto scaledVisibleRect = IntRectToRect(imageRect);
+ scaledVisibleRect.Scale(float(auPerDevPixel / scaleX),
+ float(auPerDevPixel / scaleY));
+ scaledVisibleRect.Round();
+ svgRect.SetRect(
+ int32_t(scaledVisibleRect.x), int32_t(scaledVisibleRect.y),
+ int32_t(scaledVisibleRect.width), int32_t(scaledVisibleRect.height));
+ } else {
+ auto scaledVisibleRect(imageRect);
+ scaledVisibleRect.Scale(auPerDevPixel);
+ svgRect.SetRect(scaledVisibleRect.x, scaledVisibleRect.y,
+ scaledVisibleRect.width, scaledVisibleRect.height);
+ }
+
+ RenderDocumentFlags renderDocFlags =
+ RenderDocumentFlags::IgnoreViewportScrolling;
+ if (!(mImageFlags & imgIContainer::FLAG_SYNC_DECODE)) {
+ renderDocFlags |= RenderDocumentFlags::AsyncDecodeImages;
+ }
+ if (mImageFlags & imgIContainer::FLAG_HIGH_QUALITY_SCALING) {
+ renderDocFlags |= RenderDocumentFlags::UseHighQualityScaling;
+ }
+
+ presShell->RenderDocument(svgRect, renderDocFlags,
+ NS_RGBA(0, 0, 0, 0), // transparent
+ &ctx);
+ }
+
+ 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();
+ }
+
+ DrawEventRecorderPrivate::ExternalSurfacesHolder externalSurfaces;
+ recorder->TakeExternalSurfaces(externalSurfaces);
+
+ for (auto& entry : 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(
+ entry.mSurface, 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