Exemplo n.º 1
0
bool
WebGLTexture::DoesMipmapHaveAllLevelsConsistentlyDefined(TexImageTarget texImageTarget) const
{
    // We could not have generated a mipmap if the base image wasn't defined.
    if (mHaveGeneratedMipmap)
        return true;

    if (!IsMipmapRangeValid())
        return false;

    // We want a copy here so we can modify it temporarily.
    ImageInfo expected = ImageInfoAt(texImageTarget,
                                     EffectiveBaseMipmapLevel());
    if (!expected.IsPositive())
        return false;

    // If Level{max} is > mMaxLevelWithCustomImages, then check if we are
    // missing any image levels.
    if (mMaxMipmapLevel > mMaxLevelWithCustomImages) {
        if (MipmapLevelsForSize(expected) > mMaxLevelWithCustomImages)
            return false;
    }

    // Checks if custom images are all defined up to the highest level and
    // have the expected dimensions.
    for (size_t level = EffectiveBaseMipmapLevel();
         level <= EffectiveMaxMipmapLevel(); ++level)
    {
        const ImageInfo& actual = ImageInfoAt(texImageTarget, level);
        if (actual != expected)
            return false;

        expected.mWidth = std::max(1, expected.mWidth / 2);
        expected.mHeight = std::max(1, expected.mHeight / 2);
        expected.mDepth = std::max(1, expected.mDepth / 2);

        // If the current level has size 1x1, we can stop here: The spec doesn't
        // seem to forbid the existence of extra useless levels.
        if (actual.mWidth == 1 &&
            actual.mHeight == 1 &&
            actual.mDepth == 1)
        {
            return true;
        }
    }

    return true;
}
Exemplo n.º 2
0
bool
WebGLTexture::EnsureImageDataInitialized(const char* funcName, TexImageTarget target,
                                         uint32_t level)
{
    auto& imageInfo = ImageInfoAt(target, level);
    MOZ_ASSERT(imageInfo.IsDefined());

    if (imageInfo.IsDataInitialized())
        return true;

    return InitializeImageData(funcName, target, level);
}
Exemplo n.º 3
0
bool
WebGLTexture::IsCubeComplete() const
{
    MOZ_ASSERT(mTarget == LOCAL_GL_TEXTURE_CUBE_MAP);

    const ImageInfo& first = ImageInfoAt(LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X,
                                         0);
    if (!first.IsPositive() || !first.IsSquare())
        return false;

    return AreAllLevel0ImageInfosEqual();
}
Exemplo n.º 4
0
void
WebGLTexture::GenerateMipmap(TexTarget texTarget)
{
    const TexImageTarget imageTarget = (texTarget == LOCAL_GL_TEXTURE_2D)
                                                  ? LOCAL_GL_TEXTURE_2D
                                                  : LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X;
    if (!IsMipmapRangeValid())
    {
        return mContext->ErrorInvalidOperation("generateMipmap: Texture does not have a valid mipmap range.");
    }
    if (!HasImageInfoAt(imageTarget, EffectiveBaseMipmapLevel()))
    {
        return mContext->ErrorInvalidOperation("generateMipmap: Level zero of texture is not defined.");
    }

    if (!mContext->IsWebGL2() && !IsFirstImagePowerOfTwo())
        return mContext->ErrorInvalidOperation("generateMipmap: Level zero of texture does not have power-of-two width and height.");

    TexInternalFormat internalformat = ImageInfoAt(imageTarget, 0).EffectiveInternalFormat();
    if (IsTextureFormatCompressed(internalformat))
        return mContext->ErrorInvalidOperation("generateMipmap: Texture data at level zero is compressed.");

    if (mContext->IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture) &&
        (IsGLDepthFormat(internalformat) || IsGLDepthStencilFormat(internalformat)))
    {
        return mContext->ErrorInvalidOperation("generateMipmap: "
                                     "A texture that has a base internal format of "
                                     "DEPTH_COMPONENT or DEPTH_STENCIL isn't supported");
    }

    if (!AreAllLevel0ImageInfosEqual())
        return mContext->ErrorInvalidOperation("generateMipmap: The six faces of this cube map have different dimensions, format, or type.");

    SetGeneratedMipmap();

    mContext->MakeContextCurrent();
    gl::GLContext* gl = mContext->gl;

    if (gl->WorkAroundDriverBugs()) {
        // bug 696495 - to work around failures in the texture-mips.html test on various drivers, we
        // set the minification filter before calling glGenerateMipmap. This should not carry a significant performance
        // overhead so we do it unconditionally.
        //
        // note that the choice of GL_NEAREST_MIPMAP_NEAREST really matters. See Chromium bug 101105.
        gl->fTexParameteri(texTarget.get(), LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST_MIPMAP_NEAREST);
        gl->fGenerateMipmap(texTarget.get());
        gl->fTexParameteri(texTarget.get(), LOCAL_GL_TEXTURE_MIN_FILTER, MinFilter().get());
    } else {
        gl->fGenerateMipmap(texTarget.get());
    }
}
Exemplo n.º 5
0
bool WebGLTexture::EnsureImageDataInitialized(const TexImageTarget target,
                                              const uint32_t level) {
  auto& imageInfo = ImageInfoAt(target, level);
  if (!imageInfo.IsDefined()) return true;

  if (imageInfo.mHasData) return true;

  if (!ZeroTextureData(mContext, mGLName, target, level, imageInfo.mFormat,
                       imageInfo.mWidth, imageInfo.mHeight, imageInfo.mDepth)) {
    return false;
  }
  imageInfo.mHasData = true;
  return true;
}
Exemplo n.º 6
0
bool
WebGLTexture::InitializeImageData(const char* funcName, TexImageTarget target,
                                  uint32_t level)
{
    auto& imageInfo = ImageInfoAt(target, level);
    MOZ_ASSERT(imageInfo.IsDefined());
    MOZ_ASSERT(!imageInfo.IsDataInitialized());

    const auto& usage = imageInfo.mFormat;
    const auto& width = imageInfo.mWidth;
    const auto& height = imageInfo.mHeight;
    const auto& depth = imageInfo.mDepth;

    if (!ZeroTextureData(mContext, funcName, mGLName, target, level, usage, 0, 0, 0,
                         width, height, depth))
    {
        return false;
    }

    imageInfo.SetIsDataInitialized(true, this);
    return true;
}
Exemplo n.º 7
0
void
WebGLTexture::SetImageInfo(TexImageTarget texImageTarget, GLint level,
                           GLsizei width, GLsizei height, GLsizei depth,
                           TexInternalFormat effectiveInternalFormat,
                           WebGLImageDataStatus status)
{
    MOZ_ASSERT(depth == 1 || texImageTarget == LOCAL_GL_TEXTURE_3D);
    MOZ_ASSERT(TexImageTargetToTexTarget(texImageTarget) == mTarget);

    InvalidateStatusOfAttachedFBs();

    EnsureMaxLevelWithCustomImagesAtLeast(level);

    ImageInfoAt(texImageTarget, level) = ImageInfo(width, height, depth,
                                                   effectiveInternalFormat,
                                                   status);

    if (level > 0)
        SetCustomMipmap();

    SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown);
}
Exemplo n.º 8
0
bool
WebGLTexture::EnsureInitializedImageData(TexImageTarget imageTarget,
                                         GLint level)
{
    const ImageInfo& imageInfo = ImageInfoAt(imageTarget, level);
    if (!imageInfo.HasUninitializedImageData())
        return true;

    mContext->MakeContextCurrent();

    // Try to clear with glClear.
    if (imageTarget == LOCAL_GL_TEXTURE_2D) {
        bool cleared = ClearWithTempFB(mContext, mGLName, imageTarget, level,
                                       imageInfo.mEffectiveInternalFormat,
                                       imageInfo.mHeight, imageInfo.mWidth);
        if (cleared) {
            SetImageDataStatus(imageTarget, level,
                               WebGLImageDataStatus::InitializedImageData);
            return true;
        }
    }

    // That didn't work. Try uploading zeros then.
    size_t bitspertexel = GetBitsPerTexel(imageInfo.mEffectiveInternalFormat);
    MOZ_ASSERT((bitspertexel % 8) == 0); // That would only happen for
                                         // compressed images, which cannot use
                                         // deferred initialization.
    size_t bytespertexel = bitspertexel / 8;
    CheckedUint32 checked_byteLength
        = WebGLContext::GetImageSize(
                        imageInfo.mHeight,
                        imageInfo.mWidth,
                        imageInfo.mDepth,
                        bytespertexel,
                        mContext->mPixelStoreUnpackAlignment);
    MOZ_ASSERT(checked_byteLength.isValid()); // Should have been checked
                                              // earlier.

    size_t byteCount = checked_byteLength.value();

    UniquePtr<uint8_t> zeros((uint8_t*)calloc(1, byteCount));
    if (zeros == nullptr) {
        // Failed to allocate memory. Lose the context. Return OOM error.
        mContext->ForceLoseContext(true);
        mContext->ErrorOutOfMemory("EnsureInitializedImageData: Failed to alloc %u "
                                   "bytes to clear image target `%s` level `%d`.",
                                   byteCount, mContext->EnumName(imageTarget.get()),
                                   level);
         return false;
    }

    gl::GLContext* gl = mContext->gl;
    gl::ScopedBindTexture autoBindTex(gl, mGLName, mTarget);

    GLenum driverInternalFormat = LOCAL_GL_NONE;
    GLenum driverFormat = LOCAL_GL_NONE;
    GLenum driverType = LOCAL_GL_NONE;
    DriverFormatsFromEffectiveInternalFormat(gl,
                                             imageInfo.mEffectiveInternalFormat,
                                             &driverInternalFormat,
                                             &driverFormat, &driverType);

    mContext->GetAndFlushUnderlyingGLErrors();
    if (imageTarget == LOCAL_GL_TEXTURE_3D) {
        MOZ_ASSERT(mImmutable,
                   "Shouldn't be possible to have non-immutable-format 3D"
                   " textures in WebGL");
        gl->fTexSubImage3D(imageTarget.get(), level, 0, 0, 0, imageInfo.mWidth,
                           imageInfo.mHeight, imageInfo.mDepth, driverFormat,
                           driverType, zeros.get());
    } else {
        if (mImmutable) {
            gl->fTexSubImage2D(imageTarget.get(), level, 0, 0, imageInfo.mWidth,
                               imageInfo.mHeight, driverFormat, driverType,
                               zeros.get());
        } else {
            gl->fTexImage2D(imageTarget.get(), level, driverInternalFormat,
                            imageInfo.mWidth, imageInfo.mHeight, 0,
                            driverFormat, driverType, zeros.get());
        }
    }
    GLenum error = mContext->GetAndFlushUnderlyingGLErrors();
    if (error) {
        // Should only be OUT_OF_MEMORY. Anyway, there's no good way to recover
        // from this here.
        gfxCriticalError() << "GL context GetAndFlushUnderlyingGLErrors " << gfx::hexa(error);
        printf_stderr("Error: 0x%4x\n", error);
        if (error != LOCAL_GL_OUT_OF_MEMORY) {
            // Errors on texture upload have been related to video
            // memory exposure in the past, which is a security issue.
            // Force loss of context.
            mContext->ForceLoseContext(true);
            return false;
        }

        // Out-of-memory uploading pixels to GL. Lose context and report OOM.
        mContext->ForceLoseContext(true);
        mContext->ErrorOutOfMemory("EnsureNoUninitializedImageData: Failed to "
                                   "upload texture of width: %u, height: %u, "
                                   "depth: %u to target %s level %d.",
                                   imageInfo.mWidth, imageInfo.mHeight, imageInfo.mDepth,
                                   mContext->EnumName(imageTarget.get()), level);
        return false;
    }

    SetImageDataStatus(imageTarget, level, WebGLImageDataStatus::InitializedImageData);

    return true;
}
Exemplo n.º 9
0
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;
}