1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/canvas/src/WebGLObjectModel.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,335 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#ifndef WEBGLOBJECTMODEL_H_ 1.10 +#define WEBGLOBJECTMODEL_H_ 1.11 + 1.12 +#include "nsCycleCollectionNoteChild.h" 1.13 +#include "nsICanvasRenderingContextInternal.h" 1.14 +#include "WebGLTypes.h" 1.15 + 1.16 +namespace mozilla { 1.17 + 1.18 +class WebGLBuffer; 1.19 +class WebGLContext; 1.20 + 1.21 +/* Each WebGL object class WebGLFoo wants to: 1.22 + * - inherit WebGLRefCountedObject<WebGLFoo> 1.23 + * - implement a Delete() method 1.24 + * - have its destructor call DeleteOnce() 1.25 + * 1.26 + * This base class provides two features to WebGL object types: 1.27 + * 1. support for OpenGL object reference counting 1.28 + * 2. support for OpenGL deletion statuses 1.29 + * 1.30 + ***** 1. OpenGL object reference counting ***** 1.31 + * 1.32 + * WebGL objects such as WebGLTexture's really have two different refcounts: 1.33 + * the XPCOM refcount, that is directly exposed to JavaScript, and the OpenGL 1.34 + * refcount. 1.35 + * 1.36 + * For example, when in JavaScript one does: var newname = existingTexture; 1.37 + * that increments the XPCOM refcount, but doesn't affect the OpenGL refcount. 1.38 + * When one attaches the texture to a framebuffer object, that does increment 1.39 + * its OpenGL refcount (and also its XPCOM refcount, to prevent the regular 1.40 + * XPCOM refcounting mechanism from destroying objects prematurely). 1.41 + * 1.42 + * The actual OpenGL refcount is opaque to us (it's internal to the OpenGL 1.43 + * implementation) but is affects the WebGL semantics that we have to implement: 1.44 + * for example, a WebGLTexture that is attached to a WebGLFramebuffer must not 1.45 + * be actually deleted, even if deleteTexture has been called on it, and even 1.46 + * if JavaScript doesn't have references to it anymore. We can't just rely on 1.47 + * OpenGL to keep alive the underlying OpenGL texture for us, for a variety of 1.48 + * reasons, most importantly: we'd need to know when OpenGL objects are actually 1.49 + * deleted, and OpenGL doesn't notify us about that, so we would have to query 1.50 + * status very often with glIsXxx calls which isn't practical. 1.51 + * 1.52 + * This means that we have to keep track of the OpenGL refcount ourselves, 1.53 + * in addition to the XPCOM refcount. 1.54 + * 1.55 + * This class implements such a refcount, see the mWebGLRefCnt 1.56 + * member. In order to avoid name clashes (with regular XPCOM refcounting) 1.57 + * in the derived class, we prefix members with 'WebGL', whence the names 1.58 + * WebGLAddRef, WebGLRelease, etc. 1.59 + * 1.60 + * In practice, WebGLAddRef and WebGLRelease are only called from the 1.61 + * WebGLRefPtr class. 1.62 + * 1.63 + ***** 2. OpenGL deletion statuses ***** 1.64 + * 1.65 + * In OpenGL, an object can go through 3 different deletion statuses during its 1.66 + * lifetime, which correspond to the 3 enum values for DeletionStatus in this class: 1.67 + * - the Default status, which it has from its creation to when the 1.68 + * suitable glDeleteXxx function is called on it; 1.69 + * - the DeleteRequested status, which is has from when the suitable glDeleteXxx 1.70 + * function is called on it to when it is no longer referenced by other OpenGL 1.71 + * objects. For example, a texture that is attached to a non-current FBO 1.72 + * will enter that status when glDeleteTexture is called on it. For objects 1.73 + * with that status, GL_DELETE_STATUS queries return true, but glIsXxx 1.74 + * functions still return true. 1.75 + * - the Deleted status, which is the status of objects on which the 1.76 + * suitable glDeleteXxx function has been called, and that are not referenced 1.77 + * by other OpenGL objects. 1.78 + * 1.79 + * This state is stored in the mDeletionStatus member of this class. 1.80 + * 1.81 + * When the GL refcount hits zero, if the status is DeleteRequested then we call 1.82 + * the Delete() method on the derived class and the status becomes Deleted. This is 1.83 + * what the MaybeDelete() function does. 1.84 + * 1.85 + * The DeleteOnce() function implemented here is a helper to ensure that we don't 1.86 + * call Delete() twice on the same object. Since the derived class' destructor 1.87 + * needs to call DeleteOnce() which calls Delete(), we can't allow either to be 1.88 + * virtual. Strictly speaking, we could let them be virtual if the derived class 1.89 + * were final, but that would be impossible to enforce and would lead to strange 1.90 + * bugs if it were subclassed. 1.91 + * 1.92 + * This WebGLRefCountedObject class takes the Derived type 1.93 + * as template parameter, as a means to allow DeleteOnce to call Delete() 1.94 + * on the Derived class, without either method being virtual. This is a common 1.95 + * C++ pattern known as the "curiously recursive template pattern (CRTP)". 1.96 + */ 1.97 +template<typename Derived> 1.98 +class WebGLRefCountedObject 1.99 +{ 1.100 +public: 1.101 + enum DeletionStatus { Default, DeleteRequested, Deleted }; 1.102 + 1.103 + WebGLRefCountedObject() 1.104 + : mDeletionStatus(Default) 1.105 + { } 1.106 + 1.107 + ~WebGLRefCountedObject() { 1.108 + MOZ_ASSERT(mWebGLRefCnt == 0, "destroying WebGL object still referenced by other WebGL objects"); 1.109 + MOZ_ASSERT(mDeletionStatus == Deleted, "Derived class destructor must call DeleteOnce()"); 1.110 + } 1.111 + 1.112 + // called by WebGLRefPtr 1.113 + void WebGLAddRef() { 1.114 + ++mWebGLRefCnt; 1.115 + } 1.116 + 1.117 + // called by WebGLRefPtr 1.118 + void WebGLRelease() { 1.119 + MOZ_ASSERT(mWebGLRefCnt > 0, "releasing WebGL object with WebGL refcnt already zero"); 1.120 + --mWebGLRefCnt; 1.121 + MaybeDelete(); 1.122 + } 1.123 + 1.124 + // this is the function that WebGL.deleteXxx() functions want to call 1.125 + void RequestDelete() { 1.126 + if (mDeletionStatus == Default) 1.127 + mDeletionStatus = DeleteRequested; 1.128 + MaybeDelete(); 1.129 + } 1.130 + 1.131 + bool IsDeleted() const { 1.132 + return mDeletionStatus == Deleted; 1.133 + } 1.134 + 1.135 + bool IsDeleteRequested() const { 1.136 + return mDeletionStatus != Default; 1.137 + } 1.138 + 1.139 + void DeleteOnce() { 1.140 + if (mDeletionStatus != Deleted) { 1.141 + static_cast<Derived*>(this)->Delete(); 1.142 + mDeletionStatus = Deleted; 1.143 + } 1.144 + } 1.145 + 1.146 +private: 1.147 + void MaybeDelete() { 1.148 + if (mWebGLRefCnt == 0 && 1.149 + mDeletionStatus == DeleteRequested) 1.150 + { 1.151 + DeleteOnce(); 1.152 + } 1.153 + } 1.154 + 1.155 +protected: 1.156 + nsAutoRefCnt mWebGLRefCnt; 1.157 + DeletionStatus mDeletionStatus; 1.158 +}; 1.159 + 1.160 +/* This WebGLRefPtr class is meant to be used for references between WebGL objects. 1.161 + * For example, a WebGLProgram holds WebGLRefPtr's to the WebGLShader's attached 1.162 + * to it. 1.163 + * 1.164 + * Why the need for a separate refptr class? The only special thing that WebGLRefPtr 1.165 + * does is that it increments and decrements the WebGL refcount of 1.166 + * WebGLRefCountedObject's, in addition to incrementing and decrementing the 1.167 + * usual XPCOM refcount. 1.168 + * 1.169 + * This means that by using a WebGLRefPtr instead of a nsRefPtr, you ensure that 1.170 + * the WebGL refcount is incremented, which means that the object will be kept 1.171 + * alive by this reference even if the matching webgl.deleteXxx() function is 1.172 + * called on it. 1.173 + */ 1.174 +template<typename T> 1.175 +class WebGLRefPtr 1.176 +{ 1.177 +public: 1.178 + WebGLRefPtr() 1.179 + : mRawPtr(0) 1.180 + { } 1.181 + 1.182 + WebGLRefPtr(const WebGLRefPtr<T>& aSmartPtr) 1.183 + : mRawPtr(aSmartPtr.mRawPtr) 1.184 + { 1.185 + AddRefOnPtr(mRawPtr); 1.186 + } 1.187 + 1.188 + WebGLRefPtr(T *aRawPtr) 1.189 + : mRawPtr(aRawPtr) 1.190 + { 1.191 + AddRefOnPtr(mRawPtr); 1.192 + } 1.193 + 1.194 + ~WebGLRefPtr() { 1.195 + ReleasePtr(mRawPtr); 1.196 + } 1.197 + 1.198 + WebGLRefPtr<T>& 1.199 + operator=(const WebGLRefPtr<T>& rhs) 1.200 + { 1.201 + assign_with_AddRef(rhs.mRawPtr); 1.202 + return *this; 1.203 + } 1.204 + 1.205 + WebGLRefPtr<T>& 1.206 + operator=(T* rhs) 1.207 + { 1.208 + assign_with_AddRef(rhs); 1.209 + return *this; 1.210 + } 1.211 + 1.212 + T* get() const { 1.213 + return static_cast<T*>(mRawPtr); 1.214 + } 1.215 + 1.216 + operator T*() const { 1.217 + return get(); 1.218 + } 1.219 + 1.220 + T* operator->() const { 1.221 + MOZ_ASSERT(mRawPtr != 0, "You can't dereference a nullptr WebGLRefPtr with operator->()!"); 1.222 + return get(); 1.223 + } 1.224 + 1.225 + T& operator*() const { 1.226 + MOZ_ASSERT(mRawPtr != 0, "You can't dereference a nullptr WebGLRefPtr with operator*()!"); 1.227 + return *get(); 1.228 + } 1.229 + 1.230 +private: 1.231 + 1.232 + static void AddRefOnPtr(T* rawPtr) { 1.233 + if (rawPtr) { 1.234 + rawPtr->WebGLAddRef(); 1.235 + rawPtr->AddRef(); 1.236 + } 1.237 + } 1.238 + 1.239 + static void ReleasePtr(T* rawPtr) { 1.240 + if (rawPtr) { 1.241 + rawPtr->WebGLRelease(); // must be done first before Release(), as Release() might actually destroy the object 1.242 + rawPtr->Release(); 1.243 + } 1.244 + } 1.245 + 1.246 + void assign_with_AddRef(T* rawPtr) { 1.247 + AddRefOnPtr(rawPtr); 1.248 + assign_assuming_AddRef(rawPtr); 1.249 + } 1.250 + 1.251 + void assign_assuming_AddRef(T* newPtr) { 1.252 + T* oldPtr = mRawPtr; 1.253 + mRawPtr = newPtr; 1.254 + ReleasePtr(oldPtr); 1.255 + } 1.256 + 1.257 +protected: 1.258 + T *mRawPtr; 1.259 +}; 1.260 + 1.261 +// This class is a mixin for objects that are tied to a specific 1.262 +// context (which is to say, all of them). They provide initialization 1.263 +// as well as comparison with the current context. 1.264 +class WebGLContextBoundObject 1.265 +{ 1.266 +public: 1.267 + WebGLContextBoundObject(WebGLContext *context); 1.268 + 1.269 + bool IsCompatibleWithContext(WebGLContext *other); 1.270 + 1.271 + WebGLContext *Context() const { return mContext; } 1.272 + 1.273 +protected: 1.274 + WebGLContext *mContext; 1.275 + uint32_t mContextGeneration; 1.276 +}; 1.277 + 1.278 +// this class is a mixin for GL objects that have dimensions 1.279 +// that we need to track. 1.280 +class WebGLRectangleObject 1.281 +{ 1.282 +public: 1.283 + WebGLRectangleObject() 1.284 + : mWidth(0), mHeight(0) { } 1.285 + 1.286 + WebGLRectangleObject(GLsizei width, GLsizei height) 1.287 + : mWidth(width), mHeight(height) { } 1.288 + 1.289 + GLsizei Width() const { return mWidth; } 1.290 + void width(GLsizei value) { mWidth = value; } 1.291 + 1.292 + GLsizei Height() const { return mHeight; } 1.293 + void height(GLsizei value) { mHeight = value; } 1.294 + 1.295 + void setDimensions(GLsizei width, GLsizei height) { 1.296 + mWidth = width; 1.297 + mHeight = height; 1.298 + } 1.299 + 1.300 + void setDimensions(WebGLRectangleObject *rect) { 1.301 + if (rect) { 1.302 + mWidth = rect->Width(); 1.303 + mHeight = rect->Height(); 1.304 + } else { 1.305 + mWidth = 0; 1.306 + mHeight = 0; 1.307 + } 1.308 + } 1.309 + 1.310 + bool HasSameDimensionsAs(const WebGLRectangleObject& other) const { 1.311 + return Width() == other.Width() && Height() == other.Height(); 1.312 + } 1.313 + 1.314 +protected: 1.315 + GLsizei mWidth; 1.316 + GLsizei mHeight; 1.317 +}; 1.318 + 1.319 +}// namespace mozilla 1.320 + 1.321 +template <typename T> 1.322 +inline void 1.323 +ImplCycleCollectionUnlink(mozilla::WebGLRefPtr<T>& aField) 1.324 +{ 1.325 + aField = nullptr; 1.326 +} 1.327 + 1.328 +template <typename T> 1.329 +inline void 1.330 +ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, 1.331 + mozilla::WebGLRefPtr<T>& aField, 1.332 + const char* aName, 1.333 + uint32_t aFlags = 0) 1.334 +{ 1.335 + CycleCollectionNoteChild(aCallback, aField.get(), aName, aFlags); 1.336 +} 1.337 + 1.338 +#endif