Commit 6b49449d by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Fix mipmap generation and level redefinition

When generating mipmaps, the non-base levels are redefined to be compatible. mRedefinedLevels was not updated to take this into account, resulting in invalid copies to the image. Additionally, noted a few spots where ImageDesc is used to respecify the image, but those are not up-to-date when the backend functions are called. Changed those to directly get the necessary information from the allocated image. Bug: chromium:1094644 Bug: chromium:1094599 Change-Id: I2afc9e5a53f24ef56836c5d7eec2e3e11df0ef61 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2267423Reviewed-by: 's avatarCody Northrop <cnorthrop@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
parent 77c062aa
...@@ -82,6 +82,8 @@ class BitSetT final ...@@ -82,6 +82,8 @@ class BitSetT final
std::size_t mCurrentBit; std::size_t mCurrentBit;
}; };
using value_type = BitsT;
BitSetT(); BitSetT();
constexpr explicit BitSetT(BitsT value); constexpr explicit BitSetT(BitsT value);
...@@ -455,8 +457,8 @@ BitSetT<N, BitsT, ParamT>::Iterator::Iterator(const BitSetT &bits) : mBitsCopy(b ...@@ -455,8 +457,8 @@ BitSetT<N, BitsT, ParamT>::Iterator::Iterator(const BitSetT &bits) : mBitsCopy(b
} }
template <size_t N, typename BitsT, typename ParamT> template <size_t N, typename BitsT, typename ParamT>
ANGLE_INLINE typename BitSetT<N, BitsT, ParamT>::Iterator &BitSetT<N, BitsT, ParamT>::Iterator:: ANGLE_INLINE typename BitSetT<N, BitsT, ParamT>::Iterator &
operator++() BitSetT<N, BitsT, ParamT>::Iterator::operator++()
{ {
ASSERT(mBitsCopy.any()); ASSERT(mBitsCopy.any());
mBitsCopy.reset(static_cast<ParamT>(mCurrentBit)); mBitsCopy.reset(static_cast<ParamT>(mCurrentBit));
......
...@@ -1396,13 +1396,12 @@ angle::Result TextureVk::generateMipmap(const gl::Context *context) ...@@ -1396,13 +1396,12 @@ angle::Result TextureVk::generateMipmap(const gl::Context *context)
} }
angle::Result TextureVk::copyAndStageImageSubresource(ContextVk *contextVk, angle::Result TextureVk::copyAndStageImageSubresource(ContextVk *contextVk,
const gl::ImageDesc &desc,
bool ignoreLayerCount, bool ignoreLayerCount,
uint32_t currentLayer, uint32_t currentLayer,
uint32_t srcLevelVk, uint32_t srcLevelVk,
uint32_t dstLevelGL) uint32_t dstLevelGL)
{ {
const gl::Extents &baseLevelExtents = desc.size; const gl::Extents &baseLevelExtents = mImage->getLevelExtents(srcLevelVk);
VkExtent3D updatedExtents; VkExtent3D updatedExtents;
VkOffset3D offset = {}; VkOffset3D offset = {};
...@@ -1429,13 +1428,15 @@ angle::Result TextureVk::copyAndStageImageSubresource(ContextVk *contextVk, ...@@ -1429,13 +1428,15 @@ angle::Result TextureVk::copyAndStageImageSubresource(ContextVk *contextVk,
ASSERT(stagingBuffer); ASSERT(stagingBuffer);
uint32_t bufferRowLength = updatedExtents.width; uint32_t bufferRowLength = updatedExtents.width;
uint32_t bufferImageHeight = updatedExtents.height; uint32_t bufferImageHeight = updatedExtents.height;
if (desc.format.info->compressed) const gl::InternalFormat &formatInfo =
gl::GetSizedInternalFormatInfo(mImage->getFormat().internalFormat);
if (formatInfo.compressed)
{ {
// In the case of a compressed texture, bufferRowLength can never be smaller than the // In the case of a compressed texture, bufferRowLength can never be smaller than the
// compressed format's compressed block width, and bufferImageHeight can never be smaller // compressed format's compressed block width, and bufferImageHeight can never be smaller
// than the compressed block height. // than the compressed block height.
bufferRowLength = std::max(bufferRowLength, desc.format.info->compressedBlockWidth); bufferRowLength = std::max(bufferRowLength, formatInfo.compressedBlockWidth);
bufferImageHeight = std::max(bufferImageHeight, desc.format.info->compressedBlockHeight); bufferImageHeight = std::max(bufferImageHeight, formatInfo.compressedBlockHeight);
} }
ANGLE_TRY(mImage->stageSubresourceUpdateFromBuffer( ANGLE_TRY(mImage->stageSubresourceUpdateFromBuffer(
contextVk, bufferSize, dstLevelGL, currentLayer, layerCount, bufferRowLength, contextVk, bufferSize, dstLevelGL, currentLayer, layerCount, bufferRowLength,
...@@ -1531,10 +1532,7 @@ angle::Result TextureVk::respecifyImageAttributesAndLevels(ContextVk *contextVk, ...@@ -1531,10 +1532,7 @@ angle::Result TextureVk::respecifyImageAttributesAndLevels(ContextVk *contextVk,
// Pull data from the current image and stage it as an update for the new image // Pull data from the current image and stage it as an update for the new image
// First we populate the staging buffer with current level data // First we populate the staging buffer with current level data
const gl::ImageDesc &desc = ANGLE_TRY(copyAndStageImageSubresource(contextVk, true, layer, levelVK, levelGL));
mState.getImageDesc(gl::TextureTypeToTarget(mState.getType(), layer), levelGL);
ANGLE_TRY(copyAndStageImageSubresource(contextVk, desc, true, layer, levelVK, levelGL));
} }
} }
...@@ -1705,8 +1703,33 @@ angle::Result TextureVk::syncState(const gl::Context *context, ...@@ -1705,8 +1703,33 @@ angle::Result TextureVk::syncState(const gl::Context *context,
bool isGenerateMipmap = source == gl::TextureCommand::GenerateMipmap; bool isGenerateMipmap = source == gl::TextureCommand::GenerateMipmap;
if (isGenerateMipmap) if (isGenerateMipmap)
{ {
mImage->removeStagedUpdates(contextVk, mState.getEffectiveBaseLevel() + 1, // Remove staged updates to the range that's being respecified (which is all the mips except
mState.getMipmapMaxLevel()); // mip 0).
uint32_t baseLevel = mState.getEffectiveBaseLevel() + 1;
uint32_t maxLevel = mState.getMipmapMaxLevel();
mImage->removeStagedUpdates(contextVk, baseLevel, maxLevel);
// These levels are no longer incompatibly defined if they previously were. The
// corresponding bits in mRedefinedLevels should be cleared. Note that the texture may be
// simultaneously rebased, so mImage->getBaseLevel() and getEffectiveBaseLevel() may be
// different.
static_assert(gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS < 32,
"levels mask assumes 32-bits is enough");
gl::TexLevelMask::value_type levelsMask =
angle::Bit<uint32_t>(maxLevel + 1 - baseLevel) - 1;
uint32_t imageBaseLevel = mImage->getBaseLevel();
if (imageBaseLevel > baseLevel)
{
levelsMask >>= imageBaseLevel - baseLevel;
}
else
{
levelsMask <<= baseLevel - imageBaseLevel;
}
mRedefinedLevels &= gl::TexLevelMask(~levelsMask);
} }
// Set base and max level before initializing the image // Set base and max level before initializing the image
...@@ -1735,7 +1758,16 @@ angle::Result TextureVk::syncState(const gl::Context *context, ...@@ -1735,7 +1758,16 @@ angle::Result TextureVk::syncState(const gl::Context *context,
if (oldUsageFlags != mImageUsageFlags || oldCreateFlags != mImageCreateFlags || if (oldUsageFlags != mImageUsageFlags || oldCreateFlags != mImageCreateFlags ||
mRedefinedLevels.any() || isMipmapEnabledByMinFilter) mRedefinedLevels.any() || isMipmapEnabledByMinFilter)
{ {
ANGLE_TRY(respecifyImageAttributes(contextVk)); // If generating mipmap and base level is incompatibly redefined, the image is going to be
// recreated. Don't try to preserve the other mips.
if (isGenerateMipmap && mRedefinedLevels.test(0))
{
releaseImage(contextVk);
}
else
{
ANGLE_TRY(respecifyImageAttributes(contextVk));
}
} }
// If generating mipmaps and the image is not full-mip already, make sure it's recreated to the // If generating mipmaps and the image is not full-mip already, make sure it's recreated to the
......
...@@ -345,7 +345,6 @@ class TextureVk : public TextureImpl, public angle::ObserverInterface ...@@ -345,7 +345,6 @@ class TextureVk : public TextureImpl, public angle::ObserverInterface
// each subresource of the image and stage it for another subresource. When all subresources // each subresource of the image and stage it for another subresource. When all subresources
// are taken care of, the image is recreated. // are taken care of, the image is recreated.
angle::Result copyAndStageImageSubresource(ContextVk *contextVk, angle::Result copyAndStageImageSubresource(ContextVk *contextVk,
const gl::ImageDesc &desc,
bool ignoreLayerCount, bool ignoreLayerCount,
uint32_t currentLayer, uint32_t currentLayer,
uint32_t srcLevelVk, uint32_t srcLevelVk,
......
...@@ -2794,6 +2794,191 @@ TEST_P(Texture2DBaseMaxTestES3, RedefineEveryLevelToAnotherFormat) ...@@ -2794,6 +2794,191 @@ TEST_P(Texture2DBaseMaxTestES3, RedefineEveryLevelToAnotherFormat)
} }
} }
// Test that generating mipmaps after incompatibly redefining a level works.
TEST_P(Texture2DBaseMaxTestES3, GenerateMipmapAfterRedefine)
{
initTest();
// Test that all mips have the expected data initially (this makes sure the texture image is
// created already).
for (uint32_t lod = 0; lod < kMipCount; ++lod)
{
setLodUniform(lod);
drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, kMipColors[lod]);
}
// Redefine level 1 (any level would do other than 0) to an incompatible size, say the same size
// as level 0.
const GLColor kNewMipColor = GLColor::yellow;
std::array<GLColor, getMipDataSize(kMip0Size, 0)> newMipData;
std::fill(newMipData.begin(), newMipData.end(), kNewMipColor);
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kMip0Size, kMip0Size, 0, GL_RGBA, GL_UNSIGNED_BYTE,
newMipData.data());
// Generate mipmaps. This should redefine level 1 back to being compatible with level 0.
glGenerateMipmap(GL_TEXTURE_2D);
// Test that the texture looks as expected.
const int w = getWindowWidth() - 1;
const int h = getWindowHeight() - 1;
for (uint32_t lod = 0; lod < kMipCount; ++lod)
{
setLodUniform(lod);
drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, kMipColors[0]);
EXPECT_PIXEL_COLOR_EQ(w, 0, kMipColors[0]);
EXPECT_PIXEL_COLOR_EQ(0, h, kMipColors[0]);
EXPECT_PIXEL_COLOR_EQ(w, h, kMipColors[0]);
}
}
// Test that generating mipmaps after incompatibly redefining a level while simultaneously changing
// the base level works.
TEST_P(Texture2DBaseMaxTestES3, GenerateMipmapAfterRedefineAndRebase)
{
ANGLE_SKIP_TEST_IF(IsWindows() && IsAMD() && IsOpenGL());
initTest();
// Test that all mips have the expected data initially (this makes sure the texture image is
// created already).
for (uint32_t lod = 0; lod < kMipCount; ++lod)
{
setLodUniform(lod);
drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, kMipColors[lod]);
}
// Redefine level 2 to an incompatible size, say the same size as level 0.
const GLColor kNewMipColor = GLColor::yellow;
std::array<GLColor, getMipDataSize(kMip0Size, 0)> newMipData;
std::fill(newMipData.begin(), newMipData.end(), kNewMipColor);
glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA8, kMip0Size, kMip0Size, 0, GL_RGBA, GL_UNSIGNED_BYTE,
newMipData.data());
// Set base level of the texture to 1 then generate mipmaps. Level 2 that's redefined should
// go back to being compatibly defined.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1);
glGenerateMipmap(GL_TEXTURE_2D);
// Test that the texture looks as expected.
const int w = getWindowWidth() - 1;
const int h = getWindowHeight() - 1;
for (uint32_t lod = 0; lod < kMipCount; ++lod)
{
setLodUniform(lod);
drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, kMipColors[1]);
EXPECT_PIXEL_COLOR_EQ(w, 0, kMipColors[1]);
EXPECT_PIXEL_COLOR_EQ(0, h, kMipColors[1]);
EXPECT_PIXEL_COLOR_EQ(w, h, kMipColors[1]);
}
// Redefine level 1 (current base level) to an incompatible size.
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kMip0Size, kMip0Size, 0, GL_RGBA, GL_UNSIGNED_BYTE,
newMipData.data());
// Set base level of the texture back to 0 then generate mipmaps. Level 1 should go back to
// being compatibly defined.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glGenerateMipmap(GL_TEXTURE_2D);
// Test that the texture looks as expected.
for (uint32_t lod = 0; lod < kMipCount; ++lod)
{
setLodUniform(lod);
drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, kMipColors[0]);
EXPECT_PIXEL_COLOR_EQ(w, 0, kMipColors[0]);
EXPECT_PIXEL_COLOR_EQ(0, h, kMipColors[0]);
EXPECT_PIXEL_COLOR_EQ(w, h, kMipColors[0]);
}
}
// Test that generating mipmaps after incompatibly redefining the base level of the texture works.
TEST_P(Texture2DBaseMaxTestES3, GenerateMipmapAfterRedefiningBase)
{
initTest();
// Test that all mips have the expected data initially (this makes sure the texture image is
// created already).
for (uint32_t lod = 0; lod < kMipCount; ++lod)
{
setLodUniform(lod);
drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, kMipColors[lod]);
}
// Redefine level 0 to an incompatible size.
const GLColor kNewMipColor = GLColor::yellow;
std::array<GLColor, getMipDataSize(kMip0Size * 2, 0)> newMipData;
std::fill(newMipData.begin(), newMipData.end(), kNewMipColor);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kMip0Size * 2, kMip0Size * 2, 0, GL_RGBA,
GL_UNSIGNED_BYTE, newMipData.data());
// Generate mipmaps.
glGenerateMipmap(GL_TEXTURE_2D);
// Test that the texture looks as expected.
const int w = getWindowWidth() - 1;
const int h = getWindowHeight() - 1;
for (uint32_t lod = 0; lod < kMipCount + 1; ++lod)
{
setLodUniform(lod);
drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, kNewMipColor);
EXPECT_PIXEL_COLOR_EQ(w, 0, kNewMipColor);
EXPECT_PIXEL_COLOR_EQ(0, h, kNewMipColor);
EXPECT_PIXEL_COLOR_EQ(w, h, kNewMipColor);
}
}
// Test that generating mipmaps after incompatibly redefining the base level while simultaneously
// changing MAX_LEVEL works.
TEST_P(Texture2DBaseMaxTestES3, GenerateMipmapAfterRedefiningBaseAndChangingMax)
{
initTest();
// Test that all mips have the expected data initially (this makes sure the texture image is
// created already).
for (uint32_t lod = 0; lod < kMipCount; ++lod)
{
setLodUniform(lod);
drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, kMipColors[lod]);
}
// Redefine level 0 to an incompatible size.
const GLColor kNewMipColor = GLColor::yellow;
std::array<GLColor, getMipDataSize(kMip0Size * 2, 0)> newMipData;
std::fill(newMipData.begin(), newMipData.end(), kNewMipColor);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kMip0Size * 2, kMip0Size * 2, 0, GL_RGBA,
GL_UNSIGNED_BYTE, newMipData.data());
// Set max level of the texture to 2 then generate mipmaps.
constexpr uint32_t kMaxLevel = 2;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, kMaxLevel);
glGenerateMipmap(GL_TEXTURE_2D);
// Test that the texture looks as expected.
const int w = getWindowWidth() - 1;
const int h = getWindowHeight() - 1;
for (uint32_t lod = 0; lod <= kMaxLevel; ++lod)
{
setLodUniform(lod);
drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, kNewMipColor);
EXPECT_PIXEL_COLOR_EQ(w, 0, kNewMipColor);
EXPECT_PIXEL_COLOR_EQ(0, h, kNewMipColor);
EXPECT_PIXEL_COLOR_EQ(w, h, kNewMipColor);
}
}
// Test to check that texture completeness is determined correctly when the texture base level is // Test to check that texture completeness is determined correctly when the texture base level is
// greater than 0, and also that level 0 is not sampled when base level is greater than 0. // greater than 0, and also that level 0 is not sampled when base level is greater than 0.
TEST_P(Texture2DTestES3, DrawWithBaseLevel1) TEST_P(Texture2DTestES3, DrawWithBaseLevel1)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment