Commit b5a71140 by Charlie Lao Committed by Commit Bot

Vulkan: Make storage actually immutable for immutable textures

The immutable textures are intended to be "immutable". Right now we are still re-allocating VkImage object based on base_level and max_level. This causes a problem for rendering to a level that is beyond [base_level, max_level], which is totally within OpenGL spec. This CL makes an immutable texture always allocate from 0 to max levels that are specified by a glTexStorage call. Changing base_level will not trigger re-allocation of VkImage object. Bug: b/181800403 Change-Id: I4b4ddea17b7f6f7bfd8f36bfe8bb3a35b5c180b6 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2368038Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarIan Elliott <ianelliott@google.com> Commit-Queue: Charlie Lao <cclao@google.com>
parent 969b8f31
...@@ -263,6 +263,12 @@ const ImageDesc &TextureState::getBaseLevelDesc() const ...@@ -263,6 +263,12 @@ const ImageDesc &TextureState::getBaseLevelDesc() const
return getImageDesc(getBaseImageTarget(), getEffectiveBaseLevel()); return getImageDesc(getBaseImageTarget(), getEffectiveBaseLevel());
} }
const ImageDesc &TextureState::getLevelZeroDesc() const
{
ASSERT(mType != TextureType::CubeMap || isCubeComplete());
return getImageDesc(getBaseImageTarget(), 0);
}
void TextureState::setCrop(const Rectangle &rect) void TextureState::setCrop(const Rectangle &rect)
{ {
mCropRect = rect; mCropRect = rect;
...@@ -1517,13 +1523,12 @@ angle::Result Texture::setStorage(Context *context, ...@@ -1517,13 +1523,12 @@ angle::Result Texture::setStorage(Context *context,
mState.mImmutableFormat = true; mState.mImmutableFormat = true;
mState.mImmutableLevels = static_cast<GLuint>(levels); mState.mImmutableLevels = static_cast<GLuint>(levels);
ANGLE_TRY(mTexture->setStorage(context, type, levels, internalFormat, size));
mState.clearImageDescs(); mState.clearImageDescs();
mState.setImageDescChain(0, static_cast<GLuint>(levels - 1), size, Format(internalFormat), mState.setImageDescChain(0, static_cast<GLuint>(levels - 1), size, Format(internalFormat),
InitState::MayNeedInit); InitState::MayNeedInit);
ANGLE_TRY(mTexture->setStorage(context, type, levels, internalFormat, size));
// Changing the texture to immutable can trigger a change in the base and max levels: // Changing the texture to immutable can trigger a change in the base and max levels:
// GLES 3.0.4 section 3.8.10 pg 158: // GLES 3.0.4 section 3.8.10 pg 158:
// "For immutable-format textures, levelbase is clamped to the range[0;levels],levelmax is then // "For immutable-format textures, levelbase is clamped to the range[0;levels],levelmax is then
...@@ -1581,15 +1586,15 @@ angle::Result Texture::setStorageMultisample(Context *context, ...@@ -1581,15 +1586,15 @@ angle::Result Texture::setStorageMultisample(Context *context,
const TextureCaps &formatCaps = context->getTextureCaps().get(internalFormat); const TextureCaps &formatCaps = context->getTextureCaps().get(internalFormat);
samples = formatCaps.getNearestSamples(samples); samples = formatCaps.getNearestSamples(samples);
ANGLE_TRY(mTexture->setStorageMultisample(context, type, samples, internalFormat, size,
fixedSampleLocations));
mState.mImmutableFormat = true; mState.mImmutableFormat = true;
mState.mImmutableLevels = static_cast<GLuint>(1); mState.mImmutableLevels = static_cast<GLuint>(1);
mState.clearImageDescs(); mState.clearImageDescs();
mState.setImageDescChainMultisample(size, Format(internalFormat), samples, fixedSampleLocations, mState.setImageDescChainMultisample(size, Format(internalFormat), samples, fixedSampleLocations,
InitState::MayNeedInit); InitState::MayNeedInit);
ANGLE_TRY(mTexture->setStorageMultisample(context, type, samples, internalFormat, size,
fixedSampleLocations));
signalDirtyStorage(InitState::MayNeedInit); signalDirtyStorage(InitState::MayNeedInit);
return angle::Result::Continue; return angle::Result::Continue;
......
...@@ -152,6 +152,7 @@ class TextureState final : private angle::NonCopyable ...@@ -152,6 +152,7 @@ class TextureState final : private angle::NonCopyable
// Returns the desc of the base level. Only valid for cube-complete/mip-complete textures. // Returns the desc of the base level. Only valid for cube-complete/mip-complete textures.
const ImageDesc &getBaseLevelDesc() const; const ImageDesc &getBaseLevelDesc() const;
const ImageDesc &getLevelZeroDesc() const;
// GLES1 emulation: For GL_OES_draw_texture // GLES1 emulation: For GL_OES_draw_texture
void setCrop(const Rectangle &rect); void setCrop(const Rectangle &rect);
......
...@@ -208,7 +208,7 @@ angle::Result MemoryObjectVk::createImage(ContextVk *contextVk, ...@@ -208,7 +208,7 @@ angle::Result MemoryObjectVk::createImage(ContextVk *contextVk,
contextVk, type, vkExtents, vkFormat, 1, imageUsageFlags, createFlags, contextVk, type, vkExtents, vkFormat, 1, imageUsageFlags, createFlags,
vk::ImageLayout::Undefined, &externalMemoryImageCreateInfo, gl::LevelIndex(0), vk::ImageLayout::Undefined, &externalMemoryImageCreateInfo, gl::LevelIndex(0),
gl::LevelIndex(static_cast<uint32_t>(levels) - 1), static_cast<uint32_t>(levels), gl::LevelIndex(static_cast<uint32_t>(levels) - 1), static_cast<uint32_t>(levels),
layerCount, contextVk->isRobustResourceInitEnabled(), nullptr)); layerCount, contextVk->isRobustResourceInitEnabled(), false, nullptr));
VkMemoryRequirements externalMemoryRequirements; VkMemoryRequirements externalMemoryRequirements;
image->getImage().getMemoryRequirements(renderer->getDevice(), &externalMemoryRequirements); image->getImage().getMemoryRequirements(renderer->getDevice(), &externalMemoryRequirements);
......
...@@ -106,7 +106,7 @@ angle::Result RenderbufferVk::setStorageImpl(const gl::Context *context, ...@@ -106,7 +106,7 @@ angle::Result RenderbufferVk::setStorageImpl(const gl::Context *context,
ANGLE_TRY(mImage->initExternal(contextVk, gl::TextureType::_2D, extents, format, imageSamples, ANGLE_TRY(mImage->initExternal(contextVk, gl::TextureType::_2D, extents, format, imageSamples,
usage, vk::kVkImageCreateFlagsNone, vk::ImageLayout::Undefined, usage, vk::kVkImageCreateFlagsNone, vk::ImageLayout::Undefined,
nullptr, gl::LevelIndex(0), gl::LevelIndex(0), 1, 1, robustInit, nullptr, gl::LevelIndex(0), gl::LevelIndex(0), 1, 1, robustInit,
nullptr)); false, nullptr));
VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
ANGLE_TRY(mImage->initMemory(contextVk, renderer->getMemoryProperties(), flags)); ANGLE_TRY(mImage->initMemory(contextVk, renderer->getMemoryProperties(), flags));
......
...@@ -134,7 +134,7 @@ angle::Result InitImageHelper(DisplayVk *displayVk, ...@@ -134,7 +134,7 @@ angle::Result InitImageHelper(DisplayVk *displayVk,
ANGLE_TRY(imageHelper->initExternal( ANGLE_TRY(imageHelper->initExternal(
displayVk, gl::TextureType::_2D, extents, vkFormat, samples, usage, displayVk, gl::TextureType::_2D, extents, vkFormat, samples, usage,
vk::kVkImageCreateFlagsNone, vk::ImageLayout::Undefined, nullptr, gl::LevelIndex(0), vk::kVkImageCreateFlagsNone, vk::ImageLayout::Undefined, nullptr, gl::LevelIndex(0),
gl::LevelIndex(0), 1, 1, isRobustResourceInitEnabled, nullptr)); gl::LevelIndex(0), 1, 1, isRobustResourceInitEnabled, false, nullptr));
return angle::Result::Continue; return angle::Result::Continue;
} }
......
...@@ -52,8 +52,8 @@ constexpr angle::SubjectIndex kTextureImageSubjectIndex = 0; ...@@ -52,8 +52,8 @@ constexpr angle::SubjectIndex kTextureImageSubjectIndex = 0;
bool IsTextureLevelInAllocatedImage(const vk::ImageHelper &image, bool IsTextureLevelInAllocatedImage(const vk::ImageHelper &image,
gl::LevelIndex textureLevelIndexGL) gl::LevelIndex textureLevelIndexGL)
{ {
gl::LevelIndex imageBaseLevel = image.getBaseLevel(); gl::LevelIndex imageFirstAllocateLevel = image.getFirstAllocatedLevel();
if (textureLevelIndexGL < imageBaseLevel) if (textureLevelIndexGL < imageFirstAllocateLevel)
{ {
return false; return false;
} }
...@@ -1291,6 +1291,11 @@ angle::Result TextureVk::setStorageMultisample(const gl::Context *context, ...@@ -1291,6 +1291,11 @@ angle::Result TextureVk::setStorageMultisample(const gl::Context *context,
{ {
releaseImage(contextVk); releaseImage(contextVk);
} }
ASSERT(mState.getImmutableFormat());
ASSERT(!mRedefinedLevels.any());
ANGLE_TRY(initImmutableImage(contextVk, format));
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -1589,6 +1594,9 @@ angle::Result TextureVk::redefineLevel(const gl::Context *context, ...@@ -1589,6 +1594,9 @@ angle::Result TextureVk::redefineLevel(const gl::Context *context,
bool isUpdateToSingleLevelImage = bool isUpdateToSingleLevelImage =
mImage->getLevelCount() == 1 && mImage->getBaseLevel() == levelIndexGL; mImage->getLevelCount() == 1 && mImage->getBaseLevel() == levelIndexGL;
// If it is single level image, baseLevel must equal to firstAllocateLevel
ASSERT(!isUpdateToSingleLevelImage ||
mImage->getFirstAllocatedLevel() == mImage->getBaseLevel());
// If incompatible, and redefining the single-level image, release it so it can be // If incompatible, and redefining the single-level image, release it so it can be
// recreated immediately. This is an optimization to avoid an extra copy. // recreated immediately. This is an optimization to avoid an extra copy.
...@@ -1742,8 +1750,8 @@ angle::Result TextureVk::generateMipmapsWithCompute(ContextVk *contextVk) ...@@ -1742,8 +1750,8 @@ angle::Result TextureVk::generateMipmapsWithCompute(ContextVk *contextVk)
// If the image has more levels than supported, generate as many mips as possible at a time. // If the image has more levels than supported, generate as many mips as possible at a time.
const vk::LevelIndex maxGenerateLevels(UtilsVk::GetGenerateMipmapMaxLevels(contextVk)); const vk::LevelIndex maxGenerateLevels(UtilsVk::GetGenerateMipmapMaxLevels(contextVk));
for (vk::LevelIndex destBaseLevelVk =
for (vk::LevelIndex destBaseLevelVk(1); mImage->toVkLevel(gl::LevelIndex(mState.getEffectiveBaseLevel() + 1));
destBaseLevelVk < vk::LevelIndex(mImage->getLevelCount()); destBaseLevelVk < vk::LevelIndex(mImage->getLevelCount());
destBaseLevelVk = destBaseLevelVk + maxGenerateLevels.get()) destBaseLevelVk = destBaseLevelVk + maxGenerateLevels.get())
{ {
...@@ -1812,16 +1820,17 @@ angle::Result TextureVk::generateMipmapsWithCPU(const gl::Context *context) ...@@ -1812,16 +1820,17 @@ angle::Result TextureVk::generateMipmapsWithCPU(const gl::Context *context)
{ {
ContextVk *contextVk = vk::GetImpl(context); ContextVk *contextVk = vk::GetImpl(context);
const VkExtent3D baseLevelExtents = mImage->getExtents(); gl::LevelIndex baseLevelGL(mState.getEffectiveBaseLevel());
vk::LevelIndex baseLevelVk = mImage->toVkLevel(baseLevelGL);
const gl::Extents baseLevelExtents = mImage->getLevelExtents(baseLevelVk);
uint32_t imageLayerCount = mImage->getLayerCount(); uint32_t imageLayerCount = mImage->getLayerCount();
uint8_t *imageData = nullptr; uint8_t *imageData = nullptr;
gl::Box imageArea(0, 0, 0, baseLevelExtents.width, baseLevelExtents.height, gl::Box imageArea(0, 0, 0, baseLevelExtents.width, baseLevelExtents.height,
baseLevelExtents.depth); baseLevelExtents.depth);
ANGLE_TRY(copyImageDataToBufferAndGetData(contextVk, ANGLE_TRY(copyImageDataToBufferAndGetData(contextVk, baseLevelGL, imageLayerCount, imageArea,
gl::LevelIndex(mState.getEffectiveBaseLevel()), &imageData));
imageLayerCount, imageArea, &imageData));
const angle::Format &angleFormat = mImage->getFormat().actualImageFormat(); const angle::Format &angleFormat = mImage->getFormat().actualImageFormat();
GLuint sourceRowPitch = baseLevelExtents.width * angleFormat.pixelBytes; GLuint sourceRowPitch = baseLevelExtents.width * angleFormat.pixelBytes;
...@@ -1835,11 +1844,11 @@ angle::Result TextureVk::generateMipmapsWithCPU(const gl::Context *context) ...@@ -1835,11 +1844,11 @@ angle::Result TextureVk::generateMipmapsWithCPU(const gl::Context *context)
{ {
size_t bufferOffset = layer * baseLevelAllocationSize; size_t bufferOffset = layer * baseLevelAllocationSize;
ANGLE_TRY(generateMipmapLevelsWithCPU( ANGLE_TRY(generateMipmapLevelsWithCPU(contextVk, angleFormat, layer, baseLevelGL + 1,
contextVk, angleFormat, layer, gl::LevelIndex(mState.getEffectiveBaseLevel() + 1), gl::LevelIndex(mState.getMipmapMaxLevel()),
gl::LevelIndex(mState.getMipmapMaxLevel()), baseLevelExtents.width, baseLevelExtents.width, baseLevelExtents.height,
baseLevelExtents.height, baseLevelExtents.depth, sourceRowPitch, sourceDepthPitch, baseLevelExtents.depth, sourceRowPitch,
imageData + bufferOffset)); sourceDepthPitch, imageData + bufferOffset));
} }
ASSERT(!mRedefinedLevels.any()); ASSERT(!mRedefinedLevels.any());
...@@ -1860,7 +1869,7 @@ angle::Result TextureVk::generateMipmap(const gl::Context *context) ...@@ -1860,7 +1869,7 @@ angle::Result TextureVk::generateMipmap(const gl::Context *context)
// Only staged update here is the robust resource init if any. // Only staged update here is the robust resource init if any.
ANGLE_TRY(ensureImageInitialized(contextVk, ImageMipLevels::FullMipChain)); ANGLE_TRY(ensureImageInitialized(contextVk, ImageMipLevels::FullMipChain));
vk::LevelIndex maxLevel(mState.getMipmapMaxLevel() - mState.getEffectiveBaseLevel()); vk::LevelIndex maxLevel = mImage->toVkLevel(gl::LevelIndex(mState.getMipmapMaxLevel()));
ASSERT(maxLevel != vk::LevelIndex(0)); ASSERT(maxLevel != vk::LevelIndex(0));
// If it's possible to generate mipmap in compute, that would give the best possible // If it's possible to generate mipmap in compute, that would give the best possible
...@@ -1927,14 +1936,29 @@ angle::Result TextureVk::updateBaseMaxLevels(ContextVk *contextVk, ...@@ -1927,14 +1936,29 @@ angle::Result TextureVk::updateBaseMaxLevels(ContextVk *contextVk,
return angle::Result::Continue; return angle::Result::Continue;
} }
// With a valid image, check if only changing the maxLevel to a subset of the texture's actual bool respecifyImage = false;
// number of mip levels if (mState.getImmutableFormat())
if (!baseLevelChanged && (maxLevel < baseLevel + mImage->getLevelCount()))
{ {
// Don't need to respecify the texture; but do need to update which vkImageView's are // For immutable texture, baseLevel/maxLevel should be a subset of the texture's actual
// served up by ImageViewHelper // number of mip levels. We don't need to respecify an image.
ASSERT(!baseLevelChanged || baseLevel >= mImage->getFirstAllocatedLevel());
ASSERT(!maxLevelChanged || maxLevel < gl::LevelIndex(mImage->getLevelCount()));
}
else if (!baseLevelChanged && (maxLevel < baseLevel + mImage->getLevelCount()))
{
// With a valid image, check if only changing the maxLevel to a subset of the texture's
// actual number of mip levels
ASSERT(maxLevelChanged); ASSERT(maxLevelChanged);
}
else
{
respecifyImage = true;
}
if (!respecifyImage)
{
// Don't need to respecify the texture; but do need to update which vkImageView's are
// served up by ImageViewHelper
// Track the levels in our ImageHelper // Track the levels in our ImageHelper
mImage->setBaseAndMaxLevels(baseLevel, maxLevel); mImage->setBaseAndMaxLevels(baseLevel, maxLevel);
...@@ -1952,7 +1976,7 @@ angle::Result TextureVk::updateBaseMaxLevels(ContextVk *contextVk, ...@@ -1952,7 +1976,7 @@ angle::Result TextureVk::updateBaseMaxLevels(ContextVk *contextVk,
} }
angle::Result TextureVk::copyAndStageImageData(ContextVk *contextVk, angle::Result TextureVk::copyAndStageImageData(ContextVk *contextVk,
gl::LevelIndex previousBaseLevel, gl::LevelIndex previousFirstAllocateLevel,
vk::ImageHelper *srcImage, vk::ImageHelper *srcImage,
vk::ImageHelper *dstImage) vk::ImageHelper *dstImage)
{ {
...@@ -1966,7 +1990,7 @@ angle::Result TextureVk::copyAndStageImageData(ContextVk *contextVk, ...@@ -1966,7 +1990,7 @@ angle::Result TextureVk::copyAndStageImageData(ContextVk *contextVk,
++levelVk) ++levelVk)
{ {
// Vulkan level 0 previously aligned with whatever the base level was. // Vulkan level 0 previously aligned with whatever the base level was.
gl::LevelIndex levelGL = vk_gl::GetLevelIndex(levelVk, previousBaseLevel); gl::LevelIndex levelGL = vk_gl::GetLevelIndex(levelVk, previousFirstAllocateLevel);
if (mRedefinedLevels.test(levelVk.get())) if (mRedefinedLevels.test(levelVk.get()))
{ {
...@@ -1991,13 +2015,13 @@ angle::Result TextureVk::copyAndStageImageData(ContextVk *contextVk, ...@@ -1991,13 +2015,13 @@ angle::Result TextureVk::copyAndStageImageData(ContextVk *contextVk,
angle::Result TextureVk::respecifyImageStorage(ContextVk *contextVk) angle::Result TextureVk::respecifyImageStorage(ContextVk *contextVk)
{ {
return respecifyImageStorageAndLevels(contextVk, mImage->getBaseLevel(), return respecifyImageStorageAndLevels(contextVk, mImage->getFirstAllocatedLevel(),
gl::LevelIndex(mState.getEffectiveBaseLevel()), gl::LevelIndex(mState.getEffectiveBaseLevel()),
gl::LevelIndex(mState.getEffectiveMaxLevel())); gl::LevelIndex(mState.getEffectiveMaxLevel()));
} }
angle::Result TextureVk::respecifyImageStorageAndLevels(ContextVk *contextVk, angle::Result TextureVk::respecifyImageStorageAndLevels(ContextVk *contextVk,
gl::LevelIndex previousBaseLevel, gl::LevelIndex previousFirstAllocateLevel,
gl::LevelIndex baseLevel, gl::LevelIndex baseLevel,
gl::LevelIndex maxLevel) gl::LevelIndex maxLevel)
{ {
...@@ -2036,11 +2060,18 @@ angle::Result TextureVk::respecifyImageStorageAndLevels(ContextVk *contextVk, ...@@ -2036,11 +2060,18 @@ angle::Result TextureVk::respecifyImageStorageAndLevels(ContextVk *contextVk,
ANGLE_TRY(ensureImageAllocated(contextVk, format)); ANGLE_TRY(ensureImageAllocated(contextVk, format));
// Create the image // Create the image
if (mState.getImmutableFormat())
{
ANGLE_TRY(initImmutableImage(contextVk, format));
}
else
{
const gl::ImageDesc &baseLevelDesc = mState.getBaseLevelDesc(); const gl::ImageDesc &baseLevelDesc = mState.getBaseLevelDesc();
const gl::Extents &baseLevelExtents = baseLevelDesc.size; const gl::Extents &baseLevelExtents = baseLevelDesc.size;
const uint32_t levelCount = getMipLevelCount(ImageMipLevels::EnabledLevels); const uint32_t levelCount = getMipLevelCount(ImageMipLevels::EnabledLevels);
ANGLE_TRY(initImage(contextVk, format, baseLevelDesc.format.info->sized, baseLevelExtents, ANGLE_TRY(initImage(contextVk, format, baseLevelDesc.format.info->sized,
levelCount)); baseLevelExtents, levelCount));
}
// Set the newly created mImage as the destination for the staging operation // Set the newly created mImage as the destination for the staging operation
dstImage = mImage; dstImage = mImage;
...@@ -2051,7 +2082,7 @@ angle::Result TextureVk::respecifyImageStorageAndLevels(ContextVk *contextVk, ...@@ -2051,7 +2082,7 @@ angle::Result TextureVk::respecifyImageStorageAndLevels(ContextVk *contextVk,
dstImage->setBaseAndMaxLevels(baseLevel, maxLevel); dstImage->setBaseAndMaxLevels(baseLevel, maxLevel);
// Transfer the entire contents of the source image into the destination image. // Transfer the entire contents of the source image into the destination image.
ANGLE_TRY(copyAndStageImageData(contextVk, previousBaseLevel, srcImage, dstImage)); ANGLE_TRY(copyAndStageImageData(contextVk, previousFirstAllocateLevel, srcImage, dstImage));
// Now that we've staged all the updates, release the current image so that it will be // Now that we've staged all the updates, release the current image so that it will be
// recreated with the correct number of mip levels, base level, and max level. // recreated with the correct number of mip levels, base level, and max level.
...@@ -2172,13 +2203,20 @@ angle::Result TextureVk::ensureImageInitialized(ContextVk *contextVk, ImageMipLe ...@@ -2172,13 +2203,20 @@ angle::Result TextureVk::ensureImageInitialized(ContextVk *contextVk, ImageMipLe
{ {
ASSERT(!mRedefinedLevels.any()); ASSERT(!mRedefinedLevels.any());
const vk::Format &format = getBaseLevelFormat(contextVk->getRenderer());
if (mState.getImmutableFormat())
{
ANGLE_TRY(initImmutableImage(contextVk, format));
}
else
{
const gl::ImageDesc &baseLevelDesc = mState.getBaseLevelDesc(); const gl::ImageDesc &baseLevelDesc = mState.getBaseLevelDesc();
const gl::Extents &baseLevelExtents = baseLevelDesc.size; const gl::Extents &baseLevelExtents = baseLevelDesc.size;
const uint32_t levelCount = getMipLevelCount(mipLevels); const uint32_t levelCount = getMipLevelCount(mipLevels);
const vk::Format &format = getBaseLevelFormat(contextVk->getRenderer());
ANGLE_TRY(initImage(contextVk, format, baseLevelDesc.format.info->sized, baseLevelExtents, ANGLE_TRY(initImage(contextVk, format, baseLevelDesc.format.info->sized,
levelCount)); baseLevelExtents, levelCount));
}
if (mipLevels == ImageMipLevels::FullMipChain) if (mipLevels == ImageMipLevels::FullMipChain)
{ {
...@@ -2197,11 +2235,11 @@ angle::Result TextureVk::flushImageStagedUpdates(ContextVk *contextVk) ...@@ -2197,11 +2235,11 @@ angle::Result TextureVk::flushImageStagedUpdates(ContextVk *contextVk)
{ {
ASSERT(mImage->valid()); ASSERT(mImage->valid());
gl::LevelIndex baseLevelGL = getNativeImageLevel(mImage->getBaseLevel()); gl::LevelIndex firstLevelGL = getNativeImageLevel(mImage->getFirstAllocatedLevel());
return mImage->flushStagedUpdates(contextVk, baseLevelGL, baseLevelGL + mImage->getLevelCount(), return mImage->flushStagedUpdates(
getNativeImageLayer(0), mImage->getLayerCount(), contextVk, firstLevelGL, firstLevelGL + mImage->getLevelCount(), getNativeImageLayer(0),
mRedefinedLevels); mImage->getLayerCount(), mRedefinedLevels);
} }
angle::Result TextureVk::initRenderTargets(ContextVk *contextVk, angle::Result TextureVk::initRenderTargets(ContextVk *contextVk,
...@@ -2382,6 +2420,19 @@ angle::Result TextureVk::syncState(const gl::Context *context, ...@@ -2382,6 +2420,19 @@ angle::Result TextureVk::syncState(const gl::Context *context,
prepareForGenerateMipmap(contextVk); prepareForGenerateMipmap(contextVk);
} }
// For immutable texture, base level does not affect allocation. Only usage flags are. If usage
// flag changed, we respecify image storage early on. This makes the code more reliable and also
// better performance wise. Otherwise, we will try to preserve base level by calling
// stageSelfForBaseLevel and then later on find out the mImageUsageFlags changed and the whole
// thing has to be respecified.
if (mState.getImmutableFormat() &&
(oldUsageFlags != mImageUsageFlags || oldCreateFlags != mImageCreateFlags))
{
ANGLE_TRY(respecifyImageStorage(contextVk));
oldUsageFlags = mImageUsageFlags;
oldCreateFlags = mImageCreateFlags;
}
// Set base and max level before initializing the image // Set base and max level before initializing the image
if (dirtyBits.test(gl::Texture::DIRTY_BIT_MAX_LEVEL) || if (dirtyBits.test(gl::Texture::DIRTY_BIT_MAX_LEVEL) ||
dirtyBits.test(gl::Texture::DIRTY_BIT_BASE_LEVEL)) dirtyBits.test(gl::Texture::DIRTY_BIT_BASE_LEVEL))
...@@ -2410,6 +2461,9 @@ angle::Result TextureVk::syncState(const gl::Context *context, ...@@ -2410,6 +2461,9 @@ angle::Result TextureVk::syncState(const gl::Context *context,
mImage->getLevelCount() != getMipLevelCount(ImageMipLevels::FullMipChain))) mImage->getLevelCount() != getMipLevelCount(ImageMipLevels::FullMipChain)))
{ {
ASSERT(mOwnsImage); ASSERT(mOwnsImage);
// Immutable texture is not expected to reach here. The usage flag change should have
// handled earlier and level count change should not need to reallocate
ASSERT(!mState.getImmutableFormat());
// Flush staged updates to the base level of the image. Note that updates to the rest of // Flush staged updates to the base level of the image. Note that updates to the rest of
// the levels have already been discarded through the |removeStagedUpdates| call above. // the levels have already been discarded through the |removeStagedUpdates| call above.
...@@ -2679,7 +2733,7 @@ angle::Result TextureVk::initImage(ContextVk *contextVk, ...@@ -2679,7 +2733,7 @@ angle::Result TextureVk::initImage(ContextVk *contextVk,
contextVk, mState.getType(), vkExtent, format, samples, mImageUsageFlags, mImageCreateFlags, contextVk, mState.getType(), vkExtent, format, samples, mImageUsageFlags, mImageCreateFlags,
vk::ImageLayout::Undefined, nullptr, gl::LevelIndex(mState.getEffectiveBaseLevel()), vk::ImageLayout::Undefined, nullptr, gl::LevelIndex(mState.getEffectiveBaseLevel()),
gl::LevelIndex(mState.getEffectiveMaxLevel()), levelCount, layerCount, gl::LevelIndex(mState.getEffectiveMaxLevel()), levelCount, layerCount,
contextVk->isRobustResourceInitEnabled(), &imageFormatListEnabled)); contextVk->isRobustResourceInitEnabled(), false, &imageFormatListEnabled));
mRequiresMutableStorage = (mImageCreateFlags & VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT) != 0; mRequiresMutableStorage = (mImageCreateFlags & VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT) != 0;
...@@ -2692,6 +2746,40 @@ angle::Result TextureVk::initImage(ContextVk *contextVk, ...@@ -2692,6 +2746,40 @@ angle::Result TextureVk::initImage(ContextVk *contextVk,
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result TextureVk::initImmutableImage(ContextVk *contextVk, const vk::Format &format)
{
ASSERT(mState.getImmutableFormat());
// For immutable texture, we always create a underlying image object with levels [0,
// immutableLevels-1] regardless of base level and max level. base/max level information are
// used to create ImageViewHelper object.
VkExtent3D vkExtentLevel0;
uint32_t layerCount;
const gl::ImageDesc &Level0Desc = mState.getLevelZeroDesc();
const gl::Extents &Level0Extents = Level0Desc.size;
gl_vk::GetExtentsAndLayerCount(mState.getType(), Level0Extents, &vkExtentLevel0, &layerCount);
GLint samples = mState.getBaseLevelDesc().samples ? mState.getBaseLevelDesc().samples : 1;
bool imageFormatListEnabled = false;
ANGLE_TRY(mImage->initExternal(
contextVk, mState.getType(), vkExtentLevel0, format, samples, mImageUsageFlags,
mImageCreateFlags, vk::ImageLayout::Undefined, nullptr,
gl::LevelIndex(mState.getEffectiveBaseLevel()),
gl::LevelIndex(mState.getEffectiveMaxLevel()), mState.getImmutableLevels(), layerCount,
contextVk->isRobustResourceInitEnabled(), true, &imageFormatListEnabled));
mRequiresMutableStorage = (mImageCreateFlags & VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT) != 0;
RendererVk *renderer = contextVk->getRenderer();
const VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
ANGLE_TRY(mImage->initMemory(contextVk, renderer->getMemoryProperties(), flags));
ANGLE_TRY(initImageViews(contextVk, format, Level0Desc.format.info->sized,
getMipLevelCount(ImageMipLevels::EnabledLevels), layerCount));
return angle::Result::Continue;
}
angle::Result TextureVk::initImageViews(ContextVk *contextVk, angle::Result TextureVk::initImageViews(ContextVk *contextVk,
const vk::Format &format, const vk::Format &format,
const bool sized, const bool sized,
...@@ -2778,7 +2866,11 @@ uint32_t TextureVk::getMipLevelCount(ImageMipLevels mipLevels) const ...@@ -2778,7 +2866,11 @@ uint32_t TextureVk::getMipLevelCount(ImageMipLevels mipLevels) const
case ImageMipLevels::EnabledLevels: case ImageMipLevels::EnabledLevels:
return mState.getEnabledLevelCount(); return mState.getEnabledLevelCount();
case ImageMipLevels::FullMipChain: case ImageMipLevels::FullMipChain:
return getMaxLevelCount() - mState.getEffectiveBaseLevel(); // For immutable textures, it is the same during life time of the texture regardless of
// base/max level setting.
return mState.getImmutableFormat()
? mState.getImmutableLevels()
: getMaxLevelCount() - mState.getEffectiveBaseLevel();
default: default:
UNREACHABLE(); UNREACHABLE();
...@@ -2788,8 +2880,11 @@ uint32_t TextureVk::getMipLevelCount(ImageMipLevels mipLevels) const ...@@ -2788,8 +2880,11 @@ uint32_t TextureVk::getMipLevelCount(ImageMipLevels mipLevels) const
uint32_t TextureVk::getMaxLevelCount() const uint32_t TextureVk::getMaxLevelCount() const
{ {
// getMipmapMaxLevel will be 0 here if mipmaps are not used, so the levelCount is always +1. // For immutable textures, it is always the same during life time of the texture. For mutable
return mState.getMipmapMaxLevel() + 1; // texture, getMipmapMaxLevel will be 0 here if mipmaps are not used, so the levelCount is
// always +1.
return mState.getImmutableFormat() ? mState.getImmutableLevels()
: mState.getMipmapMaxLevel() + 1;
} }
angle::Result TextureVk::generateMipmapLevelsWithCPU(ContextVk *contextVk, angle::Result TextureVk::generateMipmapLevelsWithCPU(ContextVk *contextVk,
......
...@@ -412,12 +412,13 @@ class TextureVk : public TextureImpl, public angle::ObserverInterface ...@@ -412,12 +412,13 @@ class TextureVk : public TextureImpl, public angle::ObserverInterface
const bool sized, const bool sized,
const gl::Extents &extents, const gl::Extents &extents,
const uint32_t levelCount); const uint32_t levelCount);
angle::Result initImmutableImage(ContextVk *contextVk, const vk::Format &format);
void releaseImage(ContextVk *contextVk); void releaseImage(ContextVk *contextVk);
void releaseStagingBuffer(ContextVk *contextVk); void releaseStagingBuffer(ContextVk *contextVk);
uint32_t getMipLevelCount(ImageMipLevels mipLevels) const; uint32_t getMipLevelCount(ImageMipLevels mipLevels) const;
uint32_t getMaxLevelCount() const; uint32_t getMaxLevelCount() const;
angle::Result copyAndStageImageData(ContextVk *contextVk, angle::Result copyAndStageImageData(ContextVk *contextVk,
gl::LevelIndex previousBaseLevel, gl::LevelIndex previousFirstAllocateLevel,
vk::ImageHelper *srcImage, vk::ImageHelper *srcImage,
vk::ImageHelper *dstImage); vk::ImageHelper *dstImage);
angle::Result initImageViews(ContextVk *contextVk, angle::Result initImageViews(ContextVk *contextVk,
...@@ -443,7 +444,7 @@ class TextureVk : public TextureImpl, public angle::ObserverInterface ...@@ -443,7 +444,7 @@ class TextureVk : public TextureImpl, public angle::ObserverInterface
// attributes at the next opportunity. // attributes at the next opportunity.
angle::Result respecifyImageStorage(ContextVk *contextVk); angle::Result respecifyImageStorage(ContextVk *contextVk);
angle::Result respecifyImageStorageAndLevels(ContextVk *contextVk, angle::Result respecifyImageStorageAndLevels(ContextVk *contextVk,
gl::LevelIndex previousBaseLevelGL, gl::LevelIndex previousFirstAllocateLevelGL,
gl::LevelIndex baseLevelGL, gl::LevelIndex baseLevelGL,
gl::LevelIndex maxLevelGL); gl::LevelIndex maxLevelGL);
......
...@@ -264,7 +264,7 @@ angle::Result HardwareBufferImageSiblingVkAndroid::initImpl(DisplayVk *displayVk ...@@ -264,7 +264,7 @@ angle::Result HardwareBufferImageSiblingVkAndroid::initImpl(DisplayVk *displayVk
bufferFormatProperties.format == VK_FORMAT_UNDEFINED ? externalVkFormat : vkFormat, 1, bufferFormatProperties.format == VK_FORMAT_UNDEFINED ? externalVkFormat : vkFormat, 1,
usage, vk::kVkImageCreateFlagsNone, vk::ImageLayout::ExternalPreInitialized, usage, vk::kVkImageCreateFlagsNone, vk::ImageLayout::ExternalPreInitialized,
&externalMemoryImageCreateInfo, gl::LevelIndex(0), gl::LevelIndex(0), 1, 1, &externalMemoryImageCreateInfo, gl::LevelIndex(0), gl::LevelIndex(0), 1, 1,
robustInitEnabled, nullptr)); robustInitEnabled, false, nullptr));
VkImportAndroidHardwareBufferInfoANDROID importHardwareBufferInfo = {}; VkImportAndroidHardwareBufferInfoANDROID importHardwareBufferInfo = {};
importHardwareBufferInfo.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID; importHardwareBufferInfo.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID;
......
...@@ -3625,6 +3625,7 @@ ImageHelper::ImageHelper(ImageHelper &&other) ...@@ -3625,6 +3625,7 @@ ImageHelper::ImageHelper(ImageHelper &&other)
mUsage(other.mUsage), mUsage(other.mUsage),
mExtents(other.mExtents), mExtents(other.mExtents),
mRotatedAspectRatio(other.mRotatedAspectRatio), mRotatedAspectRatio(other.mRotatedAspectRatio),
mImmutable(other.mImmutable),
mFormat(other.mFormat), mFormat(other.mFormat),
mSamples(other.mSamples), mSamples(other.mSamples),
mImageSerial(other.mImageSerial), mImageSerial(other.mImageSerial),
...@@ -3634,6 +3635,7 @@ ImageHelper::ImageHelper(ImageHelper &&other) ...@@ -3634,6 +3635,7 @@ ImageHelper::ImageHelper(ImageHelper &&other)
mCurrentShaderReadStageMask(other.mCurrentShaderReadStageMask), mCurrentShaderReadStageMask(other.mCurrentShaderReadStageMask),
mYuvConversionSampler(std::move(other.mYuvConversionSampler)), mYuvConversionSampler(std::move(other.mYuvConversionSampler)),
mExternalFormat(other.mExternalFormat), mExternalFormat(other.mExternalFormat),
mFirstAllocatedLevel(other.mFirstAllocatedLevel),
mBaseLevel(other.mBaseLevel), mBaseLevel(other.mBaseLevel),
mMaxLevel(other.mMaxLevel), mMaxLevel(other.mMaxLevel),
mLayerCount(other.mLayerCount), mLayerCount(other.mLayerCount),
...@@ -3660,6 +3662,7 @@ void ImageHelper::resetCachedProperties() ...@@ -3660,6 +3662,7 @@ void ImageHelper::resetCachedProperties()
mUsage = 0; mUsage = 0;
mExtents = {}; mExtents = {};
mRotatedAspectRatio = false; mRotatedAspectRatio = false;
mImmutable = false;
mFormat = nullptr; mFormat = nullptr;
mSamples = 1; mSamples = 1;
mImageSerial = kInvalidImageSerial; mImageSerial = kInvalidImageSerial;
...@@ -3667,6 +3670,7 @@ void ImageHelper::resetCachedProperties() ...@@ -3667,6 +3670,7 @@ void ImageHelper::resetCachedProperties()
mCurrentQueueFamilyIndex = std::numeric_limits<uint32_t>::max(); mCurrentQueueFamilyIndex = std::numeric_limits<uint32_t>::max();
mLastNonShaderReadOnlyLayout = ImageLayout::Undefined; mLastNonShaderReadOnlyLayout = ImageLayout::Undefined;
mCurrentShaderReadStageMask = 0; mCurrentShaderReadStageMask = 0;
mFirstAllocatedLevel = gl::LevelIndex(0);
mBaseLevel = gl::LevelIndex(0); mBaseLevel = gl::LevelIndex(0);
mMaxLevel = gl::LevelIndex(0); mMaxLevel = gl::LevelIndex(0);
mLayerCount = 0; mLayerCount = 0;
...@@ -3778,7 +3782,8 @@ angle::Result ImageHelper::init(Context *context, ...@@ -3778,7 +3782,8 @@ angle::Result ImageHelper::init(Context *context,
{ {
return initExternal(context, textureType, extents, format, samples, usage, return initExternal(context, textureType, extents, format, samples, usage,
kVkImageCreateFlagsNone, ImageLayout::Undefined, nullptr, baseLevel, kVkImageCreateFlagsNone, ImageLayout::Undefined, nullptr, baseLevel,
maxLevel, mipLevels, layerCount, isRobustResourceInitEnabled, nullptr); maxLevel, mipLevels, layerCount, isRobustResourceInitEnabled, false,
nullptr);
} }
angle::Result ImageHelper::initMSAASwapchain(Context *context, angle::Result ImageHelper::initMSAASwapchain(Context *context,
...@@ -3796,7 +3801,8 @@ angle::Result ImageHelper::initMSAASwapchain(Context *context, ...@@ -3796,7 +3801,8 @@ angle::Result ImageHelper::initMSAASwapchain(Context *context,
{ {
ANGLE_TRY(initExternal(context, textureType, extents, format, samples, usage, ANGLE_TRY(initExternal(context, textureType, extents, format, samples, usage,
kVkImageCreateFlagsNone, ImageLayout::Undefined, nullptr, baseLevel, kVkImageCreateFlagsNone, ImageLayout::Undefined, nullptr, baseLevel,
maxLevel, mipLevels, layerCount, isRobustResourceInitEnabled, nullptr)); maxLevel, mipLevels, layerCount, isRobustResourceInitEnabled, false,
nullptr));
if (rotatedAspectRatio) if (rotatedAspectRatio)
{ {
std::swap(mExtents.width, mExtents.height); std::swap(mExtents.width, mExtents.height);
...@@ -3819,6 +3825,7 @@ angle::Result ImageHelper::initExternal(Context *context, ...@@ -3819,6 +3825,7 @@ angle::Result ImageHelper::initExternal(Context *context,
uint32_t mipLevels, uint32_t mipLevels,
uint32_t layerCount, uint32_t layerCount,
bool isRobustResourceInitEnabled, bool isRobustResourceInitEnabled,
bool immutable,
bool *imageFormatListEnabledOut) bool *imageFormatListEnabledOut)
{ {
ASSERT(!valid()); ASSERT(!valid());
...@@ -3828,9 +3835,11 @@ angle::Result ImageHelper::initExternal(Context *context, ...@@ -3828,9 +3835,11 @@ angle::Result ImageHelper::initExternal(Context *context,
mImageType = gl_vk::GetImageType(textureType); mImageType = gl_vk::GetImageType(textureType);
mExtents = extents; mExtents = extents;
mRotatedAspectRatio = false; mRotatedAspectRatio = false;
mImmutable = immutable;
mFormat = &format; mFormat = &format;
mSamples = std::max(samples, 1); mSamples = std::max(samples, 1);
mImageSerial = context->getRenderer()->getResourceSerialFactory().generateImageSerial(); mImageSerial = context->getRenderer()->getResourceSerialFactory().generateImageSerial();
mFirstAllocatedLevel = immutable ? gl::LevelIndex(0) : baseLevel;
mBaseLevel = baseLevel; mBaseLevel = baseLevel;
mMaxLevel = maxLevel; mMaxLevel = maxLevel;
mLevelCount = mipLevels; mLevelCount = mipLevels;
...@@ -4376,11 +4385,12 @@ angle::Result ImageHelper::initImplicitMultisampledRenderToTexture( ...@@ -4376,11 +4385,12 @@ angle::Result ImageHelper::initImplicitMultisampledRenderToTexture(
: VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT); : VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
constexpr VkImageCreateFlags kMultisampledCreateFlags = 0; constexpr VkImageCreateFlags kMultisampledCreateFlags = 0;
ANGLE_TRY(initExternal( ANGLE_TRY(initExternal(context, textureType, resolveImage.getExtents(),
context, textureType, resolveImage.getExtents(), resolveImage.getFormat(), samples, resolveImage.getFormat(), samples, kMultisampledUsageFlags,
kMultisampledUsageFlags, kMultisampledCreateFlags, ImageLayout::Undefined, nullptr, kMultisampledCreateFlags, ImageLayout::Undefined, nullptr,
resolveImage.getBaseLevel(), resolveImage.getMaxLevel(), resolveImage.getLevelCount(), resolveImage.getFirstAllocatedLevel(), resolveImage.getMaxLevel(),
resolveImage.getLayerCount(), isRobustResourceInitEnabled, nullptr)); resolveImage.getLevelCount(), resolveImage.getLayerCount(),
isRobustResourceInitEnabled, false, nullptr));
const VkMemoryPropertyFlags kMultisampledMemoryFlags = const VkMemoryPropertyFlags kMultisampledMemoryFlags =
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
...@@ -4392,7 +4402,7 @@ angle::Result ImageHelper::initImplicitMultisampledRenderToTexture( ...@@ -4392,7 +4402,7 @@ angle::Result ImageHelper::initImplicitMultisampledRenderToTexture(
// Remove the emulated format clear from the multisampled image if any. There is one already // Remove the emulated format clear from the multisampled image if any. There is one already
// staged on the resolve image if needed. // staged on the resolve image if needed.
removeStagedUpdates(context, getBaseLevel(), getMaxLevel()); removeStagedUpdates(context, getFirstAllocatedLevel(), getMaxLevel());
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -4559,16 +4569,23 @@ void ImageHelper::setBaseAndMaxLevels(gl::LevelIndex baseLevel, gl::LevelIndex m ...@@ -4559,16 +4569,23 @@ void ImageHelper::setBaseAndMaxLevels(gl::LevelIndex baseLevel, gl::LevelIndex m
{ {
mBaseLevel = baseLevel; mBaseLevel = baseLevel;
mMaxLevel = maxLevel; mMaxLevel = maxLevel;
// For immutable texture, we always allocate the entire mipmap chain [0, mLevelCount-1].
// For mutable textures, we will try to reallocate based on baseLevel change
if (!mImmutable)
{
mFirstAllocatedLevel = baseLevel;
}
} }
LevelIndex ImageHelper::toVkLevel(gl::LevelIndex levelIndexGL) const LevelIndex ImageHelper::toVkLevel(gl::LevelIndex levelIndexGL) const
{ {
return gl_vk::GetLevelIndex(levelIndexGL, mBaseLevel); return gl_vk::GetLevelIndex(levelIndexGL, mFirstAllocatedLevel);
} }
gl::LevelIndex ImageHelper::toGLLevel(LevelIndex levelIndexVk) const gl::LevelIndex ImageHelper::toGLLevel(LevelIndex levelIndexVk) const
{ {
return vk_gl::GetLevelIndex(levelIndexVk, mBaseLevel); return vk_gl::GetLevelIndex(levelIndexVk, mFirstAllocatedLevel);
} }
ANGLE_INLINE void ImageHelper::initImageMemoryBarrierStruct( ANGLE_INLINE void ImageHelper::initImageMemoryBarrierStruct(
...@@ -4953,53 +4970,58 @@ angle::Result ImageHelper::generateMipmapsWithBlit(ContextVk *contextVk, LevelIn ...@@ -4953,53 +4970,58 @@ angle::Result ImageHelper::generateMipmapsWithBlit(ContextVk *contextVk, LevelIn
barrier.subresourceRange.levelCount = 1; barrier.subresourceRange.levelCount = 1;
const VkFilter filter = gl_vk::GetFilter(CalculateGenerateMipmapFilter(contextVk, getFormat())); const VkFilter filter = gl_vk::GetFilter(CalculateGenerateMipmapFilter(contextVk, getFormat()));
LevelIndex baseLevelVk = toVkLevel(mBaseLevel);
for (uint32_t mipLevel = 1; mipLevel <= maxLevel.get(); mipLevel++) for (LevelIndex mipLevel(1); mipLevel <= LevelIndex(mLevelCount); ++mipLevel)
{ {
int32_t nextMipWidth = std::max<int32_t>(1, mipWidth >> 1); int32_t nextMipWidth = std::max<int32_t>(1, mipWidth >> 1);
int32_t nextMipHeight = std::max<int32_t>(1, mipHeight >> 1); int32_t nextMipHeight = std::max<int32_t>(1, mipHeight >> 1);
int32_t nextMipDepth = std::max<int32_t>(1, mipDepth >> 1); int32_t nextMipDepth = std::max<int32_t>(1, mipDepth >> 1);
barrier.subresourceRange.baseMipLevel = mipLevel - 1; if (mipLevel > baseLevelVk && mipLevel <= maxLevel)
{
barrier.subresourceRange.baseMipLevel = mipLevel.get() - 1;
barrier.oldLayout = getCurrentLayout(); barrier.oldLayout = getCurrentLayout();
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
// We can do it for all layers at once. // We can do it for all layers at once.
commandBuffer->imageBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, commandBuffer->imageBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
barrier); VK_PIPELINE_STAGE_TRANSFER_BIT, barrier);
VkImageBlit blit = {}; VkImageBlit blit = {};
blit.srcOffsets[0] = {0, 0, 0}; blit.srcOffsets[0] = {0, 0, 0};
blit.srcOffsets[1] = {mipWidth, mipHeight, mipDepth}; blit.srcOffsets[1] = {mipWidth, mipHeight, mipDepth};
blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit.srcSubresource.mipLevel = mipLevel - 1; blit.srcSubresource.mipLevel = mipLevel.get() - 1;
blit.srcSubresource.baseArrayLayer = 0; blit.srcSubresource.baseArrayLayer = 0;
blit.srcSubresource.layerCount = mLayerCount; blit.srcSubresource.layerCount = mLayerCount;
blit.dstOffsets[0] = {0, 0, 0}; blit.dstOffsets[0] = {0, 0, 0};
blit.dstOffsets[1] = {nextMipWidth, nextMipHeight, nextMipDepth}; blit.dstOffsets[1] = {nextMipWidth, nextMipHeight, nextMipDepth};
blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit.dstSubresource.mipLevel = mipLevel; blit.dstSubresource.mipLevel = mipLevel.get();
blit.dstSubresource.baseArrayLayer = 0; blit.dstSubresource.baseArrayLayer = 0;
blit.dstSubresource.layerCount = mLayerCount; blit.dstSubresource.layerCount = mLayerCount;
mipWidth = nextMipWidth;
mipHeight = nextMipHeight;
mipDepth = nextMipDepth;
commandBuffer->blitImage(mImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, mImage, commandBuffer->blitImage(mImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, mImage,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, filter); VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, filter);
} }
else
// Transition the last mip level to the same layout as all the other ones, so we can declare {
// Transition the mip level to the same layout as all the other ones, so we can declare
// our whole image layout to be SRC_OPTIMAL. // our whole image layout to be SRC_OPTIMAL.
barrier.subresourceRange.baseMipLevel = maxLevel.get(); barrier.subresourceRange.baseMipLevel = mipLevel.get() - 1;
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
// We can do it for all layers at once. // We can do it for all layers at once.
commandBuffer->imageBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, commandBuffer->imageBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
barrier); VK_PIPELINE_STAGE_TRANSFER_BIT, barrier);
}
mipWidth = nextMipWidth;
mipHeight = nextMipHeight;
mipDepth = nextMipDepth;
}
// This is just changing the internal state of the image helper so that the next call // This is just changing the internal state of the image helper so that the next call
// to changeLayout will use this layout as the "oldLayout" argument. // to changeLayout will use this layout as the "oldLayout" argument.
mCurrentLayout = ImageLayout::TransferSrc; mCurrentLayout = ImageLayout::TransferSrc;
...@@ -5802,9 +5824,9 @@ void ImageHelper::stageSelfForBaseLevel(ContextVk *contextVk) ...@@ -5802,9 +5824,9 @@ void ImageHelper::stageSelfForBaseLevel(ContextVk *contextVk)
setEntireContentUndefined(); setEntireContentUndefined();
// Stage an update from the previous image. // Stage an update from the previous image.
const gl::ImageIndex baseLevelIndex = const gl::ImageIndex firstAllocateLevelIndex =
gl::ImageIndex::Make2DArrayRange(mBaseLevel.get(), 0, mLayerCount); gl::ImageIndex::Make2DArrayRange(mFirstAllocatedLevel.get(), 0, mLayerCount);
stageSubresourceUpdateFromImage(prevImage.release(), baseLevelIndex, gl::kOffsetZero, stageSubresourceUpdateFromImage(prevImage.release(), firstAllocateLevelIndex, gl::kOffsetZero,
getLevelExtents(LevelIndex(0)), mImageType); getLevelExtents(LevelIndex(0)), mImageType);
} }
...@@ -5930,7 +5952,8 @@ angle::Result ImageHelper::flushStagedUpdates(ContextVk *contextVk, ...@@ -5930,7 +5952,8 @@ angle::Result ImageHelper::flushStagedUpdates(ContextVk *contextVk,
// done with fine granularity as updates are applied. This is achieved by specifying a layer // done with fine granularity as updates are applied. This is achieved by specifying a layer
// that is outside the tracking range. // that is outside the tracking range.
CommandBufferAccess access; CommandBufferAccess access;
access.onImageTransferWrite(mBaseLevel, 1, kMaxContentDefinedLayerCount, 0, aspectFlags, this); access.onImageTransferWrite(levelGLStart, 1, kMaxContentDefinedLayerCount, 0, aspectFlags,
this);
CommandBuffer *commandBuffer; CommandBuffer *commandBuffer;
ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer(access, &commandBuffer)); ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer(access, &commandBuffer));
...@@ -6094,7 +6117,8 @@ angle::Result ImageHelper::flushStagedUpdates(ContextVk *contextVk, ...@@ -6094,7 +6117,8 @@ angle::Result ImageHelper::flushStagedUpdates(ContextVk *contextVk,
angle::Result ImageHelper::flushAllStagedUpdates(ContextVk *contextVk) angle::Result ImageHelper::flushAllStagedUpdates(ContextVk *contextVk)
{ {
return flushStagedUpdates(contextVk, mBaseLevel, mBaseLevel + mLevelCount, 0, mLayerCount, {}); return flushStagedUpdates(contextVk, mFirstAllocatedLevel, mFirstAllocatedLevel + mLevelCount,
0, mLayerCount, {});
} }
bool ImageHelper::hasStagedUpdatesForSubresource(gl::LevelIndex levelGL, bool ImageHelper::hasStagedUpdatesForSubresource(gl::LevelIndex levelGL,
...@@ -6130,7 +6154,7 @@ bool ImageHelper::hasStagedUpdatesForSubresource(gl::LevelIndex levelGL, ...@@ -6130,7 +6154,7 @@ bool ImageHelper::hasStagedUpdatesForSubresource(gl::LevelIndex levelGL,
bool ImageHelper::hasStagedUpdatesInAllocatedLevels() const bool ImageHelper::hasStagedUpdatesInAllocatedLevels() const
{ {
return hasStagedUpdatesInLevels(mBaseLevel, mMaxLevel + 1); return hasStagedUpdatesInLevels(mFirstAllocatedLevel, mMaxLevel + 1);
} }
bool ImageHelper::hasStagedUpdatesInLevels(gl::LevelIndex levelStart, gl::LevelIndex levelEnd) const bool ImageHelper::hasStagedUpdatesInLevels(gl::LevelIndex levelStart, gl::LevelIndex levelEnd) const
...@@ -6239,7 +6263,7 @@ void ImageHelper::removeSupersededUpdates(ContextVk *contextVk, gl::TexLevelMask ...@@ -6239,7 +6263,7 @@ void ImageHelper::removeSupersededUpdates(ContextVk *contextVk, gl::TexLevelMask
return false; return false;
}; };
for (gl::LevelIndex level = mBaseLevel; level <= mMaxLevel; ++level) for (gl::LevelIndex level = mFirstAllocatedLevel; level <= mMaxLevel; ++level)
{ {
std::vector<SubresourceUpdate> *levelUpdates = getLevelUpdates(level); std::vector<SubresourceUpdate> *levelUpdates = getLevelUpdates(level);
if (levelUpdates == nullptr) if (levelUpdates == nullptr)
......
...@@ -1449,6 +1449,7 @@ class ImageHelper final : public Resource, public angle::Subject ...@@ -1449,6 +1449,7 @@ class ImageHelper final : public Resource, public angle::Subject
uint32_t mipLevels, uint32_t mipLevels,
uint32_t layerCount, uint32_t layerCount,
bool isRobustResourceInitEnabled, bool isRobustResourceInitEnabled,
bool immutable,
bool *imageFormatListEnabledOut); bool *imageFormatListEnabledOut);
angle::Result initMemory(Context *context, angle::Result initMemory(Context *context,
const MemoryProperties &memoryProperties, const MemoryProperties &memoryProperties,
...@@ -1790,6 +1791,7 @@ class ImageHelper final : public Resource, public angle::Subject ...@@ -1790,6 +1791,7 @@ class ImageHelper final : public Resource, public angle::Subject
// Returns true if the image is owned by an external API or instance. // Returns true if the image is owned by an external API or instance.
bool isReleasedToExternal() const; bool isReleasedToExternal() const;
gl::LevelIndex getFirstAllocatedLevel() const { return mFirstAllocatedLevel; }
gl::LevelIndex getBaseLevel() const { return mBaseLevel; } gl::LevelIndex getBaseLevel() const { return mBaseLevel; }
void setBaseAndMaxLevels(gl::LevelIndex baseLevel, gl::LevelIndex maxLevel); void setBaseAndMaxLevels(gl::LevelIndex baseLevel, gl::LevelIndex maxLevel);
gl::LevelIndex getMaxLevel() const { return mMaxLevel; } gl::LevelIndex getMaxLevel() const { return mMaxLevel; }
...@@ -2040,6 +2042,9 @@ class ImageHelper final : public Resource, public angle::Subject ...@@ -2040,6 +2042,9 @@ class ImageHelper final : public Resource, public angle::Subject
// different between the rotated and non-rotated extents. // different between the rotated and non-rotated extents.
VkExtent3D mExtents; VkExtent3D mExtents;
bool mRotatedAspectRatio; bool mRotatedAspectRatio;
// True if this is created by an immutable texture glTexStorage. For immutable texture, the
// underlying VkImage object is always created from level 0 to mLevelCount-1.
bool mImmutable;
const Format *mFormat; const Format *mFormat;
GLint mSamples; GLint mSamples;
ImageSerial mImageSerial; ImageSerial mImageSerial;
...@@ -2057,6 +2062,11 @@ class ImageHelper final : public Resource, public angle::Subject ...@@ -2057,6 +2062,11 @@ class ImageHelper final : public Resource, public angle::Subject
BindingPointer<SamplerYcbcrConversion> mYuvConversionSampler; BindingPointer<SamplerYcbcrConversion> mYuvConversionSampler;
uint64_t mExternalFormat; uint64_t mExternalFormat;
// The first level that has been allocated. For mutable textures, this should be same as
// mBaseLevel since we always reallocate VkImage based on mBaseLevel change. But for immutable
// textures, we always allocate from level 0 regardless of mBaseLevel change.
gl::LevelIndex mFirstAllocatedLevel;
// Cached properties. // Cached properties.
gl::LevelIndex mBaseLevel; gl::LevelIndex mBaseLevel;
gl::LevelIndex mMaxLevel; gl::LevelIndex mMaxLevel;
......
...@@ -129,3 +129,6 @@ ...@@ -129,3 +129,6 @@
// New failures with latest dEQP roll (2020-04-28) // New failures with latest dEQP roll (2020-04-28)
4593 SWIFTSHADER : KHR-GLES31.core.nearest_edge.offset_left = FAIL 4593 SWIFTSHADER : KHR-GLES31.core.nearest_edge.offset_left = FAIL
// Test timeout due to it loops so many glTexStorageMultisample calls
5806 VULKAN NVIDIA : KHR-GLES31.core.texture_storage_multisample.APIDependencies.fbo_with_renderbuffer_and_multisample_texture_attachments_with_different_number_of_samples = SKIP
...@@ -2546,7 +2546,7 @@ TEST_P(FramebufferTest_ES3, SampleFromAttachedTextureWithDifferentLOD) ...@@ -2546,7 +2546,7 @@ TEST_P(FramebufferTest_ES3, SampleFromAttachedTextureWithDifferentLOD)
TEST_P(FramebufferTest_ES3, RenderAndInvalidateImmutableTextureWithBeyondMaxLevel) TEST_P(FramebufferTest_ES3, RenderAndInvalidateImmutableTextureWithBeyondMaxLevel)
{ {
// ToDo: https://issuetracker.google.com/181800403 // ToDo: https://issuetracker.google.com/181800403
ANGLE_SKIP_TEST_IF(IsVulkan() || IsMetal()); ANGLE_SKIP_TEST_IF(IsMetal());
constexpr GLuint kLevel0Size = 4; constexpr GLuint kLevel0Size = 4;
constexpr GLuint kLevel1Size = kLevel0Size / 2; constexpr GLuint kLevel1Size = kLevel0Size / 2;
...@@ -2596,7 +2596,7 @@ TEST_P(FramebufferTest_ES3, RenderAndInvalidateImmutableTextureWithBeyondMaxLeve ...@@ -2596,7 +2596,7 @@ TEST_P(FramebufferTest_ES3, RenderAndInvalidateImmutableTextureWithBeyondMaxLeve
TEST_P(FramebufferTest_ES3, RenderAndInvalidateImmutableTextureWithSubImageWithBeyondMaxLevel) TEST_P(FramebufferTest_ES3, RenderAndInvalidateImmutableTextureWithSubImageWithBeyondMaxLevel)
{ {
// ToDo: https://issuetracker.google.com/181800403 // ToDo: https://issuetracker.google.com/181800403
ANGLE_SKIP_TEST_IF(IsVulkan() || IsMetal()); ANGLE_SKIP_TEST_IF(IsMetal());
constexpr GLuint kLevel0Size = 4; constexpr GLuint kLevel0Size = 4;
constexpr GLuint kLevel1Size = kLevel0Size / 2; constexpr GLuint kLevel1Size = kLevel0Size / 2;
...@@ -2652,7 +2652,7 @@ TEST_P(FramebufferTest_ES3, RenderAndInvalidateImmutableTextureWithSubImageWithB ...@@ -2652,7 +2652,7 @@ TEST_P(FramebufferTest_ES3, RenderAndInvalidateImmutableTextureWithSubImageWithB
TEST_P(FramebufferTest_ES3, RenderAndInvalidateImmutableTextureWithBellowBaseLevelLOD) TEST_P(FramebufferTest_ES3, RenderAndInvalidateImmutableTextureWithBellowBaseLevelLOD)
{ {
// ToDo: https://issuetracker.google.com/181800403 // ToDo: https://issuetracker.google.com/181800403
ANGLE_SKIP_TEST_IF(IsVulkan() || IsMetal()); ANGLE_SKIP_TEST_IF(IsMetal());
constexpr GLuint kLevel0Size = 4; constexpr GLuint kLevel0Size = 4;
constexpr GLuint kLevel1Size = kLevel0Size / 2; constexpr GLuint kLevel1Size = kLevel0Size / 2;
......
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