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 "RecordedEvent.h" 12 #include "RecordingTypes.h" 13 #include "mozilla/FStream.h" 14 15 #include <unordered_set> 16 #include <unordered_map> 17 #include <functional> 18 19 #include "nsHashKeys.h" 20 #include "nsTHashtable.h" 21 22 namespace mozilla { 23 namespace gfx { 24 25 class PathRecording; 26 27 class DrawEventRecorderPrivate : public DrawEventRecorder { 28 public: 29 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderPrivate, override) 30 31 DrawEventRecorderPrivate(); 32 virtual ~DrawEventRecorderPrivate() = default; Finish()33 bool Finish() override { 34 ClearResources(); 35 return true; 36 } FlushItem(IntRect)37 virtual void FlushItem(IntRect) {} DetachResources()38 void DetachResources() { 39 // The iteration is a bit awkward here because our iterator will 40 // be invalidated by the removal 41 for (auto font = mStoredFonts.begin(); font != mStoredFonts.end();) { 42 auto oldFont = font++; 43 (*oldFont)->RemoveUserData(reinterpret_cast<UserDataKey*>(this)); 44 } 45 for (auto surface = mStoredSurfaces.begin(); 46 surface != mStoredSurfaces.end();) { 47 auto oldSurface = surface++; 48 (*oldSurface)->RemoveUserData(reinterpret_cast<UserDataKey*>(this)); 49 } 50 mStoredFonts.clear(); 51 mStoredSurfaces.clear(); 52 } 53 ClearResources()54 void ClearResources() { 55 mStoredObjects.clear(); 56 mStoredFontData.clear(); 57 mScaledFonts.clear(); 58 } 59 60 template <class S> WriteHeader(S & aStream)61 void WriteHeader(S& aStream) { 62 WriteElement(aStream, kMagicInt); 63 WriteElement(aStream, kMajorRevision); 64 WriteElement(aStream, kMinorRevision); 65 } 66 67 virtual void RecordEvent(const RecordedEvent& aEvent) = 0; 68 void WritePath(const PathRecording* aPath); 69 AddStoredObject(const ReferencePtr aObject)70 void AddStoredObject(const ReferencePtr aObject) { 71 mStoredObjects.insert(aObject); 72 } 73 RemoveStoredObject(const ReferencePtr aObject)74 void RemoveStoredObject(const ReferencePtr aObject) { 75 mStoredObjects.erase(aObject); 76 } 77 78 /** 79 * @param aUnscaledFont the UnscaledFont to increment the reference count for 80 * @return the previous reference count 81 */ IncrementUnscaledFontRefCount(const ReferencePtr aUnscaledFont)82 int32_t IncrementUnscaledFontRefCount(const ReferencePtr aUnscaledFont) { 83 int32_t& count = mUnscaledFontRefs[aUnscaledFont]; 84 return count++; 85 } 86 87 /** 88 * Decrements the reference count for aUnscaledFont and, if count is now zero, 89 * records its destruction. 90 * @param aUnscaledFont the UnscaledFont to decrement the reference count for 91 */ 92 void DecrementUnscaledFontRefCount(const ReferencePtr aUnscaledFont); 93 AddScaledFont(ScaledFont * aFont)94 void AddScaledFont(ScaledFont* aFont) { 95 if (mStoredFonts.insert(aFont).second && WantsExternalFonts()) { 96 mScaledFonts.push_back(aFont); 97 } 98 } 99 RemoveScaledFont(ScaledFont * aFont)100 void RemoveScaledFont(ScaledFont* aFont) { mStoredFonts.erase(aFont); } 101 AddSourceSurface(SourceSurface * aSurface)102 void AddSourceSurface(SourceSurface* aSurface) { 103 mStoredSurfaces.insert(aSurface); 104 } 105 RemoveSourceSurface(SourceSurface * aSurface)106 void RemoveSourceSurface(SourceSurface* aSurface) { 107 mStoredSurfaces.erase(aSurface); 108 } 109 HasStoredObject(const ReferencePtr aObject)110 bool HasStoredObject(const ReferencePtr aObject) { 111 return mStoredObjects.find(aObject) != mStoredObjects.end(); 112 } 113 AddStoredFontData(const uint64_t aFontDataKey)114 void AddStoredFontData(const uint64_t aFontDataKey) { 115 mStoredFontData.insert(aFontDataKey); 116 } 117 HasStoredFontData(const uint64_t aFontDataKey)118 bool HasStoredFontData(const uint64_t aFontDataKey) { 119 return mStoredFontData.find(aFontDataKey) != mStoredFontData.end(); 120 } 121 WantsExternalFonts()122 bool WantsExternalFonts() const { return mExternalFonts; } 123 TakeExternalSurfaces(std::vector<RefPtr<SourceSurface>> & aSurfaces)124 void TakeExternalSurfaces(std::vector<RefPtr<SourceSurface>>& aSurfaces) { 125 aSurfaces = std::move(mExternalSurfaces); 126 } 127 128 virtual void StoreSourceSurfaceRecording(SourceSurface* aSurface, 129 const char* aReason); 130 131 /** 132 * This is virtual to allow subclasses to control the recording, if for 133 * example it needs to happen on a specific thread. aSurface is a void* 134 * instead of a SourceSurface* because this is called during the SourceSurface 135 * destructor, so it is partially destructed and should not be accessed. If we 136 * use a SourceSurface* we might, for example, accidentally AddRef/Release the 137 * object by passing it to NewRunnableMethod to submit to a different thread. 138 * We are only using the pointer as a lookup ID to our internal maps and 139 * ReferencePtr to be used on the translation side. 140 * @param aSurface the surface whose destruction is being recorded 141 */ 142 virtual void RecordSourceSurfaceDestruction(void* aSurface); 143 AddDependentSurface(uint64_t aDependencyId)144 virtual void AddDependentSurface(uint64_t aDependencyId) { 145 MOZ_CRASH("GFX: AddDependentSurface"); 146 } 147 148 protected: 149 void StoreExternalSurfaceRecording(SourceSurface* aSurface, uint64_t aKey); 150 151 virtual void Flush() = 0; 152 153 std::unordered_set<const void*> mStoredObjects; 154 155 // It's difficult to track the lifetimes of UnscaledFonts directly, so we 156 // instead track the number of recorded ScaledFonts that hold a reference to 157 // an Unscaled font and use that as a proxy to the real lifetime. An 158 // UnscaledFonts lifetime could be longer than this, but we only use the 159 // ScaledFonts directly and if another uses an UnscaledFont we have destroyed 160 // on the translation side, it will be recreated. 161 std::unordered_map<const void*, int32_t> mUnscaledFontRefs; 162 163 std::unordered_set<uint64_t> mStoredFontData; 164 std::unordered_set<ScaledFont*> mStoredFonts; 165 std::vector<RefPtr<ScaledFont>> mScaledFonts; 166 std::unordered_set<SourceSurface*> mStoredSurfaces; 167 std::vector<RefPtr<SourceSurface>> mExternalSurfaces; 168 bool mExternalFonts; 169 }; 170 171 class DrawEventRecorderFile : public DrawEventRecorderPrivate { 172 using char_type = filesystem::Path::value_type; 173 174 public: 175 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderFile, override) 176 explicit DrawEventRecorderFile(const char_type* aFilename); 177 virtual ~DrawEventRecorderFile(); 178 179 void RecordEvent(const RecordedEvent& aEvent) override; 180 181 /** 182 * Returns whether a recording file is currently open. 183 */ 184 bool IsOpen(); 185 186 /** 187 * Opens new file with the provided name. The recorder does NOT forget which 188 * objects it has recorded. This can be used with Close, so that a recording 189 * can be processed in chunks. The file must not already be open. 190 */ 191 void OpenNew(const char_type* aFilename); 192 193 /** 194 * Closes the file so that it can be processed. The recorder does NOT forget 195 * which objects it has recorded. This can be used with OpenNew, so that a 196 * recording can be processed in chunks. The file must be open. 197 */ 198 void Close(); 199 200 private: 201 void Flush() override; 202 203 mozilla::OFStream mOutputStream; 204 }; 205 206 typedef std::function<void(MemStream& aStream, 207 std::vector<RefPtr<ScaledFont>>& aScaledFonts)> 208 SerializeResourcesFn; 209 210 // WARNING: This should not be used in its existing state because 211 // it is likely to OOM because of large continguous allocations. 212 class DrawEventRecorderMemory : public DrawEventRecorderPrivate { 213 public: 214 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderMemory, override) 215 216 /** 217 * Constructs a DrawEventRecorder that stores the recording in memory. 218 */ 219 DrawEventRecorderMemory(); 220 explicit DrawEventRecorderMemory(const SerializeResourcesFn& aSerialize); 221 222 void RecordEvent(const RecordedEvent& aEvent) override; 223 224 void AddDependentSurface(uint64_t aDependencyId) override; 225 226 nsTHashtable<nsUint64HashKey>&& TakeDependentSurfaces(); 227 228 /** 229 * @return the current size of the recording (in chars). 230 */ 231 size_t RecordingSize(); 232 233 /** 234 * Wipes the internal recording buffer, but the recorder does NOT forget which 235 * objects it has recorded. This can be used so that a recording can be copied 236 * and processed in chunks, releasing memory as it goes. 237 */ 238 void WipeRecording(); 239 bool Finish() override; 240 void FlushItem(IntRect) override; 241 242 MemStream mOutputStream; 243 /* The index stream is of the form: 244 * ItemIndex { size_t dataEnd; size_t extraDataEnd; } 245 * It gets concatenated to the end of mOutputStream in Finish() 246 * The last size_t in the stream is offset of the begining of the 247 * index. 248 */ 249 MemStream mIndex; 250 251 protected: 252 virtual ~DrawEventRecorderMemory() = default; 253 254 private: 255 SerializeResourcesFn mSerializeCallback; 256 nsTHashtable<nsUint64HashKey> mDependentSurfaces; 257 258 void Flush() override; 259 }; 260 261 } // namespace gfx 262 } // namespace mozilla 263 264 #endif /* MOZILLA_GFX_DRAWEVENTRECORDER_H_ */ 265