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