michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef WEBGLOBJECTMODEL_H_ michael@0: #define WEBGLOBJECTMODEL_H_ michael@0: michael@0: #include "nsCycleCollectionNoteChild.h" michael@0: #include "nsICanvasRenderingContextInternal.h" michael@0: #include "WebGLTypes.h" michael@0: michael@0: namespace mozilla { michael@0: michael@0: class WebGLBuffer; michael@0: class WebGLContext; michael@0: michael@0: /* Each WebGL object class WebGLFoo wants to: michael@0: * - inherit WebGLRefCountedObject michael@0: * - implement a Delete() method michael@0: * - have its destructor call DeleteOnce() michael@0: * michael@0: * This base class provides two features to WebGL object types: michael@0: * 1. support for OpenGL object reference counting michael@0: * 2. support for OpenGL deletion statuses michael@0: * michael@0: ***** 1. OpenGL object reference counting ***** michael@0: * michael@0: * WebGL objects such as WebGLTexture's really have two different refcounts: michael@0: * the XPCOM refcount, that is directly exposed to JavaScript, and the OpenGL michael@0: * refcount. michael@0: * michael@0: * For example, when in JavaScript one does: var newname = existingTexture; michael@0: * that increments the XPCOM refcount, but doesn't affect the OpenGL refcount. michael@0: * When one attaches the texture to a framebuffer object, that does increment michael@0: * its OpenGL refcount (and also its XPCOM refcount, to prevent the regular michael@0: * XPCOM refcounting mechanism from destroying objects prematurely). michael@0: * michael@0: * The actual OpenGL refcount is opaque to us (it's internal to the OpenGL michael@0: * implementation) but is affects the WebGL semantics that we have to implement: michael@0: * for example, a WebGLTexture that is attached to a WebGLFramebuffer must not michael@0: * be actually deleted, even if deleteTexture has been called on it, and even michael@0: * if JavaScript doesn't have references to it anymore. We can't just rely on michael@0: * OpenGL to keep alive the underlying OpenGL texture for us, for a variety of michael@0: * reasons, most importantly: we'd need to know when OpenGL objects are actually michael@0: * deleted, and OpenGL doesn't notify us about that, so we would have to query michael@0: * status very often with glIsXxx calls which isn't practical. michael@0: * michael@0: * This means that we have to keep track of the OpenGL refcount ourselves, michael@0: * in addition to the XPCOM refcount. michael@0: * michael@0: * This class implements such a refcount, see the mWebGLRefCnt michael@0: * member. In order to avoid name clashes (with regular XPCOM refcounting) michael@0: * in the derived class, we prefix members with 'WebGL', whence the names michael@0: * WebGLAddRef, WebGLRelease, etc. michael@0: * michael@0: * In practice, WebGLAddRef and WebGLRelease are only called from the michael@0: * WebGLRefPtr class. michael@0: * michael@0: ***** 2. OpenGL deletion statuses ***** michael@0: * michael@0: * In OpenGL, an object can go through 3 different deletion statuses during its michael@0: * lifetime, which correspond to the 3 enum values for DeletionStatus in this class: michael@0: * - the Default status, which it has from its creation to when the michael@0: * suitable glDeleteXxx function is called on it; michael@0: * - the DeleteRequested status, which is has from when the suitable glDeleteXxx michael@0: * function is called on it to when it is no longer referenced by other OpenGL michael@0: * objects. For example, a texture that is attached to a non-current FBO michael@0: * will enter that status when glDeleteTexture is called on it. For objects michael@0: * with that status, GL_DELETE_STATUS queries return true, but glIsXxx michael@0: * functions still return true. michael@0: * - the Deleted status, which is the status of objects on which the michael@0: * suitable glDeleteXxx function has been called, and that are not referenced michael@0: * by other OpenGL objects. michael@0: * michael@0: * This state is stored in the mDeletionStatus member of this class. michael@0: * michael@0: * When the GL refcount hits zero, if the status is DeleteRequested then we call michael@0: * the Delete() method on the derived class and the status becomes Deleted. This is michael@0: * what the MaybeDelete() function does. michael@0: * michael@0: * The DeleteOnce() function implemented here is a helper to ensure that we don't michael@0: * call Delete() twice on the same object. Since the derived class' destructor michael@0: * needs to call DeleteOnce() which calls Delete(), we can't allow either to be michael@0: * virtual. Strictly speaking, we could let them be virtual if the derived class michael@0: * were final, but that would be impossible to enforce and would lead to strange michael@0: * bugs if it were subclassed. michael@0: * michael@0: * This WebGLRefCountedObject class takes the Derived type michael@0: * as template parameter, as a means to allow DeleteOnce to call Delete() michael@0: * on the Derived class, without either method being virtual. This is a common michael@0: * C++ pattern known as the "curiously recursive template pattern (CRTP)". michael@0: */ michael@0: template michael@0: class WebGLRefCountedObject michael@0: { michael@0: public: michael@0: enum DeletionStatus { Default, DeleteRequested, Deleted }; michael@0: michael@0: WebGLRefCountedObject() michael@0: : mDeletionStatus(Default) michael@0: { } michael@0: michael@0: ~WebGLRefCountedObject() { michael@0: MOZ_ASSERT(mWebGLRefCnt == 0, "destroying WebGL object still referenced by other WebGL objects"); michael@0: MOZ_ASSERT(mDeletionStatus == Deleted, "Derived class destructor must call DeleteOnce()"); michael@0: } michael@0: michael@0: // called by WebGLRefPtr michael@0: void WebGLAddRef() { michael@0: ++mWebGLRefCnt; michael@0: } michael@0: michael@0: // called by WebGLRefPtr michael@0: void WebGLRelease() { michael@0: MOZ_ASSERT(mWebGLRefCnt > 0, "releasing WebGL object with WebGL refcnt already zero"); michael@0: --mWebGLRefCnt; michael@0: MaybeDelete(); michael@0: } michael@0: michael@0: // this is the function that WebGL.deleteXxx() functions want to call michael@0: void RequestDelete() { michael@0: if (mDeletionStatus == Default) michael@0: mDeletionStatus = DeleteRequested; michael@0: MaybeDelete(); michael@0: } michael@0: michael@0: bool IsDeleted() const { michael@0: return mDeletionStatus == Deleted; michael@0: } michael@0: michael@0: bool IsDeleteRequested() const { michael@0: return mDeletionStatus != Default; michael@0: } michael@0: michael@0: void DeleteOnce() { michael@0: if (mDeletionStatus != Deleted) { michael@0: static_cast(this)->Delete(); michael@0: mDeletionStatus = Deleted; michael@0: } michael@0: } michael@0: michael@0: private: michael@0: void MaybeDelete() { michael@0: if (mWebGLRefCnt == 0 && michael@0: mDeletionStatus == DeleteRequested) michael@0: { michael@0: DeleteOnce(); michael@0: } michael@0: } michael@0: michael@0: protected: michael@0: nsAutoRefCnt mWebGLRefCnt; michael@0: DeletionStatus mDeletionStatus; michael@0: }; michael@0: michael@0: /* This WebGLRefPtr class is meant to be used for references between WebGL objects. michael@0: * For example, a WebGLProgram holds WebGLRefPtr's to the WebGLShader's attached michael@0: * to it. michael@0: * michael@0: * Why the need for a separate refptr class? The only special thing that WebGLRefPtr michael@0: * does is that it increments and decrements the WebGL refcount of michael@0: * WebGLRefCountedObject's, in addition to incrementing and decrementing the michael@0: * usual XPCOM refcount. michael@0: * michael@0: * This means that by using a WebGLRefPtr instead of a nsRefPtr, you ensure that michael@0: * the WebGL refcount is incremented, which means that the object will be kept michael@0: * alive by this reference even if the matching webgl.deleteXxx() function is michael@0: * called on it. michael@0: */ michael@0: template michael@0: class WebGLRefPtr michael@0: { michael@0: public: michael@0: WebGLRefPtr() michael@0: : mRawPtr(0) michael@0: { } michael@0: michael@0: WebGLRefPtr(const WebGLRefPtr& aSmartPtr) michael@0: : mRawPtr(aSmartPtr.mRawPtr) michael@0: { michael@0: AddRefOnPtr(mRawPtr); michael@0: } michael@0: michael@0: WebGLRefPtr(T *aRawPtr) michael@0: : mRawPtr(aRawPtr) michael@0: { michael@0: AddRefOnPtr(mRawPtr); michael@0: } michael@0: michael@0: ~WebGLRefPtr() { michael@0: ReleasePtr(mRawPtr); michael@0: } michael@0: michael@0: WebGLRefPtr& michael@0: operator=(const WebGLRefPtr& rhs) michael@0: { michael@0: assign_with_AddRef(rhs.mRawPtr); michael@0: return *this; michael@0: } michael@0: michael@0: WebGLRefPtr& michael@0: operator=(T* rhs) michael@0: { michael@0: assign_with_AddRef(rhs); michael@0: return *this; michael@0: } michael@0: michael@0: T* get() const { michael@0: return static_cast(mRawPtr); michael@0: } michael@0: michael@0: operator T*() const { michael@0: return get(); michael@0: } michael@0: michael@0: T* operator->() const { michael@0: MOZ_ASSERT(mRawPtr != 0, "You can't dereference a nullptr WebGLRefPtr with operator->()!"); michael@0: return get(); michael@0: } michael@0: michael@0: T& operator*() const { michael@0: MOZ_ASSERT(mRawPtr != 0, "You can't dereference a nullptr WebGLRefPtr with operator*()!"); michael@0: return *get(); michael@0: } michael@0: michael@0: private: michael@0: michael@0: static void AddRefOnPtr(T* rawPtr) { michael@0: if (rawPtr) { michael@0: rawPtr->WebGLAddRef(); michael@0: rawPtr->AddRef(); michael@0: } michael@0: } michael@0: michael@0: static void ReleasePtr(T* rawPtr) { michael@0: if (rawPtr) { michael@0: rawPtr->WebGLRelease(); // must be done first before Release(), as Release() might actually destroy the object michael@0: rawPtr->Release(); michael@0: } michael@0: } michael@0: michael@0: void assign_with_AddRef(T* rawPtr) { michael@0: AddRefOnPtr(rawPtr); michael@0: assign_assuming_AddRef(rawPtr); michael@0: } michael@0: michael@0: void assign_assuming_AddRef(T* newPtr) { michael@0: T* oldPtr = mRawPtr; michael@0: mRawPtr = newPtr; michael@0: ReleasePtr(oldPtr); michael@0: } michael@0: michael@0: protected: michael@0: T *mRawPtr; michael@0: }; michael@0: michael@0: // This class is a mixin for objects that are tied to a specific michael@0: // context (which is to say, all of them). They provide initialization michael@0: // as well as comparison with the current context. michael@0: class WebGLContextBoundObject michael@0: { michael@0: public: michael@0: WebGLContextBoundObject(WebGLContext *context); michael@0: michael@0: bool IsCompatibleWithContext(WebGLContext *other); michael@0: michael@0: WebGLContext *Context() const { return mContext; } michael@0: michael@0: protected: michael@0: WebGLContext *mContext; michael@0: uint32_t mContextGeneration; michael@0: }; michael@0: michael@0: // this class is a mixin for GL objects that have dimensions michael@0: // that we need to track. michael@0: class WebGLRectangleObject michael@0: { michael@0: public: michael@0: WebGLRectangleObject() michael@0: : mWidth(0), mHeight(0) { } michael@0: michael@0: WebGLRectangleObject(GLsizei width, GLsizei height) michael@0: : mWidth(width), mHeight(height) { } michael@0: michael@0: GLsizei Width() const { return mWidth; } michael@0: void width(GLsizei value) { mWidth = value; } michael@0: michael@0: GLsizei Height() const { return mHeight; } michael@0: void height(GLsizei value) { mHeight = value; } michael@0: michael@0: void setDimensions(GLsizei width, GLsizei height) { michael@0: mWidth = width; michael@0: mHeight = height; michael@0: } michael@0: michael@0: void setDimensions(WebGLRectangleObject *rect) { michael@0: if (rect) { michael@0: mWidth = rect->Width(); michael@0: mHeight = rect->Height(); michael@0: } else { michael@0: mWidth = 0; michael@0: mHeight = 0; michael@0: } michael@0: } michael@0: michael@0: bool HasSameDimensionsAs(const WebGLRectangleObject& other) const { michael@0: return Width() == other.Width() && Height() == other.Height(); michael@0: } michael@0: michael@0: protected: michael@0: GLsizei mWidth; michael@0: GLsizei mHeight; michael@0: }; michael@0: michael@0: }// namespace mozilla michael@0: michael@0: template michael@0: inline void michael@0: ImplCycleCollectionUnlink(mozilla::WebGLRefPtr& aField) michael@0: { michael@0: aField = nullptr; michael@0: } michael@0: michael@0: template michael@0: inline void michael@0: ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, michael@0: mozilla::WebGLRefPtr& aField, michael@0: const char* aName, michael@0: uint32_t aFlags = 0) michael@0: { michael@0: CycleCollectionNoteChild(aCallback, aField.get(), aName, aFlags); michael@0: } michael@0: michael@0: #endif