bool WebGLFramebuffer::AttachPoint::IsReadableFloat() const { TexInternalFormat internalformat = EffectiveInternalFormat(); MOZ_ASSERT(internalformat != LOCAL_GL_NONE); TexType type = TypeFromInternalFormat(internalformat); return type == LOCAL_GL_FLOAT || type == LOCAL_GL_HALF_FLOAT_OES || type == LOCAL_GL_HALF_FLOAT; }
WebGLTextureFakeBlackStatus WebGLTexture::ResolvedFakeBlackStatus() { if (MOZ_LIKELY(mFakeBlackStatus != WebGLTextureFakeBlackStatus::Unknown)) return mFakeBlackStatus; // Determine if the texture needs to be faked as a black texture. // See 3.8.2 Shader Execution in the OpenGL ES 2.0.24 spec, and 3.8.13 in // the OpenGL ES 3.0.4 spec. if (!IsMipmapRangeValid()) { mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; return mFakeBlackStatus; } for (size_t face = 0; face < mFacesCount; ++face) { WebGLImageDataStatus status = ImageInfoAtFace(face, EffectiveBaseMipmapLevel()).mImageDataStatus; if (status == WebGLImageDataStatus::NoImageData) { // In case of undefined texture image, we don't print any message // because this is a very common and often legitimate case // (asynchronous texture loading). mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; return mFakeBlackStatus; } } const char preamble[] = "A texture is going to be rendered as if it were" " black, as per the OpenGL ES 2.0.24 spec section" " 3.8.2, because it"; if (mTarget == LOCAL_GL_TEXTURE_2D || mTarget == LOCAL_GL_TEXTURE_3D) { int dim = mTarget == LOCAL_GL_TEXTURE_2D ? 2 : 3; if (DoesMinFilterRequireMipmap()) { if (!IsMipmapComplete()) { mContext->GenerateWarning("%s is a %dD texture, with a" " minification filter requiring a" " mipmap, and is not mipmap complete" " (as defined in section 3.7.10).", preamble, dim); mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; } else if (!mContext->IsWebGL2() && !ImageInfoBase().IsPowerOfTwo()) { mContext->GenerateWarning("%s is a %dD texture, with a" " minification filter requiring a" " mipmap, and either its width or" " height is not a power of two.", preamble, dim); mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; } } else { // No mipmap required here. if (!ImageInfoBase().IsPositive()) { mContext->GenerateWarning("%s is a %dD texture and its width or" " height is equal to zero.", preamble, dim); mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; } else if (!AreBothWrapModesClampToEdge() && !mContext->IsWebGL2() && !ImageInfoBase().IsPowerOfTwo()) { mContext->GenerateWarning("%s is a %dD texture, with a" " minification filter not requiring a" " mipmap, with its width or height" " not a power of two, and with a wrap" " mode different from CLAMP_TO_EDGE.", preamble, dim); mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; } } } else { // Cube map bool legalImageSize = true; if (!mContext->IsWebGL2()) { for (size_t face = 0; face < mFacesCount; ++face) legalImageSize &= ImageInfoAtFace(face, 0).IsPowerOfTwo(); } if (DoesMinFilterRequireMipmap()) { if (!IsMipmapCubeComplete()) { mContext->GenerateWarning("%s is a cube map texture, with a" " minification filter requiring a" " mipmap, and is not mipmap cube" " complete (as defined in section" " 3.7.10).", preamble); mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; } else if (!legalImageSize) { mContext->GenerateWarning("%s is a cube map texture, with a" " minification filter requiring a" " mipmap, and either the width or the" " height of some level 0 image is not" " a power of two.", preamble); mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; } } else // no mipmap required { if (!IsCubeComplete()) { mContext->GenerateWarning("%s is a cube map texture, with a" " minification filter not requiring a" " mipmap, and is not cube complete" " (as defined in section 3.7.10).", preamble); mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; } else if (!AreBothWrapModesClampToEdge() && !legalImageSize) { mContext->GenerateWarning("%s is a cube map texture, with a" " minification filter not requiring a" " mipmap, with some level 0 image" " having width or height not a power" " of two, and with a wrap mode" " different from CLAMP_TO_EDGE.", preamble); mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; } } } TexType type = TypeFromInternalFormat(ImageInfoBase().mEffectiveInternalFormat); const char* badFormatText = nullptr; const char* extText = nullptr; if (type == LOCAL_GL_FLOAT && !Context()->IsExtensionEnabled(WebGLExtensionID::OES_texture_float_linear)) { badFormatText = "FLOAT"; extText = "OES_texture_float_linear"; } else if (type == LOCAL_GL_HALF_FLOAT && !Context()->IsExtensionEnabled(WebGLExtensionID::OES_texture_half_float_linear)) { badFormatText = "HALF_FLOAT"; extText = "OES_texture_half_float_linear"; } const char* badFilterText = nullptr; if (badFormatText) { if (mMinFilter == LOCAL_GL_LINEAR || mMinFilter == LOCAL_GL_LINEAR_MIPMAP_LINEAR || mMinFilter == LOCAL_GL_LINEAR_MIPMAP_NEAREST || mMinFilter == LOCAL_GL_NEAREST_MIPMAP_LINEAR) { badFilterText = "minification"; } else if (mMagFilter == LOCAL_GL_LINEAR) { badFilterText = "magnification"; } } if (badFilterText) { mContext->GenerateWarning("%s is a texture with a linear %s filter," " which is not compatible with format %s by" " default. Try enabling the %s extension, if" " supported.", preamble, badFilterText, badFormatText, extText); mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; } // We have exhausted all cases of incomplete textures, where we would need opaque black. // We may still need transparent black in case of uninitialized image data. bool hasUninitializedImageData = false; for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) { for (size_t face = 0; face < mFacesCount; ++face) { bool cur = (ImageInfoAtFace(face, level).mImageDataStatus == WebGLImageDataStatus::UninitializedImageData); hasUninitializedImageData |= cur; } } if (hasUninitializedImageData) { bool hasAnyInitializedImageData = false; for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) { for (size_t face = 0; face < mFacesCount; ++face) { if (ImageInfoAtFace(face, level).mImageDataStatus == WebGLImageDataStatus::InitializedImageData) { hasAnyInitializedImageData = true; break; } } if (hasAnyInitializedImageData) { break; } } 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 = TexImageTargetForTargetAndFace(mTarget, face); const ImageInfo& imageInfo = ImageInfoAt(imageTarget, level); if (imageInfo.mImageDataStatus == WebGLImageDataStatus::UninitializedImageData) { EnsureInitializedImageData(imageTarget, level); } } } mFakeBlackStatus = WebGLTextureFakeBlackStatus::NotNeeded; } else { // The texture only contains uninitialized image data. In this case, // we can use a black texture for it. mFakeBlackStatus = WebGLTextureFakeBlackStatus::UninitializedImageData; } } // we have exhausted all cases where we do need fakeblack, so if the status is still unknown, // that means that we do NOT need it. if (mFakeBlackStatus == WebGLTextureFakeBlackStatus::Unknown) { mFakeBlackStatus = WebGLTextureFakeBlackStatus::NotNeeded; } MOZ_ASSERT(mFakeBlackStatus != WebGLTextureFakeBlackStatus::Unknown); return mFakeBlackStatus; }