Commit 9c262ad0 by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Cleanup texture image respecify

Prior to this change, respecifying a texture image due to usage, base or max level changes incurred a copy of every level and layer to a temporary buffer which was then staged as an update to the new image. This code was somewhat messy (for example with respect to depth/stencil images), error prone (e.g. previously had bugs with compressed textures) and disallowed further optimizations such as in anglebug.com/4835. This change does the following: - ImageHelper::SubresourceUpdate now takes ref-counted images, instead of image pointers. This allows the same image to be staged for multiple updates. - Respecifying an image is still done through a copy, but to an identical (temp) image instead of buffer, and each level of the image is staged as an update. * Further optimization is to stage the old image itself directly as updates to the new image Bug: angleproject:4835 Change-Id: I4a3ef2d616c9ab459ff65f918b0fb6d9a2161b73 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2897537 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCharlie Lao <cclao@google.com>
parent 9e8fea5b
...@@ -250,58 +250,6 @@ void Set3DBaseArrayLayerAndLayerCount(VkImageSubresourceLayers *Subresource) ...@@ -250,58 +250,6 @@ void Set3DBaseArrayLayerAndLayerCount(VkImageSubresourceLayers *Subresource)
Subresource->layerCount = 1; Subresource->layerCount = 1;
} }
// Used when the image is being redefined (for example to add mips or change base level) to copy
// each subresource of the image and stage it for another subresource. When all subresources
// are taken care of, the image is recreated.
angle::Result CopyAndStageImageSubresource(ContextVk *contextVk,
gl::TextureType textureType,
bool ignoreLayerCount,
uint32_t currentLayer,
vk::LevelIndex srcLevelVk,
gl::LevelIndex dstLevelGL,
vk::ImageHelper *srcImage,
vk::ImageHelper *dstImage)
{
const gl::Extents &baseLevelExtents = srcImage->getLevelExtents(srcLevelVk);
VkExtent3D updatedExtents;
VkOffset3D offset = {};
uint32_t layerCount;
gl_vk::GetExtentsAndLayerCount(textureType, baseLevelExtents, &updatedExtents, &layerCount);
gl::Box area(offset.x, offset.y, offset.z, updatedExtents.width, updatedExtents.height,
updatedExtents.depth);
// TODO: Refactor TextureVk::respecifyImageAttributesAndLevels() to avoid this workaround.
if (ignoreLayerCount)
{
layerCount = 1;
}
// Copy from the base level image to the staging buffer
vk::BufferHelper *stagingBuffer = nullptr;
vk::StagingBufferOffsetArray stagingBufferOffsets = {0, 0};
size_t bufferSize = 0;
ANGLE_TRY(srcImage->copyImageDataToBuffer(contextVk, srcImage->toGLLevel(srcLevelVk),
layerCount, currentLayer, area, &stagingBuffer,
&bufferSize, &stagingBufferOffsets, nullptr));
// Stage an update to the new image
ASSERT(stagingBuffer);
const gl::InternalFormat &formatInfo =
gl::GetSizedInternalFormatInfo(dstImage->getFormat().intendedGLFormat);
uint32_t bufferRowLength;
uint32_t bufferImageHeight;
ANGLE_VK_CHECK_MATH(contextVk,
formatInfo.computeBufferRowLength(updatedExtents.width, &bufferRowLength));
ANGLE_VK_CHECK_MATH(
contextVk, formatInfo.computeBufferImageHeight(updatedExtents.height, &bufferImageHeight));
ANGLE_TRY(dstImage->stageSubresourceUpdateFromBuffer(
contextVk, bufferSize, dstLevelGL, currentLayer, layerCount, bufferRowLength,
bufferImageHeight, updatedExtents, offset, stagingBuffer, stagingBufferOffsets));
return angle::Result::Continue;
}
const vk::Format *AdjustStorageViewFormatPerWorkarounds(ContextVk *contextVk, const vk::Format *AdjustStorageViewFormatPerWorkarounds(ContextVk *contextVk,
const vk::Format *intended) const vk::Format *intended)
{ {
...@@ -1051,17 +999,17 @@ angle::Result TextureVk::copySubImageImplWithTransfer(ContextVk *contextVk, ...@@ -1051,17 +999,17 @@ angle::Result TextureVk::copySubImageImplWithTransfer(ContextVk *contextVk,
} }
else else
{ {
std::unique_ptr<vk::ImageHelper> stagingImage;
// Create a temporary image to stage the copy // Create a temporary image to stage the copy
stagingImage = std::make_unique<vk::ImageHelper>(); std::unique_ptr<vk::RefCounted<vk::ImageHelper>> stagingImage;
stagingImage = std::make_unique<vk::RefCounted<vk::ImageHelper>>();
ANGLE_TRY(stagingImage->init2DStaging(contextVk, renderer->getMemoryProperties(), ANGLE_TRY(
stagingImage->get().init2DStaging(contextVk, renderer->getMemoryProperties(),
gl::Extents(sourceBox.width, sourceBox.height, 1), gl::Extents(sourceBox.width, sourceBox.height, 1),
destFormat, kTransferStagingImageFlags, layerCount)); destFormat, kTransferStagingImageFlags, layerCount));
access.onImageTransferWrite(gl::LevelIndex(0), 1, 0, layerCount, VK_IMAGE_ASPECT_COLOR_BIT, access.onImageTransferWrite(gl::LevelIndex(0), 1, 0, layerCount, VK_IMAGE_ASPECT_COLOR_BIT,
stagingImage.get()); &stagingImage->get());
vk::CommandBuffer *commandBuffer; vk::CommandBuffer *commandBuffer;
ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer(access, &commandBuffer)); ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer(access, &commandBuffer));
...@@ -1078,7 +1026,7 @@ angle::Result TextureVk::copySubImageImplWithTransfer(ContextVk *contextVk, ...@@ -1078,7 +1026,7 @@ angle::Result TextureVk::copySubImageImplWithTransfer(ContextVk *contextVk,
extents.depth = 1; extents.depth = 1;
} }
vk::ImageHelper::Copy(srcImage, stagingImage.get(), srcOffset, gl::kOffsetZero, extents, vk::ImageHelper::Copy(srcImage, &stagingImage->get(), srcOffset, gl::kOffsetZero, extents,
srcSubresource, destSubresource, commandBuffer); srcSubresource, destSubresource, commandBuffer);
// Stage the copy for when the image storage is actually created. // Stage the copy for when the image storage is actually created.
...@@ -1086,7 +1034,8 @@ angle::Result TextureVk::copySubImageImplWithTransfer(ContextVk *contextVk, ...@@ -1086,7 +1034,8 @@ angle::Result TextureVk::copySubImageImplWithTransfer(ContextVk *contextVk,
const gl::ImageIndex stagingIndex = const gl::ImageIndex stagingIndex =
gl::ImageIndex::Make2DArrayRange(level.get(), baseLayer, layerCount); gl::ImageIndex::Make2DArrayRange(level.get(), baseLayer, layerCount);
mImage->stageSubresourceUpdateFromImage(stagingImage.release(), stagingIndex, mImage->stageSubresourceUpdateFromImage(stagingImage.release(), stagingIndex,
destOffsetModified, extents, imageType); vk::LevelIndex(0), destOffsetModified, extents,
imageType);
} }
return angle::Result::Continue; return angle::Result::Continue;
...@@ -1201,15 +1150,15 @@ angle::Result TextureVk::copySubImageImplWithDraw(ContextVk *contextVk, ...@@ -1201,15 +1150,15 @@ angle::Result TextureVk::copySubImageImplWithDraw(ContextVk *contextVk,
} }
else else
{ {
std::unique_ptr<vk::ImageHelper> stagingImage;
GLint samples = srcImage->getSamples(); GLint samples = srcImage->getSamples();
gl::TextureType stagingTextureType = vk::Get2DTextureType(layerCount, samples); gl::TextureType stagingTextureType = vk::Get2DTextureType(layerCount, samples);
// Create a temporary image to stage the copy // Create a temporary image to stage the copy
stagingImage = std::make_unique<vk::ImageHelper>(); std::unique_ptr<vk::RefCounted<vk::ImageHelper>> stagingImage;
stagingImage = std::make_unique<vk::RefCounted<vk::ImageHelper>>();
ANGLE_TRY(stagingImage->init2DStaging(contextVk, renderer->getMemoryProperties(), ANGLE_TRY(
stagingImage->get().init2DStaging(contextVk, renderer->getMemoryProperties(),
gl::Extents(sourceBox.width, sourceBox.height, 1), gl::Extents(sourceBox.width, sourceBox.height, 1),
destFormat, kDrawStagingImageFlags, layerCount)); destFormat, kDrawStagingImageFlags, layerCount));
...@@ -1223,12 +1172,12 @@ angle::Result TextureVk::copySubImageImplWithDraw(ContextVk *contextVk, ...@@ -1223,12 +1172,12 @@ angle::Result TextureVk::copySubImageImplWithDraw(ContextVk *contextVk,
// Create a temporary view for this layer. // Create a temporary view for this layer.
vk::ImageView stagingView; vk::ImageView stagingView;
ANGLE_TRY(stagingImage->initLayerImageView( ANGLE_TRY(stagingImage->get().initLayerImageView(
contextVk, stagingTextureType, VK_IMAGE_ASPECT_COLOR_BIT, gl::SwizzleState(), contextVk, stagingTextureType, VK_IMAGE_ASPECT_COLOR_BIT, gl::SwizzleState(),
&stagingView, vk::LevelIndex(0), 1, layerIndex, 1, &stagingView, vk::LevelIndex(0), 1, layerIndex, 1,
gl::SrgbWriteControlMode::Default)); gl::SrgbWriteControlMode::Default));
ANGLE_TRY(utilsVk.copyImage(contextVk, stagingImage.get(), &stagingView, srcImage, ANGLE_TRY(utilsVk.copyImage(contextVk, &stagingImage->get(), &stagingView, srcImage,
srcView, params)); srcView, params));
// Queue the resource for cleanup as soon as the copy above is finished. There's no // Queue the resource for cleanup as soon as the copy above is finished. There's no
...@@ -1255,7 +1204,8 @@ angle::Result TextureVk::copySubImageImplWithDraw(ContextVk *contextVk, ...@@ -1255,7 +1204,8 @@ angle::Result TextureVk::copySubImageImplWithDraw(ContextVk *contextVk,
const gl::ImageIndex stagingIndex = const gl::ImageIndex stagingIndex =
gl::ImageIndex::Make2DArrayRange(level.get(), baseLayer, layerCount); gl::ImageIndex::Make2DArrayRange(level.get(), baseLayer, layerCount);
mImage->stageSubresourceUpdateFromImage(stagingImage.release(), stagingIndex, mImage->stageSubresourceUpdateFromImage(stagingImage.release(), stagingIndex,
destOffsetModified, extents, imageType); vk::LevelIndex(0), destOffsetModified, extents,
imageType);
} }
return angle::Result::Continue; return angle::Result::Continue;
...@@ -1985,34 +1935,62 @@ angle::Result TextureVk::copyAndStageImageData(ContextVk *contextVk, ...@@ -1985,34 +1935,62 @@ angle::Result TextureVk::copyAndStageImageData(ContextVk *contextVk,
{ {
// Preserve the data in the Vulkan image. GL texture's staged updates that correspond to // Preserve the data in the Vulkan image. GL texture's staged updates that correspond to
// levels outside the range of the Vulkan image will remain intact. // levels outside the range of the Vulkan image will remain intact.
RendererVk *renderer = contextVk->getRenderer();
// The staged updates won't be applied until the image has the requisite mip levels // Create a temp copy of srcImage for staging.
for (uint32_t layer = 0; layer < srcImage->getLayerCount(); layer++) std::unique_ptr<vk::RefCounted<vk::ImageHelper>> stagingImage;
{ stagingImage = std::make_unique<vk::RefCounted<vk::ImageHelper>>();
for (vk::LevelIndex levelVk(0); levelVk < vk::LevelIndex(srcImage->getLevelCount());
++levelVk)
{
// Vulkan level 0 previously aligned with whatever the base level was.
gl::LevelIndex levelGL = vk_gl::GetLevelIndex(levelVk, previousFirstAllocateLevel);
const uint32_t levelCount = srcImage->getLevelCount();
const uint32_t layerCount = srcImage->getLayerCount();
ANGLE_TRY(stagingImage->get().initStaging(contextVk, renderer->getMemoryProperties(),
srcImage->getType(), srcImage->getExtents(),
srcImage->getFormat(), srcImage->getSamples(),
kTransferStagingImageFlags, levelCount, layerCount));
// Copy the src image wholly into the staging image
const VkImageAspectFlags aspectFlags = srcImage->getAspectFlags();
vk::CommandBufferAccess access;
access.onImageTransferWrite(gl::LevelIndex(0), levelCount, 0, layerCount, aspectFlags,
&stagingImage->get());
access.onImageTransferRead(aspectFlags, srcImage);
vk::CommandBuffer *commandBuffer;
ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer(access, &commandBuffer));
VkImageCopy copyRegion = {};
copyRegion.srcSubresource.aspectMask = aspectFlags;
copyRegion.srcSubresource.layerCount = layerCount;
copyRegion.dstSubresource = copyRegion.srcSubresource;
for (vk::LevelIndex levelVk(0); levelVk < vk::LevelIndex(levelCount); ++levelVk)
{
if (mRedefinedLevels.test(levelVk.get())) if (mRedefinedLevels.test(levelVk.get()))
{ {
// Note: if this level is incompatibly redefined, there will necessarily be a // Note: if this level is incompatibly redefined, there will necessarily be a
// staged update, and the contents of the image are to be thrown away. // staged update, and the contents of the image are to be thrown away.
ASSERT(srcImage->hasStagedUpdatesForSubresource(levelGL, layer, 1)); ASSERT(srcImage->hasStagedUpdatesForSubresource(
vk_gl::GetLevelIndex(levelVk, previousFirstAllocateLevel), 0, layerCount));
continue; continue;
} }
ASSERT(!srcImage->hasStagedUpdatesForSubresource(levelGL, layer, 1)); gl::Extents levelExtents = srcImage->getLevelExtents(levelVk);
// Pull data from the current image and stage it as an update for the new image copyRegion.srcSubresource.mipLevel = levelVk.get();
copyRegion.dstSubresource.mipLevel = levelVk.get();
gl_vk::GetExtent(levelExtents, &copyRegion.extent);
// First we populate the staging buffer with current level data commandBuffer->copyImage(srcImage->getImage(), srcImage->getCurrentLayout(),
ANGLE_TRY(CopyAndStageImageSubresource(contextVk, mState.getType(), true, layer, stagingImage->get().getImage(),
levelVk, levelGL, srcImage, dstImage)); stagingImage->get().getCurrentLayout(), 1, &copyRegion);
}
} }
// Stage the staging image in the destination
dstImage->stageSubresourceUpdatesFromAllImageLevels(
renderer, stagingImage.release(), previousFirstAllocateLevel, mRedefinedLevels);
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -2044,19 +2022,17 @@ angle::Result TextureVk::respecifyImageStorageAndLevels(ContextVk *contextVk, ...@@ -2044,19 +2022,17 @@ angle::Result TextureVk::respecifyImageStorageAndLevels(ContextVk *contextVk,
ANGLE_TRY(flushImageStagedUpdates(contextVk)); ANGLE_TRY(flushImageStagedUpdates(contextVk));
} }
if (!mOwnsImage)
{
// Cache values needed for copy and stage operations // Cache values needed for copy and stage operations
bool ownsCurrentImage = mOwnsImage;
const vk::Format &format = mImage->getFormat();
vk::ImageHelper *srcImage = mImage; vk::ImageHelper *srcImage = mImage;
vk::ImageHelper *dstImage = mImage; const vk::Format &format = mImage->getFormat();
if (!ownsCurrentImage)
{
// If any level was redefined but the image was not owned by the Texture, it's already // If any level was redefined but the image was not owned by the Texture, it's already
// released and deleted by TextureVk::redefineLevel(). // released and deleted by TextureVk::redefineLevel().
ASSERT(!mRedefinedLevels.any()); ASSERT(!mRedefinedLevels.any());
// If we din't own the image, release the current and create a new one // If we didn't own the image, release the current and create a new one
releaseImage(contextVk); releaseImage(contextVk);
// Create the image helper // Create the image helper
...@@ -2083,23 +2059,25 @@ angle::Result TextureVk::respecifyImageStorageAndLevels(ContextVk *contextVk, ...@@ -2083,23 +2059,25 @@ angle::Result TextureVk::respecifyImageStorageAndLevels(ContextVk *contextVk,
baseLevelExtents, mState.getEffectiveBaseLevel(), levelCount)); baseLevelExtents, mState.getEffectiveBaseLevel(), levelCount));
} }
// Set the newly created mImage as the destination for the staging operation // Make a copy of the old image (that's being released) and stage that as an update to the
dstImage = mImage; // new image.
ANGLE_TRY(copyAndStageImageData(contextVk, previousFirstAllocateLevel, srcImage, mImage));
} }
else
{
// TODO: Make the image stage itself as an update to its levels. http://anglebug.com/4835
// Transfer the entire contents of the source image into the destination image. // Make a copy of the current image and stage that as an update to the new image.
ANGLE_TRY(copyAndStageImageData(contextVk, previousFirstAllocateLevel, srcImage, dstImage)); ANGLE_TRY(copyAndStageImageData(contextVk, previousFirstAllocateLevel, mImage, mImage));
// 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.
// Do this iff we owned the image and didn't create a new one. // Do this iff we owned the image and didn't create a new one.
if (ownsCurrentImage)
{
releaseImage(contextVk); releaseImage(contextVk);
if (!mState.getImmutableFormat()) if (!mState.getImmutableFormat())
{ {
dstImage->setFirstAllocatedLevel(baseLevel); mImage->setFirstAllocatedLevel(baseLevel);
} }
} }
......
...@@ -3987,7 +3987,7 @@ angle::Result ImageHelper::initExternal(Context *context, ...@@ -3987,7 +3987,7 @@ angle::Result ImageHelper::initExternal(Context *context,
imageInfo.imageType = mImageType; imageInfo.imageType = mImageType;
imageInfo.format = format.actualImageVkFormat(); imageInfo.format = format.actualImageVkFormat();
imageInfo.extent = mExtents; imageInfo.extent = mExtents;
imageInfo.mipLevels = mipLevels; imageInfo.mipLevels = mLevelCount;
imageInfo.arrayLayers = mLayerCount; imageInfo.arrayLayers = mLayerCount;
imageInfo.samples = gl_vk::GetSamples(mSamples); imageInfo.samples = gl_vk::GetSamples(mSamples);
imageInfo.tiling = mTilingMode; imageInfo.tiling = mTilingMode;
...@@ -4038,6 +4038,8 @@ void ImageHelper::releaseImageFromShareContexts(RendererVk *renderer, ContextVk ...@@ -4038,6 +4038,8 @@ void ImageHelper::releaseImageFromShareContexts(RendererVk *renderer, ContextVk
void ImageHelper::releaseStagingBuffer(RendererVk *renderer) void ImageHelper::releaseStagingBuffer(RendererVk *renderer)
{ {
ASSERT(validateSubresourceUpdateImageRefsConsistent());
// Remove updates that never made it to the texture. // Remove updates that never made it to the texture.
for (std::vector<SubresourceUpdate> &levelUpdates : mSubresourceUpdates) for (std::vector<SubresourceUpdate> &levelUpdates : mSubresourceUpdates)
{ {
...@@ -4046,6 +4048,9 @@ void ImageHelper::releaseStagingBuffer(RendererVk *renderer) ...@@ -4046,6 +4048,9 @@ void ImageHelper::releaseStagingBuffer(RendererVk *renderer)
update.release(renderer); update.release(renderer);
} }
} }
ASSERT(validateSubresourceUpdateImageRefsConsistent());
mStagingBuffer.release(renderer); mStagingBuffer.release(renderer);
mSubresourceUpdates.clear(); mSubresourceUpdates.clear();
mCurrentSingleClearValue.reset(); mCurrentSingleClearValue.reset();
...@@ -4407,18 +4412,39 @@ angle::Result ImageHelper::init2DStaging(Context *context, ...@@ -4407,18 +4412,39 @@ angle::Result ImageHelper::init2DStaging(Context *context,
VkImageUsageFlags usage, VkImageUsageFlags usage,
uint32_t layerCount) uint32_t layerCount)
{ {
gl_vk::GetExtent(glExtents, &mExtents);
return initStaging(context, memoryProperties, VK_IMAGE_TYPE_2D, mExtents, format, 1, usage, 1,
layerCount);
}
angle::Result ImageHelper::initStaging(Context *context,
const MemoryProperties &memoryProperties,
VkImageType imageType,
const VkExtent3D &extents,
const Format &format,
GLint samples,
VkImageUsageFlags usage,
uint32_t mipLevels,
uint32_t layerCount)
{
ASSERT(!valid()); ASSERT(!valid());
ASSERT(!IsAnySubresourceContentDefined(mContentDefined)); ASSERT(!IsAnySubresourceContentDefined(mContentDefined));
ASSERT(!IsAnySubresourceContentDefined(mStencilContentDefined)); ASSERT(!IsAnySubresourceContentDefined(mStencilContentDefined));
gl_vk::GetExtent(glExtents, &mExtents); mImageType = imageType;
mExtents = extents;
mRotatedAspectRatio = false; mRotatedAspectRatio = false;
mImageType = VK_IMAGE_TYPE_2D;
mFormat = &format; mFormat = &format;
mSamples = 1; mSamples = std::max(samples, 1);
mImageSerial = context->getRenderer()->getResourceSerialFactory().generateImageSerial(); mImageSerial = context->getRenderer()->getResourceSerialFactory().generateImageSerial();
mLayerCount = layerCount; mLayerCount = layerCount;
mLevelCount = 1; mLevelCount = mipLevels;
mUsage = usage;
// Validate that mLayerCount is compatible with the image type
ASSERT(imageType != VK_IMAGE_TYPE_3D || mLayerCount == 1);
ASSERT(imageType != VK_IMAGE_TYPE_2D || mExtents.depth == 1);
mCurrentLayout = ImageLayout::Undefined; mCurrentLayout = ImageLayout::Undefined;
...@@ -4428,7 +4454,7 @@ angle::Result ImageHelper::init2DStaging(Context *context, ...@@ -4428,7 +4454,7 @@ angle::Result ImageHelper::init2DStaging(Context *context,
imageInfo.imageType = mImageType; imageInfo.imageType = mImageType;
imageInfo.format = format.actualImageVkFormat(); imageInfo.format = format.actualImageVkFormat();
imageInfo.extent = mExtents; imageInfo.extent = mExtents;
imageInfo.mipLevels = 1; imageInfo.mipLevels = mLevelCount;
imageInfo.arrayLayers = mLayerCount; imageInfo.arrayLayers = mLayerCount;
imageInfo.samples = gl_vk::GetSamples(mSamples); imageInfo.samples = gl_vk::GetSamples(mSamples);
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
...@@ -5138,7 +5164,7 @@ void ImageHelper::removeSingleSubresourceStagedUpdates(ContextVk *contextVk, ...@@ -5138,7 +5164,7 @@ void ImageHelper::removeSingleSubresourceStagedUpdates(ContextVk *contextVk,
{ {
mCurrentSingleClearValue.reset(); mCurrentSingleClearValue.reset();
// Find any staged updates for this index and removes them from the pending list. // Find any staged updates for this index and remove them from the pending list.
std::vector<SubresourceUpdate> *levelUpdates = getLevelUpdates(levelIndexGL); std::vector<SubresourceUpdate> *levelUpdates = getLevelUpdates(levelIndexGL);
if (levelUpdates == nullptr) if (levelUpdates == nullptr)
{ {
...@@ -5164,6 +5190,8 @@ void ImageHelper::removeStagedUpdates(Context *context, ...@@ -5164,6 +5190,8 @@ void ImageHelper::removeStagedUpdates(Context *context,
gl::LevelIndex levelGLStart, gl::LevelIndex levelGLStart,
gl::LevelIndex levelGLEnd) gl::LevelIndex levelGLEnd)
{ {
ASSERT(validateSubresourceUpdateImageRefsConsistent());
// Remove all updates to levels [start, end]. // Remove all updates to levels [start, end].
for (gl::LevelIndex level = levelGLStart; level <= levelGLEnd; ++level) for (gl::LevelIndex level = levelGLStart; level <= levelGLEnd; ++level)
{ {
...@@ -5181,6 +5209,8 @@ void ImageHelper::removeStagedUpdates(Context *context, ...@@ -5181,6 +5209,8 @@ void ImageHelper::removeStagedUpdates(Context *context,
levelUpdates->clear(); levelUpdates->clear();
} }
ASSERT(validateSubresourceUpdateImageRefsConsistent());
} }
angle::Result ImageHelper::stageSubresourceUpdateImpl(ContextVk *contextVk, angle::Result ImageHelper::stageSubresourceUpdateImpl(ContextVk *contextVk,
...@@ -5585,51 +5615,6 @@ angle::Result ImageHelper::stageSubresourceUpdateAndGetData(ContextVk *contextVk ...@@ -5585,51 +5615,6 @@ angle::Result ImageHelper::stageSubresourceUpdateAndGetData(ContextVk *contextVk
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result ImageHelper::stageSubresourceUpdateFromBuffer(ContextVk *contextVk,
size_t allocationSize,
gl::LevelIndex mipLevelGL,
uint32_t baseArrayLayer,
uint32_t layerCount,
uint32_t bufferRowLength,
uint32_t bufferImageHeight,
const VkExtent3D &extent,
const VkOffset3D &offset,
BufferHelper *bufferHelper,
StagingBufferOffsetArray stagingOffsets)
{
// This function stages an update from explicitly provided handle and offset
// It is used when the texture base level has changed, and we need to propagate data
//
// Note that staged updates have the GL mip level so that changing base level doesn't require
// modifying all staged updates.
VkBufferImageCopy copy[2] = {};
copy[0].bufferOffset = stagingOffsets[0];
copy[0].bufferRowLength = bufferRowLength;
copy[0].bufferImageHeight = bufferImageHeight;
copy[0].imageSubresource.aspectMask = getAspectFlags();
copy[0].imageSubresource.mipLevel = mipLevelGL.get();
copy[0].imageSubresource.baseArrayLayer = baseArrayLayer;
copy[0].imageSubresource.layerCount = layerCount;
copy[0].imageOffset = offset;
copy[0].imageExtent = extent;
if (isCombinedDepthStencilFormat())
{
// Force aspect to depth for first copy
copy[0].imageSubresource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
// Copy stencil aspect separately
copy[1] = copy[0];
copy[1].bufferOffset = stagingOffsets[1];
copy[1].imageSubresource.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
appendSubresourceUpdate(mipLevelGL, SubresourceUpdate(bufferHelper, copy[1]));
}
appendSubresourceUpdate(mipLevelGL, SubresourceUpdate(bufferHelper, copy[0]));
return angle::Result::Continue;
}
angle::Result ImageHelper::stageSubresourceUpdateFromFramebuffer( angle::Result ImageHelper::stageSubresourceUpdateFromFramebuffer(
const gl::Context *context, const gl::Context *context,
const gl::ImageIndex &index, const gl::ImageIndex &index,
...@@ -5736,8 +5721,9 @@ angle::Result ImageHelper::stageSubresourceUpdateFromFramebuffer( ...@@ -5736,8 +5721,9 @@ angle::Result ImageHelper::stageSubresourceUpdateFromFramebuffer(
return angle::Result::Continue; return angle::Result::Continue;
} }
void ImageHelper::stageSubresourceUpdateFromImage(ImageHelper *image, void ImageHelper::stageSubresourceUpdateFromImage(RefCounted<ImageHelper> *image,
const gl::ImageIndex &index, const gl::ImageIndex &index,
LevelIndex srcMipLevel,
const gl::Offset &destOffset, const gl::Offset &destOffset,
const gl::Extents &glExtents, const gl::Extents &glExtents,
const VkImageType imageType) const VkImageType imageType)
...@@ -5746,6 +5732,7 @@ void ImageHelper::stageSubresourceUpdateFromImage(ImageHelper *image, ...@@ -5746,6 +5732,7 @@ void ImageHelper::stageSubresourceUpdateFromImage(ImageHelper *image,
VkImageCopy copyToImage = {}; VkImageCopy copyToImage = {};
copyToImage.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; copyToImage.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copyToImage.srcSubresource.mipLevel = srcMipLevel.get();
copyToImage.srcSubresource.layerCount = index.getLayerCount(); copyToImage.srcSubresource.layerCount = index.getLayerCount();
copyToImage.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; copyToImage.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copyToImage.dstSubresource.mipLevel = updateLevelGL.get(); copyToImage.dstSubresource.mipLevel = updateLevelGL.get();
...@@ -5774,6 +5761,40 @@ void ImageHelper::stageSubresourceUpdateFromImage(ImageHelper *image, ...@@ -5774,6 +5761,40 @@ void ImageHelper::stageSubresourceUpdateFromImage(ImageHelper *image,
appendSubresourceUpdate(updateLevelGL, SubresourceUpdate(image, copyToImage)); appendSubresourceUpdate(updateLevelGL, SubresourceUpdate(image, copyToImage));
} }
void ImageHelper::stageSubresourceUpdatesFromAllImageLevels(RendererVk *renderer,
RefCounted<ImageHelper> *image,
gl::LevelIndex baseLevel,
gl::TexLevelMask skipLevelsMask)
{
uint32_t levelsStaged = 0;
for (LevelIndex levelVk(0); levelVk < LevelIndex(image->get().getLevelCount()); ++levelVk)
{
if (skipLevelsMask.test(levelVk.get()))
{
continue;
}
++levelsStaged;
const gl::LevelIndex levelGL = vk_gl::GetLevelIndex(levelVk, baseLevel);
const gl::ImageIndex index =
gl::ImageIndex::Make2DArrayRange(levelGL.get(), 0, image->get().getLayerCount());
stageSubresourceUpdateFromImage(image, index, levelVk, gl::kOffsetZero,
image->get().getLevelExtents(levelVk),
image->get().getType());
}
// TODO: remove skipLevelsMask and this code after optimizing image-respecify to stage itself
// (instead of a copy). http://anglebug.com/4835
if (levelsStaged == 0)
{
image->get().releaseImage(renderer);
image->get().releaseStagingBuffer(renderer);
SafeDelete(image);
}
}
void ImageHelper::stageClear(const gl::ImageIndex &index, void ImageHelper::stageClear(const gl::ImageIndex &index,
VkImageAspectFlags aspectFlags, VkImageAspectFlags aspectFlags,
const VkClearValue &clearValue) const VkClearValue &clearValue)
...@@ -5888,24 +5909,26 @@ void ImageHelper::stageSelfForBaseLevel(ContextVk *contextVk) ...@@ -5888,24 +5909,26 @@ void ImageHelper::stageSelfForBaseLevel(ContextVk *contextVk)
// since it is determined at endRenderPass time. // since it is determined at endRenderPass time.
contextVk->finalizeImageLayout(this); contextVk->finalizeImageLayout(this);
std::unique_ptr<ImageHelper> prevImage = std::make_unique<ImageHelper>(); std::unique_ptr<RefCounted<ImageHelper>> prevImage =
std::make_unique<RefCounted<ImageHelper>>();
// Move the necessary information for staged update to work, and keep the rest as part of this // Move the necessary information for staged update to work, and keep the rest as part of this
// object. // object.
// Vulkan objects // Vulkan objects
prevImage->mImage = std::move(mImage); prevImage->get().mImage = std::move(mImage);
prevImage->mDeviceMemory = std::move(mDeviceMemory); prevImage->get().mDeviceMemory = std::move(mDeviceMemory);
// Barrier information. Note: mLevelCount is set to 1 so that only the base level is // Barrier information. Note: mLevelCount is set to 1 so that only the base level is
// transitioned when flushing the update. // transitioned when flushing the update.
prevImage->mFormat = mFormat; prevImage->get().mFormat = mFormat;
prevImage->mCurrentLayout = mCurrentLayout; prevImage->get().mCurrentLayout = mCurrentLayout;
prevImage->mCurrentQueueFamilyIndex = mCurrentQueueFamilyIndex; prevImage->get().mCurrentQueueFamilyIndex = mCurrentQueueFamilyIndex;
prevImage->mLastNonShaderReadOnlyLayout = mLastNonShaderReadOnlyLayout; prevImage->get().mLastNonShaderReadOnlyLayout = mLastNonShaderReadOnlyLayout;
prevImage->mCurrentShaderReadStageMask = mCurrentShaderReadStageMask; prevImage->get().mCurrentShaderReadStageMask = mCurrentShaderReadStageMask;
prevImage->mLevelCount = 1; prevImage->get().mLevelCount = 1;
prevImage->mLayerCount = mLayerCount; prevImage->get().mLayerCount = mLayerCount;
prevImage->mImageSerial = mImageSerial; prevImage->get().mImageSerial = mImageSerial;
// Reset information for current (invalid) image. // Reset information for current (invalid) image.
mCurrentLayout = ImageLayout::Undefined; mCurrentLayout = ImageLayout::Undefined;
...@@ -5919,8 +5942,8 @@ void ImageHelper::stageSelfForBaseLevel(ContextVk *contextVk) ...@@ -5919,8 +5942,8 @@ void ImageHelper::stageSelfForBaseLevel(ContextVk *contextVk)
// Stage an update from the previous image. // Stage an update from the previous image.
const gl::ImageIndex firstAllocateLevelIndex = const gl::ImageIndex firstAllocateLevelIndex =
gl::ImageIndex::Make2DArrayRange(mFirstAllocatedLevel.get(), 0, mLayerCount); gl::ImageIndex::Make2DArrayRange(mFirstAllocatedLevel.get(), 0, mLayerCount);
stageSubresourceUpdateFromImage(prevImage.release(), firstAllocateLevelIndex, gl::kOffsetZero, stageSubresourceUpdateFromImage(prevImage.release(), firstAllocateLevelIndex, LevelIndex(0),
getLevelExtents(LevelIndex(0)), mImageType); gl::kOffsetZero, getLevelExtents(LevelIndex(0)), mImageType);
} }
angle::Result ImageHelper::flushSingleSubresourceStagedUpdates(ContextVk *contextVk, angle::Result ImageHelper::flushSingleSubresourceStagedUpdates(ContextVk *contextVk,
...@@ -5951,12 +5974,12 @@ angle::Result ImageHelper::flushSingleSubresourceStagedUpdates(ContextVk *contex ...@@ -5951,12 +5974,12 @@ angle::Result ImageHelper::flushSingleSubresourceStagedUpdates(ContextVk *contex
{ {
// On any data update, exit out. We'll need to do a full upload. // On any data update, exit out. We'll need to do a full upload.
const bool isClear = update.updateSource == UpdateSource::Clear; const bool isClear = update.updateSource == UpdateSource::Clear;
const uint32_t updateLayerCount = isClear ? update.clear.layerCount : 0; const uint32_t updateLayerCount = isClear ? update.data.clear.layerCount : 0;
const uint32_t imageLayerCount = const uint32_t imageLayerCount =
mImageType == VK_IMAGE_TYPE_3D ? getLevelExtents(levelVk).depth : mLayerCount; mImageType == VK_IMAGE_TYPE_3D ? getLevelExtents(levelVk).depth : mLayerCount;
if (!isClear || (updateLayerCount != layerCount && if (!isClear || (updateLayerCount != layerCount &&
!(update.clear.layerCount == VK_REMAINING_ARRAY_LAYERS && !(update.data.clear.layerCount == VK_REMAINING_ARRAY_LAYERS &&
imageLayerCount == layerCount))) imageLayerCount == layerCount)))
{ {
foundClear.reset(); foundClear.reset();
...@@ -5972,7 +5995,7 @@ angle::Result ImageHelper::flushSingleSubresourceStagedUpdates(ContextVk *contex ...@@ -5972,7 +5995,7 @@ angle::Result ImageHelper::flushSingleSubresourceStagedUpdates(ContextVk *contex
if (foundClear.valid()) if (foundClear.valid())
{ {
size_t foundIndex = foundClear.value(); size_t foundIndex = foundClear.value();
const ClearUpdate &update = (*levelUpdates)[foundIndex].clear; const ClearUpdate &update = (*levelUpdates)[foundIndex].data.clear;
// Note that this set command handles combined or separate depth/stencil clears. // Note that this set command handles combined or separate depth/stencil clears.
deferredClears->store(deferredClearIndex, update.aspectFlags, update.value); deferredClears->store(deferredClearIndex, update.aspectFlags, update.value);
...@@ -6016,7 +6039,7 @@ angle::Result ImageHelper::flushStagedUpdates(ContextVk *contextVk, ...@@ -6016,7 +6039,7 @@ angle::Result ImageHelper::flushStagedUpdates(ContextVk *contextVk,
{ {
SubresourceUpdate &update = (*levelUpdates)[0]; SubresourceUpdate &update = (*levelUpdates)[0];
if (update.updateSource == UpdateSource::Clear && if (update.updateSource == UpdateSource::Clear &&
mCurrentSingleClearValue.value() == update.clear) mCurrentSingleClearValue.value() == update.data.clear)
{ {
ANGLE_PERF_WARNING(contextVk->getDebug(), GL_DEBUG_SEVERITY_LOW, ANGLE_PERF_WARNING(contextVk->getDebug(), GL_DEBUG_SEVERITY_LOW,
"Repeated Clear on framebuffer attachment dropped"); "Repeated Clear on framebuffer attachment dropped");
...@@ -6027,6 +6050,8 @@ angle::Result ImageHelper::flushStagedUpdates(ContextVk *contextVk, ...@@ -6027,6 +6050,8 @@ angle::Result ImageHelper::flushStagedUpdates(ContextVk *contextVk,
} }
} }
ASSERT(validateSubresourceUpdateImageRefsConsistent());
ANGLE_TRY(mStagingBuffer.flush(contextVk)); ANGLE_TRY(mStagingBuffer.flush(contextVk));
const VkImageAspectFlags aspectFlags = GetFormatAspectFlags(mFormat->actualImageFormat()); const VkImageAspectFlags aspectFlags = GetFormatAspectFlags(mFormat->actualImageFormat());
...@@ -6070,9 +6095,9 @@ angle::Result ImageHelper::flushStagedUpdates(ContextVk *contextVk, ...@@ -6070,9 +6095,9 @@ angle::Result ImageHelper::flushStagedUpdates(ContextVk *contextVk,
{ {
ASSERT(update.updateSource == UpdateSource::Clear || ASSERT(update.updateSource == UpdateSource::Clear ||
(update.updateSource == UpdateSource::Buffer && (update.updateSource == UpdateSource::Buffer &&
update.buffer.bufferHelper != nullptr) || update.data.buffer.bufferHelper != nullptr) ||
(update.updateSource == UpdateSource::Image && update.image.image != nullptr && (update.updateSource == UpdateSource::Image && update.image != nullptr &&
update.image.image->valid())); update.image->isReferenced() && update.image->get().valid()));
uint32_t updateBaseLayer, updateLayerCount; uint32_t updateBaseLayer, updateLayerCount;
update.getDestSubresource(mLayerCount, &updateBaseLayer, &updateLayerCount); update.getDestSubresource(mLayerCount, &updateBaseLayer, &updateLayerCount);
...@@ -6098,15 +6123,15 @@ angle::Result ImageHelper::flushStagedUpdates(ContextVk *contextVk, ...@@ -6098,15 +6123,15 @@ angle::Result ImageHelper::flushStagedUpdates(ContextVk *contextVk,
// about to take effect, we need to change miplevel to LevelIndex. // about to take effect, we need to change miplevel to LevelIndex.
if (update.updateSource == UpdateSource::Clear) if (update.updateSource == UpdateSource::Clear)
{ {
update.clear.levelIndex = updateMipLevelVk.get(); update.data.clear.levelIndex = updateMipLevelVk.get();
} }
else if (update.updateSource == UpdateSource::Buffer) else if (update.updateSource == UpdateSource::Buffer)
{ {
update.buffer.copyRegion.imageSubresource.mipLevel = updateMipLevelVk.get(); update.data.buffer.copyRegion.imageSubresource.mipLevel = updateMipLevelVk.get();
} }
else if (update.updateSource == UpdateSource::Image) else if (update.updateSource == UpdateSource::Image)
{ {
update.image.copyRegion.dstSubresource.mipLevel = updateMipLevelVk.get(); update.data.image.copyRegion.dstSubresource.mipLevel = updateMipLevelVk.get();
} }
if (updateLayerCount >= kMaxParallelSubresourceUpload) if (updateLayerCount >= kMaxParallelSubresourceUpload)
...@@ -6135,19 +6160,19 @@ angle::Result ImageHelper::flushStagedUpdates(ContextVk *contextVk, ...@@ -6135,19 +6160,19 @@ angle::Result ImageHelper::flushStagedUpdates(ContextVk *contextVk,
if (update.updateSource == UpdateSource::Clear) if (update.updateSource == UpdateSource::Clear)
{ {
clear(update.clear.aspectFlags, update.clear.value, updateMipLevelVk, clear(update.data.clear.aspectFlags, update.data.clear.value, updateMipLevelVk,
updateBaseLayer, updateLayerCount, commandBuffer); updateBaseLayer, updateLayerCount, commandBuffer);
// Remember the latest operation is a clear call // Remember the latest operation is a clear call
mCurrentSingleClearValue = update.clear; mCurrentSingleClearValue = update.data.clear;
// Do not call onWrite as it removes mCurrentSingleClearValue, but instead call // Do not call onWrite as it removes mCurrentSingleClearValue, but instead call
// setContentDefined directly. // setContentDefined directly.
setContentDefined(updateMipLevelVk, 1, updateBaseLayer, updateLayerCount, setContentDefined(updateMipLevelVk, 1, updateBaseLayer, updateLayerCount,
update.clear.aspectFlags); update.data.clear.aspectFlags);
} }
else if (update.updateSource == UpdateSource::Buffer) else if (update.updateSource == UpdateSource::Buffer)
{ {
BufferUpdate &bufferUpdate = update.buffer; BufferUpdate &bufferUpdate = update.data.buffer;
BufferHelper *currentBuffer = bufferUpdate.bufferHelper; BufferHelper *currentBuffer = bufferUpdate.bufferHelper;
ASSERT(currentBuffer && currentBuffer->valid()); ASSERT(currentBuffer && currentBuffer->valid());
...@@ -6158,22 +6183,23 @@ angle::Result ImageHelper::flushStagedUpdates(ContextVk *contextVk, ...@@ -6158,22 +6183,23 @@ angle::Result ImageHelper::flushStagedUpdates(ContextVk *contextVk,
contextVk->getOutsideRenderPassCommandBuffer(bufferAccess, &commandBuffer)); contextVk->getOutsideRenderPassCommandBuffer(bufferAccess, &commandBuffer));
commandBuffer->copyBufferToImage(currentBuffer->getBuffer().getHandle(), mImage, commandBuffer->copyBufferToImage(currentBuffer->getBuffer().getHandle(), mImage,
getCurrentLayout(), 1, &update.buffer.copyRegion); getCurrentLayout(), 1,
&update.data.buffer.copyRegion);
onWrite(updateMipLevelGL, 1, updateBaseLayer, updateLayerCount, onWrite(updateMipLevelGL, 1, updateBaseLayer, updateLayerCount,
update.buffer.copyRegion.imageSubresource.aspectMask); update.data.buffer.copyRegion.imageSubresource.aspectMask);
} }
else else
{ {
CommandBufferAccess imageAccess; CommandBufferAccess imageAccess;
imageAccess.onImageTransferRead(aspectFlags, update.image.image); imageAccess.onImageTransferRead(aspectFlags, &update.image->get());
ANGLE_TRY( ANGLE_TRY(
contextVk->getOutsideRenderPassCommandBuffer(imageAccess, &commandBuffer)); contextVk->getOutsideRenderPassCommandBuffer(imageAccess, &commandBuffer));
commandBuffer->copyImage(update.image.image->getImage(), commandBuffer->copyImage(update.image->get().getImage(),
update.image.image->getCurrentLayout(), mImage, update.image->get().getCurrentLayout(), mImage,
getCurrentLayout(), 1, &update.image.copyRegion); getCurrentLayout(), 1, &update.data.image.copyRegion);
onWrite(updateMipLevelGL, 1, updateBaseLayer, updateLayerCount, onWrite(updateMipLevelGL, 1, updateBaseLayer, updateLayerCount,
update.image.copyRegion.dstSubresource.aspectMask); update.data.image.copyRegion.dstSubresource.aspectMask);
} }
update.release(contextVk->getRenderer()); update.release(contextVk->getRenderer());
...@@ -6194,6 +6220,8 @@ angle::Result ImageHelper::flushStagedUpdates(ContextVk *contextVk, ...@@ -6194,6 +6220,8 @@ angle::Result ImageHelper::flushStagedUpdates(ContextVk *contextVk,
} }
mSubresourceUpdates.resize(compactSize); mSubresourceUpdates.resize(compactSize);
ASSERT(validateSubresourceUpdateImageRefsConsistent());
// If no updates left, release the staging buffers to save memory. // If no updates left, release the staging buffers to save memory.
if (mSubresourceUpdates.empty()) if (mSubresourceUpdates.empty())
{ {
...@@ -6270,6 +6298,46 @@ bool ImageHelper::hasStagedUpdatesInLevels(gl::LevelIndex levelStart, gl::LevelI ...@@ -6270,6 +6298,46 @@ bool ImageHelper::hasStagedUpdatesInLevels(gl::LevelIndex levelStart, gl::LevelI
return false; return false;
} }
bool ImageHelper::validateSubresourceUpdateImageRefConsistent(RefCounted<ImageHelper> *image) const
{
if (image == nullptr)
{
return true;
}
uint32_t refs = 0;
for (const std::vector<SubresourceUpdate> &levelUpdates : mSubresourceUpdates)
{
for (const SubresourceUpdate &update : levelUpdates)
{
if (update.updateSource == UpdateSource::Image && update.image == image)
{
++refs;
}
}
}
return image->isRefCountAsExpected(refs);
}
bool ImageHelper::validateSubresourceUpdateImageRefsConsistent() const
{
for (const std::vector<SubresourceUpdate> &levelUpdates : mSubresourceUpdates)
{
for (const SubresourceUpdate &update : levelUpdates)
{
if (update.updateSource == UpdateSource::Image &&
!validateSubresourceUpdateImageRefConsistent(update.image))
{
return false;
}
}
}
return true;
}
void ImageHelper::removeSupersededUpdates(ContextVk *contextVk, gl::TexLevelMask skipLevelsMask) void ImageHelper::removeSupersededUpdates(ContextVk *contextVk, gl::TexLevelMask skipLevelsMask)
{ {
if (mLayerCount > 64) if (mLayerCount > 64)
...@@ -6279,6 +6347,8 @@ void ImageHelper::removeSupersededUpdates(ContextVk *contextVk, gl::TexLevelMask ...@@ -6279,6 +6347,8 @@ void ImageHelper::removeSupersededUpdates(ContextVk *contextVk, gl::TexLevelMask
return; return;
} }
ASSERT(validateSubresourceUpdateImageRefsConsistent());
RendererVk *renderer = contextVk->getRenderer(); RendererVk *renderer = contextVk->getRenderer();
// Go over updates in reverse order, and mark the layers they completely overwrite. If an // Go over updates in reverse order, and mark the layers they completely overwrite. If an
...@@ -6333,12 +6403,13 @@ void ImageHelper::removeSupersededUpdates(ContextVk *contextVk, gl::TexLevelMask ...@@ -6333,12 +6403,13 @@ void ImageHelper::removeSupersededUpdates(ContextVk *contextVk, gl::TexLevelMask
if (update.updateSource == UpdateSource::Buffer) if (update.updateSource == UpdateSource::Buffer)
{ {
updateBox = updateBox = gl::Box(update.data.buffer.copyRegion.imageOffset,
gl::Box(update.buffer.copyRegion.imageOffset, update.buffer.copyRegion.imageExtent); update.data.buffer.copyRegion.imageExtent);
} }
else if (update.updateSource == UpdateSource::Image) else if (update.updateSource == UpdateSource::Image)
{ {
updateBox = gl::Box(update.image.copyRegion.dstOffset, update.image.copyRegion.extent); updateBox = gl::Box(update.data.image.copyRegion.dstOffset,
update.data.image.copyRegion.extent);
} }
// Only if the update is to the whole subresource, mark its layers. // Only if the update is to the whole subresource, mark its layers.
...@@ -6382,6 +6453,8 @@ void ImageHelper::removeSupersededUpdates(ContextVk *contextVk, gl::TexLevelMask ...@@ -6382,6 +6453,8 @@ void ImageHelper::removeSupersededUpdates(ContextVk *contextVk, gl::TexLevelMask
markLayersAndDropSuperseded) markLayersAndDropSuperseded)
.base()); .base());
} }
ASSERT(validateSubresourceUpdateImageRefsConsistent());
} }
angle::Result ImageHelper::copyImageDataToBuffer(ContextVk *contextVk, angle::Result ImageHelper::copyImageDataToBuffer(ContextVk *contextVk,
...@@ -6768,51 +6841,58 @@ angle::Result ImageHelper::readPixels(ContextVk *contextVk, ...@@ -6768,51 +6841,58 @@ angle::Result ImageHelper::readPixels(ContextVk *contextVk,
} }
// ImageHelper::SubresourceUpdate implementation // ImageHelper::SubresourceUpdate implementation
ImageHelper::SubresourceUpdate::SubresourceUpdate() : updateSource(UpdateSource::Buffer), buffer{} ImageHelper::SubresourceUpdate::SubresourceUpdate()
{} : updateSource(UpdateSource::Buffer), image(nullptr)
ImageHelper::SubresourceUpdate::~SubresourceUpdate()
{ {
ASSERT(updateSource != UpdateSource::Image || image.image == nullptr); data.buffer.bufferHelper = nullptr;
} }
ImageHelper::SubresourceUpdate::~SubresourceUpdate() {}
ImageHelper::SubresourceUpdate::SubresourceUpdate(BufferHelper *bufferHelperIn, ImageHelper::SubresourceUpdate::SubresourceUpdate(BufferHelper *bufferHelperIn,
const VkBufferImageCopy &copyRegionIn) const VkBufferImageCopy &copyRegionIn)
: updateSource(UpdateSource::Buffer), buffer{bufferHelperIn, copyRegionIn} : updateSource(UpdateSource::Buffer), image(nullptr)
{} {
data.buffer.bufferHelper = bufferHelperIn;
data.buffer.copyRegion = copyRegionIn;
}
ImageHelper::SubresourceUpdate::SubresourceUpdate(ImageHelper *imageIn, ImageHelper::SubresourceUpdate::SubresourceUpdate(RefCounted<ImageHelper> *imageIn,
const VkImageCopy &copyRegionIn) const VkImageCopy &copyRegionIn)
: updateSource(UpdateSource::Image), image{imageIn, copyRegionIn} : updateSource(UpdateSource::Image), image(imageIn)
{} {
image->addRef();
data.image.copyRegion = copyRegionIn;
}
ImageHelper::SubresourceUpdate::SubresourceUpdate(VkImageAspectFlags aspectFlags, ImageHelper::SubresourceUpdate::SubresourceUpdate(VkImageAspectFlags aspectFlags,
const VkClearValue &clearValue, const VkClearValue &clearValue,
const gl::ImageIndex &imageIndex) const gl::ImageIndex &imageIndex)
: updateSource(UpdateSource::Clear) : updateSource(UpdateSource::Clear), image(nullptr)
{ {
clear.aspectFlags = aspectFlags; data.clear.aspectFlags = aspectFlags;
clear.value = clearValue; data.clear.value = clearValue;
clear.levelIndex = imageIndex.getLevelIndex(); data.clear.levelIndex = imageIndex.getLevelIndex();
clear.layerIndex = imageIndex.hasLayer() ? imageIndex.getLayerIndex() : 0; data.clear.layerIndex = imageIndex.hasLayer() ? imageIndex.getLayerIndex() : 0;
clear.layerCount = data.clear.layerCount =
imageIndex.hasLayer() ? imageIndex.getLayerCount() : VK_REMAINING_ARRAY_LAYERS; imageIndex.hasLayer() ? imageIndex.getLayerCount() : VK_REMAINING_ARRAY_LAYERS;
} }
ImageHelper::SubresourceUpdate::SubresourceUpdate(SubresourceUpdate &&other) ImageHelper::SubresourceUpdate::SubresourceUpdate(SubresourceUpdate &&other)
: updateSource(other.updateSource) : updateSource(other.updateSource), image(nullptr)
{ {
switch (updateSource) switch (updateSource)
{ {
case UpdateSource::Clear: case UpdateSource::Clear:
clear = other.clear; data.clear = other.data.clear;
break; break;
case UpdateSource::Buffer: case UpdateSource::Buffer:
buffer = other.buffer; data.buffer = other.data.buffer;
break; break;
case UpdateSource::Image: case UpdateSource::Image:
data.image = other.data.image;
image = other.image; image = other.image;
other.image.image = nullptr; other.image = nullptr;
break; break;
default: default:
UNREACHABLE(); UNREACHABLE();
...@@ -6845,10 +6925,17 @@ void ImageHelper::SubresourceUpdate::release(RendererVk *renderer) ...@@ -6845,10 +6925,17 @@ void ImageHelper::SubresourceUpdate::release(RendererVk *renderer)
{ {
if (updateSource == UpdateSource::Image) if (updateSource == UpdateSource::Image)
{ {
image->releaseRef();
if (!image->isReferenced())
{
// Staging images won't be used in render pass attachments. // Staging images won't be used in render pass attachments.
image.image->releaseImage(renderer); image->get().releaseImage(renderer);
image.image->releaseStagingBuffer(renderer); image->get().releaseStagingBuffer(renderer);
SafeDelete(image.image); SafeDelete(image);
}
image = nullptr;
} }
} }
...@@ -6868,8 +6955,8 @@ void ImageHelper::SubresourceUpdate::getDestSubresource(uint32_t imageLayerCount ...@@ -6868,8 +6955,8 @@ void ImageHelper::SubresourceUpdate::getDestSubresource(uint32_t imageLayerCount
{ {
if (updateSource == UpdateSource::Clear) if (updateSource == UpdateSource::Clear)
{ {
*baseLayerOut = clear.layerIndex; *baseLayerOut = data.clear.layerIndex;
*layerCountOut = clear.layerCount; *layerCountOut = data.clear.layerCount;
if (*layerCountOut == static_cast<uint32_t>(gl::ImageIndex::kEntireLevel)) if (*layerCountOut == static_cast<uint32_t>(gl::ImageIndex::kEntireLevel))
{ {
...@@ -6878,9 +6965,9 @@ void ImageHelper::SubresourceUpdate::getDestSubresource(uint32_t imageLayerCount ...@@ -6878,9 +6965,9 @@ void ImageHelper::SubresourceUpdate::getDestSubresource(uint32_t imageLayerCount
} }
else else
{ {
const VkImageSubresourceLayers &dstSubresource = updateSource == UpdateSource::Buffer const VkImageSubresourceLayers &dstSubresource =
? buffer.copyRegion.imageSubresource updateSource == UpdateSource::Buffer ? data.buffer.copyRegion.imageSubresource
: image.copyRegion.dstSubresource; : data.image.copyRegion.dstSubresource;
*baseLayerOut = dstSubresource.baseArrayLayer; *baseLayerOut = dstSubresource.baseArrayLayer;
*layerCountOut = dstSubresource.layerCount; *layerCountOut = dstSubresource.layerCount;
...@@ -6892,16 +6979,16 @@ VkImageAspectFlags ImageHelper::SubresourceUpdate::getDestAspectFlags() const ...@@ -6892,16 +6979,16 @@ VkImageAspectFlags ImageHelper::SubresourceUpdate::getDestAspectFlags() const
{ {
if (updateSource == UpdateSource::Clear) if (updateSource == UpdateSource::Clear)
{ {
return clear.aspectFlags; return data.clear.aspectFlags;
} }
else if (updateSource == UpdateSource::Buffer) else if (updateSource == UpdateSource::Buffer)
{ {
return buffer.copyRegion.imageSubresource.aspectMask; return data.buffer.copyRegion.imageSubresource.aspectMask;
} }
else else
{ {
ASSERT(updateSource == UpdateSource::Image); ASSERT(updateSource == UpdateSource::Image);
return image.copyRegion.dstSubresource.aspectMask; return data.image.copyRegion.dstSubresource.aspectMask;
} }
} }
......
...@@ -1512,6 +1512,19 @@ class ImageHelper final : public Resource, public angle::Subject ...@@ -1512,6 +1512,19 @@ class ImageHelper final : public Resource, public angle::Subject
const Format &format, const Format &format,
VkImageUsageFlags usage, VkImageUsageFlags usage,
uint32_t layerCount); uint32_t layerCount);
// Create an image for staging purposes. Used by:
//
// - TextureVk::copyAndStageImageData
//
angle::Result initStaging(Context *context,
const MemoryProperties &memoryProperties,
VkImageType imageType,
const VkExtent3D &extents,
const Format &format,
GLint samples,
VkImageUsageFlags usage,
uint32_t mipLevels,
uint32_t layerCount);
// Create a multisampled image for use as the implicit image in multisampled render to texture // Create a multisampled image for use as the implicit image in multisampled render to texture
// rendering. If LAZILY_ALLOCATED memory is available, it will prefer that. // rendering. If LAZILY_ALLOCATED memory is available, it will prefer that.
angle::Result initImplicitMultisampledRenderToTexture(Context *context, angle::Result initImplicitMultisampledRenderToTexture(Context *context,
...@@ -1665,18 +1678,6 @@ class ImageHelper final : public Resource, public angle::Subject ...@@ -1665,18 +1678,6 @@ class ImageHelper final : public Resource, public angle::Subject
uint8_t **destData, uint8_t **destData,
DynamicBuffer *stagingBufferOverride); DynamicBuffer *stagingBufferOverride);
angle::Result stageSubresourceUpdateFromBuffer(ContextVk *contextVk,
size_t allocationSize,
gl::LevelIndex mipLevelGL,
uint32_t baseArrayLayer,
uint32_t layerCount,
uint32_t bufferRowLength,
uint32_t bufferImageHeight,
const VkExtent3D &extent,
const VkOffset3D &offset,
BufferHelper *stagingBuffer,
StagingBufferOffsetArray stagingOffsets);
angle::Result stageSubresourceUpdateFromFramebuffer(const gl::Context *context, angle::Result stageSubresourceUpdateFromFramebuffer(const gl::Context *context,
const gl::ImageIndex &index, const gl::ImageIndex &index,
const gl::Rectangle &sourceArea, const gl::Rectangle &sourceArea,
...@@ -1686,12 +1687,20 @@ class ImageHelper final : public Resource, public angle::Subject ...@@ -1686,12 +1687,20 @@ class ImageHelper final : public Resource, public angle::Subject
FramebufferVk *framebufferVk, FramebufferVk *framebufferVk,
DynamicBuffer *stagingBufferOverride); DynamicBuffer *stagingBufferOverride);
void stageSubresourceUpdateFromImage(ImageHelper *image, void stageSubresourceUpdateFromImage(RefCounted<ImageHelper> *image,
const gl::ImageIndex &index, const gl::ImageIndex &index,
LevelIndex srcMipLevel,
const gl::Offset &destOffset, const gl::Offset &destOffset,
const gl::Extents &glExtents, const gl::Extents &glExtents,
const VkImageType imageType); const VkImageType imageType);
// Takes an image and stages a subresource update for each level of it, including its full
// extent and all its layers, at the specified GL level.
void stageSubresourceUpdatesFromAllImageLevels(RendererVk *renderer,
RefCounted<ImageHelper> *image,
gl::LevelIndex baseLevel,
gl::TexLevelMask skipLevelsMask);
// Stage a clear to an arbitrary value. // Stage a clear to an arbitrary value.
void stageClear(const gl::ImageIndex &index, void stageClear(const gl::ImageIndex &index,
VkImageAspectFlags aspectFlags, VkImageAspectFlags aspectFlags,
...@@ -1908,7 +1917,6 @@ class ImageHelper final : public Resource, public angle::Subject ...@@ -1908,7 +1917,6 @@ class ImageHelper final : public Resource, public angle::Subject
}; };
struct ImageUpdate struct ImageUpdate
{ {
ImageHelper *image;
VkImageCopy copyRegion; VkImageCopy copyRegion;
}; };
...@@ -1917,7 +1925,7 @@ class ImageHelper final : public Resource, public angle::Subject ...@@ -1917,7 +1925,7 @@ class ImageHelper final : public Resource, public angle::Subject
SubresourceUpdate(); SubresourceUpdate();
~SubresourceUpdate(); ~SubresourceUpdate();
SubresourceUpdate(BufferHelper *bufferHelperIn, const VkBufferImageCopy &copyRegion); SubresourceUpdate(BufferHelper *bufferHelperIn, const VkBufferImageCopy &copyRegion);
SubresourceUpdate(ImageHelper *image, const VkImageCopy &copyRegion); SubresourceUpdate(RefCounted<ImageHelper> *imageIn, const VkImageCopy &copyRegion);
SubresourceUpdate(VkImageAspectFlags aspectFlags, SubresourceUpdate(VkImageAspectFlags aspectFlags,
const VkClearValue &clearValue, const VkClearValue &clearValue,
const gl::ImageIndex &imageIndex); const gl::ImageIndex &imageIndex);
...@@ -1939,7 +1947,8 @@ class ImageHelper final : public Resource, public angle::Subject ...@@ -1939,7 +1947,8 @@ class ImageHelper final : public Resource, public angle::Subject
ClearUpdate clear; ClearUpdate clear;
BufferUpdate buffer; BufferUpdate buffer;
ImageUpdate image; ImageUpdate image;
}; } data;
RefCounted<ImageHelper> *image;
}; };
// Called from flushStagedUpdates, removes updates that are later superseded by another. This // Called from flushStagedUpdates, removes updates that are later superseded by another. This
...@@ -1989,6 +1998,11 @@ class ImageHelper final : public Resource, public angle::Subject ...@@ -1989,6 +1998,11 @@ class ImageHelper final : public Resource, public angle::Subject
// Whether there are any updates in [start, end). // Whether there are any updates in [start, end).
bool hasStagedUpdatesInLevels(gl::LevelIndex levelStart, gl::LevelIndex levelEnd) const; bool hasStagedUpdatesInLevels(gl::LevelIndex levelStart, gl::LevelIndex levelEnd) const;
// Used only for assertions, these functions verify that SubresourceUpdate::image references
// have the correct ref count. This is to prevent accidental leaks.
bool validateSubresourceUpdateImageRefConsistent(RefCounted<ImageHelper> *image) const;
bool validateSubresourceUpdateImageRefsConsistent() const;
void resetCachedProperties(); void resetCachedProperties();
void setEntireContentDefined(); void setEntireContentDefined();
void setEntireContentUndefined(); void setEntireContentUndefined();
......
...@@ -531,6 +531,9 @@ class RefCounted : angle::NonCopyable ...@@ -531,6 +531,9 @@ class RefCounted : angle::NonCopyable
T &get() { return mObject; } T &get() { return mObject; }
const T &get() const { return mObject; } const T &get() const { return mObject; }
// A debug function to validate that the reference count is as expected used for assertions.
bool isRefCountAsExpected(uint32_t expectedRefCount) { return mRefCount == expectedRefCount; }
private: private:
uint32_t mRefCount; uint32_t mRefCount;
T mObject; T mObject;
......
...@@ -3955,7 +3955,7 @@ TEST_P(Texture2DBaseMaxTestES3, GenerateMipmapAfterRedefineAndRebase) ...@@ -3955,7 +3955,7 @@ TEST_P(Texture2DBaseMaxTestES3, GenerateMipmapAfterRedefineAndRebase)
{ {
setLodUniform(lod); setLodUniform(lod);
drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f); drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, kMipColors[lod]); EXPECT_PIXEL_COLOR_EQ(0, 0, kMipColors[lod]) << lod;
} }
// Redefine level 2 to an incompatible size, say the same size as level 0. // Redefine level 2 to an incompatible size, say the same size as level 0.
...@@ -3978,10 +3978,10 @@ TEST_P(Texture2DBaseMaxTestES3, GenerateMipmapAfterRedefineAndRebase) ...@@ -3978,10 +3978,10 @@ TEST_P(Texture2DBaseMaxTestES3, GenerateMipmapAfterRedefineAndRebase)
{ {
setLodUniform(lod); setLodUniform(lod);
drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f); drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, kMipColors[1]); EXPECT_PIXEL_COLOR_EQ(0, 0, kMipColors[1]) << lod;
EXPECT_PIXEL_COLOR_EQ(w, 0, kMipColors[1]); EXPECT_PIXEL_COLOR_EQ(w, 0, kMipColors[1]) << lod;
EXPECT_PIXEL_COLOR_EQ(0, h, kMipColors[1]); EXPECT_PIXEL_COLOR_EQ(0, h, kMipColors[1]) << lod;
EXPECT_PIXEL_COLOR_EQ(w, h, kMipColors[1]); EXPECT_PIXEL_COLOR_EQ(w, h, kMipColors[1]) << lod;
} }
// Redefine level 1 (current base level) to an incompatible size. // Redefine level 1 (current base level) to an incompatible size.
...@@ -3998,10 +3998,10 @@ TEST_P(Texture2DBaseMaxTestES3, GenerateMipmapAfterRedefineAndRebase) ...@@ -3998,10 +3998,10 @@ TEST_P(Texture2DBaseMaxTestES3, GenerateMipmapAfterRedefineAndRebase)
{ {
setLodUniform(lod); setLodUniform(lod);
drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f); drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, kMipColors[0]); EXPECT_PIXEL_COLOR_EQ(0, 0, kMipColors[0]) << lod;
EXPECT_PIXEL_COLOR_EQ(w, 0, kMipColors[0]); EXPECT_PIXEL_COLOR_EQ(w, 0, kMipColors[0]) << lod;
EXPECT_PIXEL_COLOR_EQ(0, h, kMipColors[0]); EXPECT_PIXEL_COLOR_EQ(0, h, kMipColors[0]) << lod;
EXPECT_PIXEL_COLOR_EQ(w, h, kMipColors[0]); EXPECT_PIXEL_COLOR_EQ(w, h, kMipColors[0]) << lod;
} }
} }
......
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