Esempio n. 1
0
void
WebGLTexture::SetCustomMipmap()
{
    if (mHaveGeneratedMipmap) {
        if (!IsMipmapRangeValid())
            return;

        // If we were in GeneratedMipmap mode and are now switching to
        // CustomMipmap mode, we now need to compute all the mipmap image info.
        ImageInfo imageInfo = ImageInfoAtFace(0, EffectiveBaseMipmapLevel());
        MOZ_ASSERT(mContext->IsWebGL2() || imageInfo.IsPowerOfTwo(),
                   "This texture is NPOT, so how could GenerateMipmap() ever"
                   " accept it?");

        size_t maxRelativeLevel = MipmapLevelsForSize(imageInfo);
        size_t maxLevel = EffectiveBaseMipmapLevel() + maxRelativeLevel;
        EnsureMaxLevelWithCustomImagesAtLeast(maxLevel);

        for (size_t level = EffectiveBaseMipmapLevel() + 1;
             level <= EffectiveMaxMipmapLevel(); ++level)
        {
            imageInfo.mWidth = std::max(imageInfo.mWidth / 2, 1);
            imageInfo.mHeight = std::max(imageInfo.mHeight / 2, 1);
            imageInfo.mDepth = std::max(imageInfo.mDepth / 2, 1);
            for (size_t face = 0; face < mFacesCount; ++face) {
                ImageInfoAtFace(face, level) = imageInfo;
            }
        }
    }
    mHaveGeneratedMipmap = false;
}
Esempio n. 2
0
bool
WebGLTexture::AreAllLevel0ImageInfosEqual() const
{
    for (size_t face = 1; face < mFacesCount; ++face) {
        if (ImageInfoAtFace(face, 0) != ImageInfoAtFace(0, 0))
            return false;
    }
    return true;
}
Esempio n. 3
0
void
WebGLTexture::PopulateMipChain(uint32_t firstLevel, uint32_t lastLevel)
{
    const ImageInfo& baseImageInfo = ImageInfoAtFace(0, firstLevel);
    MOZ_ASSERT(baseImageInfo.IsDefined());

    uint32_t refWidth = baseImageInfo.mWidth;
    uint32_t refHeight = baseImageInfo.mHeight;
    uint32_t refDepth = baseImageInfo.mDepth;
    if (!refWidth || !refHeight || !refDepth)
        return;

    for (uint32_t level = firstLevel + 1; level <= lastLevel; level++) {
        bool isMinimal = (refWidth == 1 &&
                          refHeight == 1);
        if (mTarget == LOCAL_GL_TEXTURE_3D) {
            isMinimal &= (refDepth == 1);
        }

        // Higher levels are unaffected.
        if (isMinimal)
            break;

        refWidth = std::max(uint32_t(1), refWidth / 2);
        refHeight = std::max(uint32_t(1), refHeight / 2);
        if (mTarget == LOCAL_GL_TEXTURE_3D) { // But not TEXTURE_2D_ARRAY!
            refDepth = std::max(uint32_t(1), refDepth / 2);
        }

        const ImageInfo cur(baseImageInfo.mFormat, refWidth, refHeight, refDepth,
                            baseImageInfo.IsDataInitialized());

        SetImageInfosAtLevel(level, cur);
    }
}
Esempio n. 4
0
bool
WebGLTexture::ResolveForDraw(const char* funcName, uint32_t texUnit,
                             FakeBlackType* const out_fakeBlack)
{
    if (!mIsResolved) {
        if (!GetFakeBlackType(funcName, texUnit, &mResolved_FakeBlack))
            return false;

        // Check which swizzle we should use. Since the texture must be complete at this
        // point, just grab the format off any valid image.
        const GLint* newSwizzle = nullptr;
        if (mResolved_FakeBlack == FakeBlackType::None) {
            const auto& cur = ImageInfoAtFace(0, mBaseMipmapLevel);
            newSwizzle = cur.mFormat->textureSwizzleRGBA;
        }

        // Only set the swizzle if it changed since last time we did it.
        if (newSwizzle != mResolved_Swizzle) {
            mResolved_Swizzle = newSwizzle;

            // Set the new swizzle!
            mContext->gl->fActiveTexture(LOCAL_GL_TEXTURE0 + texUnit);
            SetSwizzle(mContext->gl, mTarget, mResolved_Swizzle);
            mContext->gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mContext->mActiveTexture);
        }

        mIsResolved = true;
    }

    *out_fakeBlack = mResolved_FakeBlack;
    return true;
}
Esempio n. 5
0
void
WebGLTexture::SetImageInfosAtLevel(uint32_t level, const ImageInfo& newInfo)
{
    for (uint8_t i = 0; i < mFaceCount; i++) {
        ImageInfoAtFace(i, level) = newInfo;
    }

    InvalidateResolveCache();
}
Esempio n. 6
0
void
WebGLTexture::SetImageInfosAtLevel(const char* funcName, uint32_t level,
                                   const ImageInfo& newInfo)
{
    for (uint8_t i = 0; i < mFaceCount; i++) {
        ImageInfoAtFace(i, level).Set(funcName, newInfo);
    }

    InvalidateResolveCache();
}
Esempio n. 7
0
bool WebGLTexture::IsMipAndCubeComplete(const uint32_t maxLevel,
                                        const bool ensureInit,
                                        bool* const out_initFailed) const {
  *out_initFailed = false;

  // Reference dimensions based on baseLevel.
  auto ref = BaseImageInfo();
  MOZ_ASSERT(ref.mWidth && ref.mHeight && ref.mDepth);

  for (auto level = mBaseMipmapLevel; level <= maxLevel; ++level) {
    // GLES 3.0.4, p161
    // "A cube map texture is mipmap complete if each of the six texture images,
    // considered individually, is mipmap complete."

    for (uint8_t face = 0; face < mFaceCount; face++) {
      auto& cur = ImageInfoAtFace(face, level);

      // "* The set of mipmap arrays `level_base` through `q` (where `q`
      //    is defined the "Mipmapping" discussion of section 3.8.10) were
      //    each specified with the same effective internal format."

      // "* The dimensions of the arrays follow the sequence described in
      //    the "Mipmapping" discussion of section 3.8.10."

      if (cur.mWidth != ref.mWidth || cur.mHeight != ref.mHeight ||
          cur.mDepth != ref.mDepth || cur.mFormat != ref.mFormat) {
        return false;
      }

      if (MOZ_UNLIKELY(ensureInit && !cur.mHasData)) {
        auto imageTarget = mTarget.get();
        if (imageTarget == LOCAL_GL_TEXTURE_CUBE_MAP) {
          imageTarget = LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + face;
        }
        if (!ZeroTextureData(mContext, mGLName, imageTarget, level, cur.mFormat,
                             cur.mWidth, cur.mHeight, cur.mDepth)) {
          mContext->ErrorOutOfMemory("Failed to zero tex image data.");
          *out_initFailed = true;
          return false;
        }
        cur.mHasData = true;
      }
    }

    const auto next = ref.NextMip(mTarget.get());
    if (!next) break;
    ref = next.ref();
  }

  return true;
}
Esempio n. 8
0
size_t
WebGLTexture::MemoryUsage() const
{
    if (IsDeleted())
        return 0;

    size_t result = 0;
    for(size_t face = 0; face < mFacesCount; face++) {
        for(size_t level = 0; level <= mMaxLevelWithCustomImages; level++) {
            result += ImageInfoAtFace(face, level).MemoryUsage();
        }
    }
    return result;
}
Esempio n. 9
0
void WebGLTexture::PopulateMipChain(const uint32_t maxLevel) {
  // Used by GenerateMipmap and TexStorage.
  // Populates based on mBaseMipmapLevel.

  auto ref = BaseImageInfo();
  MOZ_ASSERT(ref.mWidth && ref.mHeight && ref.mDepth);

  for (auto level = mBaseMipmapLevel; level <= maxLevel; ++level) {
    // GLES 3.0.4, p161
    // "A cube map texture is mipmap complete if each of the six texture images,
    //  considered individually, is mipmap complete."

    for (uint8_t face = 0; face < mFaceCount; face++) {
      auto& cur = ImageInfoAtFace(face, level);
      cur = ref;
    }

    const auto next = ref.NextMip(mTarget.get());
    if (!next) break;
    ref = next.ref();
  }
  InvalidateCaches();
}
Esempio n. 10
0
bool
WebGLTexture::IsCubeComplete() const
{
    // GLES 3.0.4, p161
    // "[...] a cube map texture is cube complete if the following conditions all hold
    //  true:
    //  * The `level_base` arrays of each of the six texture images making up the cube map
    //    have identical, positive, and square dimensions.
    //  * The `level_base` arrays were each specified with the same effective internal
    //    format."

    // Note that "cube complete" does not imply "mipmap complete".

    const ImageInfo& reference = BaseImageInfo();
    if (!reference.IsDefined())
        return false;

    auto refWidth = reference.mWidth;
    auto refFormat = reference.mFormat;

    for (uint8_t face = 0; face < mFaceCount; face++) {
        const ImageInfo& cur = ImageInfoAtFace(face, mBaseMipmapLevel);
        if (!cur.IsDefined())
            return false;

        MOZ_ASSERT(cur.mDepth == 1);
        if (cur.mFormat != refFormat || // Check effective formats.
            cur.mWidth != refWidth ||   // Check both width and height against refWidth to
            cur.mHeight != refWidth)    // to enforce positive and square dimensions.
        {
            return false;
        }
    }

    return true;
}
Esempio n. 11
0
bool
WebGLTexture::GetFakeBlackType(const char* funcName, uint32_t texUnit,
                               FakeBlackType* const out_fakeBlack)
{
    const char* incompleteReason;
    if (!IsComplete(texUnit, &incompleteReason)) {
        if (incompleteReason) {
            mContext->GenerateWarning("%s: Active texture %u for target 0x%04x is"
                                      " 'incomplete', and will be rendered as"
                                      " RGBA(0,0,0,1), as per the GLES 2.0.24 $3.8.2: %s",
                                      funcName, texUnit, mTarget.get(),
                                      incompleteReason);
        }
        *out_fakeBlack = FakeBlackType::RGBA0001;
        return true;
    }

    // We may still want FakeBlack as an optimization for uninitialized image data.
    bool hasUninitializedData = false;
    bool hasInitializedData = false;

    const auto maxLevel = MaxEffectiveMipmapLevel(texUnit);
    MOZ_ASSERT(mBaseMipmapLevel <= maxLevel);
    for (uint32_t level = mBaseMipmapLevel; level <= maxLevel; level++) {
        for (uint8_t face = 0; face < mFaceCount; face++) {
            const auto& cur = ImageInfoAtFace(face, level);
            if (cur.IsDataInitialized())
                hasInitializedData = true;
            else
                hasUninitializedData = true;
        }
    }
    MOZ_ASSERT(hasUninitializedData || hasInitializedData);

    if (!hasUninitializedData) {
        *out_fakeBlack = FakeBlackType::None;
        return true;
    }

    if (!hasInitializedData) {
        const auto format = ImageInfoAtFace(0, mBaseMipmapLevel).mFormat->format;
        if (format->isColorFormat) {
            *out_fakeBlack = (format->hasAlpha ? FakeBlackType::RGBA0000
                                               : FakeBlackType::RGBA0001);
            return true;
        }

        mContext->GenerateWarning("%s: Active texture %u for target 0x%04x is"
                                  " uninitialized, and will be (perhaps slowly) cleared"
                                  " by the implementation.",
                                  funcName, texUnit, mTarget.get());
    } else {
        mContext->GenerateWarning("%s: Active texture %u for target 0x%04x contains"
                                  " TexImages with uninitialized data along with"
                                  " TexImages with initialized data, forcing the"
                                  " implementation to (slowly) initialize the"
                                  " uninitialized TexImages.",
                                  funcName, texUnit, mTarget.get());
    }

    GLenum baseImageTarget = mTarget.get();
    if (baseImageTarget == LOCAL_GL_TEXTURE_CUBE_MAP)
        baseImageTarget = LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X;

    for (uint32_t level = mBaseMipmapLevel; level <= maxLevel; level++) {
        for (uint8_t face = 0; face < mFaceCount; face++) {
            TexImageTarget imageTarget = baseImageTarget + face;
            if (!EnsureImageDataInitialized(funcName, imageTarget, level))
                return false; // The world just exploded.
        }
    }

    *out_fakeBlack = FakeBlackType::None;
    return true;
}
Esempio n. 12
0
bool
WebGLTexture::IsMipmapComplete(uint32_t texUnit) const
{
    MOZ_ASSERT(DoesMinFilterRequireMipmap());
    // GLES 3.0.4, p161

    const uint32_t maxLevel = MaxEffectiveMipmapLevel(texUnit);

    // "* `level_base <= level_max`"
    if (mBaseMipmapLevel > maxLevel)
        return false;

    // Make a copy so we can modify it.
    const ImageInfo& baseImageInfo = BaseImageInfo();
    if (!baseImageInfo.IsDefined())
        return false;

    // Reference dimensions based on the current level.
    uint32_t refWidth = baseImageInfo.mWidth;
    uint32_t refHeight = baseImageInfo.mHeight;
    uint32_t refDepth = baseImageInfo.mDepth;
    MOZ_ASSERT(refWidth && refHeight && refDepth);

    for (uint32_t level = mBaseMipmapLevel; level <= maxLevel; level++) {
        // "A cube map texture is mipmap complete if each of the six texture images,
        //  considered individually, is mipmap complete."

        for (uint8_t face = 0; face < mFaceCount; face++) {
            const ImageInfo& cur = ImageInfoAtFace(face, level);

            // "* The set of mipmap arrays `level_base` through `q` (where `q` is defined
            //    the "Mipmapping" discussion of section 3.8.10) were each specified with
            //    the same effective internal format."

            // "* The dimensions of the arrays follow the sequence described in the
            //    "Mipmapping" discussion of section 3.8.10."

            if (cur.mWidth != refWidth ||
                cur.mHeight != refHeight ||
                cur.mDepth != refDepth ||
                cur.mFormat != baseImageInfo.mFormat)
            {
                return false;
            }
        }

        // GLES 3.0.4, p158:
        // "[...] until the last array is reached with dimension 1 x 1 x 1."
        if (refWidth == 1 &&
            refHeight == 1 &&
            refDepth == 1)
        {
            break;
        }

        refWidth  = std::max(uint32_t(1), refWidth  / 2);
        refHeight = std::max(uint32_t(1), refHeight / 2);
        refDepth  = std::max(uint32_t(1), refDepth  / 2);
    }

    return true;
}
Esempio n. 13
0
bool
WebGLTexture::IsMipmapComplete(const char* funcName, uint32_t texUnit,
                               bool* const out_initFailed)
{
    *out_initFailed = false;
    MOZ_ASSERT(DoesMinFilterRequireMipmap());
    // GLES 3.0.4, p161

    uint32_t maxLevel;
    if (!MaxEffectiveMipmapLevel(texUnit, &maxLevel))
        return false;

    // "* `level_base <= level_max`"
    if (mBaseMipmapLevel > maxLevel)
        return false;

    // Make a copy so we can modify it.
    const ImageInfo& baseImageInfo = BaseImageInfo();

    // Reference dimensions based on the current level.
    uint32_t refWidth = baseImageInfo.mWidth;
    uint32_t refHeight = baseImageInfo.mHeight;
    uint32_t refDepth = baseImageInfo.mDepth;
    MOZ_ASSERT(refWidth && refHeight && refDepth);

    for (uint32_t level = mBaseMipmapLevel; level <= maxLevel; level++) {
        if (!EnsureLevelInitialized(funcName, level)) {
            *out_initFailed = true;
            return false;
        }

        // "A cube map texture is mipmap complete if each of the six texture images,
        //  considered individually, is mipmap complete."

        for (uint8_t face = 0; face < mFaceCount; face++) {
            const ImageInfo& cur = ImageInfoAtFace(face, level);

            // "* The set of mipmap arrays `level_base` through `q` (where `q` is defined
            //    the "Mipmapping" discussion of section 3.8.10) were each specified with
            //    the same effective internal format."

            // "* The dimensions of the arrays follow the sequence described in the
            //    "Mipmapping" discussion of section 3.8.10."

            if (cur.mWidth != refWidth ||
                cur.mHeight != refHeight ||
                cur.mDepth != refDepth ||
                cur.mFormat != baseImageInfo.mFormat)
            {
                return false;
            }
        }

        // GLES 3.0.4, p158:
        // "[...] until the last array is reached with dimension 1 x 1 x 1."
        if (mTarget == LOCAL_GL_TEXTURE_3D) {
            if (refWidth == 1 &&
                refHeight == 1 &&
                refDepth == 1)
            {
                break;
            }

            refDepth = std::max(uint32_t(1), refDepth / 2);
        } else {
            // TEXTURE_2D_ARRAY may have depth != 1, but that's normal.
            if (refWidth == 1 &&
                refHeight == 1)
            {
                break;
            }
        }

        refWidth  = std::max(uint32_t(1), refWidth  / 2);
        refHeight = std::max(uint32_t(1), refHeight / 2);
    }

    return true;
}
Esempio n. 14
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;
}
Esempio n. 15
0
Maybe<const WebGLTexture::CompletenessInfo> WebGLTexture::CalcCompletenessInfo(
    const bool ensureInit, const bool skipMips) const {
  Maybe<CompletenessInfo> ret = Some(CompletenessInfo());

  // -

  if (mBaseMipmapLevel > kMaxLevelCount - 1) {
    ret->incompleteReason = "`level_base` too high.";
    return ret;
  }

  // Texture completeness is established at GLES 3.0.4, p160-161.
  // "[A] texture is complete unless any of the following conditions hold true:"

  // "* Any dimension of the `level_base` array is not positive."
  const auto& baseImageInfo = ImageInfoAtFace(0, mBaseMipmapLevel);
  if (!baseImageInfo.IsDefined()) {
    // 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).
    ret->incompleteReason = nullptr;
    return ret;
  }

  if (!baseImageInfo.mWidth || !baseImageInfo.mHeight ||
      !baseImageInfo.mDepth) {
    ret->incompleteReason =
        "The dimensions of `level_base` are not all positive.";
    return ret;
  }

  // "* The texture is a cube map texture, and is not cube complete."
  bool initFailed = false;
  if (!IsMipAndCubeComplete(mBaseMipmapLevel, ensureInit, &initFailed)) {
    if (initFailed) return {};

    // Can only fail if not cube-complete.
    ret->incompleteReason = "Cubemaps must be \"cube complete\".";
    return ret;
  }
  ret->levels = 1;
  ret->usage = baseImageInfo.mFormat;
  RefreshSwizzle();

  ret->powerOfTwo = mozilla::IsPowerOfTwo(baseImageInfo.mWidth) &&
                    mozilla::IsPowerOfTwo(baseImageInfo.mHeight);
  if (mTarget == LOCAL_GL_TEXTURE_3D) {
    ret->powerOfTwo &= mozilla::IsPowerOfTwo(baseImageInfo.mDepth);
  }

  // -

  if (!mContext->IsWebGL2() && !ret->powerOfTwo) {
    // WebGL 1 mipmaps require POT.
    ret->incompleteReason = "Mipmapping requires power-of-two sizes.";
    return ret;
  }

  // "* `level_base <= level_max`"

  const auto maxLevel = EffectiveMaxLevel();
  if (mBaseMipmapLevel > maxLevel) {
    ret->incompleteReason = "`level_base > level_max`.";
    return ret;
  }

  if (skipMips) return ret;

  if (!IsMipAndCubeComplete(maxLevel, ensureInit, &initFailed)) {
    if (initFailed) return {};

    ret->incompleteReason = "Bad mipmap dimension or format.";
    return ret;
  }
  ret->levels = maxLevel - mBaseMipmapLevel + 1;
  ret->mipmapComplete = true;

  // -

  return ret;
}