void WebGLTexture::Bind(TexTarget aTexTarget) { // this function should only be called by bindTexture(). // it assumes that the GL context is already current. bool firstTimeThisTextureIsBound = !HasEverBeenBound(); if (firstTimeThisTextureIsBound) { BindTo(aTexTarget); } else if (aTexTarget != Target()) { mContext->ErrorInvalidOperation("bindTexture: this texture has already been bound to a different target"); // very important to return here before modifying texture state! This was the place when I lost a whole day figuring // very strange 'invalid write' crashes. return; } GLuint name = GLName(); mContext->gl->fBindTexture(aTexTarget.get(), name); if (firstTimeThisTextureIsBound) { mFacesCount = (aTexTarget == LOCAL_GL_TEXTURE_CUBE_MAP) ? 6 : 1; EnsureMaxLevelWithCustomImagesAtLeast(0); SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); // thanks to the WebKit people for finding this out: GL_TEXTURE_WRAP_R is not // present in GLES 2, but is present in GL and it seems as if for cube maps // we need to set it to GL_CLAMP_TO_EDGE to get the expected GLES behavior. if (mTarget == LOCAL_GL_TEXTURE_CUBE_MAP && !mContext->gl->IsGLES()) mContext->gl->fTexParameteri(aTexTarget.get(), LOCAL_GL_TEXTURE_WRAP_R, LOCAL_GL_CLAMP_TO_EDGE); } }
WebGLContext::FakeBlackTexture::FakeBlackTexture(GLContext *gl, TexTarget target, GLenum format) : mGL(gl) , mGLName(0) { MOZ_ASSERT(format == LOCAL_GL_RGB || format == LOCAL_GL_RGBA); mGL->MakeCurrent(); GLuint formerBinding = 0; gl->GetUIntegerv(target == LOCAL_GL_TEXTURE_2D ? LOCAL_GL_TEXTURE_BINDING_2D : LOCAL_GL_TEXTURE_BINDING_CUBE_MAP, &formerBinding); gl->fGenTextures(1, &mGLName); gl->fBindTexture(target.get(), mGLName); // we allocate our zeros on the heap, and we overallocate (16 bytes instead of 4) // to minimize the risk of running into a driver bug in texImage2D, as it is // a bit unusual maybe to create 1x1 textures, and the stack may not have the alignment // that texImage2D expects. UniquePtr<uint8_t> zeros((uint8_t*)moz_xcalloc(1, 16)); if (target == LOCAL_GL_TEXTURE_2D) { gl->fTexImage2D(target.get(), 0, format, 1, 1, 0, format, LOCAL_GL_UNSIGNED_BYTE, zeros.get()); } else { for (GLuint i = 0; i < 6; ++i) { gl->fTexImage2D(LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, format, 1, 1, 0, format, LOCAL_GL_UNSIGNED_BYTE, zeros.get()); } } gl->fBindTexture(target.get(), formerBinding); }
JS::Value WebGL2Context::GetTexParameterInternal(const TexTarget& target, GLenum pname) { switch (pname) { case LOCAL_GL_TEXTURE_BASE_LEVEL: case LOCAL_GL_TEXTURE_COMPARE_FUNC: case LOCAL_GL_TEXTURE_COMPARE_MODE: case LOCAL_GL_TEXTURE_IMMUTABLE_FORMAT: case LOCAL_GL_TEXTURE_IMMUTABLE_LEVELS: case LOCAL_GL_TEXTURE_MAX_LEVEL: case LOCAL_GL_TEXTURE_SWIZZLE_A: case LOCAL_GL_TEXTURE_SWIZZLE_B: case LOCAL_GL_TEXTURE_SWIZZLE_G: case LOCAL_GL_TEXTURE_SWIZZLE_R: case LOCAL_GL_TEXTURE_WRAP_R: { GLint i = 0; gl->fGetTexParameteriv(target.get(), pname, &i); return JS::NumberValue(uint32_t(i)); } case LOCAL_GL_TEXTURE_MAX_LOD: case LOCAL_GL_TEXTURE_MIN_LOD: { GLfloat f = 0.0f; gl->fGetTexParameterfv(target.get(), pname, &f); return JS::NumberValue(float(f)); } } return WebGLContext::GetTexParameterInternal(target, pname); }
JS::Value WebGLTexture::GetTexParameter(TexTarget texTarget, GLenum pname) { mContext->MakeContextCurrent(); GLint i = 0; GLfloat f = 0.0f; switch (pname) { case LOCAL_GL_TEXTURE_MIN_FILTER: return JS::NumberValue(uint32_t(mMinFilter.get())); case LOCAL_GL_TEXTURE_MAG_FILTER: return JS::NumberValue(uint32_t(mMagFilter.get())); case LOCAL_GL_TEXTURE_WRAP_S: return JS::NumberValue(uint32_t(mWrapS.get())); case LOCAL_GL_TEXTURE_WRAP_T: return JS::NumberValue(uint32_t(mWrapT.get())); case LOCAL_GL_TEXTURE_BASE_LEVEL: return JS::NumberValue(mBaseMipmapLevel); case LOCAL_GL_TEXTURE_COMPARE_MODE: return JS::NumberValue(uint32_t(mTexCompareMode)); case LOCAL_GL_TEXTURE_MAX_LEVEL: return JS::NumberValue(mMaxMipmapLevel); case LOCAL_GL_TEXTURE_IMMUTABLE_FORMAT: return JS::BooleanValue(mImmutable); case LOCAL_GL_TEXTURE_IMMUTABLE_LEVELS: return JS::NumberValue(uint32_t(mImmutableLevelCount)); case LOCAL_GL_TEXTURE_COMPARE_FUNC: case LOCAL_GL_TEXTURE_WRAP_R: mContext->gl->fGetTexParameteriv(texTarget.get(), pname, &i); return JS::NumberValue(uint32_t(i)); case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT: case LOCAL_GL_TEXTURE_MAX_LOD: case LOCAL_GL_TEXTURE_MIN_LOD: mContext->gl->fGetTexParameterfv(texTarget.get(), pname, &f); return JS::NumberValue(float(f)); default: MOZ_CRASH("GFX: Unhandled pname."); } }
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()); } }
bool WebGLContext::BindFakeBlack(uint32_t texUnit, TexTarget target, FakeBlackType fakeBlack) { MOZ_ASSERT(fakeBlack == FakeBlackType::RGBA0000 || fakeBlack == FakeBlackType::RGBA0001); const auto fnGetSlot = [this, target, fakeBlack]() -> UniquePtr<FakeBlackTexture>* { switch (fakeBlack) { case FakeBlackType::RGBA0000: switch (target.get()) { case LOCAL_GL_TEXTURE_2D : return &mFakeBlack_2D_0000; case LOCAL_GL_TEXTURE_CUBE_MAP: return &mFakeBlack_CubeMap_0000; case LOCAL_GL_TEXTURE_3D : return &mFakeBlack_3D_0000; case LOCAL_GL_TEXTURE_2D_ARRAY: return &mFakeBlack_2D_Array_0000; default: return nullptr; } case FakeBlackType::RGBA0001: switch (target.get()) { case LOCAL_GL_TEXTURE_2D : return &mFakeBlack_2D_0001; case LOCAL_GL_TEXTURE_CUBE_MAP: return &mFakeBlack_CubeMap_0001; case LOCAL_GL_TEXTURE_3D : return &mFakeBlack_3D_0001; case LOCAL_GL_TEXTURE_2D_ARRAY: return &mFakeBlack_2D_Array_0001; default: return nullptr; } default: return nullptr; } }; UniquePtr<FakeBlackTexture>* slot = fnGetSlot(); if (!slot) { MOZ_CRASH("GFX: fnGetSlot failed."); } UniquePtr<FakeBlackTexture>& fakeBlackTex = *slot; if (!fakeBlackTex) { fakeBlackTex = FakeBlackTexture::Create(gl, target, fakeBlack); if (!fakeBlackTex) { return false; } } gl->fActiveTexture(LOCAL_GL_TEXTURE0 + texUnit); gl->fBindTexture(target.get(), fakeBlackTex->mGLName); gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mActiveTexture); return true; }
static void SetSwizzle(gl::GLContext* gl, TexTarget target, const GLint* swizzle) { static const GLint kNoSwizzle[4] = {LOCAL_GL_RED, LOCAL_GL_GREEN, LOCAL_GL_BLUE, LOCAL_GL_ALPHA}; if (!swizzle) { swizzle = kNoSwizzle; } else if (!gl->IsSupported(gl::GLFeature::texture_swizzle)) { MOZ_CRASH("GFX: Needs swizzle feature to swizzle!"); } gl->fTexParameteri(target.get(), LOCAL_GL_TEXTURE_SWIZZLE_R, swizzle[0]); gl->fTexParameteri(target.get(), LOCAL_GL_TEXTURE_SWIZZLE_G, swizzle[1]); gl->fTexParameteri(target.get(), LOCAL_GL_TEXTURE_SWIZZLE_B, swizzle[2]); gl->fTexParameteri(target.get(), LOCAL_GL_TEXTURE_SWIZZLE_A, swizzle[3]); }
WebGLContext::FakeBlackTexture::FakeBlackTexture(gl::GLContext* gl, TexTarget target, FakeBlackType type) : mGL(gl) , mGLName(CreateGLTexture(gl)) { GLenum texFormat; switch (type) { case FakeBlackType::RGBA0000: texFormat = LOCAL_GL_RGBA; break; case FakeBlackType::RGBA0001: texFormat = LOCAL_GL_RGB; break; default: MOZ_CRASH("bad type"); } gl::ScopedBindTexture scopedBind(mGL, mGLName, target.get()); mGL->fTexParameteri(target.get(), LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST); mGL->fTexParameteri(target.get(), LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_NEAREST); // We allocate our zeros on the heap, and we overallocate (16 bytes instead of 4) to // minimize the risk of running into a driver bug in texImage2D, as it is a bit // unusual maybe to create 1x1 textures, and the stack may not have the alignment that // TexImage2D expects. const webgl::DriverUnpackInfo dui = {texFormat, texFormat, LOCAL_GL_UNSIGNED_BYTE}; UniqueBuffer zeros = moz_xcalloc(1, 16); // Infallible allocation. if (target == LOCAL_GL_TEXTURE_CUBE_MAP) { for (int i = 0; i < 6; ++i) { const TexImageTarget curTarget = LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + i; const GLenum error = DoTexImage(mGL, curTarget.get(), 0, &dui, 1, 1, 1, zeros.get()); if (error) MOZ_CRASH("Unexpected error during FakeBlack creation."); } } else { const GLenum error = DoTexImage(mGL, target.get(), 0, &dui, 1, 1, 1, zeros.get()); if (error) MOZ_CRASH("Unexpected error during FakeBlack creation."); } }
JS::Value WebGLTexture::GetTexParameter(TexTarget texTarget, GLenum pname) { mContext->MakeContextCurrent(); GLint i = 0; GLfloat f = 0.0f; switch (pname) { case LOCAL_GL_TEXTURE_MIN_FILTER: case LOCAL_GL_TEXTURE_MAG_FILTER: case LOCAL_GL_TEXTURE_WRAP_S: case LOCAL_GL_TEXTURE_WRAP_T: case LOCAL_GL_TEXTURE_BASE_LEVEL: case LOCAL_GL_TEXTURE_COMPARE_FUNC: case LOCAL_GL_TEXTURE_COMPARE_MODE: case LOCAL_GL_TEXTURE_IMMUTABLE_LEVELS: case LOCAL_GL_TEXTURE_MAX_LEVEL: case LOCAL_GL_TEXTURE_SWIZZLE_A: case LOCAL_GL_TEXTURE_SWIZZLE_B: case LOCAL_GL_TEXTURE_SWIZZLE_G: case LOCAL_GL_TEXTURE_SWIZZLE_R: case LOCAL_GL_TEXTURE_WRAP_R: mContext->gl->fGetTexParameteriv(texTarget.get(), pname, &i); return JS::NumberValue(uint32_t(i)); case LOCAL_GL_TEXTURE_IMMUTABLE_FORMAT: mContext->gl->fGetTexParameteriv(texTarget.get(), pname, &i); return JS::BooleanValue(bool(i)); case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT: case LOCAL_GL_TEXTURE_MAX_LOD: case LOCAL_GL_TEXTURE_MIN_LOD: mContext->gl->fGetTexParameterfv(texTarget.get(), pname, &f); return JS::NumberValue(float(f)); default: MOZ_CRASH("GFX: Unhandled pname."); } }
JS::Value WebGL2Context::GetTexParameterInternal(const TexTarget& target, GLenum pname) { switch (pname) { case LOCAL_GL_TEXTURE_IMMUTABLE_FORMAT: case LOCAL_GL_TEXTURE_BASE_LEVEL: case LOCAL_GL_TEXTURE_MAX_LEVEL: { GLint i = 0; gl->fGetTexParameteriv(target.get(), pname, &i); return JS::NumberValue(uint32_t(i)); } } return WebGLContext::GetTexParameterInternal(target, pname); }
bool WebGLTexture::BindTexture(TexTarget texTarget) { if (IsDeleted()) { mContext->ErrorInvalidOperation("bindTexture: Cannot bind a deleted object."); return false; } const bool isFirstBinding = !HasEverBeenBound(); if (!isFirstBinding && mTarget != texTarget) { mContext->ErrorInvalidOperation("bindTexture: This texture has already been bound" " to a different target."); return false; } mTarget = texTarget; mContext->gl->fBindTexture(mTarget.get(), mGLName); if (isFirstBinding) { mFaceCount = IsCubeMap() ? 6 : 1; gl::GLContext* gl = mContext->gl; // Thanks to the WebKit people for finding this out: GL_TEXTURE_WRAP_R // is not present in GLES 2, but is present in GL and it seems as if for // cube maps we need to set it to GL_CLAMP_TO_EDGE to get the expected // GLES behavior. // If we are WebGL 2 though, we'll want to leave it as REPEAT. const bool hasWrapR = gl->IsSupported(gl::GLFeature::texture_3D); if (IsCubeMap() && hasWrapR && !mContext->IsWebGL2()) { gl->fTexParameteri(texTarget.get(), LOCAL_GL_TEXTURE_WRAP_R, LOCAL_GL_CLAMP_TO_EDGE); } } return true; }
// Here we have to support all pnames with both int and float params. // See this discussion: // https://www.khronos.org/webgl/public-mailing-list/archives/1008/msg00014.html void WebGLTexture::TexParameter(TexTarget texTarget, GLenum pname, GLint* maybeIntParam, GLfloat* maybeFloatParam) { MOZ_ASSERT(maybeIntParam || maybeFloatParam); GLint intParam = maybeIntParam ? *maybeIntParam : GLint(*maybeFloatParam); GLfloat floatParam = maybeFloatParam ? *maybeFloatParam : GLfloat(*maybeIntParam); bool isPNameValid = false; switch (pname) { // GLES 2.0.25 p76: case LOCAL_GL_TEXTURE_WRAP_S: case LOCAL_GL_TEXTURE_WRAP_T: case LOCAL_GL_TEXTURE_MIN_FILTER: case LOCAL_GL_TEXTURE_MAG_FILTER: isPNameValid = true; break; // GLES 3.0.4 p149-150: case LOCAL_GL_TEXTURE_BASE_LEVEL: case LOCAL_GL_TEXTURE_COMPARE_MODE: case LOCAL_GL_TEXTURE_COMPARE_FUNC: case LOCAL_GL_TEXTURE_MAX_LEVEL: case LOCAL_GL_TEXTURE_MAX_LOD: case LOCAL_GL_TEXTURE_MIN_LOD: case LOCAL_GL_TEXTURE_WRAP_R: if (mContext->IsWebGL2()) isPNameValid = true; break; case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT: if (mContext->IsExtensionEnabled(WebGLExtensionID::EXT_texture_filter_anisotropic)) isPNameValid = true; break; } if (!isPNameValid) { mContext->ErrorInvalidEnumInfo("texParameter: pname", pname); return; } //////////////// // Validate params and invalidate if needed. bool paramBadEnum = false; bool paramBadValue = false; switch (pname) { case LOCAL_GL_TEXTURE_BASE_LEVEL: case LOCAL_GL_TEXTURE_MAX_LEVEL: paramBadValue = (intParam < 0); break; case LOCAL_GL_TEXTURE_COMPARE_MODE: paramBadValue = (intParam != LOCAL_GL_NONE && intParam != LOCAL_GL_COMPARE_REF_TO_TEXTURE); break; case LOCAL_GL_TEXTURE_COMPARE_FUNC: switch (intParam) { case LOCAL_GL_LEQUAL: case LOCAL_GL_GEQUAL: case LOCAL_GL_LESS: case LOCAL_GL_GREATER: case LOCAL_GL_EQUAL: case LOCAL_GL_NOTEQUAL: case LOCAL_GL_ALWAYS: case LOCAL_GL_NEVER: break; default: paramBadValue = true; break; } break; case LOCAL_GL_TEXTURE_MIN_FILTER: switch (intParam) { case LOCAL_GL_NEAREST: case LOCAL_GL_LINEAR: case LOCAL_GL_NEAREST_MIPMAP_NEAREST: case LOCAL_GL_LINEAR_MIPMAP_NEAREST: case LOCAL_GL_NEAREST_MIPMAP_LINEAR: case LOCAL_GL_LINEAR_MIPMAP_LINEAR: break; default: paramBadEnum = true; break; } break; case LOCAL_GL_TEXTURE_MAG_FILTER: switch (intParam) { case LOCAL_GL_NEAREST: case LOCAL_GL_LINEAR: break; default: paramBadEnum = true; break; } break; case LOCAL_GL_TEXTURE_WRAP_S: case LOCAL_GL_TEXTURE_WRAP_T: case LOCAL_GL_TEXTURE_WRAP_R: switch (intParam) { case LOCAL_GL_CLAMP_TO_EDGE: case LOCAL_GL_MIRRORED_REPEAT: case LOCAL_GL_REPEAT: break; default: paramBadEnum = true; break; } break; case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT: if (maybeFloatParam && floatParam < 1.0f) paramBadValue = true; else if (maybeIntParam && intParam < 1) paramBadValue = true; break; } if (paramBadEnum) { if (maybeIntParam) { mContext->ErrorInvalidEnum("texParameteri: pname 0x%04x: Invalid param" " 0x%04x.", pname, intParam); } else { mContext->ErrorInvalidEnum("texParameterf: pname 0x%04x: Invalid param %g.", pname, floatParam); } return; } if (paramBadValue) { if (maybeIntParam) { mContext->ErrorInvalidValue("texParameteri: pname 0x%04x: Invalid param %i" " (0x%x).", pname, intParam, intParam); } else { mContext->ErrorInvalidValue("texParameterf: pname 0x%04x: Invalid param %g.", pname, floatParam); } return; } //////////////// // Store any needed values switch (pname) { case LOCAL_GL_TEXTURE_BASE_LEVEL: mBaseMipmapLevel = intParam; ClampLevelBaseAndMax(); break; case LOCAL_GL_TEXTURE_MAX_LEVEL: mMaxMipmapLevel = intParam; ClampLevelBaseAndMax(); break; case LOCAL_GL_TEXTURE_MIN_FILTER: mMinFilter = intParam; break; case LOCAL_GL_TEXTURE_MAG_FILTER: mMagFilter = intParam; break; case LOCAL_GL_TEXTURE_WRAP_S: mWrapS = intParam; break; case LOCAL_GL_TEXTURE_WRAP_T: mWrapT = intParam; break; // We don't actually need to store the WRAP_R, since it doesn't change texture // completeness rules. } // Only a couple of pnames don't need to invalidate our resolve status cache. switch (pname) { case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT: case LOCAL_GL_TEXTURE_WRAP_R: break; default: InvalidateResolveCache(); break; } //////////////// mContext->MakeContextCurrent(); if (maybeIntParam) mContext->gl->fTexParameteri(texTarget.get(), pname, intParam); else mContext->gl->fTexParameterf(texTarget.get(), pname, floatParam); }
void WebGLTexture::GenerateMipmap(TexTarget texTarget) { // GLES 3.0.4 p160: // "Mipmap generation replaces texel array levels level base + 1 through q with arrays // derived from the level base array, regardless of their previous contents. All // other mipmap arrays, including the level base array, are left unchanged by this // computation." const ImageInfo& baseImageInfo = BaseImageInfo(); if (!baseImageInfo.IsDefined()) { mContext->ErrorInvalidOperation("generateMipmap: The base level of the texture is" " not defined."); return; } if (IsCubeMap() && !IsCubeComplete()) { mContext->ErrorInvalidOperation("generateMipmap: Cube maps must be \"cube" " complete\"."); return; } if (!mContext->IsWebGL2() && !baseImageInfo.IsPowerOfTwo()) { mContext->ErrorInvalidOperation("generateMipmap: The base level of the texture" " does not have power-of-two dimensions."); return; } auto format = baseImageInfo.mFormat->format; if (format->compression) { mContext->ErrorInvalidOperation("generateMipmap: Texture data at base level is" " compressed."); return; } if (format->hasDepth) { mContext->ErrorInvalidOperation("generateMipmap: Depth textures are not" " supported."); return; } // OpenGL ES 3.0.4 p160: // If the level base array was not specified with an unsized internal format from // table 3.3 or a sized internal format that is both color-renderable and // texture-filterable according to table 3.13, an INVALID_OPERATION error // is generated. const auto usage = baseImageInfo.mFormat; bool canGenerateMipmap = (usage->isRenderable && usage->isFilterable); switch (usage->format->effectiveFormat) { case webgl::EffectiveFormat::Luminance8: case webgl::EffectiveFormat::Alpha8: case webgl::EffectiveFormat::Luminance8Alpha8: // Non-color-renderable formats from Table 3.3. canGenerateMipmap = true; break; default: break; } if (!canGenerateMipmap) { mContext->ErrorInvalidOperation("generateMipmap: Texture at base level is not unsized" " internal format or is not" " color-renderable or texture-filterable."); return; } // Done with validation. Do the operation. 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, mMinFilter.get()); } else { gl->fGenerateMipmap(texTarget.get()); } // Record the results. // Note that we don't use MaxEffectiveMipmapLevel() here, since that returns // mBaseMipmapLevel if the min filter doesn't require mipmaps. const uint32_t lastLevel = mBaseMipmapLevel + baseImageInfo.MaxMipmapLevels() - 1; PopulateMipChain(mBaseMipmapLevel, lastLevel); }
// Here we have to support all pnames with both int and float params. // See this discussion: // https://www.khronos.org/webgl/public-mailing-list/archives/1008/msg00014.html void WebGLTexture::TexParameter(TexTarget texTarget, GLenum pname, GLint* maybeIntParam, GLfloat* maybeFloatParam) { MOZ_ASSERT(maybeIntParam || maybeFloatParam); GLint intParam = maybeIntParam ? *maybeIntParam : GLint(*maybeFloatParam); GLfloat floatParam = maybeFloatParam ? *maybeFloatParam : GLfloat(*maybeIntParam); bool paramBadEnum = false; bool paramBadValue = false; switch (pname) { case LOCAL_GL_TEXTURE_BASE_LEVEL: case LOCAL_GL_TEXTURE_MAX_LEVEL: if (!mContext->IsWebGL2()) return mContext->ErrorInvalidEnumInfo("texParameter: pname", pname); if (intParam < 0) { paramBadValue = true; break; } SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); if (pname == LOCAL_GL_TEXTURE_BASE_LEVEL) mBaseMipmapLevel = intParam; else mMaxMipmapLevel = intParam; break; case LOCAL_GL_TEXTURE_COMPARE_MODE: if (!mContext->IsWebGL2()) return mContext->ErrorInvalidEnumInfo("texParameter: pname", pname); paramBadValue = (intParam != LOCAL_GL_NONE && intParam != LOCAL_GL_COMPARE_REF_TO_TEXTURE); break; case LOCAL_GL_TEXTURE_COMPARE_FUNC: if (!mContext->IsWebGL2()) return mContext->ErrorInvalidEnumInfo("texParameter: pname", pname); switch (intParam) { case LOCAL_GL_LEQUAL: case LOCAL_GL_GEQUAL: case LOCAL_GL_LESS: case LOCAL_GL_GREATER: case LOCAL_GL_EQUAL: case LOCAL_GL_NOTEQUAL: case LOCAL_GL_ALWAYS: case LOCAL_GL_NEVER: break; default: paramBadValue = true; } break; case LOCAL_GL_TEXTURE_MIN_FILTER: switch (intParam) { case LOCAL_GL_NEAREST: case LOCAL_GL_LINEAR: case LOCAL_GL_NEAREST_MIPMAP_NEAREST: case LOCAL_GL_LINEAR_MIPMAP_NEAREST: case LOCAL_GL_NEAREST_MIPMAP_LINEAR: case LOCAL_GL_LINEAR_MIPMAP_LINEAR: SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); mMinFilter = intParam; break; default: paramBadEnum = true; } break; case LOCAL_GL_TEXTURE_MAG_FILTER: switch (intParam) { case LOCAL_GL_NEAREST: case LOCAL_GL_LINEAR: SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); mMagFilter = intParam; break; default: paramBadEnum = true; } break; case LOCAL_GL_TEXTURE_WRAP_S: switch (intParam) { case LOCAL_GL_CLAMP_TO_EDGE: case LOCAL_GL_MIRRORED_REPEAT: case LOCAL_GL_REPEAT: SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); mWrapS = intParam; break; default: paramBadEnum = true; } break; case LOCAL_GL_TEXTURE_WRAP_T: switch (intParam) { case LOCAL_GL_CLAMP_TO_EDGE: case LOCAL_GL_MIRRORED_REPEAT: case LOCAL_GL_REPEAT: SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); mWrapT = intParam; break; default: paramBadEnum = true; } break; case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT: if (!mContext->IsExtensionEnabled(WebGLExtensionID::EXT_texture_filter_anisotropic)) return mContext->ErrorInvalidEnumInfo("texParameter: pname", pname); if (maybeFloatParam && floatParam < 1.0f) paramBadValue = true; else if (maybeIntParam && intParam < 1) paramBadValue = true; break; default: return mContext->ErrorInvalidEnumInfo("texParameter: pname", pname); } if (paramBadEnum) { if (maybeIntParam) { mContext->ErrorInvalidEnum("texParameteri: pname 0x%04x: Invalid param" " 0x%04x.", pname, intParam); } else { mContext->ErrorInvalidEnum("texParameterf: pname 0x%04x: Invalid param %g.", pname, floatParam); } return; } if (paramBadValue) { if (maybeIntParam) { mContext->ErrorInvalidValue("texParameteri: pname 0x%04x: Invalid param %i" " (0x%x).", pname, intParam, intParam); } else { mContext->ErrorInvalidValue("texParameterf: pname 0x%04x: Invalid param %g.", pname, floatParam); } return; } mContext->MakeContextCurrent(); if (maybeIntParam) mContext->gl->fTexParameteri(texTarget.get(), pname, intParam); else mContext->gl->fTexParameterf(texTarget.get(), pname, floatParam); }
// Here we have to support all pnames with both int and float params. // See this discussion: // https://www.khronos.org/webgl/public-mailing-list/archives/1008/msg00014.html void WebGLTexture::TexParameter(TexTarget texTarget, GLenum pname, const FloatOrInt& param) { bool isPNameValid = false; switch (pname) { // GLES 2.0.25 p76: case LOCAL_GL_TEXTURE_WRAP_S: case LOCAL_GL_TEXTURE_WRAP_T: case LOCAL_GL_TEXTURE_MIN_FILTER: case LOCAL_GL_TEXTURE_MAG_FILTER: isPNameValid = true; break; // GLES 3.0.4 p149-150: case LOCAL_GL_TEXTURE_BASE_LEVEL: case LOCAL_GL_TEXTURE_COMPARE_MODE: case LOCAL_GL_TEXTURE_COMPARE_FUNC: case LOCAL_GL_TEXTURE_MAX_LEVEL: case LOCAL_GL_TEXTURE_MAX_LOD: case LOCAL_GL_TEXTURE_MIN_LOD: case LOCAL_GL_TEXTURE_WRAP_R: if (mContext->IsWebGL2()) isPNameValid = true; break; case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT: if (mContext->IsExtensionEnabled( WebGLExtensionID::EXT_texture_filter_anisotropic)) isPNameValid = true; break; } if (!isPNameValid) { mContext->ErrorInvalidEnumInfo("texParameter: pname", pname); return; } //////////////// // Validate params and invalidate if needed. bool paramBadEnum = false; bool paramBadValue = false; switch (pname) { case LOCAL_GL_TEXTURE_BASE_LEVEL: case LOCAL_GL_TEXTURE_MAX_LEVEL: paramBadValue = (param.i < 0); break; case LOCAL_GL_TEXTURE_COMPARE_MODE: paramBadValue = (param.i != LOCAL_GL_NONE && param.i != LOCAL_GL_COMPARE_REF_TO_TEXTURE); break; case LOCAL_GL_TEXTURE_COMPARE_FUNC: switch (param.i) { case LOCAL_GL_LEQUAL: case LOCAL_GL_GEQUAL: case LOCAL_GL_LESS: case LOCAL_GL_GREATER: case LOCAL_GL_EQUAL: case LOCAL_GL_NOTEQUAL: case LOCAL_GL_ALWAYS: case LOCAL_GL_NEVER: break; default: paramBadValue = true; break; } break; case LOCAL_GL_TEXTURE_MIN_FILTER: switch (param.i) { case LOCAL_GL_NEAREST: case LOCAL_GL_LINEAR: case LOCAL_GL_NEAREST_MIPMAP_NEAREST: case LOCAL_GL_LINEAR_MIPMAP_NEAREST: case LOCAL_GL_NEAREST_MIPMAP_LINEAR: case LOCAL_GL_LINEAR_MIPMAP_LINEAR: break; default: paramBadEnum = true; break; } break; case LOCAL_GL_TEXTURE_MAG_FILTER: switch (param.i) { case LOCAL_GL_NEAREST: case LOCAL_GL_LINEAR: break; default: paramBadEnum = true; break; } break; case LOCAL_GL_TEXTURE_WRAP_S: case LOCAL_GL_TEXTURE_WRAP_T: case LOCAL_GL_TEXTURE_WRAP_R: switch (param.i) { case LOCAL_GL_CLAMP_TO_EDGE: case LOCAL_GL_MIRRORED_REPEAT: case LOCAL_GL_REPEAT: break; default: paramBadEnum = true; break; } break; case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT: if (param.f < 1.0f) paramBadValue = true; break; } if (paramBadEnum) { if (!param.isFloat) { mContext->ErrorInvalidEnum( "pname 0x%04x: Invalid param" " 0x%04x.", pname, param.i); } else { mContext->ErrorInvalidEnum("pname 0x%04x: Invalid param %g.", pname, param.f); } return; } if (paramBadValue) { if (!param.isFloat) { mContext->ErrorInvalidValue( "pname 0x%04x: Invalid param %i" " (0x%x).", pname, param.i, param.i); } else { mContext->ErrorInvalidValue("pname 0x%04x: Invalid param %g.", pname, param.f); } return; } //////////////// // Store any needed values FloatOrInt clamped = param; bool invalidate = true; switch (pname) { case LOCAL_GL_TEXTURE_BASE_LEVEL: mBaseMipmapLevel = clamped.i; ClampLevelBaseAndMax(); clamped = FloatOrInt(GLint(mBaseMipmapLevel)); break; case LOCAL_GL_TEXTURE_MAX_LEVEL: mMaxMipmapLevel = clamped.i; ClampLevelBaseAndMax(); clamped = FloatOrInt(GLint(mMaxMipmapLevel)); break; case LOCAL_GL_TEXTURE_MIN_FILTER: mSamplingState.minFilter = clamped.i; break; case LOCAL_GL_TEXTURE_MAG_FILTER: mSamplingState.magFilter = clamped.i; break; case LOCAL_GL_TEXTURE_WRAP_S: mSamplingState.wrapS = clamped.i; break; case LOCAL_GL_TEXTURE_WRAP_T: mSamplingState.wrapT = clamped.i; break; case LOCAL_GL_TEXTURE_COMPARE_MODE: mSamplingState.compareMode = clamped.i; break; default: invalidate = false; // Texture completeness will not change. break; } if (invalidate) { InvalidateCaches(); } //////////////// if (!clamped.isFloat) mContext->gl->fTexParameteri(texTarget.get(), pname, clamped.i); else mContext->gl->fTexParameterf(texTarget.get(), pname, clamped.f); }