DrawEventRecorder.h (10976B)
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 #ifndef MOZILLA_GFX_DRAWEVENTRECORDER_H_ 8 #define MOZILLA_GFX_DRAWEVENTRECORDER_H_ 9 10 #include "2D.h" 11 #include "DrawEventRecorderTypes.h" 12 #include "RecordedEvent.h" 13 #include "RecordingTypes.h" 14 15 #include <deque> 16 #include <functional> 17 #include <vector> 18 19 #include "ImageContainer.h" 20 #include "mozilla/DataMutex.h" 21 #include "mozilla/ThreadSafeWeakPtr.h" 22 #include "nsTHashMap.h" 23 #include "nsTHashSet.h" 24 #include "nsISupportsImpl.h" 25 26 namespace mozilla { 27 namespace layers { 28 class CanvasChild; 29 } // namespace layers 30 31 namespace gfx { 32 33 class DrawTargetRecording; 34 class PathRecording; 35 class RecordedEvent; 36 37 class DrawEventRecorderPrivate : public DrawEventRecorder { 38 public: 39 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderPrivate, override) 40 41 DrawEventRecorderPrivate(); 42 virtual ~DrawEventRecorderPrivate(); 43 RecorderType GetRecorderType() const override { 44 return RecorderType::PRIVATE; 45 } 46 bool Finish() override { 47 ClearResources(); 48 return true; 49 } 50 virtual void FlushItem(IntRect) {} 51 virtual void DetachResources() { 52 NS_ASSERT_OWNINGTHREAD(DrawEventRecorderPrivate); 53 54 nsTHashSet<ScaledFont*> fonts = std::move(mStoredFonts); 55 for (const auto& font : fonts) { 56 font->RemoveUserData(reinterpret_cast<UserDataKey*>(this)); 57 } 58 59 // SourceSurfaces can be deleted off the main thread, so we use 60 // ThreadSafeWeakPtrs to allow for this. RemoveUserData is thread safe. 61 nsTHashMap<void*, ThreadSafeWeakPtr<SourceSurface>> surfaces = 62 std::move(mStoredSurfaces); 63 for (const auto& entry : surfaces) { 64 RefPtr<SourceSurface> strongRef(entry.GetData()); 65 if (strongRef) { 66 strongRef->RemoveUserData(reinterpret_cast<UserDataKey*>(this)); 67 } 68 } 69 70 // Now that we've detached we can't get any more pending deletions, so 71 // processing now should mean we include all clean up operations. 72 ProcessPendingDeletions(); 73 } 74 75 void ClearResources() { 76 NS_ASSERT_OWNINGTHREAD(DrawEventRecorderPrivate); 77 mStoredObjects.Clear(); 78 mStoredFontData.Clear(); 79 mScaledFonts.clear(); 80 mCurrentDT = nullptr; 81 } 82 83 template <class S> 84 void WriteHeader(S& aStream) { 85 NS_ASSERT_OWNINGTHREAD(DrawEventRecorderPrivate); 86 WriteElement(aStream, kMagicInt); 87 WriteElement(aStream, kMajorRevision); 88 WriteElement(aStream, kMinorRevision); 89 } 90 91 virtual void RecordEvent(const RecordedEvent& aEvent) = 0; 92 93 void RecordEvent(const DrawTargetRecording* aDT, 94 const RecordedEvent& aEvent) { 95 ReferencePtr dt = aDT; 96 if (mCurrentDT != dt) { 97 SetDrawTarget(dt); 98 } 99 RecordEvent(aEvent); 100 } 101 102 void SetDrawTarget(ReferencePtr aDT); 103 104 void ClearDrawTarget(const DrawTargetRecording* aDT) { 105 ReferencePtr dt = aDT; 106 if (mCurrentDT == dt) { 107 mCurrentDT = nullptr; 108 } 109 } 110 111 void AddStoredObject(const ReferencePtr aObject) { 112 ProcessPendingDeletions(); 113 mStoredObjects.Insert(aObject); 114 } 115 116 /** 117 * This is a combination of HasStoredObject and AddStoredObject, so that we 118 * only have to call ProcessPendingDeletions once, which involves locking. 119 * @param aObject the object to store if not already stored 120 * @return true if the object was not already stored, false if it was 121 */ 122 bool TryAddStoredObject(const ReferencePtr aObject) { 123 ProcessPendingDeletions(); 124 return mStoredObjects.EnsureInserted(aObject); 125 } 126 127 virtual void AddPendingDeletion(std::function<void()>&& aPendingDeletion) { 128 auto lockedPendingDeletions = mPendingDeletions.Lock(); 129 lockedPendingDeletions->emplace_back(std::move(aPendingDeletion)); 130 } 131 132 void RemoveStoredObject(const ReferencePtr aObject) { 133 NS_ASSERT_OWNINGTHREAD(DrawEventRecorderPrivate); 134 mStoredObjects.Remove(aObject); 135 } 136 137 /** 138 * @param aUnscaledFont the UnscaledFont to increment the reference count for 139 * @return the previous reference count 140 */ 141 int32_t IncrementUnscaledFontRefCount(const ReferencePtr aUnscaledFont) { 142 NS_ASSERT_OWNINGTHREAD(DrawEventRecorderPrivate); 143 int32_t& count = mUnscaledFontRefs.LookupOrInsert(aUnscaledFont, 0); 144 return count++; 145 } 146 147 /** 148 * Decrements the reference count for aUnscaledFont and, if count is now zero, 149 * records its destruction. 150 * @param aUnscaledFont the UnscaledFont to decrement the reference count for 151 */ 152 void DecrementUnscaledFontRefCount(const ReferencePtr aUnscaledFont); 153 154 void AddScaledFont(ScaledFont* aFont) { 155 NS_ASSERT_OWNINGTHREAD(DrawEventRecorderPrivate); 156 if (mStoredFonts.EnsureInserted(aFont) && WantsExternalFonts()) { 157 mScaledFonts.push_back(aFont); 158 } 159 } 160 161 void RemoveScaledFont(ScaledFont* aFont) { mStoredFonts.Remove(aFont); } 162 163 void AddSourceSurface(SourceSurface* aSurface) { 164 NS_ASSERT_OWNINGTHREAD(DrawEventRecorderPrivate); 165 mStoredSurfaces.InsertOrUpdate(aSurface, aSurface); 166 } 167 168 void RemoveSourceSurface(SourceSurface* aSurface) { 169 NS_ASSERT_OWNINGTHREAD(DrawEventRecorderPrivate); 170 mStoredSurfaces.Remove(aSurface); 171 } 172 173 #if defined(DEBUG) 174 // Only used within debug assertions. 175 bool HasStoredObject(const ReferencePtr aObject) { 176 ProcessPendingDeletions(); 177 return mStoredObjects.Contains(aObject); 178 } 179 #endif 180 181 void AddStoredFontData(const uint64_t aFontDataKey) { 182 NS_ASSERT_OWNINGTHREAD(DrawEventRecorderPrivate); 183 mStoredFontData.Insert(aFontDataKey); 184 } 185 186 bool HasStoredFontData(const uint64_t aFontDataKey) { 187 NS_ASSERT_OWNINGTHREAD(DrawEventRecorderPrivate); 188 return mStoredFontData.Contains(aFontDataKey); 189 } 190 191 bool WantsExternalFonts() const { return mExternalFonts; } 192 193 virtual void StoreSourceSurfaceRecording(SourceSurface* aSurface, 194 const char* aReason); 195 196 virtual void StoreImageRecording( 197 const RefPtr<layers::Image>& aImageOfSurfaceDescriptor, 198 const char* aReasony) { 199 MOZ_ASSERT_UNREACHABLE("unexpected to be called"); 200 } 201 202 /** 203 * Used when a source surface is destroyed, aSurface is a void* instead of a 204 * SourceSurface* because this is called during the SourceSurface destructor, 205 * so it is partially destructed and should not be accessed. 206 * @param aSurface the surface whose destruction is being recorded 207 */ 208 void RecordSourceSurfaceDestruction(void* aSurface); 209 210 virtual void AddDependentSurface(uint64_t aDependencyId) { 211 MOZ_CRASH("GFX: AddDependentSurface"); 212 } 213 214 struct ExternalSurfaceEntry { 215 RefPtr<SourceSurface> mSurface; 216 int64_t mEventCount = -1; 217 }; 218 219 using ExternalSurfacesHolder = 220 DrawEventRecorderPrivate_ExternalSurfacesHolder; 221 222 void TakeExternalSurfaces(ExternalSurfacesHolder& aSurfaces) { 223 NS_ASSERT_OWNINGTHREAD(DrawEventRecorderPrivate); 224 aSurfaces = std::move(mExternalSurfaces); 225 } 226 227 struct ExternalImageEntry { 228 RefPtr<layers::Image> mImage; 229 int64_t mEventCount = -1; 230 }; 231 232 using ExternalImagesHolder = std::deque<ExternalImageEntry>; 233 234 virtual already_AddRefed<layers::CanvasChild> GetCanvasChild() const { 235 return nullptr; 236 } 237 238 protected: 239 NS_DECL_OWNINGTHREAD 240 241 void StoreExternalSurfaceRecording(SourceSurface* aSurface, uint64_t aKey); 242 243 void StoreExternalImageRecording( 244 const RefPtr<layers::Image>& aImageOfSurfaceDescriptor); 245 246 void ProcessPendingDeletions() { 247 NS_ASSERT_OWNINGTHREAD(DrawEventRecorderPrivate); 248 249 PendingDeletionsVector pendingDeletions; 250 { 251 auto lockedPendingDeletions = mPendingDeletions.Lock(); 252 pendingDeletions.swap(*lockedPendingDeletions); 253 } 254 for (const auto& pendingDeletion : pendingDeletions) { 255 pendingDeletion(); 256 } 257 } 258 259 virtual void Flush() = 0; 260 261 nsTHashSet<const void*> mStoredObjects; 262 263 using PendingDeletionsVector = std::vector<std::function<void()>>; 264 DataMutex<PendingDeletionsVector> mPendingDeletions{ 265 "DrawEventRecorderPrivate::mPendingDeletions"}; 266 267 // It's difficult to track the lifetimes of UnscaledFonts directly, so we 268 // instead track the number of recorded ScaledFonts that hold a reference to 269 // an Unscaled font and use that as a proxy to the real lifetime. An 270 // UnscaledFonts lifetime could be longer than this, but we only use the 271 // ScaledFonts directly and if another uses an UnscaledFont we have destroyed 272 // on the translation side, it will be recreated. 273 nsTHashMap<const void*, int32_t> mUnscaledFontRefs; 274 275 nsTHashSet<uint64_t> mStoredFontData; 276 nsTHashSet<ScaledFont*> mStoredFonts; 277 std::vector<RefPtr<ScaledFont>> mScaledFonts; 278 279 // SourceSurfaces can get deleted off the main thread, so we hold a map of the 280 // raw pointer to a ThreadSafeWeakPtr to protect against this. 281 nsTHashMap<void*, ThreadSafeWeakPtr<SourceSurface>> mStoredSurfaces; 282 283 ReferencePtr mCurrentDT; 284 ExternalSurfacesHolder mExternalSurfaces; 285 ExternalImagesHolder mExternalImages; 286 bool mExternalFonts; 287 }; 288 289 typedef std::function<void(MemStream& aStream, 290 std::vector<RefPtr<ScaledFont>>& aScaledFonts)> 291 SerializeResourcesFn; 292 293 // WARNING: This should not be used in its existing state because 294 // it is likely to OOM because of large continguous allocations. 295 class DrawEventRecorderMemory : public DrawEventRecorderPrivate { 296 public: 297 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderMemory, override) 298 299 /** 300 * Constructs a DrawEventRecorder that stores the recording in memory. 301 */ 302 DrawEventRecorderMemory(); 303 explicit DrawEventRecorderMemory(const SerializeResourcesFn& aSerialize); 304 305 RecorderType GetRecorderType() const override { return RecorderType::MEMORY; } 306 307 void RecordEvent(const RecordedEvent& aEvent) override; 308 309 void AddDependentSurface(uint64_t aDependencyId) override; 310 311 nsTHashSet<uint64_t>&& TakeDependentSurfaces(); 312 313 /** 314 * @return the current size of the recording (in chars). 315 */ 316 size_t RecordingSize(); 317 318 /** 319 * Wipes the internal recording buffer, but the recorder does NOT forget which 320 * objects it has recorded. This can be used so that a recording can be copied 321 * and processed in chunks, releasing memory as it goes. 322 */ 323 void WipeRecording(); 324 bool Finish() override; 325 void FlushItem(IntRect) override; 326 327 MemStream mOutputStream; 328 /* The index stream is of the form: 329 * ItemIndex { size_t dataEnd; size_t extraDataEnd; } 330 * It gets concatenated to the end of mOutputStream in Finish() 331 * The last size_t in the stream is offset of the begining of the 332 * index. 333 */ 334 MemStream mIndex; 335 336 protected: 337 virtual ~DrawEventRecorderMemory() = default; 338 339 private: 340 SerializeResourcesFn mSerializeCallback; 341 nsTHashSet<uint64_t> mDependentSurfaces; 342 343 void Flush() override; 344 }; 345 346 } // namespace gfx 347 } // namespace mozilla 348 349 #endif /* MOZILLA_GFX_DRAWEVENTRECORDER_H_ */