--- a/dom/canvas/WebGL2Context.h
+++ b/dom/canvas/WebGL2Context.h
@@ -238,13 +238,19 @@ public:
void DeleteVertexArray(WebGLVertexArrayObject* vertexArray);
bool IsVertexArray(WebGLVertexArrayObject* vertexArray);
void BindVertexArray(WebGLVertexArrayObject* vertexArray);
*/
private:
WebGL2Context();
+
+ bool ValidateSizedInternalFormat(GLenum internalFormat, const char* info);
+ bool ValidateTexStorage(GLenum target, GLsizei levels, GLenum internalformat,
+ GLsizei width, GLsizei height, GLsizei depth,
+ const char* info);
+
};
} // namespace mozilla
#endif
--- a/dom/canvas/WebGL2ContextTextures.cpp
+++ b/dom/canvas/WebGL2ContextTextures.cpp
@@ -4,23 +4,177 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "WebGL2Context.h"
#include "GLContext.h"
using namespace mozilla;
using namespace mozilla::dom;
+bool
+WebGL2Context::ValidateSizedInternalFormat(GLenum internalformat, const char* info)
+{
+ switch (internalformat) {
+ // Sized Internal Formats
+ // https://www.khronos.org/opengles/sdk/docs/man3/html/glTexStorage2D.xhtml
+ case LOCAL_GL_R8:
+ case LOCAL_GL_R8_SNORM:
+ case LOCAL_GL_R16F:
+ case LOCAL_GL_R32F:
+ case LOCAL_GL_R8UI:
+ case LOCAL_GL_R8I:
+ case LOCAL_GL_R16UI:
+ case LOCAL_GL_R16I:
+ case LOCAL_GL_R32UI:
+ case LOCAL_GL_R32I:
+ case LOCAL_GL_RG8:
+ case LOCAL_GL_RG8_SNORM:
+ case LOCAL_GL_RG16F:
+ case LOCAL_GL_RG32F:
+ case LOCAL_GL_RG8UI:
+ case LOCAL_GL_RG8I:
+ case LOCAL_GL_RG16UI:
+ case LOCAL_GL_RG16I:
+ case LOCAL_GL_RG32UI:
+ case LOCAL_GL_RG32I:
+ case LOCAL_GL_RGB8:
+ case LOCAL_GL_SRGB8:
+ case LOCAL_GL_RGB565:
+ case LOCAL_GL_RGB8_SNORM:
+ case LOCAL_GL_R11F_G11F_B10F:
+ case LOCAL_GL_RGB9_E5:
+ case LOCAL_GL_RGB16F:
+ case LOCAL_GL_RGB32F:
+ case LOCAL_GL_RGB8UI:
+ case LOCAL_GL_RGB8I:
+ case LOCAL_GL_RGB16UI:
+ case LOCAL_GL_RGB16I:
+ case LOCAL_GL_RGB32UI:
+ case LOCAL_GL_RGB32I:
+ case LOCAL_GL_RGBA8:
+ case LOCAL_GL_SRGB8_ALPHA8:
+ case LOCAL_GL_RGBA8_SNORM:
+ case LOCAL_GL_RGB5_A1:
+ case LOCAL_GL_RGBA4:
+ case LOCAL_GL_RGB10_A2:
+ case LOCAL_GL_RGBA16F:
+ case LOCAL_GL_RGBA32F:
+ case LOCAL_GL_RGBA8UI:
+ case LOCAL_GL_RGBA8I:
+ case LOCAL_GL_RGB10_A2UI:
+ case LOCAL_GL_RGBA16UI:
+ case LOCAL_GL_RGBA16I:
+ case LOCAL_GL_RGBA32I:
+ case LOCAL_GL_RGBA32UI:
+ case LOCAL_GL_DEPTH_COMPONENT16:
+ case LOCAL_GL_DEPTH_COMPONENT24:
+ case LOCAL_GL_DEPTH_COMPONENT32F:
+ case LOCAL_GL_DEPTH24_STENCIL8:
+ case LOCAL_GL_DEPTH32F_STENCIL8:
+ return true;
+ }
+
+ if (IsCompressedTextureFormat(internalformat)) {
+ return true;
+ }
+
+ const char* name = EnumName(internalformat);
+ if (name && name[0] != '[')
+ ErrorInvalidEnum("%s: invalid internal format %s", info, name);
+ else
+ ErrorInvalidEnum("%s: invalid internal format 0x%04X", info, internalformat);
+
+ return false;
+}
+
+/** Validates parameters to texStorage{2D,3D} */
+bool
+WebGL2Context::ValidateTexStorage(GLenum target, GLsizei levels, GLenum internalformat,
+ GLsizei width, GLsizei height, GLsizei depth,
+ const char* info)
+{
+ // GL_INVALID_OPERATION is generated if the default texture object is curently bound to target.
+ WebGLTexture* tex = activeBoundTextureForTarget(target);
+ if (!tex) {
+ ErrorInvalidOperation("%s: no texture is bound to target %s", info, EnumName(target));
+ return false;
+ }
+
+ // GL_INVALID_OPERATION is generated if the texture object currently bound to target already has
+ // GL_TEXTURE_IMMUTABLE_FORMAT set to GL_TRUE.
+ if (tex->IsImmutable()) {
+ ErrorInvalidOperation("%s: texture bound to target %s is already immutable", info, EnumName(target));
+ return false;
+ }
+
+ // GL_INVALID_ENUM is generated if internalformat is not a valid sized internal format.
+ if (!ValidateSizedInternalFormat(internalformat, info))
+ return false;
+
+ // GL_INVALID_VALUE is generated if width, height or levels are less than 1.
+ if (width < 1) { ErrorInvalidValue("%s: width is < 1", info); return false; }
+ if (height < 1) { ErrorInvalidValue("%s: height is < 1", info); return false; }
+ if (depth < 1) { ErrorInvalidValue("%s: depth is < 1", info); return false; }
+ if (levels < 1) { ErrorInvalidValue("%s: levels is < 1", info); return false; }
+
+ // The following check via FloorLog2 only requires a depth value if target is TEXTURE_3D.
+ bool is3D = (target != LOCAL_GL_TEXTURE_3D);
+ if (!is3D)
+ depth = 1;
+
+ // GL_INVALID_OPERATION is generated if levels is greater than floor(log2(max(width, height, depth)))+1.
+ if (FloorLog2(std::max(std::max(width, height), depth))+1 < levels) {
+ ErrorInvalidOperation("%s: levels > floor(log2(max(width, height%s)))+1", info, is3D ? ", depth" : "");
+ return false;
+ }
+
+ return true;
+}
+
// -------------------------------------------------------------------------
// Texture objects
void
WebGL2Context::TexStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
{
- MOZ_CRASH("Not Implemented.");
+ if (IsContextLost())
+ return;
+
+ // GL_INVALID_ENUM is generated if target is not one of the accepted target enumerants.
+ if (target != LOCAL_GL_TEXTURE_2D && target != LOCAL_GL_TEXTURE_CUBE_MAP)
+ return ErrorInvalidEnum("texStorage2D: target is not TEXTURE_2D or TEXTURE_CUBE_MAP.");
+
+ if (!ValidateTexStorage(target, levels, internalformat, width, height, 1, "texStorage2D"))
+ return;
+
+ WebGLTexture* tex = activeBoundTextureForTarget(target);
+ tex->SetImmutable();
+
+ const size_t facesCount = (target == LOCAL_GL_TEXTURE_2D) ? 1 : 6;
+ GLsizei w = width;
+ GLsizei h = height;
+ for (size_t l = 0; l < size_t(levels); l++) {
+ for (size_t f = 0; f < facesCount; f++) {
+ TexImageTarget imageTarget = TexImageTargetForTargetAndFace(target, f);
+ // FIXME: SetImageInfo wants a type, to go with the internalformat that it stores.
+ // 'type' is deprecated by sized internalformats, which are how TexStorage works.
+ // We must fix WebGLTexture::ImageInfo to store an "effective internalformat",
+ // which in the present case is just the sized internalformat, and drop 'types'
+ // altogether. For now, we just pass LOCAL_GL_UNSIGNED_BYTE, which works For
+ // the most commonly used formats.
+ const GLenum type = LOCAL_GL_UNSIGNED_BYTE;
+ tex->SetImageInfo(imageTarget, l, w, h,
+ internalformat, type,
+ WebGLImageDataStatus::UninitializedImageData);
+ }
+ w = std::max(1, w/2);
+ h = std::max(1, h/2);
+ }
+
+ gl->fTexStorage2D(target, levels, internalformat, width, height);
}
void
WebGL2Context::TexStorage3D(GLenum target, GLsizei levels, GLenum internalformat,
GLsizei width, GLsizei height, GLsizei depth)
{
MOZ_CRASH("Not Implemented.");
}
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -390,16 +390,22 @@ WebGLContext::CopyTexSubImage2D_base(Tex
MakeContextCurrent();
WebGLTexture *tex = activeBoundTextureForTexImageTarget(texImageTarget);
if (!tex)
return ErrorInvalidOperation("%s: no texture is bound to this target");
+ if (tex->IsImmutable()) {
+ if (!sub) {
+ return ErrorInvalidOperation("copyTexImage2D: disallowed because the texture bound to this target has already been made immutable by texStorage2D");
+ }
+ }
+
if (CanvasUtils::CheckSaneSubrectSize(x, y, width, height, framebufferWidth, framebufferHeight)) {
if (sub)
gl->fCopyTexSubImage2D(texImageTarget.get(), level, xoffset, yoffset, x, y, width, height);
else
gl->fCopyTexImage2D(texImageTarget.get(), level, internalformat, x, y, width, height, 0);
} else {
// the rect doesn't fit in the framebuffer
@@ -3331,20 +3337,27 @@ WebGLContext::CompressedTexImage2D(GLenu
if (!ValidateCompTexImageSize(level, internalformat, 0, 0, width, height, width, height, func))
{
return;
}
const TexImageTarget texImageTarget(rawTexImgTarget);
+ WebGLTexture* tex = activeBoundTextureForTexImageTarget(texImageTarget);
+ MOZ_ASSERT(tex);
+ if (tex->IsImmutable()) {
+ return ErrorInvalidOperation(
+ "compressedTexImage2D: disallowed because the texture bound to "
+ "this target has already been made immutable by texStorage2D");
+ }
+
MakeContextCurrent();
gl->fCompressedTexImage2D(texImageTarget.get(), level, internalformat, width, height, border, byteLength, view.Data());
- WebGLTexture* tex = activeBoundTextureForTexImageTarget(texImageTarget);
- MOZ_ASSERT(tex);
+
tex->SetImageInfo(texImageTarget, level, width, height, internalformat, LOCAL_GL_UNSIGNED_BYTE,
WebGLImageDataStatus::InitializedImageData);
}
void
WebGLContext::CompressedTexSubImage2D(GLenum rawTexImgTarget, GLint level, GLint xoffset,
GLint yoffset, GLsizei width, GLsizei height,
GLenum internalformat,
@@ -3682,16 +3695,21 @@ WebGLContext::TexImage2D_base(TexImageTa
return ErrorInvalidOperation("texImage2D: not enough data for operation (need %d, have %d)",
bytesNeeded, byteLength);
WebGLTexture *tex = activeBoundTextureForTexImageTarget(texImageTarget);
if (!tex)
return ErrorInvalidOperation("texImage2D: no texture is bound to this target");
+ if (tex->IsImmutable()) {
+ return ErrorInvalidOperation(
+ "texImage2D: disallowed because the texture "
+ "bound to this target has already been made immutable by texStorage2D");
+ }
MakeContextCurrent();
nsAutoArrayPtr<uint8_t> convertedData;
void* pixels = nullptr;
WebGLImageDataStatus imageInfoStatusIfSuccess = WebGLImageDataStatus::UninitializedImageData;
if (byteLength) {
size_t srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value();
--- a/dom/canvas/WebGLContextUtils.cpp
+++ b/dom/canvas/WebGLContextUtils.cpp
@@ -501,16 +501,26 @@ WebGLContext::IsCompressedTextureFormat(
case LOCAL_GL_ATC_RGB:
case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA:
case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA:
case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1:
case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1:
case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1:
case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1:
case LOCAL_GL_ETC1_RGB8_OES:
+ case LOCAL_GL_COMPRESSED_R11_EAC:
+ case LOCAL_GL_COMPRESSED_SIGNED_R11_EAC:
+ case LOCAL_GL_COMPRESSED_RG11_EAC:
+ case LOCAL_GL_COMPRESSED_SIGNED_RG11_EAC:
+ case LOCAL_GL_COMPRESSED_RGB8_ETC2:
+ case LOCAL_GL_COMPRESSED_SRGB8_ETC2:
+ case LOCAL_GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
+ case LOCAL_GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
+ case LOCAL_GL_COMPRESSED_RGBA8_ETC2_EAC:
+ case LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
return true;
default:
return false;
}
}
bool
--- a/dom/canvas/WebGLTexture.cpp
+++ b/dom/canvas/WebGLTexture.cpp
@@ -27,16 +27,17 @@ WebGLTexture::WebGLTexture(WebGLContext
, WebGLContextBoundObject(context)
, mMinFilter(LOCAL_GL_NEAREST_MIPMAP_LINEAR)
, mMagFilter(LOCAL_GL_LINEAR)
, mWrapS(LOCAL_GL_REPEAT)
, mWrapT(LOCAL_GL_REPEAT)
, mFacesCount(0)
, mMaxLevelWithCustomImages(0)
, mHaveGeneratedMipmap(false)
+ , mImmutable(false)
, mFakeBlackStatus(WebGLTextureFakeBlackStatus::IncompleteTexture)
{
SetIsDOMBinding();
mContext->MakeContextCurrent();
mContext->gl->fGenTextures(1, &mGLName);
mContext->mTextures.insertBack(this);
}
@@ -222,29 +223,22 @@ WebGLTexture::IsCubeComplete() const {
if (mTarget != LOCAL_GL_TEXTURE_CUBE_MAP)
return false;
const ImageInfo &first = ImageInfoAt(LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0);
if (!first.IsPositive() || !first.IsSquare())
return false;
return AreAllLevel0ImageInfosEqual();
}
-static TexImageTarget
-GLCubeMapFaceById(int id)
-{
- // Correctness is checked by the constructor for TexImageTarget
- return TexImageTarget(LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + id);
-}
-
bool
WebGLTexture::IsMipmapCubeComplete() const {
if (!IsCubeComplete()) // in particular, this checks that this is a cube map
return false;
for (int i = 0; i < 6; i++) {
- const TexImageTarget face = GLCubeMapFaceById(i);
+ const TexImageTarget face = TexImageTargetForTargetAndFace(LOCAL_GL_TEXTURE_CUBE_MAP, i);
if (!DoesTexture2DMipmapHaveAllLevelsConsistentlyDefined(face))
return false;
}
return true;
}
WebGLTextureFakeBlackStatus
WebGLTexture::ResolvedFakeBlackStatus() {
@@ -404,19 +398,17 @@ WebGLTexture::ResolvedFakeBlackStatus()
if (hasAnyInitializedImageData) {
// The texture contains some initialized image data, and some uninitialized image data.
// In this case, we have no choice but to initialize all image data now. Fortunately,
// in this case we know that we can't be dealing with a depth texture per WEBGL_depth_texture
// and ANGLE_depth_texture (which allow only one image per texture) so we can assume that
// glTexImage2D is able to upload data to images.
for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) {
for (size_t face = 0; face < mFacesCount; ++face) {
- TexImageTarget imageTarget = mTarget == LOCAL_GL_TEXTURE_2D
- ? LOCAL_GL_TEXTURE_2D
- : LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + face;
+ TexImageTarget imageTarget = TexImageTargetForTargetAndFace(mTarget, face);
const ImageInfo& imageInfo = ImageInfoAt(imageTarget, level);
if (imageInfo.mImageDataStatus == WebGLImageDataStatus::UninitializedImageData) {
DoDeferredImageInitialization(imageTarget, level);
}
}
}
mFakeBlackStatus = WebGLTextureFakeBlackStatus::NotNeeded;
} else {
--- a/dom/canvas/WebGLTexture.h
+++ b/dom/canvas/WebGLTexture.h
@@ -197,17 +197,19 @@ protected:
TexMinFilter mMinFilter;
TexMagFilter mMagFilter;
TexWrap mWrapS, mWrapT;
size_t mFacesCount, mMaxLevelWithCustomImages;
nsTArray<ImageInfo> mImageInfos;
- bool mHaveGeneratedMipmap;
+ bool mHaveGeneratedMipmap; // set by generateMipmap
+ bool mImmutable; // set by texStorage*
+
WebGLTextureFakeBlackStatus mFakeBlackStatus;
void EnsureMaxLevelWithCustomImagesAtLeast(size_t aMaxLevelWithCustomImages) {
mMaxLevelWithCustomImages = std::max(mMaxLevelWithCustomImages, aMaxLevelWithCustomImages);
mImageInfos.EnsureLengthAtLeast((mMaxLevelWithCustomImages + 1) * mFacesCount);
}
bool CheckFloatTextureFilterParams() const {
@@ -266,16 +268,29 @@ public:
bool IsMipmapTexture2DComplete() const;
bool IsCubeComplete() const;
bool IsMipmapCubeComplete() const;
void SetFakeBlackStatus(WebGLTextureFakeBlackStatus x);
+ bool IsImmutable() const { return mImmutable; }
+ void SetImmutable() { mImmutable = true; }
+
+ size_t MaxLevelWithCustomImages() const { return mMaxLevelWithCustomImages; }
+
// Returns the current fake-black-status, except if it was Unknown,
// in which case this function resolves it first, so it never returns Unknown.
WebGLTextureFakeBlackStatus ResolvedFakeBlackStatus();
};
+inline TexImageTarget
+TexImageTargetForTargetAndFace(TexTarget target, size_t face)
+{
+ return target == LOCAL_GL_TEXTURE_2D
+ ? LOCAL_GL_TEXTURE_2D
+ : LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + face;
+}
+
} // namespace mozilla
#endif
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -121,16 +121,17 @@ static const char *sExtensionNames[] = {
"GL_EXT_sRGB",
"GL_EXT_shader_texture_lod",
"GL_EXT_texture3D",
"GL_EXT_texture_compression_dxt1",
"GL_EXT_texture_compression_s3tc",
"GL_EXT_texture_filter_anisotropic",
"GL_EXT_texture_format_BGRA8888",
"GL_EXT_texture_sRGB",
+ "GL_EXT_texture_storage",
"GL_EXT_transform_feedback",
"GL_EXT_unpack_subimage",
"GL_IMG_read_format",
"GL_IMG_texture_compression_pvrtc",
"GL_IMG_texture_npot",
"GL_KHR_debug",
"GL_NV_draw_instanced",
"GL_NV_fence",
@@ -904,16 +905,39 @@ GLContext::InitWithPrefix(const char *pr
if (!LoadSymbols(useCore ? coreSymbols : extSymbols, trygl, prefix)) {
NS_ERROR("GL supports array instanced without supplying it function.");
MarkUnsupported(GLFeature::instanced_arrays);
ClearSymbols(coreSymbols);
}
}
+ if (IsSupported(GLFeature::texture_storage)) {
+ SymLoadStruct coreSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fTexStorage2D, { "TexStorage2D", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fTexStorage3D, { "TexStorage3D", nullptr } },
+ END_SYMBOLS
+ };
+
+ SymLoadStruct extSymbols[] = {
+ { (PRFuncPtr*) &mSymbols.fTexStorage2D, { "TexStorage2DEXT", nullptr } },
+ { (PRFuncPtr*) &mSymbols.fTexStorage3D, { "TexStorage3DEXT", nullptr } },
+ END_SYMBOLS
+ };
+
+ bool useCore = IsFeatureProvidedByCoreSymbols(GLFeature::texture_storage);
+ if (!LoadSymbols(useCore ? coreSymbols : extSymbols, trygl, prefix)) {
+ NS_ERROR("GL supports texture storage without supplying its functions.");
+
+ MarkUnsupported(GLFeature::texture_storage);
+ MarkExtensionSupported(useCore ? ARB_texture_storage : EXT_texture_storage);
+ ClearSymbols(coreSymbols);
+ }
+ }
+
if (IsSupported(GLFeature::sampler_objects)) {
SymLoadStruct samplerObjectsSymbols[] = {
{ (PRFuncPtr*) &mSymbols.fGenSamplers, { "GenSamplers", nullptr } },
{ (PRFuncPtr*) &mSymbols.fDeleteSamplers, { "DeleteSamplers", nullptr } },
{ (PRFuncPtr*) &mSymbols.fIsSampler, { "IsSampler", nullptr } },
{ (PRFuncPtr*) &mSymbols.fBindSampler, { "BindSampler", nullptr } },
{ (PRFuncPtr*) &mSymbols.fSamplerParameteri, { "SamplerParameteri", nullptr } },
{ (PRFuncPtr*) &mSymbols.fSamplerParameteriv, { "SamplerParameteriv", nullptr } },
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -403,16 +403,17 @@ public:
EXT_sRGB,
EXT_shader_texture_lod,
EXT_texture3D,
EXT_texture_compression_dxt1,
EXT_texture_compression_s3tc,
EXT_texture_filter_anisotropic,
EXT_texture_format_BGRA8888,
EXT_texture_sRGB,
+ EXT_texture_storage,
EXT_transform_feedback,
EXT_unpack_subimage,
IMG_read_format,
IMG_texture_compression_pvrtc,
IMG_texture_npot,
KHR_debug,
NV_draw_instanced,
NV_fence,