WebGLObjectModel.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  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. #ifndef WEBGLOBJECTMODEL_H_
  6. #define WEBGLOBJECTMODEL_H_
  7. #include "mozilla/WeakPtr.h"
  8. #include "nsCycleCollectionNoteChild.h"
  9. #include "WebGLTypes.h"
  10. namespace mozilla {
  11. template<typename> class LinkedList;
  12. class WebGLContext;
  13. ////
  14. // This class is a mixin for objects that are tied to a specific
  15. // context (which is to say, all of them). They provide initialization
  16. // as well as comparison with the current context.
  17. class WebGLContextBoundObject
  18. {
  19. public:
  20. const WeakPtr<WebGLContext> mContext;
  21. private:
  22. const uint32_t mContextGeneration;
  23. public:
  24. explicit WebGLContextBoundObject(WebGLContext* webgl);
  25. bool IsCompatibleWithContext(const WebGLContext* other) const;
  26. };
  27. ////
  28. class WebGLDeletableObject : public WebGLContextBoundObject
  29. {
  30. template<typename> friend class WebGLRefCountedObject;
  31. private:
  32. enum DeletionStatus { Default, DeleteRequested, Deleted };
  33. DeletionStatus mDeletionStatus;
  34. ////
  35. explicit WebGLDeletableObject(WebGLContext* webgl)
  36. : WebGLContextBoundObject(webgl)
  37. , mDeletionStatus(Default)
  38. { }
  39. ~WebGLDeletableObject() {
  40. MOZ_ASSERT(mDeletionStatus == Deleted,
  41. "Derived class destructor must call DeleteOnce().");
  42. }
  43. public:
  44. bool IsDeleted() const { return mDeletionStatus == Deleted; }
  45. bool IsDeleteRequested() const { return mDeletionStatus != Default; }
  46. };
  47. /* Each WebGL object class WebGLFoo wants to:
  48. * - inherit WebGLRefCountedObject<WebGLFoo>
  49. * - implement a Delete() method
  50. * - have its destructor call DeleteOnce()
  51. *
  52. * This base class provides two features to WebGL object types:
  53. * 1. support for OpenGL object reference counting
  54. * 2. support for OpenGL deletion statuses
  55. *
  56. ***** 1. OpenGL object reference counting *****
  57. *
  58. * WebGL objects such as WebGLTexture's really have two different refcounts:
  59. * the XPCOM refcount, that is directly exposed to JavaScript, and the OpenGL
  60. * refcount.
  61. *
  62. * For example, when in JavaScript one does: var newname = existingTexture;
  63. * that increments the XPCOM refcount, but doesn't affect the OpenGL refcount.
  64. * When one attaches the texture to a framebuffer object, that does increment
  65. * its OpenGL refcount (and also its XPCOM refcount, to prevent the regular
  66. * XPCOM refcounting mechanism from destroying objects prematurely).
  67. *
  68. * The actual OpenGL refcount is opaque to us (it's internal to the OpenGL
  69. * implementation) but is affects the WebGL semantics that we have to implement:
  70. * for example, a WebGLTexture that is attached to a WebGLFramebuffer must not
  71. * be actually deleted, even if deleteTexture has been called on it, and even
  72. * if JavaScript doesn't have references to it anymore. We can't just rely on
  73. * OpenGL to keep alive the underlying OpenGL texture for us, for a variety of
  74. * reasons, most importantly: we'd need to know when OpenGL objects are actually
  75. * deleted, and OpenGL doesn't notify us about that, so we would have to query
  76. * status very often with glIsXxx calls which isn't practical.
  77. *
  78. * This means that we have to keep track of the OpenGL refcount ourselves,
  79. * in addition to the XPCOM refcount.
  80. *
  81. * This class implements such a refcount, see the mWebGLRefCnt
  82. * member. In order to avoid name clashes (with regular XPCOM refcounting)
  83. * in the derived class, we prefix members with 'WebGL', whence the names
  84. * WebGLAddRef, WebGLRelease, etc.
  85. *
  86. * In practice, WebGLAddRef and WebGLRelease are only called from the
  87. * WebGLRefPtr class.
  88. *
  89. ***** 2. OpenGL deletion statuses *****
  90. *
  91. * In OpenGL, an object can go through 3 different deletion statuses during its
  92. * lifetime, which correspond to the 3 enum values for DeletionStatus in this
  93. * class:
  94. * - the Default status, which it has from its creation to when the suitable
  95. * glDeleteXxx function is called on it;
  96. * - the DeleteRequested status, which is has from when the suitable
  97. * glDeleteXxx function is called on it to when it is no longer referenced by
  98. * other OpenGL objects. For example, a texture that is attached to a
  99. * non-current FBO will enter that status when glDeleteTexture is called on
  100. * it. For objects with that status, GL_DELETE_STATUS queries return true,
  101. * but glIsXxx functions still return true.
  102. * - the Deleted status, which is the status of objects on which the suitable
  103. * glDeleteXxx function has been called, and that are not referenced by other
  104. * OpenGL objects.
  105. *
  106. * This state is stored in the mDeletionStatus member of this class.
  107. *
  108. * When the GL refcount hits zero, if the status is DeleteRequested then we call
  109. * the Delete() method on the derived class and the status becomes Deleted. This
  110. * is what the MaybeDelete() function does.
  111. *
  112. * The DeleteOnce() function implemented here is a helper to ensure that we
  113. * don't call Delete() twice on the same object. Since the derived class's
  114. * destructor needs to call DeleteOnce() which calls Delete(), we can't allow
  115. * either to be virtual. Strictly speaking, we could let them be virtual if the
  116. * derived class were final, but that would be impossible to enforce and would
  117. * lead to strange bugs if it were subclassed.
  118. *
  119. * This WebGLRefCountedObject class takes the Derived type as template
  120. * parameter, as a means to allow DeleteOnce to call Delete() on the Derived
  121. * class, without either method being virtual. This is a common C++ pattern
  122. * known as the "curiously recursive template pattern (CRTP)".
  123. */
  124. template<typename Derived>
  125. class WebGLRefCountedObject : public WebGLDeletableObject
  126. {
  127. friend class WebGLContext;
  128. template<typename T> friend void ClearLinkedList(LinkedList<T>& list);
  129. private:
  130. nsAutoRefCnt mWebGLRefCnt;
  131. public:
  132. explicit WebGLRefCountedObject(WebGLContext* webgl)
  133. : WebGLDeletableObject(webgl)
  134. { }
  135. ~WebGLRefCountedObject() {
  136. MOZ_ASSERT(mWebGLRefCnt == 0,
  137. "Destroying WebGL object still referenced by other WebGL"
  138. " objects.");
  139. }
  140. // called by WebGLRefPtr
  141. void WebGLAddRef() {
  142. ++mWebGLRefCnt;
  143. }
  144. // called by WebGLRefPtr
  145. void WebGLRelease() {
  146. MOZ_ASSERT(mWebGLRefCnt > 0,
  147. "Releasing WebGL object with WebGL refcnt already zero");
  148. --mWebGLRefCnt;
  149. MaybeDelete();
  150. }
  151. // this is the function that WebGL.deleteXxx() functions want to call
  152. void RequestDelete() {
  153. if (mDeletionStatus == Default)
  154. mDeletionStatus = DeleteRequested;
  155. MaybeDelete();
  156. }
  157. protected:
  158. void DeleteOnce() {
  159. if (mDeletionStatus != Deleted) {
  160. static_cast<Derived*>(this)->Delete();
  161. mDeletionStatus = Deleted;
  162. }
  163. }
  164. private:
  165. void MaybeDelete() {
  166. if (mWebGLRefCnt == 0 &&
  167. mDeletionStatus == DeleteRequested)
  168. {
  169. DeleteOnce();
  170. }
  171. }
  172. };
  173. /* This WebGLRefPtr class is meant to be used for references between WebGL
  174. * objects. For example, a WebGLProgram holds WebGLRefPtr's to the WebGLShader's
  175. * attached to it.
  176. *
  177. * Why the need for a separate refptr class? The only special thing that
  178. * WebGLRefPtr does is that it increments and decrements the WebGL refcount of
  179. * WebGLRefCountedObject's, in addition to incrementing and decrementing the
  180. * usual XPCOM refcount.
  181. *
  182. * This means that by using a WebGLRefPtr instead of a nsRefPtr, you ensure that
  183. * the WebGL refcount is incremented, which means that the object will be kept
  184. * alive by this reference even if the matching webgl.deleteXxx() function is
  185. * called on it.
  186. */
  187. template<typename T>
  188. class WebGLRefPtr
  189. {
  190. public:
  191. WebGLRefPtr()
  192. : mRawPtr(0)
  193. {}
  194. WebGLRefPtr(const WebGLRefPtr<T>& smartPtr)
  195. : mRawPtr(smartPtr.mRawPtr)
  196. {
  197. AddRefOnPtr(mRawPtr);
  198. }
  199. explicit WebGLRefPtr(T* rawPtr)
  200. : mRawPtr(rawPtr)
  201. {
  202. AddRefOnPtr(mRawPtr);
  203. }
  204. ~WebGLRefPtr() {
  205. ReleasePtr(mRawPtr);
  206. }
  207. WebGLRefPtr<T>&
  208. operator=(const WebGLRefPtr<T>& rhs)
  209. {
  210. assign_with_AddRef(rhs.mRawPtr);
  211. return *this;
  212. }
  213. WebGLRefPtr<T>&
  214. operator=(T* rhs)
  215. {
  216. assign_with_AddRef(rhs);
  217. return *this;
  218. }
  219. T* get() const {
  220. return static_cast<T*>(mRawPtr);
  221. }
  222. operator T*() const {
  223. return get();
  224. }
  225. T* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN {
  226. MOZ_ASSERT(mRawPtr != 0, "You can't dereference a nullptr WebGLRefPtr with operator->()!");
  227. return get();
  228. }
  229. T& operator*() const {
  230. MOZ_ASSERT(mRawPtr != 0, "You can't dereference a nullptr WebGLRefPtr with operator*()!");
  231. return *get();
  232. }
  233. private:
  234. static void AddRefOnPtr(T* rawPtr) {
  235. if (rawPtr) {
  236. rawPtr->WebGLAddRef();
  237. rawPtr->AddRef();
  238. }
  239. }
  240. static void ReleasePtr(T* rawPtr) {
  241. if (rawPtr) {
  242. rawPtr->WebGLRelease(); // must be done first before Release(), as Release() might actually destroy the object
  243. rawPtr->Release();
  244. }
  245. }
  246. void assign_with_AddRef(T* rawPtr) {
  247. AddRefOnPtr(rawPtr);
  248. assign_assuming_AddRef(rawPtr);
  249. }
  250. void assign_assuming_AddRef(T* newPtr) {
  251. T* oldPtr = mRawPtr;
  252. mRawPtr = newPtr;
  253. ReleasePtr(oldPtr);
  254. }
  255. protected:
  256. T* mRawPtr;
  257. };
  258. // this class is a mixin for GL objects that have dimensions
  259. // that we need to track.
  260. class WebGLRectangleObject
  261. {
  262. public:
  263. WebGLRectangleObject()
  264. : mWidth(0)
  265. , mHeight(0)
  266. {}
  267. WebGLRectangleObject(GLsizei width, GLsizei height)
  268. : mWidth(width)
  269. , mHeight(height)
  270. {}
  271. GLsizei Width() const { return mWidth; }
  272. void width(GLsizei value) { mWidth = value; }
  273. GLsizei Height() const { return mHeight; }
  274. void height(GLsizei value) { mHeight = value; }
  275. void setDimensions(GLsizei width, GLsizei height) {
  276. mWidth = width;
  277. mHeight = height;
  278. }
  279. void setDimensions(WebGLRectangleObject* rect) {
  280. if (rect) {
  281. mWidth = rect->Width();
  282. mHeight = rect->Height();
  283. } else {
  284. mWidth = 0;
  285. mHeight = 0;
  286. }
  287. }
  288. bool HasSameDimensionsAs(const WebGLRectangleObject& other) const {
  289. return Width() == other.Width() && Height() == other.Height();
  290. }
  291. protected:
  292. GLsizei mWidth;
  293. GLsizei mHeight;
  294. };
  295. }// namespace mozilla
  296. template <typename T>
  297. inline void
  298. ImplCycleCollectionUnlink(mozilla::WebGLRefPtr<T>& field)
  299. {
  300. field = nullptr;
  301. }
  302. template <typename T>
  303. inline void
  304. ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
  305. mozilla::WebGLRefPtr<T>& field,
  306. const char* name,
  307. uint32_t flags = 0)
  308. {
  309. CycleCollectionNoteChild(callback, field.get(), name, flags);
  310. }
  311. template <typename T>
  312. inline void
  313. ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
  314. const mozilla::WebGLRefPtr<T>& field,
  315. const char* name,
  316. uint32_t flags = 0)
  317. {
  318. CycleCollectionNoteChild(callback, field.get(), name, flags);
  319. }
  320. #endif