content/canvas/src/WebGLTexture.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/canvas/src/WebGLTexture.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,485 @@
     1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "WebGLContext.h"
    1.10 +#include "WebGLContextUtils.h"
    1.11 +#include "WebGLTexture.h"
    1.12 +#include "GLContext.h"
    1.13 +#include "ScopedGLHelpers.h"
    1.14 +#include "WebGLTexelConversions.h"
    1.15 +#include "mozilla/dom/WebGLRenderingContextBinding.h"
    1.16 +#include <algorithm>
    1.17 +
    1.18 +using namespace mozilla;
    1.19 +
    1.20 +JSObject*
    1.21 +WebGLTexture::WrapObject(JSContext *cx) {
    1.22 +    return dom::WebGLTextureBinding::Wrap(cx, this);
    1.23 +}
    1.24 +
    1.25 +WebGLTexture::WebGLTexture(WebGLContext *context)
    1.26 +    : WebGLContextBoundObject(context)
    1.27 +    , mHasEverBeenBound(false)
    1.28 +    , mTarget(0)
    1.29 +    , mMinFilter(LOCAL_GL_NEAREST_MIPMAP_LINEAR)
    1.30 +    , mMagFilter(LOCAL_GL_LINEAR)
    1.31 +    , mWrapS(LOCAL_GL_REPEAT)
    1.32 +    , mWrapT(LOCAL_GL_REPEAT)
    1.33 +    , mFacesCount(0)
    1.34 +    , mMaxLevelWithCustomImages(0)
    1.35 +    , mHaveGeneratedMipmap(false)
    1.36 +    , mFakeBlackStatus(WebGLTextureFakeBlackStatus::IncompleteTexture)
    1.37 +{
    1.38 +    SetIsDOMBinding();
    1.39 +    mContext->MakeContextCurrent();
    1.40 +    mContext->gl->fGenTextures(1, &mGLName);
    1.41 +    mContext->mTextures.insertBack(this);
    1.42 +}
    1.43 +
    1.44 +void
    1.45 +WebGLTexture::Delete() {
    1.46 +    mImageInfos.Clear();
    1.47 +    mContext->MakeContextCurrent();
    1.48 +    mContext->gl->fDeleteTextures(1, &mGLName);
    1.49 +    LinkedListElement<WebGLTexture>::removeFrom(mContext->mTextures);
    1.50 +}
    1.51 +
    1.52 +int64_t
    1.53 +WebGLTexture::ImageInfo::MemoryUsage() const {
    1.54 +    if (mImageDataStatus == WebGLImageDataStatus::NoImageData)
    1.55 +        return 0;
    1.56 +    int64_t bitsPerTexel = WebGLContext::GetBitsPerTexel(mWebGLFormat, mWebGLType);
    1.57 +    return int64_t(mWidth) * int64_t(mHeight) * bitsPerTexel/8;
    1.58 +}
    1.59 +
    1.60 +int64_t
    1.61 +WebGLTexture::MemoryUsage() const {
    1.62 +    if (IsDeleted())
    1.63 +        return 0;
    1.64 +    int64_t result = 0;
    1.65 +    for(size_t face = 0; face < mFacesCount; face++) {
    1.66 +        if (mHaveGeneratedMipmap) {
    1.67 +            // Each mipmap level is 1/4 the size of the previous level
    1.68 +            // 1 + x + x^2 + ... = 1/(1-x)
    1.69 +            // for x = 1/4, we get 1/(1-1/4) = 4/3
    1.70 +            result += ImageInfoAtFace(face, 0).MemoryUsage() * 4 / 3;
    1.71 +        } else {
    1.72 +            for(size_t level = 0; level <= mMaxLevelWithCustomImages; level++)
    1.73 +                result += ImageInfoAtFace(face, level).MemoryUsage();
    1.74 +        }
    1.75 +    }
    1.76 +    return result;
    1.77 +}
    1.78 +
    1.79 +bool
    1.80 +WebGLTexture::DoesTexture2DMipmapHaveAllLevelsConsistentlyDefined(GLenum texImageTarget) const {
    1.81 +    if (mHaveGeneratedMipmap)
    1.82 +        return true;
    1.83 +
    1.84 +    // We want a copy here so we can modify it temporarily.
    1.85 +    ImageInfo expected = ImageInfoAt(texImageTarget, 0);
    1.86 +
    1.87 +    // checks if custom level>0 images are all defined up to the highest level defined
    1.88 +    // and have the expected dimensions
    1.89 +    for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) {
    1.90 +        const ImageInfo& actual = ImageInfoAt(texImageTarget, level);
    1.91 +        if (actual != expected)
    1.92 +            return false;
    1.93 +        expected.mWidth = std::max(1, expected.mWidth >> 1);
    1.94 +        expected.mHeight = std::max(1, expected.mHeight >> 1);
    1.95 +
    1.96 +        // if the current level has size 1x1, we can stop here: the spec doesn't seem to forbid the existence
    1.97 +        // of extra useless levels.
    1.98 +        if (actual.mWidth == 1 && actual.mHeight == 1)
    1.99 +            return true;
   1.100 +    }
   1.101 +
   1.102 +    // if we're here, we've exhausted all levels without finding a 1x1 image
   1.103 +    return false;
   1.104 +}
   1.105 +
   1.106 +void
   1.107 +WebGLTexture::Bind(GLenum aTarget) {
   1.108 +    // this function should only be called by bindTexture().
   1.109 +    // it assumes that the GL context is already current.
   1.110 +
   1.111 +    bool firstTimeThisTextureIsBound = !mHasEverBeenBound;
   1.112 +
   1.113 +    if (!firstTimeThisTextureIsBound && aTarget != mTarget) {
   1.114 +        mContext->ErrorInvalidOperation("bindTexture: this texture has already been bound to a different target");
   1.115 +        // very important to return here before modifying texture state! This was the place when I lost a whole day figuring
   1.116 +        // very strange 'invalid write' crashes.
   1.117 +        return;
   1.118 +    }
   1.119 +
   1.120 +    mTarget = aTarget;
   1.121 +
   1.122 +    mContext->gl->fBindTexture(mTarget, mGLName);
   1.123 +
   1.124 +    if (firstTimeThisTextureIsBound) {
   1.125 +        mFacesCount = (mTarget == LOCAL_GL_TEXTURE_2D) ? 1 : 6;
   1.126 +        EnsureMaxLevelWithCustomImagesAtLeast(0);
   1.127 +        SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown);
   1.128 +
   1.129 +        // thanks to the WebKit people for finding this out: GL_TEXTURE_WRAP_R is not
   1.130 +        // present in GLES 2, but is present in GL and it seems as if for cube maps
   1.131 +        // we need to set it to GL_CLAMP_TO_EDGE to get the expected GLES behavior.
   1.132 +        if (mTarget == LOCAL_GL_TEXTURE_CUBE_MAP && !mContext->gl->IsGLES())
   1.133 +            mContext->gl->fTexParameteri(mTarget, LOCAL_GL_TEXTURE_WRAP_R, LOCAL_GL_CLAMP_TO_EDGE);
   1.134 +    }
   1.135 +
   1.136 +    mHasEverBeenBound = true;
   1.137 +}
   1.138 +
   1.139 +void
   1.140 +WebGLTexture::SetImageInfo(GLenum aTarget, GLint aLevel,
   1.141 +                  GLsizei aWidth, GLsizei aHeight,
   1.142 +                  GLenum aFormat, GLenum aType, WebGLImageDataStatus aStatus)
   1.143 +{
   1.144 +    if ( (aTarget == LOCAL_GL_TEXTURE_2D) != (mTarget == LOCAL_GL_TEXTURE_2D) )
   1.145 +        return;
   1.146 +
   1.147 +    EnsureMaxLevelWithCustomImagesAtLeast(aLevel);
   1.148 +
   1.149 +    ImageInfoAt(aTarget, aLevel) = ImageInfo(aWidth, aHeight, aFormat, aType, aStatus);
   1.150 +
   1.151 +    if (aLevel > 0)
   1.152 +        SetCustomMipmap();
   1.153 +
   1.154 +    // Invalidate framebuffer status cache
   1.155 +    NotifyFBsStatusChanged();
   1.156 +
   1.157 +    SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown);
   1.158 +}
   1.159 +
   1.160 +void
   1.161 +WebGLTexture::SetGeneratedMipmap() {
   1.162 +    if (!mHaveGeneratedMipmap) {
   1.163 +        mHaveGeneratedMipmap = true;
   1.164 +        SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown);
   1.165 +    }
   1.166 +}
   1.167 +
   1.168 +void
   1.169 +WebGLTexture::SetCustomMipmap() {
   1.170 +    if (mHaveGeneratedMipmap) {
   1.171 +        // if we were in GeneratedMipmap mode and are now switching to CustomMipmap mode,
   1.172 +        // we need to compute now all the mipmap image info.
   1.173 +
   1.174 +        // since we were in GeneratedMipmap mode, we know that the level 0 images all have the same info,
   1.175 +        // and are power-of-two.
   1.176 +        ImageInfo imageInfo = ImageInfoAtFace(0, 0);
   1.177 +        NS_ASSERTION(imageInfo.IsPowerOfTwo(), "this texture is NPOT, so how could GenerateMipmap() ever accept it?");
   1.178 +
   1.179 +        GLsizei size = std::max(imageInfo.mWidth, imageInfo.mHeight);
   1.180 +
   1.181 +        // so, the size is a power of two, let's find its log in base 2.
   1.182 +        size_t maxLevel = 0;
   1.183 +        for (GLsizei n = size; n > 1; n >>= 1)
   1.184 +            ++maxLevel;
   1.185 +
   1.186 +        EnsureMaxLevelWithCustomImagesAtLeast(maxLevel);
   1.187 +
   1.188 +        for (size_t level = 1; level <= maxLevel; ++level) {
   1.189 +            // again, since the sizes are powers of two, no need for any max(1,x) computation
   1.190 +            imageInfo.mWidth >>= 1;
   1.191 +            imageInfo.mHeight >>= 1;
   1.192 +            for(size_t face = 0; face < mFacesCount; ++face)
   1.193 +                ImageInfoAtFace(face, level) = imageInfo;
   1.194 +        }
   1.195 +    }
   1.196 +    mHaveGeneratedMipmap = false;
   1.197 +}
   1.198 +
   1.199 +bool
   1.200 +WebGLTexture::AreAllLevel0ImageInfosEqual() const {
   1.201 +    for (size_t face = 1; face < mFacesCount; ++face) {
   1.202 +        if (ImageInfoAtFace(face, 0) != ImageInfoAtFace(0, 0))
   1.203 +            return false;
   1.204 +    }
   1.205 +    return true;
   1.206 +}
   1.207 +
   1.208 +bool
   1.209 +WebGLTexture::IsMipmapTexture2DComplete() const {
   1.210 +    if (mTarget != LOCAL_GL_TEXTURE_2D)
   1.211 +        return false;
   1.212 +    if (!ImageInfoAt(LOCAL_GL_TEXTURE_2D, 0).IsPositive())
   1.213 +        return false;
   1.214 +    if (mHaveGeneratedMipmap)
   1.215 +        return true;
   1.216 +    return DoesTexture2DMipmapHaveAllLevelsConsistentlyDefined(LOCAL_GL_TEXTURE_2D);
   1.217 +}
   1.218 +
   1.219 +bool
   1.220 +WebGLTexture::IsCubeComplete() const {
   1.221 +    if (mTarget != LOCAL_GL_TEXTURE_CUBE_MAP)
   1.222 +        return false;
   1.223 +    const ImageInfo &first = ImageInfoAt(LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0);
   1.224 +    if (!first.IsPositive() || !first.IsSquare())
   1.225 +        return false;
   1.226 +    return AreAllLevel0ImageInfosEqual();
   1.227 +}
   1.228 +
   1.229 +static GLenum
   1.230 +GLCubeMapFaceById(int id)
   1.231 +{
   1.232 +    GLenum result = LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + id;
   1.233 +    MOZ_ASSERT(result >= LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X &&
   1.234 +               result <= LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z);
   1.235 +    return result;
   1.236 +}
   1.237 +
   1.238 +bool
   1.239 +WebGLTexture::IsMipmapCubeComplete() const {
   1.240 +    if (!IsCubeComplete()) // in particular, this checks that this is a cube map
   1.241 +        return false;
   1.242 +    for (int i = 0; i < 6; i++) {
   1.243 +        GLenum face = GLCubeMapFaceById(i);
   1.244 +        if (!DoesTexture2DMipmapHaveAllLevelsConsistentlyDefined(face))
   1.245 +            return false;
   1.246 +    }
   1.247 +    return true;
   1.248 +}
   1.249 +
   1.250 +WebGLTextureFakeBlackStatus
   1.251 +WebGLTexture::ResolvedFakeBlackStatus() {
   1.252 +    if (MOZ_LIKELY(mFakeBlackStatus != WebGLTextureFakeBlackStatus::Unknown)) {
   1.253 +        return mFakeBlackStatus;
   1.254 +    }
   1.255 +
   1.256 +    // Determine if the texture needs to be faked as a black texture.
   1.257 +    // See 3.8.2 Shader Execution in the OpenGL ES 2.0.24 spec.
   1.258 +
   1.259 +    for (size_t face = 0; face < mFacesCount; ++face) {
   1.260 +        if (ImageInfoAtFace(face, 0).mImageDataStatus == WebGLImageDataStatus::NoImageData) {
   1.261 +            // In case of undefined texture image, we don't print any message because this is a very common
   1.262 +            // and often legitimate case (asynchronous texture loading).
   1.263 +            mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
   1.264 +            return mFakeBlackStatus;
   1.265 +        }
   1.266 +    }
   1.267 +
   1.268 +    const char *msg_rendering_as_black
   1.269 +        = "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, "
   1.270 +          "because it";
   1.271 +
   1.272 +    if (mTarget == LOCAL_GL_TEXTURE_2D)
   1.273 +    {
   1.274 +        if (DoesMinFilterRequireMipmap())
   1.275 +        {
   1.276 +            if (!IsMipmapTexture2DComplete()) {
   1.277 +                mContext->GenerateWarning
   1.278 +                    ("%s is a 2D texture, with a minification filter requiring a mipmap, "
   1.279 +                      "and is not mipmap complete (as defined in section 3.7.10).", msg_rendering_as_black);
   1.280 +                mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
   1.281 +            } else if (!ImageInfoAt(mTarget, 0).IsPowerOfTwo()) {
   1.282 +                mContext->GenerateWarning
   1.283 +                    ("%s is a 2D texture, with a minification filter requiring a mipmap, "
   1.284 +                      "and either its width or height is not a power of two.", msg_rendering_as_black);
   1.285 +                mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
   1.286 +            }
   1.287 +        }
   1.288 +        else // no mipmap required
   1.289 +        {
   1.290 +            if (!ImageInfoAt(mTarget, 0).IsPositive()) {
   1.291 +                mContext->GenerateWarning
   1.292 +                    ("%s is a 2D texture and its width or height is equal to zero.",
   1.293 +                      msg_rendering_as_black);
   1.294 +                mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
   1.295 +            } else if (!AreBothWrapModesClampToEdge() && !ImageInfoAt(mTarget, 0).IsPowerOfTwo()) {
   1.296 +                mContext->GenerateWarning
   1.297 +                    ("%s is a 2D texture, with a minification filter not requiring a mipmap, "
   1.298 +                      "with its width or height not a power of two, and with a wrap mode "
   1.299 +                      "different from CLAMP_TO_EDGE.", msg_rendering_as_black);
   1.300 +                mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
   1.301 +            }
   1.302 +        }
   1.303 +    }
   1.304 +    else // cube map
   1.305 +    {
   1.306 +        bool areAllLevel0ImagesPOT = true;
   1.307 +        for (size_t face = 0; face < mFacesCount; ++face)
   1.308 +            areAllLevel0ImagesPOT &= ImageInfoAtFace(face, 0).IsPowerOfTwo();
   1.309 +
   1.310 +        if (DoesMinFilterRequireMipmap())
   1.311 +        {
   1.312 +            if (!IsMipmapCubeComplete()) {
   1.313 +                mContext->GenerateWarning("%s is a cube map texture, with a minification filter requiring a mipmap, "
   1.314 +                            "and is not mipmap cube complete (as defined in section 3.7.10).",
   1.315 +                            msg_rendering_as_black);
   1.316 +                mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
   1.317 +            } else if (!areAllLevel0ImagesPOT) {
   1.318 +                mContext->GenerateWarning("%s is a cube map texture, with a minification filter requiring a mipmap, "
   1.319 +                            "and either the width or the height of some level 0 image is not a power of two.",
   1.320 +                            msg_rendering_as_black);
   1.321 +                mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
   1.322 +            }
   1.323 +        }
   1.324 +        else // no mipmap required
   1.325 +        {
   1.326 +            if (!IsCubeComplete()) {
   1.327 +                mContext->GenerateWarning("%s is a cube map texture, with a minification filter not requiring a mipmap, "
   1.328 +                            "and is not cube complete (as defined in section 3.7.10).",
   1.329 +                            msg_rendering_as_black);
   1.330 +                mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
   1.331 +            } else if (!AreBothWrapModesClampToEdge() && !areAllLevel0ImagesPOT) {
   1.332 +                mContext->GenerateWarning("%s is a cube map texture, with a minification filter not requiring a mipmap, "
   1.333 +                            "with some level 0 image having width or height not a power of two, and with a wrap mode "
   1.334 +                            "different from CLAMP_TO_EDGE.", msg_rendering_as_black);
   1.335 +                mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
   1.336 +            }
   1.337 +        }
   1.338 +    }
   1.339 +
   1.340 +    if (ImageInfoBase().mWebGLType == LOCAL_GL_FLOAT &&
   1.341 +        !Context()->IsExtensionEnabled(WebGLExtensionID::OES_texture_float_linear))
   1.342 +    {
   1.343 +        if (mMinFilter == LOCAL_GL_LINEAR ||
   1.344 +            mMinFilter == LOCAL_GL_LINEAR_MIPMAP_LINEAR ||
   1.345 +            mMinFilter == LOCAL_GL_LINEAR_MIPMAP_NEAREST ||
   1.346 +            mMinFilter == LOCAL_GL_NEAREST_MIPMAP_LINEAR)
   1.347 +        {
   1.348 +            mContext->GenerateWarning("%s is a texture with a linear minification filter, "
   1.349 +                                      "which is not compatible with gl.FLOAT by default. "
   1.350 +                                      "Try enabling the OES_texture_float_linear extension if supported.", msg_rendering_as_black);
   1.351 +            mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
   1.352 +        }
   1.353 +        else if (mMagFilter == LOCAL_GL_LINEAR)
   1.354 +        {
   1.355 +            mContext->GenerateWarning("%s is a texture with a linear magnification filter, "
   1.356 +                                      "which is not compatible with gl.FLOAT by default. "
   1.357 +                                      "Try enabling the OES_texture_float_linear extension if supported.", msg_rendering_as_black);
   1.358 +            mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
   1.359 +        }
   1.360 +    } else if (ImageInfoBase().mWebGLType == LOCAL_GL_HALF_FLOAT_OES &&
   1.361 +               !Context()->IsExtensionEnabled(WebGLExtensionID::OES_texture_half_float_linear))
   1.362 +    {
   1.363 +        if (mMinFilter == LOCAL_GL_LINEAR ||
   1.364 +            mMinFilter == LOCAL_GL_LINEAR_MIPMAP_LINEAR ||
   1.365 +            mMinFilter == LOCAL_GL_LINEAR_MIPMAP_NEAREST ||
   1.366 +            mMinFilter == LOCAL_GL_NEAREST_MIPMAP_LINEAR)
   1.367 +        {
   1.368 +            mContext->GenerateWarning("%s is a texture with a linear minification filter, "
   1.369 +                                      "which is not compatible with gl.HALF_FLOAT by default. "
   1.370 +                                      "Try enabling the OES_texture_half_float_linear extension if supported.", msg_rendering_as_black);
   1.371 +            mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
   1.372 +        }
   1.373 +        else if (mMagFilter == LOCAL_GL_LINEAR)
   1.374 +        {
   1.375 +            mContext->GenerateWarning("%s is a texture with a linear magnification filter, "
   1.376 +                                      "which is not compatible with gl.HALF_FLOAT by default. "
   1.377 +                                      "Try enabling the OES_texture_half_float_linear extension if supported.", msg_rendering_as_black);
   1.378 +            mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
   1.379 +        }
   1.380 +    }
   1.381 +
   1.382 +    // We have exhausted all cases of incomplete textures, where we would need opaque black.
   1.383 +    // We may still need transparent black in case of uninitialized image data.
   1.384 +    bool hasUninitializedImageData = false;
   1.385 +    for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) {
   1.386 +        for (size_t face = 0; face < mFacesCount; ++face) {
   1.387 +            hasUninitializedImageData |= (ImageInfoAtFace(face, level).mImageDataStatus == WebGLImageDataStatus::UninitializedImageData);
   1.388 +        }
   1.389 +    }
   1.390 +
   1.391 +    if (hasUninitializedImageData) {
   1.392 +        bool hasAnyInitializedImageData = false;
   1.393 +        for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) {
   1.394 +            for (size_t face = 0; face < mFacesCount; ++face) {
   1.395 +                if (ImageInfoAtFace(face, level).mImageDataStatus == WebGLImageDataStatus::InitializedImageData) {
   1.396 +                    hasAnyInitializedImageData = true;
   1.397 +                    break;
   1.398 +                }
   1.399 +            }
   1.400 +            if (hasAnyInitializedImageData) {
   1.401 +                break;
   1.402 +            }
   1.403 +        }
   1.404 +
   1.405 +        if (hasAnyInitializedImageData) {
   1.406 +            // The texture contains some initialized image data, and some uninitialized image data.
   1.407 +            // In this case, we have no choice but to initialize all image data now. Fortunately,
   1.408 +            // in this case we know that we can't be dealing with a depth texture per WEBGL_depth_texture
   1.409 +            // and ANGLE_depth_texture (which allow only one image per texture) so we can assume that
   1.410 +            // glTexImage2D is able to upload data to images.
   1.411 +            for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) {
   1.412 +                for (size_t face = 0; face < mFacesCount; ++face) {
   1.413 +                    GLenum imageTarget = mTarget == LOCAL_GL_TEXTURE_2D
   1.414 +                                         ? LOCAL_GL_TEXTURE_2D
   1.415 +                                         : LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + face;
   1.416 +                    const ImageInfo& imageInfo = ImageInfoAt(imageTarget, level);
   1.417 +                    if (imageInfo.mImageDataStatus == WebGLImageDataStatus::UninitializedImageData) {
   1.418 +                        DoDeferredImageInitialization(imageTarget, level);
   1.419 +                    }
   1.420 +                }
   1.421 +            }
   1.422 +            mFakeBlackStatus = WebGLTextureFakeBlackStatus::NotNeeded;
   1.423 +        } else {
   1.424 +            // The texture only contains uninitialized image data. In this case,
   1.425 +            // we can use a black texture for it.
   1.426 +            mFakeBlackStatus = WebGLTextureFakeBlackStatus::UninitializedImageData;
   1.427 +        }
   1.428 +    }
   1.429 +
   1.430 +    // we have exhausted all cases where we do need fakeblack, so if the status is still unknown,
   1.431 +    // that means that we do NOT need it.
   1.432 +    if (mFakeBlackStatus == WebGLTextureFakeBlackStatus::Unknown) {
   1.433 +        mFakeBlackStatus = WebGLTextureFakeBlackStatus::NotNeeded;
   1.434 +    }
   1.435 +
   1.436 +    MOZ_ASSERT(mFakeBlackStatus != WebGLTextureFakeBlackStatus::Unknown);
   1.437 +    return mFakeBlackStatus;
   1.438 +}
   1.439 +
   1.440 +void
   1.441 +WebGLTexture::DoDeferredImageInitialization(GLenum imageTarget, GLint level)
   1.442 +{
   1.443 +    const ImageInfo& imageInfo = ImageInfoAt(imageTarget, level);
   1.444 +    MOZ_ASSERT(imageInfo.mImageDataStatus == WebGLImageDataStatus::UninitializedImageData);
   1.445 +
   1.446 +    mContext->MakeContextCurrent();
   1.447 +    gl::ScopedBindTexture autoBindTex(mContext->gl, GLName(), mTarget);
   1.448 +
   1.449 +    GLenum format = imageInfo.mWebGLFormat;
   1.450 +    GLenum type = imageInfo.mWebGLType;
   1.451 +    WebGLTexelFormat texelformat = GetWebGLTexelFormat(format, type);
   1.452 +    uint32_t texelsize = WebGLTexelConversions::TexelBytesForFormat(texelformat);
   1.453 +    CheckedUint32 checked_byteLength
   1.454 +        = WebGLContext::GetImageSize(
   1.455 +                        imageInfo.mHeight,
   1.456 +                        imageInfo.mWidth,
   1.457 +                        texelsize,
   1.458 +                        mContext->mPixelStoreUnpackAlignment);
   1.459 +    MOZ_ASSERT(checked_byteLength.isValid()); // should have been checked earlier
   1.460 +    void *zeros = calloc(1, checked_byteLength.value());
   1.461 +
   1.462 +    gl::GLContext* gl = mContext->gl;
   1.463 +    GLenum driverType = DriverTypeFromType(gl, type);
   1.464 +    GLenum driverInternalFormat = LOCAL_GL_NONE;
   1.465 +    GLenum driverFormat = LOCAL_GL_NONE;
   1.466 +    DriverFormatsFromFormatAndType(gl, format, type, &driverInternalFormat, &driverFormat);
   1.467 +
   1.468 +    mContext->GetAndFlushUnderlyingGLErrors();
   1.469 +    gl->fTexImage2D(imageTarget, level, driverInternalFormat,
   1.470 +                    imageInfo.mWidth, imageInfo.mHeight,
   1.471 +                    0, driverFormat, driverType,
   1.472 +                    zeros);
   1.473 +    GLenum error = mContext->GetAndFlushUnderlyingGLErrors();
   1.474 +
   1.475 +    free(zeros);
   1.476 +    SetImageDataStatus(imageTarget, level, WebGLImageDataStatus::InitializedImageData);
   1.477 +
   1.478 +    if (error) {
   1.479 +      // Should only be OUT_OF_MEMORY. Anyway, there's no good way to recover from this here.
   1.480 +      MOZ_CRASH(); // errors on texture upload have been related to video memory exposure in the past.
   1.481 +      return;
   1.482 +    }
   1.483 +}
   1.484 +
   1.485 +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLTexture)
   1.486 +
   1.487 +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLTexture, AddRef)
   1.488 +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLTexture, Release)

mercurial