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