ISurfaceProvider.h (11730B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 /** 7 * An interface for objects which can either store a surface or dynamically 8 * generate one, and various implementations. 9 */ 10 11 #ifndef mozilla_image_ISurfaceProvider_h 12 #define mozilla_image_ISurfaceProvider_h 13 14 #include "mozilla/Attributes.h" 15 #include "mozilla/Maybe.h" 16 #include "mozilla/MemoryReporting.h" 17 #include "mozilla/NotNull.h" 18 #include "mozilla/TimeStamp.h" 19 #include "mozilla/gfx/2D.h" 20 #include "mozilla/image/WebRenderImageProvider.h" 21 22 #include "imgFrame.h" 23 #include "SurfaceCache.h" 24 25 namespace mozilla { 26 namespace image { 27 28 class CachedSurface; 29 class DrawableSurface; 30 31 /** 32 * An interface for objects which can either store a surface or dynamically 33 * generate one. 34 */ 35 class ISurfaceProvider : public WebRenderImageProvider { 36 public: 37 // Subclasses may or may not be XPCOM classes, so we just require that they 38 // implement AddRef and Release. 39 NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING 40 41 /// @return key data used for identifying which image this ISurfaceProvider is 42 /// associated with in the surface cache. 43 ImageKey GetImageKey() const { return mImageKey; } 44 45 /// @return key data used to uniquely identify this ISurfaceProvider's cache 46 /// entry in the surface cache. 47 const SurfaceKey& GetSurfaceKey() const { return mSurfaceKey; } 48 49 /// @return a drawable reference to a surface. 50 DrawableSurface Surface(); 51 52 /// @return true if DrawableRef() will return a completely decoded surface. 53 virtual bool IsFinished() const = 0; 54 55 /// @return true if the underlying decoder is currently fully decoded. For 56 /// animated images, this means that at least every frame has been decoded 57 /// at least once. It does not guarantee that all of the frames are present, 58 /// as the surface provider has the option to discard as it deems necessary. 59 virtual bool IsFullyDecoded() const { return IsFinished(); } 60 61 /// @return the number of bytes of memory this ISurfaceProvider is expected to 62 /// require. Optimizations may result in lower real memory usage. Trivial 63 /// overhead is ignored. Because this value is used in bookkeeping, it's 64 /// important that it be constant over the lifetime of this object. 65 virtual size_t LogicalSizeInBytes() const = 0; 66 67 typedef imgFrame::AddSizeOfCbData AddSizeOfCbData; 68 typedef imgFrame::AddSizeOfCb AddSizeOfCb; 69 70 /// @return the actual number of bytes of memory this ISurfaceProvider is 71 /// using. May vary over the lifetime of the ISurfaceProvider. The default 72 /// implementation is appropriate for static ISurfaceProviders. 73 virtual void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, 74 const AddSizeOfCb& aCallback) { 75 DrawableFrameRef ref = DrawableRef(/* aFrame = */ 0); 76 if (!ref) { 77 return; 78 } 79 80 ref->AddSizeOfExcludingThis(aMallocSizeOf, aCallback); 81 } 82 83 virtual void Reset() {} 84 virtual void Advance(size_t aFrame) {} 85 virtual bool MayAdvance() const { return false; } 86 virtual void MarkMayAdvance() {} 87 88 /// @return the availability state of this ISurfaceProvider, which indicates 89 /// whether DrawableRef() could successfully return a surface. Should only be 90 /// called from SurfaceCache code as it relies on SurfaceCache for 91 /// synchronization. 92 AvailabilityState& Availability() { return mAvailability; } 93 const AvailabilityState& Availability() const { return mAvailability; } 94 95 protected: 96 ISurfaceProvider(const ImageKey aImageKey, const SurfaceKey& aSurfaceKey, 97 AvailabilityState aAvailability) 98 : WebRenderImageProvider(aImageKey), 99 mImageKey(aImageKey), 100 mSurfaceKey(aSurfaceKey), 101 mAvailability(aAvailability) { 102 MOZ_ASSERT(aImageKey, "Must have a valid image key"); 103 } 104 105 virtual ~ISurfaceProvider() {} 106 107 /// @return an eagerly computed drawable reference to a surface. For 108 /// dynamically generated animation surfaces, @aFrame specifies the 0-based 109 /// index of the desired frame. 110 virtual DrawableFrameRef DrawableRef(size_t aFrame) = 0; 111 112 /// @return an imgFrame at the 0-based index of the desired frame, as 113 /// specified by @aFrame. Only applies for animated images. 114 virtual already_AddRefed<imgFrame> GetFrame(size_t aFrame) { 115 MOZ_ASSERT_UNREACHABLE("Surface provider does not support direct access!"); 116 return nullptr; 117 } 118 119 /// @return true if this ISurfaceProvider is locked. (@see SetLocked()) 120 /// Should only be called from SurfaceCache code as it relies on SurfaceCache 121 /// for synchronization. 122 virtual bool IsLocked() const = 0; 123 124 /// If @aLocked is true, hint that this ISurfaceProvider is in use and it 125 /// should avoid releasing its resources. Should only be called from 126 /// SurfaceCache code as it relies on SurfaceCache for synchronization. 127 virtual void SetLocked(bool aLocked) = 0; 128 129 private: 130 friend class CachedSurface; 131 friend class DrawableSurface; 132 133 const ImageKey mImageKey; 134 const SurfaceKey mSurfaceKey; 135 AvailabilityState mAvailability; 136 }; 137 138 /** 139 * A reference to a surface (stored in an imgFrame) that holds the surface in 140 * memory, guaranteeing that it can be drawn. If you have a DrawableSurface 141 * |surf| and |if (surf)| returns true, then calls to |surf->Draw()| and 142 * |surf->GetSourceSurface()| are guaranteed to succeed. 143 * 144 * Note that the surface may be computed lazily, so a DrawableSurface should not 145 * be dereferenced (i.e., operator->() should not be called) until you're 146 * sure that you want to draw it. 147 */ 148 class MOZ_STACK_CLASS DrawableSurface final { 149 public: 150 DrawableSurface() : mHaveSurface(false) {} 151 152 explicit DrawableSurface(NotNull<ISurfaceProvider*> aProvider) 153 : mProvider(aProvider), mHaveSurface(true) {} 154 155 DrawableSurface(DrawableSurface&& aOther) 156 : mDrawableRef(std::move(aOther.mDrawableRef)), 157 mProvider(std::move(aOther.mProvider)), 158 mHaveSurface(aOther.mHaveSurface) { 159 aOther.mHaveSurface = false; 160 } 161 162 DrawableSurface& operator=(DrawableSurface&& aOther) { 163 MOZ_ASSERT(this != &aOther, "Self-moves are prohibited"); 164 mDrawableRef = std::move(aOther.mDrawableRef); 165 mProvider = std::move(aOther.mProvider); 166 mHaveSurface = aOther.mHaveSurface; 167 aOther.mHaveSurface = false; 168 return *this; 169 } 170 171 /** 172 * If this DrawableSurface is dynamically generated from an animation, attempt 173 * to seek to frame @aFrame, where @aFrame is a 0-based index into the frames 174 * of the animation. Otherwise, nothing will blow up at runtime, but we assert 175 * in debug builds, since calling this in an unexpected situation probably 176 * indicates a bug. 177 * 178 * @return a successful result if we could obtain frame @aFrame. Note that 179 * |mHaveSurface| being true means that we're guaranteed to have *some* frame, 180 * so the caller can dereference this DrawableSurface even if Seek() fails, 181 * but while nothing will blow up, the frame won't be the one they expect. 182 */ 183 nsresult Seek(size_t aFrame) { 184 MOZ_ASSERT(mHaveSurface, "Trying to seek an empty DrawableSurface?"); 185 186 if (!mProvider) { 187 MOZ_ASSERT_UNREACHABLE("Trying to seek a static DrawableSurface?"); 188 return NS_ERROR_FAILURE; 189 } 190 191 mDrawableRef = mProvider->DrawableRef(aFrame); 192 193 return mDrawableRef ? NS_OK : NS_ERROR_FAILURE; 194 } 195 196 already_AddRefed<imgFrame> GetFrame(size_t aFrame) { 197 MOZ_ASSERT(mHaveSurface, "Trying to get on an empty DrawableSurface?"); 198 199 if (!mProvider) { 200 MOZ_ASSERT_UNREACHABLE("Trying to get on a static DrawableSurface?"); 201 return nullptr; 202 } 203 204 return mProvider->GetFrame(aFrame); 205 } 206 207 void Reset() { 208 if (!mProvider) { 209 MOZ_ASSERT_UNREACHABLE("Trying to reset a static DrawableSurface?"); 210 return; 211 } 212 213 mProvider->Reset(); 214 } 215 216 void Advance(size_t aFrame) { 217 if (!mProvider) { 218 MOZ_ASSERT_UNREACHABLE("Trying to advance a static DrawableSurface?"); 219 return; 220 } 221 222 mProvider->Advance(aFrame); 223 } 224 225 bool MayAdvance() const { 226 if (!mProvider) { 227 MOZ_ASSERT_UNREACHABLE("Trying to advance a static DrawableSurface?"); 228 return false; 229 } 230 231 return mProvider->MayAdvance(); 232 } 233 234 void MarkMayAdvance() { 235 if (!mProvider) { 236 MOZ_ASSERT_UNREACHABLE("Trying to advance a static DrawableSurface?"); 237 return; 238 } 239 240 mProvider->MarkMayAdvance(); 241 } 242 243 bool IsFullyDecoded() const { 244 if (!mProvider) { 245 MOZ_ASSERT_UNREACHABLE( 246 "Trying to check decoding state of a static DrawableSurface?"); 247 return false; 248 } 249 250 return mProvider->IsFullyDecoded(); 251 } 252 253 void TakeProvider(WebRenderImageProvider** aOutProvider) { 254 mProvider.forget(aOutProvider); 255 } 256 257 explicit operator bool() const { return mHaveSurface; } 258 imgFrame* operator->() { return DrawableRef().get(); } 259 260 private: 261 DrawableSurface(const DrawableSurface& aOther) = delete; 262 DrawableSurface& operator=(const DrawableSurface& aOther) = delete; 263 264 DrawableFrameRef& DrawableRef() { 265 MOZ_ASSERT(mHaveSurface); 266 267 // If we weren't created with a DrawableFrameRef directly, we should've been 268 // created with an ISurfaceProvider which can give us one. Note that if 269 // Seek() has been called, we'll already have a DrawableFrameRef, so we 270 // won't need to get one here. 271 if (!mDrawableRef) { 272 MOZ_ASSERT(mProvider); 273 mDrawableRef = mProvider->DrawableRef(/* aFrame = */ 0); 274 } 275 276 MOZ_ASSERT(mDrawableRef); 277 return mDrawableRef; 278 } 279 280 DrawableFrameRef mDrawableRef; 281 RefPtr<ISurfaceProvider> mProvider; 282 bool mHaveSurface; 283 }; 284 285 // Surface() is implemented here so that DrawableSurface's definition is 286 // visible. 287 inline DrawableSurface ISurfaceProvider::Surface() { 288 return DrawableSurface(WrapNotNull(this)); 289 } 290 291 /** 292 * An ISurfaceProvider that stores a single surface. 293 */ 294 class SimpleSurfaceProvider final : public ISurfaceProvider { 295 public: 296 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SimpleSurfaceProvider, override) 297 298 SimpleSurfaceProvider(const ImageKey aImageKey, const SurfaceKey& aSurfaceKey, 299 NotNull<imgFrame*> aSurface) 300 : ISurfaceProvider(aImageKey, aSurfaceKey, 301 AvailabilityState::StartAvailable()), 302 mSurface(aSurface) { 303 MOZ_ASSERT(aSurfaceKey.Size() == mSurface->GetSize()); 304 } 305 306 bool IsFinished() const override { return mSurface->IsFinished(); } 307 308 size_t LogicalSizeInBytes() const override { 309 gfx::IntSize size = mSurface->GetSize(); 310 return size.width * size.height * mSurface->GetBytesPerPixel(); 311 } 312 313 nsresult UpdateKey(layers::RenderRootStateManager* aManager, 314 wr::IpcResourceUpdateQueue& aResources, 315 wr::ImageKey& aKey) override; 316 317 void InvalidateSurface() override; 318 319 protected: 320 DrawableFrameRef DrawableRef(size_t aFrame) override { 321 MOZ_ASSERT(aFrame == 0, 322 "Requesting an animation frame from a SimpleSurfaceProvider?"); 323 return mSurface->DrawableRef(); 324 } 325 326 bool IsLocked() const override { return bool(mLockRef); } 327 328 void SetLocked(bool aLocked) override { 329 if (aLocked == IsLocked()) { 330 return; // Nothing changed. 331 } 332 333 // If we're locked, hold a DrawableFrameRef to |mSurface|, which will keep 334 // any volatile buffer it owns in memory. 335 mLockRef = aLocked ? mSurface->DrawableRef() : DrawableFrameRef(); 336 } 337 338 private: 339 virtual ~SimpleSurfaceProvider() {} 340 341 NotNull<RefPtr<imgFrame>> mSurface; 342 DrawableFrameRef mLockRef; 343 bool mDirty = false; 344 }; 345 346 } // namespace image 347 } // namespace mozilla 348 349 #endif // mozilla_image_ISurfaceProvider_h