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