Commit 29669138 authored by Paul Bone's avatar Paul Bone
Browse files

Bug 1781728 - Clamp WebGL mipmap levels. r=jgilbert, a=RyanVM

parent e1ba7439
Loading
Loading
Loading
Loading
+49 −31
Original line number Diff line number Diff line
@@ -664,21 +664,12 @@ static bool ZeroTextureData(const WebGLContext* webgl, GLuint tex,
}

template <typename T, typename R>
static R Clamp(const T val, const R min, const R max) {
static constexpr R Clamp(const T val, const R min, const R max) {
  if (val < min) return min;
  if (val > max) return max;
  return static_cast<R>(val);
}

template <typename T, typename A, typename B>
static void ClampSelf(T* const out, const A min, const B max) {
  if (*out < min) {
    *out = T{min};
  } else if (*out > max) {
    *out = T{max};
  }
}

void WebGLTexture::ClampLevelBaseAndMax() {
  if (!mImmutable) return;

@@ -687,8 +678,20 @@ void WebGLTexture::ClampLevelBaseAndMax() {
  //  `[0, levels-1]`, `level_max` is then clamped to the range `
  //  `[level_base, levels-1]`, where `levels` is the parameter passed to
  //   TexStorage* for the texture object."
  ClampSelf(&mBaseMipmapLevel, 0u, mImmutableLevelCount - 1u);
  ClampSelf(&mMaxMipmapLevel, mBaseMipmapLevel, mImmutableLevelCount - 1u);
  MOZ_ASSERT(mImmutableLevelCount > 0);
  const auto oldBase = mBaseMipmapLevel;
  const auto oldMax = mMaxMipmapLevel;
  mBaseMipmapLevel = Clamp(mBaseMipmapLevel, 0u, mImmutableLevelCount - 1u);
  mMaxMipmapLevel =
      Clamp(mMaxMipmapLevel, mBaseMipmapLevel, mImmutableLevelCount - 1u);
  if (oldBase != mBaseMipmapLevel &&
      mBaseMipmapLevelState != MIPMAP_LEVEL_DEFAULT) {
    mBaseMipmapLevelState = MIPMAP_LEVEL_DIRTY;
  }
  if (oldMax != mMaxMipmapLevel &&
      mMaxMipmapLevelState != MIPMAP_LEVEL_DEFAULT) {
    mMaxMipmapLevelState = MIPMAP_LEVEL_DIRTY;
  }

  // Note: This means that immutable textures are *always* texture-complete!
}
@@ -729,6 +732,10 @@ bool WebGLTexture::BindTexture(TexTarget texTarget) {
  return true;
}

static constexpr GLint ClampMipmapLevelForDriver(uint32_t level) {
  return Clamp(level, uint8_t{0}, WebGLTexture::kMaxLevelCount);
}

void WebGLTexture::GenerateMipmap() {
  // GLES 3.0.4 p160:
  // "Mipmap generation replaces texel array levels level base + 1 through q
@@ -806,8 +813,21 @@ void WebGLTexture::GenerateMipmap() {
  gl::GLContext* gl = mContext->gl;

  if (gl->WorkAroundDriverBugs()) {
    // Don't generate mipmaps when the base level is out of range:
    if (!(mImmutable && mBaseMipmapLevel >= mImmutableLevelCount)) {
    // If we first set GL_TEXTURE_BASE_LEVEL to a number such as 20, then set
    // MGL_TEXTURE_MAX_LEVEL to a smaller number like 8, our copy of the
    // base level will be lowered, but we havn't yet updated the driver, we
    // should do so now, before calling glGenerateMipmap().
    if (mBaseMipmapLevelState == MIPMAP_LEVEL_DIRTY) {
      gl->fTexParameteri(mTarget.get(), LOCAL_GL_TEXTURE_BASE_LEVEL,
                         ClampMipmapLevelForDriver(mBaseMipmapLevel));
      mBaseMipmapLevelState = MIPMAP_LEVEL_CLEAN;
    }
    if (mMaxMipmapLevelState == MIPMAP_LEVEL_DIRTY) {
      gl->fTexParameteri(mTarget.get(), LOCAL_GL_TEXTURE_MAX_LEVEL,
                         ClampMipmapLevelForDriver(mMaxMipmapLevel));
      mMaxMipmapLevelState = MIPMAP_LEVEL_CLEAN;
    }

    // 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
@@ -820,7 +840,6 @@ void WebGLTexture::GenerateMipmap() {
    gl->fGenerateMipmap(mTarget.get());
    gl->fTexParameteri(mTarget.get(), LOCAL_GL_TEXTURE_MIN_FILTER,
                       mSamplingState.minFilter.get());
    }
  } else {
    gl->fGenerateMipmap(mTarget.get());
  }
@@ -1031,18 +1050,17 @@ void WebGLTexture::TexParameter(TexTarget texTarget, GLenum pname,
  switch (pname) {
    case LOCAL_GL_TEXTURE_BASE_LEVEL: {
      mBaseMipmapLevel = clamped.i;
      mBaseMipmapLevelState = MIPMAP_LEVEL_CLEAN;
      ClampLevelBaseAndMax();
      const auto forDriver =
          Clamp(mBaseMipmapLevel, uint8_t{0}, kMaxLevelCount);
      clamped = FloatOrInt(forDriver);
      clamped = FloatOrInt(ClampMipmapLevelForDriver(mBaseMipmapLevel));
      break;
    }

    case LOCAL_GL_TEXTURE_MAX_LEVEL: {
      mMaxMipmapLevel = clamped.i;
      mMaxMipmapLevelState = MIPMAP_LEVEL_CLEAN;
      ClampLevelBaseAndMax();
      const auto forDriver = Clamp(mMaxMipmapLevel, uint8_t{0}, kMaxLevelCount);
      clamped = FloatOrInt(forDriver);
      clamped = FloatOrInt(ClampMipmapLevelForDriver(mMaxMipmapLevel));
      break;
    }

+10 −0
Original line number Diff line number Diff line
@@ -120,6 +120,16 @@ class WebGLTexture final : public WebGLContextBoundObject,
  // You almost certainly don't want to query mMaxMipmapLevel.
  // You almost certainly want MaxEffectiveMipmapLevel().

  // These "dirty" flags are set when the level is updated (eg indirectly by
  // clamping) and cleared when we tell the driver.
  enum MipmapLevelState : uint8_t {
    MIPMAP_LEVEL_DEFAULT,
    MIPMAP_LEVEL_CLEAN,
    MIPMAP_LEVEL_DIRTY
  };
  MipmapLevelState mBaseMipmapLevelState = MIPMAP_LEVEL_DEFAULT;
  MipmapLevelState mMaxMipmapLevelState = MIPMAP_LEVEL_DEFAULT;

  webgl::SamplingState mSamplingState;

  mutable const GLint* mCurSwizzle =