BlobSurfaceProvider.cpp (10970B)
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/. */ 6 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" 17 18 using namespace mozilla::gfx; 19 using namespace mozilla::layers; 20 21 namespace mozilla::image { 22 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); 32 } 33 34 BlobSurfaceProvider::~BlobSurfaceProvider() { 35 if (NS_IsMainThread()) { 36 DestroyKeys(mKeys); 37 return; 38 } 39 40 NS_ReleaseOnMainThread("SourceSurfaceBlobImage::mSVGDocumentWrapper", 41 mSVGDocumentWrapper.forget()); 42 NS_DispatchToMainThread( 43 NS_NewRunnableFunction("SourceSurfaceBlobImage::DestroyKeys", 44 [keys = std::move(mKeys)] { DestroyKeys(keys); })); 45 } 46 47 /* static */ void BlobSurfaceProvider::DestroyKeys( 48 const AutoTArray<BlobImageKeyData, 1>& aKeys) { 49 for (const auto& entry : aKeys) { 50 if (entry.mManager->IsDestroyed()) { 51 continue; 52 } 53 54 WebRenderBridgeChild* wrBridge = entry.mManager->WrBridge(); 55 if (!wrBridge || !wrBridge->MatchesNamespace(entry.mBlobKey)) { 56 continue; 57 } 58 59 entry.mManager->GetRenderRootStateManager()->AddBlobImageKeyForDiscard( 60 entry.mBlobKey); 61 } 62 } 63 64 nsresult BlobSurfaceProvider::UpdateKey( 65 layers::RenderRootStateManager* aManager, 66 wr::IpcResourceUpdateQueue& aResources, wr::ImageKey& aKey) { 67 MOZ_ASSERT(NS_IsMainThread()); 68 69 layers::WebRenderLayerManager* manager = aManager->LayerManager(); 70 MOZ_ASSERT(manager); 71 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); 82 83 bool ownsKey = wrBridge->MatchesNamespace(entry.mBlobKey); 84 if (ownsKey && !entry.mDirty) { 85 key.emplace(entry.mBlobKey); 86 continue; 87 } 88 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); 98 } 99 mKeys.RemoveElementAt(i); 100 continue; 101 } 102 103 key.emplace(newEntry.ref().mBlobKey); 104 entry = std::move(newEntry.ref()); 105 MOZ_ASSERT(!entry.mDirty); 106 } 107 } 108 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())); 115 } 116 } 117 118 if (key) { 119 aKey = wr::AsImageKey(key.value()); 120 return NS_OK; 121 } 122 123 return NS_ERROR_FAILURE; 124 } 125 126 void BlobSurfaceProvider::InvalidateSurface() { 127 MOZ_ASSERT(NS_IsMainThread()); 128 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; 137 } 138 } 139 } 140 141 Maybe<BlobImageKeyData> BlobSurfaceProvider::RecordDrawing( 142 WebRenderLayerManager* aManager, wr::IpcResourceUpdateQueue& aResources, 143 Maybe<wr::BlobImageKey> aBlobKey) { 144 MOZ_ASSERT(!aManager->IsDestroyed()); 145 146 if (mSVGDocumentWrapper->IsDrawing()) { 147 return Nothing(); 148 } 149 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(); 154 155 const auto& size = GetSurfaceKey().Size(); 156 const auto& region = GetSurfaceKey().Region(); 157 const auto& svgContext = GetSurfaceKey().SVGContext(); 158 159 IntRect imageRect = region ? region->Rect() : IntRect(IntPoint(0, 0), size); 160 IntRect imageRectOrigin = imageRect - imageRect.TopLeft(); 161 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)); 170 171 for (auto& scaled : aScaledFonts) { 172 Maybe<wr::FontInstanceKey> key = 173 wrBridge->GetFontKeyForScaledFont(scaled, aResources); 174 if (key.isNothing()) { 175 validFonts = false; 176 break; 177 } 178 BlobFont font = {key.value(), scaled}; 179 aStream.write((const char*)&font, sizeof(font)); 180 } 181 182 fonts = std::move(aScaledFonts); 183 }); 184 185 RefPtr<DrawTarget> dummyDt = 186 gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget(); 187 RefPtr<DrawTarget> dt = 188 Factory::CreateRecordingDrawTarget(recorder, dummyDt, imageRectOrigin); 189 190 if (!dt || !dt->IsValid()) { 191 return Nothing(); 192 } 193 194 bool contextPaint = svgContext.GetContextPaint(); 195 196 float animTime = (GetSurfaceKey().Playback() == PlaybackType::eStatic) 197 ? 0.0f 198 : mSVGDocumentWrapper->GetCurrentTimeAsFloat(); 199 200 IntSize viewportSize = size; 201 if (auto cssViewportSize = svgContext.GetViewportSize()) { 202 // XXX losing unit 203 viewportSize.SizeTo(cssViewportSize->width, cssViewportSize->height); 204 } 205 206 { 207 // Get (& sanity-check) the helper-doc's presShell 208 RefPtr<PresShell> presShell = mSVGDocumentWrapper->GetPresShell(); 209 MOZ_ASSERT(presShell, "GetPresShell returned null for an SVG image?"); 210 211 nsPresContext* presContext = presShell->GetPresContext(); 212 MOZ_ASSERT(presContext, "pres shell w/out pres context"); 213 214 auto* doc = presShell->GetDocument(); 215 [[maybe_unused]] nsIURI* uri = doc ? doc->GetDocumentURI() : nullptr; 216 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING( 217 "SVG Image recording", GRAPHICS, 218 nsPrintfCString("(%d,%d) %dx%d from %dx%d %s", imageRect.x, imageRect.y, 219 imageRect.width, imageRect.height, size.width, 220 size.height, 221 uri ? uri->GetSpecOrDefault().get() : "N/A")); 222 223 AutoRestoreSVGState autoRestore(svgContext, animTime, mSVGDocumentWrapper, 224 contextPaint); 225 226 mSVGDocumentWrapper->UpdateViewportBounds(viewportSize); 227 mSVGDocumentWrapper->FlushImageTransformInvalidation(); 228 229 gfxContext ctx(dt); 230 231 nsRect svgRect; 232 auto auPerDevPixel = presContext->AppUnitsPerDevPixel(); 233 if (size != viewportSize) { 234 auto scaleX = double(size.width) / viewportSize.width; 235 auto scaleY = double(size.height) / viewportSize.height; 236 ctx.SetMatrix(Matrix::Scaling(float(scaleX), float(scaleY))); 237 238 auto scaledVisibleRect = IntRectToRect(imageRect); 239 scaledVisibleRect.Scale(float(auPerDevPixel / scaleX), 240 float(auPerDevPixel / scaleY)); 241 scaledVisibleRect.Round(); 242 svgRect.SetRect( 243 int32_t(scaledVisibleRect.x), int32_t(scaledVisibleRect.y), 244 int32_t(scaledVisibleRect.width), int32_t(scaledVisibleRect.height)); 245 } else { 246 auto scaledVisibleRect(imageRect); 247 scaledVisibleRect.Scale(auPerDevPixel); 248 svgRect.SetRect(scaledVisibleRect.x, scaledVisibleRect.y, 249 scaledVisibleRect.width, scaledVisibleRect.height); 250 } 251 252 RenderDocumentFlags renderDocFlags = 253 RenderDocumentFlags::IgnoreViewportScrolling; 254 if (!(mImageFlags & imgIContainer::FLAG_SYNC_DECODE)) { 255 renderDocFlags |= RenderDocumentFlags::AsyncDecodeImages; 256 } 257 if (mImageFlags & imgIContainer::FLAG_HIGH_QUALITY_SCALING) { 258 renderDocFlags |= RenderDocumentFlags::UseHighQualityScaling; 259 } 260 261 presShell->RenderDocument(svgRect, renderDocFlags, 262 NS_RGBA(0, 0, 0, 0), // transparent 263 &ctx); 264 } 265 266 recorder->FlushItem(imageRectOrigin); 267 recorder->Finish(); 268 269 if (!validFonts) { 270 gfxCriticalNote << "Failed serializing fonts for blob vector image"; 271 return Nothing(); 272 } 273 274 Range<uint8_t> bytes((uint8_t*)recorder->mOutputStream.mData, 275 recorder->mOutputStream.mLength); 276 wr::BlobImageKey key = aBlobKey 277 ? aBlobKey.value() 278 : wr::BlobImageKey{wrBridge->GetNextImageKey()}; 279 wr::ImageDescriptor descriptor(imageRect.Size(), 0, SurfaceFormat::OS_RGBA, 280 wr::OpacityType::HasAlphaChannel); 281 282 auto visibleRect = ImageIntRect::FromUnknownRect(imageRectOrigin); 283 if (aBlobKey) { 284 if (!aResources.UpdateBlobImage(key, descriptor, bytes, visibleRect, 285 visibleRect)) { 286 return Nothing(); 287 } 288 } else if (!aResources.AddBlobImage(key, descriptor, bytes, visibleRect)) { 289 return Nothing(); 290 } 291 292 DrawEventRecorderPrivate::ExternalSurfacesHolder externalSurfaces; 293 recorder->TakeExternalSurfaces(externalSurfaces); 294 295 for (auto& entry : externalSurfaces) { 296 // While we don't use the image key with the surface, because the blob image 297 // renderer doesn't have easy access to the resource set, we still want to 298 // ensure one is generated. That will ensure the surface remains alive until 299 // at least the last epoch which the blob image could be used in. 300 wr::ImageKey key = {}; 301 DebugOnly<nsresult> rv = SharedSurfacesChild::Share( 302 entry.mSurface, rootManager, aResources, key); 303 MOZ_ASSERT(rv.value != NS_ERROR_NOT_IMPLEMENTED); 304 } 305 306 return Some(BlobImageKeyData(aManager, key, std::move(fonts), 307 std::move(externalSurfaces))); 308 } 309 310 } // namespace mozilla::image