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