Bug 1744524: part 2) Add `WindowContext::GetUserGestureStart` and remove `WindowConte...
[gecko.git] / image / BlobSurfaceProvider.cpp
blob4894a7d02c2ce02da70f731324f15aab2235d726
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()) {
36 DestroyKeys(mKeys);
37 return;
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()) {
51 continue;
54 WebRenderBridgeChild* wrBridge = entry.mManager->WrBridge();
55 if (!wrBridge || !wrBridge->MatchesNamespace(entry.mBlobKey)) {
56 continue;
59 entry.mManager->GetRenderRootStateManager()->AddBlobImageKeyForDiscard(
60 entry.mBlobKey);
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();
70 MOZ_ASSERT(manager);
72 Maybe<wr::BlobImageKey> key;
73 auto i = mKeys.Length();
74 while (i > 0) {
75 --i;
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();
81 MOZ_ASSERT(wrBridge);
83 bool ownsKey = wrBridge->MatchesNamespace(entry.mBlobKey);
84 if (ownsKey && !entry.mDirty) {
85 key.emplace(entry.mBlobKey);
86 continue;
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
92 // to rerecord it.
93 auto newEntry = RecordDrawing(manager, aResources,
94 ownsKey ? Some(entry.mBlobKey) : Nothing());
95 if (!newEntry) {
96 if (ownsKey) {
97 aManager->AddBlobImageKeyForDiscard(entry.mBlobKey);
99 mKeys.RemoveElementAt(i);
100 continue;
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.
110 if (!key) {
111 auto newEntry = RecordDrawing(manager, aResources, Nothing());
112 if (newEntry) {
113 key.emplace(newEntry.ref().mBlobKey);
114 mKeys.AppendElement(std::move(newEntry.ref()));
118 if (key) {
119 aKey = wr::AsImageKey(key.value());
120 return NS_OK;
123 return NS_ERROR_FAILURE;
126 void BlobSurfaceProvider::InvalidateRecording() {
127 MOZ_ASSERT(NS_IsMainThread());
129 auto i = mKeys.Length();
130 while (i > 0) {
131 --i;
132 BlobImageKeyData& entry = mKeys[i];
133 if (entry.mManager->IsDestroyed()) {
134 mKeys.RemoveElementAt(i);
135 } else {
136 entry.mDirty = true;
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()) {
147 return Nothing();
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()) {
175 validFonts = false;
176 break;
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()) {
191 return Nothing();
194 bool contextPaint = svgContext && svgContext->GetContextPaint();
196 float animTime = (GetSurfaceKey().Playback() == PlaybackType::eStatic)
197 ? 0.0f
198 : mSVGDocumentWrapper->GetCurrentTimeAsFloat();
200 IntSize viewportSize = size;
201 if (svgContext) {
202 auto cssViewportSize = svgContext->GetViewportSize();
203 if (cssViewportSize) {
204 // XXX losing unit
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,
223 size.height,
224 uri ? uri->GetSpecOrDefault().get() : "N/A"));
226 AutoRestoreSVGState autoRestore(svgContext, animTime, mSVGDocumentWrapper,
227 contextPaint);
229 mSVGDocumentWrapper->UpdateViewportBounds(viewportSize);
230 mSVGDocumentWrapper->FlushImageTransformInvalidation();
232 RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
233 MOZ_ASSERT(ctx); // Already checked the draw target above.
235 nsRect svgRect;
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();
246 svgRect.SetRect(
247 int32_t(scaledVisibleRect.x), int32_t(scaledVisibleRect.y),
248 int32_t(scaledVisibleRect.width), int32_t(scaledVisibleRect.height));
249 } else {
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
267 ctx);
270 recorder->FlushItem(imageRectOrigin);
271 recorder->Finish();
273 if (!validFonts) {
274 gfxCriticalNote << "Failed serializing fonts for blob vector image";
275 return Nothing();
278 Range<uint8_t> bytes((uint8_t*)recorder->mOutputStream.mData,
279 recorder->mOutputStream.mLength);
280 wr::BlobImageKey key = aBlobKey
281 ? aBlobKey.value()
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);
287 if (aBlobKey) {
288 if (!aResources.UpdateBlobImage(key, descriptor, bytes, visibleRect,
289 visibleRect)) {
290 return Nothing();
292 } else if (!aResources.AddBlobImage(key, descriptor, bytes, visibleRect)) {
293 return Nothing();
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