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 WEBGLTEXTURE_H_ michael@0: #define WEBGLTEXTURE_H_ michael@0: michael@0: #include "WebGLObjectModel.h" michael@0: #include "WebGLFramebufferAttachable.h" michael@0: michael@0: #include "nsWrapperCache.h" michael@0: michael@0: #include "mozilla/LinkedList.h" michael@0: #include michael@0: michael@0: namespace mozilla { michael@0: michael@0: // Zero is not an integer power of two. michael@0: inline bool is_pot_assuming_nonnegative(GLsizei x) michael@0: { michael@0: return x && (x & (x-1)) == 0; michael@0: } michael@0: michael@0: // NOTE: When this class is switched to new DOM bindings, update the (then-slow) michael@0: // WrapObject calls in GetParameter and GetFramebufferAttachmentParameter. michael@0: class WebGLTexture MOZ_FINAL michael@0: : public nsWrapperCache michael@0: , public WebGLRefCountedObject michael@0: , public LinkedListElement michael@0: , public WebGLContextBoundObject michael@0: , public WebGLFramebufferAttachable michael@0: { michael@0: public: michael@0: WebGLTexture(WebGLContext *context); michael@0: michael@0: ~WebGLTexture() { michael@0: DeleteOnce(); michael@0: } michael@0: michael@0: void Delete(); michael@0: michael@0: bool HasEverBeenBound() const { return mHasEverBeenBound; } michael@0: void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; } michael@0: GLuint GLName() const { return mGLName; } michael@0: GLenum Target() const { return mTarget; } michael@0: michael@0: WebGLContext *GetParentObject() const { michael@0: return Context(); michael@0: } michael@0: michael@0: virtual JSObject* WrapObject(JSContext *cx) MOZ_OVERRIDE; michael@0: michael@0: NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLTexture) michael@0: NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLTexture) michael@0: michael@0: protected: michael@0: michael@0: friend class WebGLContext; michael@0: friend class WebGLFramebuffer; michael@0: michael@0: bool mHasEverBeenBound; michael@0: GLuint mGLName; michael@0: michael@0: // we store information about the various images that are part of michael@0: // this texture (cubemap faces, mipmap levels) michael@0: michael@0: public: michael@0: michael@0: class ImageInfo michael@0: : public WebGLRectangleObject michael@0: { michael@0: public: michael@0: ImageInfo() michael@0: : mWebGLFormat(LOCAL_GL_NONE) michael@0: , mWebGLType(LOCAL_GL_NONE) michael@0: , mImageDataStatus(WebGLImageDataStatus::NoImageData) michael@0: {} michael@0: michael@0: ImageInfo(GLsizei width, michael@0: GLsizei height, michael@0: GLenum webGLFormat, michael@0: GLenum webGLType, michael@0: WebGLImageDataStatus status) michael@0: : WebGLRectangleObject(width, height) michael@0: , mWebGLFormat(webGLFormat) michael@0: , mWebGLType(webGLType) michael@0: , mImageDataStatus(status) michael@0: { michael@0: // shouldn't use this constructor to construct a null ImageInfo michael@0: MOZ_ASSERT(status != WebGLImageDataStatus::NoImageData); michael@0: } michael@0: michael@0: bool operator==(const ImageInfo& a) const { michael@0: return mImageDataStatus == a.mImageDataStatus && michael@0: mWidth == a.mWidth && michael@0: mHeight == a.mHeight && michael@0: mWebGLFormat == a.mWebGLFormat && michael@0: mWebGLType == a.mWebGLType; michael@0: } michael@0: bool operator!=(const ImageInfo& a) const { michael@0: return !(*this == a); michael@0: } michael@0: bool IsSquare() const { michael@0: return mWidth == mHeight; michael@0: } michael@0: bool IsPositive() const { michael@0: return mWidth > 0 && mHeight > 0; michael@0: } michael@0: bool IsPowerOfTwo() const { michael@0: return is_pot_assuming_nonnegative(mWidth) && michael@0: is_pot_assuming_nonnegative(mHeight); // negative sizes should never happen (caught in texImage2D...) michael@0: } michael@0: bool HasUninitializedImageData() const { michael@0: return mImageDataStatus == WebGLImageDataStatus::UninitializedImageData; michael@0: } michael@0: int64_t MemoryUsage() const; michael@0: /*! This is the format passed from JS to WebGL. michael@0: * It can be converted to a value to be passed to driver with michael@0: * DriverFormatsFromFormatAndType(). michael@0: */ michael@0: GLenum WebGLFormat() const { return mWebGLFormat; } michael@0: /*! This is the type passed from JS to WebGL. michael@0: * It can be converted to a value to be passed to driver with michael@0: * DriverTypeFromType(). michael@0: */ michael@0: GLenum WebGLType() const { return mWebGLType; } michael@0: michael@0: protected: michael@0: GLenum mWebGLFormat; //!< This is the WebGL/GLES format michael@0: GLenum mWebGLType; //!< This is the WebGL/GLES type michael@0: WebGLImageDataStatus mImageDataStatus; michael@0: michael@0: friend class WebGLTexture; michael@0: }; michael@0: michael@0: private: michael@0: static size_t FaceForTarget(GLenum target) { michael@0: // Call this out explicitly: michael@0: MOZ_ASSERT(target != LOCAL_GL_TEXTURE_CUBE_MAP); michael@0: MOZ_ASSERT(target == LOCAL_GL_TEXTURE_2D || michael@0: (target >= LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X && michael@0: target <= LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)); michael@0: return target == LOCAL_GL_TEXTURE_2D ? 0 : target - LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X; michael@0: } michael@0: michael@0: ImageInfo& ImageInfoAtFace(size_t face, GLint level) { michael@0: MOZ_ASSERT(face < mFacesCount, "wrong face index, must be 0 for TEXTURE_2D and at most 5 for cube maps"); michael@0: michael@0: // no need to check level as a wrong value would be caught by ElementAt(). michael@0: return mImageInfos.ElementAt(level * mFacesCount + face); michael@0: } michael@0: michael@0: const ImageInfo& ImageInfoAtFace(size_t face, GLint level) const { michael@0: return const_cast( michael@0: const_cast(this)->ImageInfoAtFace(face, level) michael@0: ); michael@0: } michael@0: michael@0: public: michael@0: ImageInfo& ImageInfoAt(GLenum imageTarget, GLint level) { michael@0: MOZ_ASSERT(imageTarget); michael@0: michael@0: size_t face = FaceForTarget(imageTarget); michael@0: return ImageInfoAtFace(face, level); michael@0: } michael@0: michael@0: const ImageInfo& ImageInfoAt(GLenum imageTarget, GLint level) const { michael@0: return const_cast(this)->ImageInfoAt(imageTarget, level); michael@0: } michael@0: michael@0: bool HasImageInfoAt(GLenum imageTarget, GLint level) const { michael@0: MOZ_ASSERT(imageTarget); michael@0: michael@0: size_t face = FaceForTarget(imageTarget); michael@0: CheckedUint32 checked_index = CheckedUint32(level) * mFacesCount + face; michael@0: return checked_index.isValid() && michael@0: checked_index.value() < mImageInfos.Length() && michael@0: ImageInfoAt(imageTarget, level).mImageDataStatus != WebGLImageDataStatus::NoImageData; michael@0: } michael@0: michael@0: ImageInfo& ImageInfoBase() { michael@0: return ImageInfoAtFace(0, 0); michael@0: } michael@0: michael@0: const ImageInfo& ImageInfoBase() const { michael@0: return ImageInfoAtFace(0, 0); michael@0: } michael@0: michael@0: int64_t MemoryUsage() const; michael@0: michael@0: void SetImageDataStatus(GLenum imageTarget, GLint level, WebGLImageDataStatus newStatus) { michael@0: MOZ_ASSERT(HasImageInfoAt(imageTarget, level)); michael@0: ImageInfo& imageInfo = ImageInfoAt(imageTarget, level); michael@0: // there is no way to go from having image data to not having any michael@0: MOZ_ASSERT(newStatus != WebGLImageDataStatus::NoImageData || michael@0: imageInfo.mImageDataStatus == WebGLImageDataStatus::NoImageData); michael@0: if (imageInfo.mImageDataStatus != newStatus) { michael@0: SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); michael@0: } michael@0: imageInfo.mImageDataStatus = newStatus; michael@0: } michael@0: michael@0: void DoDeferredImageInitialization(GLenum imageTarget, GLint level); michael@0: michael@0: protected: michael@0: michael@0: GLenum mTarget; michael@0: GLenum mMinFilter, mMagFilter, mWrapS, mWrapT; michael@0: michael@0: size_t mFacesCount, mMaxLevelWithCustomImages; michael@0: nsTArray mImageInfos; michael@0: michael@0: bool mHaveGeneratedMipmap; michael@0: WebGLTextureFakeBlackStatus mFakeBlackStatus; michael@0: michael@0: void EnsureMaxLevelWithCustomImagesAtLeast(size_t aMaxLevelWithCustomImages) { michael@0: mMaxLevelWithCustomImages = std::max(mMaxLevelWithCustomImages, aMaxLevelWithCustomImages); michael@0: mImageInfos.EnsureLengthAtLeast((mMaxLevelWithCustomImages + 1) * mFacesCount); michael@0: } michael@0: michael@0: bool CheckFloatTextureFilterParams() const { michael@0: // Without OES_texture_float_linear, only NEAREST and NEAREST_MIMPAMP_NEAREST are supported michael@0: return (mMagFilter == LOCAL_GL_NEAREST) && michael@0: (mMinFilter == LOCAL_GL_NEAREST || mMinFilter == LOCAL_GL_NEAREST_MIPMAP_NEAREST); michael@0: } michael@0: michael@0: bool AreBothWrapModesClampToEdge() const { michael@0: return mWrapS == LOCAL_GL_CLAMP_TO_EDGE && mWrapT == LOCAL_GL_CLAMP_TO_EDGE; michael@0: } michael@0: michael@0: bool DoesTexture2DMipmapHaveAllLevelsConsistentlyDefined(GLenum texImageTarget) const; michael@0: michael@0: public: michael@0: michael@0: void Bind(GLenum aTarget); michael@0: michael@0: void SetImageInfo(GLenum aTarget, GLint aLevel, michael@0: GLsizei aWidth, GLsizei aHeight, michael@0: GLenum aFormat, GLenum aType, WebGLImageDataStatus aStatus); michael@0: michael@0: void SetMinFilter(GLenum aMinFilter) { michael@0: mMinFilter = aMinFilter; michael@0: SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); michael@0: } michael@0: void SetMagFilter(GLenum aMagFilter) { michael@0: mMagFilter = aMagFilter; michael@0: SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); michael@0: } michael@0: void SetWrapS(GLenum aWrapS) { michael@0: mWrapS = aWrapS; michael@0: SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); michael@0: } michael@0: void SetWrapT(GLenum aWrapT) { michael@0: mWrapT = aWrapT; michael@0: SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); michael@0: } michael@0: GLenum MinFilter() const { return mMinFilter; } michael@0: michael@0: bool DoesMinFilterRequireMipmap() const { michael@0: return !(mMinFilter == LOCAL_GL_NEAREST || mMinFilter == LOCAL_GL_LINEAR); michael@0: } michael@0: michael@0: void SetGeneratedMipmap(); michael@0: michael@0: void SetCustomMipmap(); michael@0: michael@0: bool IsFirstImagePowerOfTwo() const { michael@0: return ImageInfoBase().IsPowerOfTwo(); michael@0: } michael@0: michael@0: bool AreAllLevel0ImageInfosEqual() const; michael@0: michael@0: bool IsMipmapTexture2DComplete() const; michael@0: michael@0: bool IsCubeComplete() const; michael@0: michael@0: bool IsMipmapCubeComplete() const; michael@0: michael@0: void SetFakeBlackStatus(WebGLTextureFakeBlackStatus x) { michael@0: mFakeBlackStatus = x; michael@0: mContext->SetFakeBlackStatus(WebGLContextFakeBlackStatus::Unknown); michael@0: } michael@0: // Returns the current fake-black-status, except if it was Unknown, michael@0: // in which case this function resolves it first, so it never returns Unknown. michael@0: WebGLTextureFakeBlackStatus ResolvedFakeBlackStatus(); michael@0: }; michael@0: michael@0: } // namespace mozilla michael@0: michael@0: #endif