1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "BlobSurfaceProvider.h"
8 #include "AutoRestoreSVGState.h"
9 #include "ImageRegion.h"
10 #include "SVGDocumentWrapper.h"
11 #include "mozilla/PresShell.h"
12 #include "mozilla/dom/Document.h"
13 #include "mozilla/gfx/2D.h"
14 #include "mozilla/layers/IpcResourceUpdateQueue.h"
15 #include "mozilla/layers/WebRenderBridgeChild.h"
16 #include "mozilla/layers/WebRenderDrawEventRecorder.h"
18 using namespace mozilla::gfx
;
19 using namespace mozilla::layers
;
21 namespace mozilla::image
{
23 BlobSurfaceProvider::BlobSurfaceProvider(
24 const ImageKey aImageKey
, const SurfaceKey
& aSurfaceKey
,
25 image::SVGDocumentWrapper
* aSVGDocumentWrapper
, uint32_t aImageFlags
)
26 : ISurfaceProvider(aImageKey
, aSurfaceKey
,
27 AvailabilityState::StartAvailable()),
28 mSVGDocumentWrapper(aSVGDocumentWrapper
),
29 mImageFlags(aImageFlags
) {
30 MOZ_ASSERT(mSVGDocumentWrapper
);
31 MOZ_ASSERT(aImageFlags
& imgIContainer::FLAG_RECORD_BLOB
);
34 BlobSurfaceProvider::~BlobSurfaceProvider() {
35 if (NS_IsMainThread()) {
40 NS_ReleaseOnMainThread("SourceSurfaceBlobImage::mSVGDocumentWrapper",
41 mSVGDocumentWrapper
.forget());
42 NS_DispatchToMainThread(
43 NS_NewRunnableFunction("SourceSurfaceBlobImage::DestroyKeys",
44 [keys
= std::move(mKeys
)] { DestroyKeys(keys
); }));
47 /* static */ void BlobSurfaceProvider::DestroyKeys(
48 const AutoTArray
<BlobImageKeyData
, 1>& aKeys
) {
49 for (const auto& entry
: aKeys
) {
50 if (entry
.mManager
->IsDestroyed()) {
54 WebRenderBridgeChild
* wrBridge
= entry
.mManager
->WrBridge();
55 if (!wrBridge
|| !wrBridge
->MatchesNamespace(entry
.mBlobKey
)) {
59 entry
.mManager
->GetRenderRootStateManager()->AddBlobImageKeyForDiscard(
64 nsresult
BlobSurfaceProvider::UpdateKey(
65 layers::RenderRootStateManager
* aManager
,
66 wr::IpcResourceUpdateQueue
& aResources
, wr::ImageKey
& aKey
) {
67 MOZ_ASSERT(NS_IsMainThread());
69 layers::WebRenderLayerManager
* manager
= aManager
->LayerManager();
72 Maybe
<wr::BlobImageKey
> key
;
73 auto i
= mKeys
.Length();
76 BlobImageKeyData
& entry
= mKeys
[i
];
77 if (entry
.mManager
->IsDestroyed()) {
78 mKeys
.RemoveElementAt(i
);
79 } else if (entry
.mManager
== manager
) {
80 WebRenderBridgeChild
* wrBridge
= manager
->WrBridge();
83 bool ownsKey
= wrBridge
->MatchesNamespace(entry
.mBlobKey
);
84 if (ownsKey
&& !entry
.mDirty
) {
85 key
.emplace(entry
.mBlobKey
);
89 // Even if the manager is the same, its underlying WebRenderBridgeChild
90 // can change state. Either our namespace differs, and our old key has
91 // already been discarded, or the blob has changed. Either way, we need
93 auto newEntry
= RecordDrawing(manager
, aResources
,
94 ownsKey
? Some(entry
.mBlobKey
) : Nothing());
97 aManager
->AddBlobImageKeyForDiscard(entry
.mBlobKey
);
99 mKeys
.RemoveElementAt(i
);
103 key
.emplace(newEntry
.ref().mBlobKey
);
104 entry
= std::move(newEntry
.ref());
105 MOZ_ASSERT(!entry
.mDirty
);
109 // We didn't find an entry. Attempt to record the blob with a new key.
111 auto newEntry
= RecordDrawing(manager
, aResources
, Nothing());
113 key
.emplace(newEntry
.ref().mBlobKey
);
114 mKeys
.AppendElement(std::move(newEntry
.ref()));
119 aKey
= wr::AsImageKey(key
.value());
123 return NS_ERROR_FAILURE
;
126 void BlobSurfaceProvider::InvalidateRecording() {
127 MOZ_ASSERT(NS_IsMainThread());
129 auto i
= mKeys
.Length();
132 BlobImageKeyData
& entry
= mKeys
[i
];
133 if (entry
.mManager
->IsDestroyed()) {
134 mKeys
.RemoveElementAt(i
);
141 Maybe
<BlobImageKeyData
> BlobSurfaceProvider::RecordDrawing(
142 WebRenderLayerManager
* aManager
, wr::IpcResourceUpdateQueue
& aResources
,
143 Maybe
<wr::BlobImageKey
> aBlobKey
) {
144 MOZ_ASSERT(!aManager
->IsDestroyed());
146 if (mSVGDocumentWrapper
->IsDrawing()) {
150 // This is either our first pass, or we have a stale key requiring us to
151 // re-record the SVG image draw commands.
152 auto* rootManager
= aManager
->GetRenderRootStateManager();
153 auto* wrBridge
= aManager
->WrBridge();
155 const auto& size
= GetSurfaceKey().Size();
156 const auto& region
= GetSurfaceKey().Region();
157 const auto& svgContext
= GetSurfaceKey().SVGContext();
159 IntRect imageRect
= region
? region
->Rect() : IntRect(IntPoint(0, 0), size
);
160 IntRect imageRectOrigin
= imageRect
- imageRect
.TopLeft();
162 std::vector
<RefPtr
<ScaledFont
>> fonts
;
163 bool validFonts
= true;
164 RefPtr
<WebRenderDrawEventRecorder
> recorder
=
165 MakeAndAddRef
<WebRenderDrawEventRecorder
>(
166 [&](MemStream
& aStream
,
167 std::vector
<RefPtr
<ScaledFont
>>& aScaledFonts
) {
168 auto count
= aScaledFonts
.size();
169 aStream
.write((const char*)&count
, sizeof(count
));
171 for (auto& scaled
: aScaledFonts
) {
172 Maybe
<wr::FontInstanceKey
> key
=
173 wrBridge
->GetFontKeyForScaledFont(scaled
, aResources
);
174 if (key
.isNothing()) {
178 BlobFont font
= {key
.value(), scaled
};
179 aStream
.write((const char*)&font
, sizeof(font
));
182 fonts
= std::move(aScaledFonts
);
185 RefPtr
<DrawTarget
> dummyDt
=
186 gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
187 RefPtr
<DrawTarget
> dt
=
188 Factory::CreateRecordingDrawTarget(recorder
, dummyDt
, imageRectOrigin
);
190 if (!dt
|| !dt
->IsValid()) {
194 bool contextPaint
= svgContext
&& svgContext
->GetContextPaint();
196 float animTime
= (GetSurfaceKey().Playback() == PlaybackType::eStatic
)
198 : mSVGDocumentWrapper
->GetCurrentTimeAsFloat();
200 IntSize viewportSize
= size
;
202 auto cssViewportSize
= svgContext
->GetViewportSize();
203 if (cssViewportSize
) {
205 viewportSize
.SizeTo(cssViewportSize
->width
, cssViewportSize
->height
);
210 // Get (& sanity-check) the helper-doc's presShell
211 RefPtr
<PresShell
> presShell
= mSVGDocumentWrapper
->GetPresShell();
212 MOZ_ASSERT(presShell
, "GetPresShell returned null for an SVG image?");
214 nsPresContext
* presContext
= presShell
->GetPresContext();
215 MOZ_ASSERT(presContext
, "pres shell w/out pres context");
217 auto* doc
= presShell
->GetDocument();
218 [[maybe_unused
]] nsIURI
* uri
= doc
? doc
->GetDocumentURI() : nullptr;
219 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(
220 "SVG Image recording", GRAPHICS
,
221 nsPrintfCString("(%d,%d) %dx%d from %dx%d %s", imageRect
.x
, imageRect
.y
,
222 imageRect
.width
, imageRect
.height
, size
.width
,
224 uri
? uri
->GetSpecOrDefault().get() : "N/A"));
226 AutoRestoreSVGState
autoRestore(svgContext
, animTime
, mSVGDocumentWrapper
,
229 mSVGDocumentWrapper
->UpdateViewportBounds(viewportSize
);
230 mSVGDocumentWrapper
->FlushImageTransformInvalidation();
232 RefPtr
<gfxContext
> ctx
= gfxContext::CreateOrNull(dt
);
233 MOZ_ASSERT(ctx
); // Already checked the draw target above.
236 auto auPerDevPixel
= presContext
->AppUnitsPerDevPixel();
237 if (size
!= viewportSize
) {
238 auto scaleX
= double(size
.width
) / viewportSize
.width
;
239 auto scaleY
= double(size
.height
) / viewportSize
.height
;
240 ctx
->SetMatrix(Matrix::Scaling(float(scaleX
), float(scaleY
)));
242 auto scaledVisibleRect
= IntRectToRect(imageRect
);
243 scaledVisibleRect
.Scale(float(auPerDevPixel
/ scaleX
),
244 float(auPerDevPixel
/ scaleY
));
245 scaledVisibleRect
.Round();
247 int32_t(scaledVisibleRect
.x
), int32_t(scaledVisibleRect
.y
),
248 int32_t(scaledVisibleRect
.width
), int32_t(scaledVisibleRect
.height
));
250 auto scaledVisibleRect(imageRect
);
251 scaledVisibleRect
.Scale(auPerDevPixel
);
252 svgRect
.SetRect(scaledVisibleRect
.x
, scaledVisibleRect
.y
,
253 scaledVisibleRect
.width
, scaledVisibleRect
.height
);
256 RenderDocumentFlags renderDocFlags
=
257 RenderDocumentFlags::IgnoreViewportScrolling
;
258 if (!(mImageFlags
& imgIContainer::FLAG_SYNC_DECODE
)) {
259 renderDocFlags
|= RenderDocumentFlags::AsyncDecodeImages
;
261 if (mImageFlags
& imgIContainer::FLAG_HIGH_QUALITY_SCALING
) {
262 renderDocFlags
|= RenderDocumentFlags::UseHighQualityScaling
;
265 presShell
->RenderDocument(svgRect
, renderDocFlags
,
266 NS_RGBA(0, 0, 0, 0), // transparent
270 recorder
->FlushItem(imageRectOrigin
);
274 gfxCriticalNote
<< "Failed serializing fonts for blob vector image";
278 Range
<uint8_t> bytes((uint8_t*)recorder
->mOutputStream
.mData
,
279 recorder
->mOutputStream
.mLength
);
280 wr::BlobImageKey key
= aBlobKey
282 : wr::BlobImageKey
{wrBridge
->GetNextImageKey()};
283 wr::ImageDescriptor
descriptor(imageRect
.Size(), 0, SurfaceFormat::OS_RGBA
,
284 wr::OpacityType::HasAlphaChannel
);
286 auto visibleRect
= ImageIntRect::FromUnknownRect(imageRectOrigin
);
288 if (!aResources
.UpdateBlobImage(key
, descriptor
, bytes
, visibleRect
,
292 } else if (!aResources
.AddBlobImage(key
, descriptor
, bytes
, visibleRect
)) {
296 std::vector
<RefPtr
<SourceSurface
>> externalSurfaces
;
297 recorder
->TakeExternalSurfaces(externalSurfaces
);
299 for (auto& surface
: externalSurfaces
) {
300 // While we don't use the image key with the surface, because the blob image
301 // renderer doesn't have easy access to the resource set, we still want to
302 // ensure one is generated. That will ensure the surface remains alive until
303 // at least the last epoch which the blob image could be used in.
304 wr::ImageKey key
= {};
305 DebugOnly
<nsresult
> rv
=
306 SharedSurfacesChild::Share(surface
, rootManager
, aResources
, key
);
307 MOZ_ASSERT(rv
.value
!= NS_ERROR_NOT_IMPLEMENTED
);
310 return Some(BlobImageKeyData(aManager
, key
, std::move(fonts
),
311 std::move(externalSurfaces
)));
314 } // namespace mozilla::image