Commit 65d10f3b by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Implement robust resource initialization

If a texture or renderbuffer needs to be cleared for robust access or due to having emulated channels, it is immediately cleared. The former relies on a front-end feature that optimizes robust access clears only to levels and layers that are not fully initialized through data upload. Bug: angleproject:2722 Change-Id: Icdab856eb4ffe963f78569b6d80d9ff5cb27ff9b Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1535056 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent 8413faba
......@@ -172,9 +172,6 @@ void DisplayVk::generateExtensions(egl::DisplayExtensions *outExtensions) const
outExtensions->createContextRobustness = true;
outExtensions->surfaceOrientation = true;
outExtensions->displayTextureShareGroup = true;
// TODO(geofflang): Extension is exposed but not implemented so that other aspects of the Vulkan
// backend can be tested in Chrome. http://anglebug.com/2722
outExtensions->robustResourceInitialization = true;
// The Vulkan implementation will always say that EGL_KHR_swap_buffers_with_damage is supported.
......
......@@ -182,8 +182,11 @@ angle::Result RenderTargetVk::ensureImageInitialized(ContextVk *contextVk)
{
if (mOwner)
{
// If the render target source is a texture, make sure the image is initialized and its
// staged updates flushed.
return mOwner->ensureImageInitialized(contextVk);
}
return angle::Result::Continue;
}
......
......@@ -17,14 +17,6 @@
namespace rx
{
namespace
{
constexpr VkClearDepthStencilValue kDefaultClearDepthStencilValue = {0.0f, 1};
constexpr VkClearColorValue kBlackClearColorValue = {{0}};
} // anonymous namespace
RenderbufferVk::RenderbufferVk(const gl::RenderbufferState &state)
: RenderbufferImpl(state), mOwnsImage(false), mImage(nullptr)
{}
......@@ -92,18 +84,12 @@ angle::Result RenderbufferVk::setStorage(const gl::Context *context,
ANGLE_TRY(mImage->initImageView(contextVk, gl::TextureType::_2D, aspect, gl::SwizzleState(),
&mImageView, 0, 1));
// TODO(jmadill): Fold this into the RenderPass load/store ops. http://anglebug.com/2361
vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(mImage->recordCommands(contextVk, &commandBuffer));
if (isDepthOrStencilFormat)
// Clear the renderbuffer if it has emulated channels.
if (vkFormat.hasEmulatedChannels())
{
mImage->clearDepthStencil(aspect, aspect, kDefaultClearDepthStencilValue,
commandBuffer);
}
else
{
mImage->clearColor(kBlackClearColorValue, 0, 1, commandBuffer);
mImage->stageSubresourceEmulatedClear(gl::ImageIndex::Make2D(0),
vkFormat.angleFormat());
ANGLE_TRY(mImage->flushAllStagedUpdates(vk::GetImpl(context)));
}
mRenderTarget.init(mImage, &mImageView, 0, 0, nullptr);
......@@ -172,8 +158,8 @@ angle::Result RenderbufferVk::getAttachmentRenderTarget(const gl::Context *conte
angle::Result RenderbufferVk::initializeContents(const gl::Context *context,
const gl::ImageIndex &imageIndex)
{
UNIMPLEMENTED();
return angle::Result::Continue;
mImage->stageSubresourceRobustClear(imageIndex, mImage->getFormat().angleFormat());
return mImage->flushAllStagedUpdates(vk::GetImpl(context));
}
void RenderbufferVk::releaseOwnershipOfImage(const gl::Context *context)
......
......@@ -256,7 +256,21 @@ angle::Result OffscreenSurfaceVk::getAttachmentRenderTarget(
angle::Result OffscreenSurfaceVk::initializeContents(const gl::Context *context,
const gl::ImageIndex &imageIndex)
{
UNIMPLEMENTED();
ContextVk *contextVk = vk::GetImpl(context);
if (mColorAttachment.image.valid())
{
mColorAttachment.image.stageSubresourceRobustClear(
imageIndex, mColorAttachment.image.getFormat().angleFormat());
ANGLE_TRY(mColorAttachment.image.flushAllStagedUpdates(contextVk));
}
if (mDepthStencilAttachment.image.valid())
{
mDepthStencilAttachment.image.stageSubresourceRobustClear(
imageIndex, mDepthStencilAttachment.image.getFormat().angleFormat());
ANGLE_TRY(mDepthStencilAttachment.image.flushAllStagedUpdates(contextVk));
}
return angle::Result::Continue;
}
......@@ -544,12 +558,6 @@ angle::Result WindowSurfaceVk::recreateSwapchain(DisplayVk *displayVk,
ANGLE_VK_TRY(displayVk,
vkGetSwapchainImagesKHR(device, mSwapchain, &imageCount, swapchainImages.data()));
VkClearColorValue transparentBlack = {};
transparentBlack.float32[0] = 0.0f;
transparentBlack.float32[1] = 0.0f;
transparentBlack.float32[2] = 0.0f;
transparentBlack.float32[3] = 0.0f;
mSwapchainImages.resize(imageCount);
ANGLE_TRY(resizeSwapHistory(displayVk, imageCount));
......@@ -561,13 +569,6 @@ angle::Result WindowSurfaceVk::recreateSwapchain(DisplayVk *displayVk,
ANGLE_TRY(member.image.initImageView(displayVk, gl::TextureType::_2D,
VK_IMAGE_ASPECT_COLOR_BIT, gl::SwizzleState(),
&member.imageView, 0, 1));
// Allocate a command buffer for clearing our images to black.
vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(member.image.recordCommands(displayVk, &commandBuffer));
// Set transfer dest layout, and clear the image to black.
member.image.clearColor(transparentBlack, 0, 1, commandBuffer);
}
// Initialize depth/stencil if requested.
......@@ -583,13 +584,6 @@ angle::Result WindowSurfaceVk::recreateSwapchain(DisplayVk *displayVk,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT));
const VkImageAspectFlags aspect = vk::GetDepthStencilAspectFlags(dsFormat.textureFormat());
VkClearDepthStencilValue depthStencilClearValue = {1.0f, 0};
// Clear the image.
vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(mDepthStencilImage.recordCommands(displayVk, &commandBuffer));
mDepthStencilImage.clearDepthStencil(aspect, aspect, depthStencilClearValue, commandBuffer);
ANGLE_TRY(mDepthStencilImage.initImageView(displayVk, gl::TextureType::_2D, aspect,
gl::SwizzleState(), &mDepthStencilImageView, 0,
1));
......@@ -1093,7 +1087,22 @@ angle::Result WindowSurfaceVk::generateSemaphoresForFlush(vk::Context *context,
angle::Result WindowSurfaceVk::initializeContents(const gl::Context *context,
const gl::ImageIndex &imageIndex)
{
UNIMPLEMENTED();
ContextVk *contextVk = vk::GetImpl(context);
ASSERT(mSwapchainImages.size() > 0);
ASSERT(mCurrentSwapchainImageIndex < mSwapchainImages.size());
vk::ImageHelper *image = &mSwapchainImages[mCurrentSwapchainImageIndex].image;
image->stageSubresourceRobustClear(imageIndex, image->getFormat().angleFormat());
ANGLE_TRY(image->flushAllStagedUpdates(contextVk));
if (mDepthStencilImage.valid())
{
mDepthStencilImage.stageSubresourceRobustClear(
gl::ImageIndex::Make2D(0), mDepthStencilImage.getFormat().angleFormat());
ANGLE_TRY(mDepthStencilImage.flushAllStagedUpdates(contextVk));
}
return angle::Result::Continue;
}
......
......@@ -382,17 +382,28 @@ angle::Result TextureVk::copySubImageImpl(const gl::Context *context,
return angle::Result::Continue;
}
gl::Rectangle destArea(destOffset.x, destOffset.y, clippedSourceArea.width,
clippedSourceArea.height);
ContextVk *contextVk = vk::GetImpl(context);
RendererVk *renderer = contextVk->getRenderer();
FramebufferVk *framebufferVk = vk::GetImpl(source);
const gl::ImageIndex offsetImageIndex = getNativeImageIndex(index);
const gl::Offset modifiedDestOffset(destOffset.x, destOffset.y, 0);
const vk::Format &srcFormat = framebufferVk->getColorReadRenderTarget()->getImageFormat();
// If negative offsets are given, clippedSourceArea ensures we don't read from those offsets.
// However, that changes the sourceOffset->destOffset mapping. Here, destOffset is shifted by
// the same amount as clipped to correct the error.
//
// TODO(syoussefi): a bug here is that we need to clip the extents to make sure the copy
// region does not overflow the image size. For example, if an FBO of size 16x16 is used as
// source and glCopyTexImage2D(..., -8, -8, 16, 16, ...) is used, then we will be copying to
// the region expanding from (8, 8) through (23, 23), while the image is only 16x16.
// http://anglebug.com/3355
const gl::Offset modifiedDestOffset(destOffset.x + clippedSourceArea.x - sourceArea.x,
destOffset.y + clippedSourceArea.y - sourceArea.y, 0);
RenderTargetVk *colorReadRT = framebufferVk->getColorReadRenderTarget();
ANGLE_TRY(colorReadRT->ensureImageInitialized(contextVk));
const vk::Format &srcFormat = colorReadRT->getImageFormat();
const vk::Format &destFormat = renderer->getFormat(internalFormat.sizedInternalFormat);
bool isViewportFlipY = contextVk->isViewportFlipEnabledForDrawFBO();
......@@ -400,8 +411,6 @@ angle::Result TextureVk::copySubImageImpl(const gl::Context *context,
// If it's possible to perform the copy with a transfer, that's the best option.
if (!isViewportFlipY && CanCopyWithTransfer(renderer, srcFormat, destFormat))
{
RenderTargetVk *colorReadRT = framebufferVk->getColorReadRenderTarget();
return copySubImageImplWithTransfer(contextVk, offsetImageIndex, modifiedDestOffset,
destFormat, 0, clippedSourceArea,
&colorReadRT->getImage());
......@@ -412,8 +421,6 @@ angle::Result TextureVk::copySubImageImpl(const gl::Context *context,
// If it's possible to perform the copy with a draw call, do that.
if (CanCopyWithDraw(renderer, srcFormat, destFormat) && !forceCpuPath)
{
RenderTargetVk *colorReadRT = framebufferVk->getColorReadRenderTarget();
// Layer count can only be 1 as the source is a framebuffer.
ASSERT(offsetImageIndex.getLayerCount() == 1);
......@@ -1016,7 +1023,8 @@ angle::Result TextureVk::generateMipmapsWithCPU(const gl::Context *context)
vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(mImage->recordCommands(contextVk, &commandBuffer));
return mImage->flushStagedUpdates(contextVk, getNativeImageLevel(0), getLevelCount(),
return mImage->flushStagedUpdates(contextVk, getNativeImageLevel(0), mImage->getLevelCount(),
getNativeImageLayer(0), mImage->getLayerCount(),
commandBuffer);
}
......@@ -1027,7 +1035,7 @@ angle::Result TextureVk::generateMipmap(const gl::Context *context)
// Some data is pending, or the image has not been defined at all yet
if (!mImage->valid())
{
// lets initialize the image so we can generate the next levels.
// Let's initialize the image so we can generate the next levels.
if (mImage->hasStagedUpdates())
{
ANGLE_TRY(ensureImageInitialized(contextVk));
......@@ -1142,6 +1150,7 @@ angle::Result TextureVk::ensureImageInitializedImpl(ContextVk *contextVk,
{
return angle::Result::Continue;
}
vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(mImage->recordCommands(contextVk, &commandBuffer));
......@@ -1150,7 +1159,9 @@ angle::Result TextureVk::ensureImageInitializedImpl(ContextVk *contextVk,
ANGLE_TRY(initImage(contextVk, format, baseLevelExtents, levelCount, commandBuffer));
}
return mImage->flushStagedUpdates(contextVk, getNativeImageLevel(0), levelCount, commandBuffer);
return mImage->flushStagedUpdates(contextVk, getNativeImageLevel(0), mImage->getLevelCount(),
getNativeImageLayer(0), mImage->getLayerCount(),
commandBuffer);
}
angle::Result TextureVk::initCubeMapRenderTargets(ContextVk *contextVk)
......@@ -1229,7 +1240,12 @@ angle::Result TextureVk::setStorageMultisample(const gl::Context *context,
angle::Result TextureVk::initializeContents(const gl::Context *context,
const gl::ImageIndex &imageIndex)
{
UNIMPLEMENTED();
const gl::ImageDesc &desc = mState.getImageDesc(imageIndex);
const vk::Format &format =
vk::GetImpl(context)->getRenderer()->getFormat(desc.format.info->sizedInternalFormat);
mImage->stageSubresourceRobustClear(imageIndex, format.angleFormat());
return angle::Result::Continue;
}
......@@ -1302,14 +1318,14 @@ angle::Result TextureVk::initImage(ContextVk *contextVk,
const uint32_t levelCount,
vk::CommandBuffer *commandBuffer)
{
const RendererVk *renderer = contextVk->getRenderer();
const angle::Format &angleFormat = format.textureFormat();
const RendererVk *renderer = contextVk->getRenderer();
const angle::Format &textureFormat = format.textureFormat();
VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT;
if (!angleFormat.isBlock)
if (!textureFormat.isBlock)
{
imageUsageFlags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
}
......@@ -1324,13 +1340,20 @@ angle::Result TextureVk::initImage(ContextVk *contextVk,
ANGLE_TRY(initImageViews(contextVk, format, levelCount));
if (!angleFormat.isBlock)
// If the image has an emulated channel, always clear it. These channels will be masked out in
// future writes, and shouldn't contain uninitialized values.
if (format.hasEmulatedChannels())
{
// TODO(jmadill): Fold this into the RenderPass load/store ops if possible, or defer to
// first use. This is only necessary if robustness is required. http://anglebug.com/2361
VkClearColorValue black = {{0, 0, 0, 1.0f}};
mImage->clearColor(black, 0, levelCount, commandBuffer);
uint32_t levelCount = mImage->getLevelCount();
uint32_t layerCount = mImage->getLayerCount();
for (uint32_t level = 0; level < levelCount; ++level)
{
gl::ImageIndex index = gl::ImageIndex::Make2DArrayRange(level, 0, layerCount);
mImage->stageSubresourceEmulatedClear(index, format.angleFormat());
}
}
return angle::Result::Continue;
}
......
......@@ -178,6 +178,18 @@ size_t Format::getImageCopyBufferAlignment() const
return alignment;
}
bool Format::hasEmulatedChannels() const
{
const angle::Format &angleFmt = angleFormat();
const angle::Format &textureFmt = textureFormat();
return (angleFmt.alphaBits == 0 && textureFmt.alphaBits > 0) ||
(angleFmt.blueBits == 0 && textureFmt.blueBits > 0) ||
(angleFmt.greenBits == 0 && textureFmt.greenBits > 0) ||
(angleFmt.depthBits == 0 && textureFmt.depthBits > 0) ||
(angleFmt.stencilBits == 0 && textureFmt.stencilBits > 0);
}
bool operator==(const Format &lhs, const Format &rhs)
{
return &lhs == &rhs;
......
......@@ -73,6 +73,8 @@ struct Format final : private angle::NonCopyable
}
size_t getImageCopyBufferAlignment() const;
bool hasEmulatedChannels() const;
angle::FormatID angleFormatID;
GLenum internalFormat;
angle::FormatID textureFormatID;
......
......@@ -23,6 +23,14 @@ namespace vk
{
namespace
{
// WebGL requires color textures to be initialized to transparent black.
constexpr VkClearColorValue kWebGLInitColorValue = {{0, 0, 0, 0}};
// When emulating a texture, we want the emulated channels to be 0, with alpha 1.
constexpr VkClearColorValue kEmulatedInitColorValue = {{0, 0, 0, 1.0f}};
// WebGL requires depth/stencil textures to be initialized to depth=1, stencil=0. We are fine with
// these values for emulated depth/stencil textures too.
constexpr VkClearDepthStencilValue kWebGLInitDepthStencilValue = {1.0f, 0};
constexpr VkBufferUsageFlags kLineLoopDynamicBufferUsage =
VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
......@@ -1257,6 +1265,7 @@ ImageHelper::ImageHelper(ImageHelper &&other)
mStagingBuffer(std::move(other.mStagingBuffer)),
mSubresourceUpdates(std::move(other.mSubresourceUpdates))
{
ASSERT(this != &other);
other.mCurrentLayout = ImageLayout::Undefined;
other.mLayerCount = 0;
other.mLevelCount = 0;
......@@ -1516,31 +1525,6 @@ void ImageHelper::dumpResources(Serial serial, std::vector<GarbageObject> *garba
mDeviceMemory.dumpResources(serial, garbageQueue);
}
const Image &ImageHelper::getImage() const
{
return mImage;
}
const DeviceMemory &ImageHelper::getDeviceMemory() const
{
return mDeviceMemory;
}
const gl::Extents &ImageHelper::getExtents() const
{
return mExtents;
}
const Format &ImageHelper::getFormat() const
{
return *mFormat;
}
GLint ImageHelper::getSamples() const
{
return mSamples;
}
VkImageLayout ImageHelper::getCurrentLayout() const
{
return kImageMemoryBarrierData[mCurrentLayout].layout;
......@@ -1620,18 +1604,10 @@ void ImageHelper::forceChangeLayoutAndQueue(VkImageAspectFlags aspectMask,
void ImageHelper::clearColor(const VkClearColorValue &color,
uint32_t baseMipLevel,
uint32_t levelCount,
uint32_t baseArrayLayer,
uint32_t layerCount,
vk::CommandBuffer *commandBuffer)
{
clearColorLayer(color, baseMipLevel, levelCount, 0, mLayerCount, commandBuffer);
}
void ImageHelper::clearColorLayer(const VkClearColorValue &color,
uint32_t baseMipLevel,
uint32_t levelCount,
uint32_t baseArrayLayer,
uint32_t layerCount,
vk::CommandBuffer *commandBuffer)
{
ASSERT(valid());
changeLayout(VK_IMAGE_ASPECT_COLOR_BIT, ImageLayout::TransferDst, commandBuffer);
......@@ -1666,6 +1642,27 @@ void ImageHelper::clearDepthStencil(VkImageAspectFlags imageAspectFlags,
commandBuffer->clearDepthStencilImage(mImage, getCurrentLayout(), depthStencil, 1, &clearRange);
}
void ImageHelper::clear(const VkClearValue &value,
uint32_t mipLevel,
uint32_t baseArrayLayer,
uint32_t layerCount,
vk::CommandBuffer *commandBuffer)
{
const angle::Format &angleFormat = mFormat->angleFormat();
bool isDepthStencil = angleFormat.depthBits > 0 || angleFormat.stencilBits > 0;
if (isDepthStencil)
{
ASSERT(mipLevel == 0 && baseArrayLayer == 0 && layerCount == 1);
const VkImageAspectFlags aspect = vk::GetDepthStencilAspectFlags(mFormat->textureFormat());
clearDepthStencil(aspect, aspect, value.depthStencil, commandBuffer);
}
else
{
clearColor(value.color, mipLevel, 1, baseArrayLayer, layerCount, commandBuffer);
}
}
gl::Extents ImageHelper::getSize(const gl::ImageIndex &index) const
{
ASSERT(mExtents.depth == 1);
......@@ -2052,6 +2049,40 @@ void ImageHelper::stageSubresourceUpdateFromImage(vk::ImageHelper *image,
mSubresourceUpdates.emplace_back(image, copyToImage);
}
void ImageHelper::stageSubresourceRobustClear(const gl::ImageIndex &index,
const angle::Format &format)
{
stageSubresourceClear(index, format, kWebGLInitColorValue, kWebGLInitDepthStencilValue);
}
void ImageHelper::stageSubresourceEmulatedClear(const gl::ImageIndex &index,
const angle::Format &format)
{
stageSubresourceClear(index, format, kEmulatedInitColorValue, kWebGLInitDepthStencilValue);
}
void ImageHelper::stageSubresourceClear(const gl::ImageIndex &index,
const angle::Format &format,
const VkClearColorValue &colorValue,
const VkClearDepthStencilValue &depthStencilValue)
{
VkClearValue clearValue;
bool isDepthStencil = format.depthBits > 0 || format.stencilBits > 0;
if (isDepthStencil)
{
clearValue.depthStencil = depthStencilValue;
}
else
{
clearValue.color = colorValue;
}
// Note that clears can arrive out of order from the front-end with respect to staged changes,
// but they are intended to be done first.
mSubresourceUpdates.emplace(mSubresourceUpdates.begin(), clearValue, index);
}
angle::Result ImageHelper::allocateStagingMemory(ContextVk *contextVk,
size_t sizeInBytes,
uint8_t **ptrOut,
......@@ -2064,8 +2095,10 @@ angle::Result ImageHelper::allocateStagingMemory(ContextVk *contextVk,
}
angle::Result ImageHelper::flushStagedUpdates(Context *context,
uint32_t baseLevel,
uint32_t levelCount,
uint32_t levelStart,
uint32_t levelEnd,
uint32_t layerStart,
uint32_t layerEnd,
vk::CommandBuffer *commandBuffer)
{
if (mSubresourceUpdates.empty())
......@@ -2078,40 +2111,69 @@ angle::Result ImageHelper::flushStagedUpdates(Context *context,
ANGLE_TRY(mStagingBuffer.flush(context));
std::vector<SubresourceUpdate> updatesToKeep;
const VkImageAspectFlags aspectFlags = GetFormatAspectFlags(mFormat->textureFormat());
for (SubresourceUpdate &update : mSubresourceUpdates)
{
ASSERT((update.updateSource == SubresourceUpdate::UpdateSource::Buffer &&
ASSERT(update.updateSource == SubresourceUpdate::UpdateSource::Clear ||
(update.updateSource == SubresourceUpdate::UpdateSource::Buffer &&
update.buffer.bufferHandle != VK_NULL_HANDLE) ||
(update.updateSource == SubresourceUpdate::UpdateSource::Image &&
update.image.image != nullptr && update.image.image->valid()));
const uint32_t updateMipLevel = update.dstSubresource().mipLevel;
uint32_t updateMipLevel;
uint32_t updateBaseLayer;
uint32_t updateLayerCount;
if (update.updateSource == SubresourceUpdate::UpdateSource::Clear)
{
updateMipLevel = update.clear.levelIndex;
updateBaseLayer = update.clear.layerIndex;
updateLayerCount = update.clear.layerCount;
if (updateLayerCount == static_cast<uint32_t>(gl::ImageIndex::kEntireLevel))
{
updateLayerCount = mLayerCount;
}
}
else
{
const VkImageSubresourceLayers &dstSubresource = update.dstSubresource();
updateMipLevel = dstSubresource.mipLevel;
updateBaseLayer = dstSubresource.baseArrayLayer;
updateLayerCount = dstSubresource.layerCount;
}
// It's possible we've accumulated updates that are no longer applicable if the image has
// never been flushed but the image description has changed. Check if this level exist for
// this image.
if (updateMipLevel < baseLevel || updateMipLevel >= baseLevel + levelCount)
// If the update level is not within the requested range, skip the update.
const bool isUpdateLevelOutsideRange =
updateMipLevel < levelStart || updateMipLevel >= levelEnd;
// If the update layers don't intersect the requested layers, skip the update.
const bool areUpdateLayersOutsideRange =
updateBaseLayer + updateLayerCount <= layerStart || updateBaseLayer >= layerEnd;
if (isUpdateLevelOutsideRange || areUpdateLayersOutsideRange)
{
updatesToKeep.emplace_back(update);
continue;
}
// Conservatively flush all writes to the image. We could use a more restricted barrier.
// Do not move this above the for loop, otherwise multiple updates can have race conditions
// and not be applied correctly as seen in:
// dEQP-gles2.functional_texture_specification_texsubimage2d_align_2d* tests on Windows AMD
changeLayout(VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::TransferDst, commandBuffer);
// Conservatively add a barrier between every update. This is to avoid races when updating
// the same subresource. A possible optimization could be to only issue this barrier when
// an overlap in updates is observed.
changeLayout(aspectFlags, vk::ImageLayout::TransferDst, commandBuffer);
if (update.updateSource == SubresourceUpdate::UpdateSource::Buffer)
if (update.updateSource == SubresourceUpdate::UpdateSource::Clear)
{
clear(update.clear.value, updateMipLevel, updateBaseLayer, updateLayerCount,
commandBuffer);
}
else if (update.updateSource == SubresourceUpdate::UpdateSource::Buffer)
{
commandBuffer->copyBufferToImage(update.buffer.bufferHandle, mImage, getCurrentLayout(),
1, &update.buffer.copyRegion);
}
else
{
update.image.image->changeLayout(VK_IMAGE_ASPECT_COLOR_BIT,
vk::ImageLayout::TransferSrc, commandBuffer);
update.image.image->changeLayout(aspectFlags, vk::ImageLayout::TransferSrc,
commandBuffer);
update.image.image->addReadDependency(this);
......@@ -2130,18 +2192,16 @@ angle::Result ImageHelper::flushStagedUpdates(Context *context,
{
mStagingBuffer.releaseRetainedBuffers(context->getRenderer());
}
else
{
WARN() << "Internal Vulkan buffer could not be released. This is likely due to having "
"extra images defined in the Texture.";
}
return angle::Result::Continue;
}
bool ImageHelper::hasStagedUpdates() const
angle::Result ImageHelper::flushAllStagedUpdates(Context *context)
{
return !mSubresourceUpdates.empty();
// Clear the image.
vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(recordCommands(context, &commandBuffer));
return flushStagedUpdates(context, 0, mLevelCount, 0, mLayerCount, commandBuffer);
}
// ImageHelper::SubresourceUpdate implementation
......@@ -2159,10 +2219,24 @@ ImageHelper::SubresourceUpdate::SubresourceUpdate(vk::ImageHelper *imageIn,
: updateSource(UpdateSource::Image), image{imageIn, copyRegionIn}
{}
ImageHelper::SubresourceUpdate::SubresourceUpdate(const VkClearValue &clearValue,
const gl::ImageIndex &imageIndex)
: updateSource(UpdateSource::Clear)
{
clear.value = clearValue;
clear.levelIndex = imageIndex.getLevelIndex();
clear.layerIndex = imageIndex.hasLayer() ? imageIndex.getLayerIndex() : 0;
clear.layerCount = imageIndex.getLayerCount();
}
ImageHelper::SubresourceUpdate::SubresourceUpdate(const SubresourceUpdate &other)
: updateSource(other.updateSource)
{
if (updateSource == UpdateSource::Buffer)
if (updateSource == UpdateSource::Clear)
{
clear = other.clear;
}
else if (updateSource == UpdateSource::Buffer)
{
buffer = other.buffer;
}
......@@ -2185,6 +2259,11 @@ void ImageHelper::SubresourceUpdate::release(RendererVk *renderer)
bool ImageHelper::SubresourceUpdate::isUpdateToLayerLevel(uint32_t layerIndex,
uint32_t levelIndex) const
{
if (updateSource == UpdateSource::Clear)
{
return clear.levelIndex == levelIndex && clear.layerIndex == layerIndex;
}
const VkImageSubresourceLayers &dst = dstSubresource();
return dst.baseArrayLayer == layerIndex && dst.mipLevel == levelIndex;
}
......
......@@ -599,14 +599,14 @@ class ImageHelper final : public CommandGraphResource
GLint samples);
void resetImageWeakReference();
const Image &getImage() const;
const DeviceMemory &getDeviceMemory() const;
const Image &getImage() const { return mImage; }
const DeviceMemory &getDeviceMemory() const { return mDeviceMemory; }
const gl::Extents &getExtents() const;
const gl::Extents &getExtents() const { return mExtents; }
uint32_t getLayerCount() const { return mLayerCount; }
uint32_t getLevelCount() const { return mLevelCount; }
const Format &getFormat() const;
GLint getSamples() const;
const Format &getFormat() const { return *mFormat; }
GLint getSamples() const { return mSamples; }
VkImageLayout getCurrentLayout() const;
......@@ -614,22 +614,13 @@ class ImageHelper final : public CommandGraphResource
// image.
gl::Extents getLevelExtents2D(uint32_t level) const;
void clearColor(const VkClearColorValue &color,
uint32_t baseMipLevel,
uint32_t levelCount,
vk::CommandBuffer *commandBuffer);
void clearColorLayer(const VkClearColorValue &color,
uint32_t baseMipLevel,
uint32_t levelCount,
uint32_t baseArrayLayer,
uint32_t layerCount,
vk::CommandBuffer *commandBuffer);
// Clear either color or depth/stencil based on image format.
void clear(const VkClearValue &value,
uint32_t mipLevel,
uint32_t baseArrayLayer,
uint32_t layerCount,
vk::CommandBuffer *commandBuffer);
void clearDepthStencil(VkImageAspectFlags imageAspectFlags,
VkImageAspectFlags clearAspectFlags,
const VkClearDepthStencilValue &depthStencil,
vk::CommandBuffer *commandBuffer);
gl::Extents getSize(const gl::ImageIndex &index) const;
static void Copy(ImageHelper *srcImage,
......@@ -676,6 +667,13 @@ class ImageHelper final : public CommandGraphResource
const gl::Offset &destOffset,
const gl::Extents &extents);
// Stage a clear operation to a clear value based on WebGL requirements.
void stageSubresourceRobustClear(const gl::ImageIndex &index, const angle::Format &format);
// Stage a clear operation to a clear value that initializes emulated channels to the desired
// values.
void stageSubresourceEmulatedClear(const gl::ImageIndex &index, const angle::Format &format);
// This will use the underlying dynamic buffer to allocate some memory to be used as a src or
// dst.
angle::Result allocateStagingMemory(ContextVk *contextVk,
......@@ -685,12 +683,21 @@ class ImageHelper final : public CommandGraphResource
VkDeviceSize *offsetOut,
bool *newBufferAllocatedOut);
// Flushes staged updates to a range of levels and layers from start to (but not including) end.
// Due to the nature of updates (done wholly to a VkImageSubresourceLayers), some unsolicited
// layers may also be updated.
angle::Result flushStagedUpdates(Context *context,
uint32_t baseLevel,
uint32_t levelCount,
uint32_t levelStart,
uint32_t levelEnd,
uint32_t layerStart,
uint32_t layerEnd,
vk::CommandBuffer *commandBuffer);
// Creates a command buffer and flushes all staged updates. This is used for one-time
// initialization of resources that we don't expect to accumulate further staged updates, such
// as with renderbuffers or surface images.
angle::Result flushAllStagedUpdates(Context *context);
bool hasStagedUpdates() const;
bool hasStagedUpdates() const { return !mSubresourceUpdates.empty(); }
// changeLayout automatically skips the layout change if it's unnecessary. This function can be
// used to prevent creating a command graph node and subsequently a command buffer for the sole
......@@ -717,17 +724,36 @@ class ImageHelper final : public CommandGraphResource
uint32_t newQueueFamilyIndex,
vk::CommandBuffer *commandBuffer);
void stageSubresourceClear(const gl::ImageIndex &index,
const angle::Format &format,
const VkClearColorValue &colorValue,
const VkClearDepthStencilValue &depthStencilValue);
void clearColor(const VkClearColorValue &color,
uint32_t baseMipLevel,
uint32_t levelCount,
uint32_t baseArrayLayer,
uint32_t layerCount,
vk::CommandBuffer *commandBuffer);
void clearDepthStencil(VkImageAspectFlags imageAspectFlags,
VkImageAspectFlags clearAspectFlags,
const VkClearDepthStencilValue &depthStencil,
vk::CommandBuffer *commandBuffer);
struct SubresourceUpdate
{
SubresourceUpdate();
SubresourceUpdate(VkBuffer bufferHandle, const VkBufferImageCopy &copyRegion);
SubresourceUpdate(vk::ImageHelper *image, const VkImageCopy &copyRegion);
SubresourceUpdate(const VkClearValue &clearValue, const gl::ImageIndex &imageIndex);
SubresourceUpdate(const SubresourceUpdate &other);
void release(RendererVk *renderer);
const VkImageSubresourceLayers &dstSubresource() const
{
ASSERT(updateSource == UpdateSource::Buffer || updateSource == UpdateSource::Image);
return updateSource == UpdateSource::Buffer ? buffer.copyRegion.imageSubresource
: image.copyRegion.dstSubresource;
}
......@@ -735,9 +761,17 @@ class ImageHelper final : public CommandGraphResource
enum class UpdateSource
{
Clear,
Buffer,
Image,
};
struct ClearUpdate
{
VkClearValue value;
uint32_t levelIndex;
uint32_t layerIndex;
uint32_t layerCount;
};
struct BufferUpdate
{
VkBuffer bufferHandle;
......@@ -752,6 +786,7 @@ class ImageHelper final : public CommandGraphResource
UpdateSource updateSource;
union
{
ClearUpdate clear;
BufferUpdate buffer;
ImageUpdate image;
};
......
......@@ -262,8 +262,11 @@ VkImageAspectFlags GetDepthStencilAspectFlags(const angle::Format &format)
VkImageAspectFlags GetFormatAspectFlags(const angle::Format &format)
{
return (format.redBits > 0 ? VK_IMAGE_ASPECT_COLOR_BIT : 0) |
GetDepthStencilAspectFlags(format);
VkImageAspectFlags dsAspect = GetDepthStencilAspectFlags(format);
// If the image is not depth stencil, assume color aspect. Note that detecting color formats
// is less trivial than depth/stencil, e.g. as block formats don't indicate any bits for RGBA
// channels.
return dsAspect != 0 ? dsAspect : VK_IMAGE_ASPECT_COLOR_BIT;
}
VkImageAspectFlags GetDepthStencilAspectFlagsForCopy(bool copyDepth, bool copyStencil)
......
......@@ -307,7 +307,8 @@ class RobustResourceInitTestES31 : public RobustResourceInitTest
// it only works on the implemented renderers
TEST_P(RobustResourceInitTest, ExpectedRendererSupport)
{
bool shouldHaveSupport = IsD3D11() || IsD3D11_FL93() || IsD3D9() || IsOpenGL() || IsOpenGLES();
bool shouldHaveSupport =
IsD3D11() || IsD3D11_FL93() || IsD3D9() || IsOpenGL() || IsOpenGLES() || IsVulkan();
EXPECT_EQ(shouldHaveSupport, hasGLExtension());
EXPECT_EQ(shouldHaveSupport, hasEGLExtension());
EXPECT_EQ(shouldHaveSupport, hasRobustSurfaceInit());
......@@ -775,7 +776,7 @@ TEST_P(RobustResourceInitTest, UninitializedPartsOfCopied2DTexturesAreBlack)
// succeed with all bytes set to 0. Regression test for a bug where the zeroing out of the
// texture was done via the same code path as glTexImage2D, causing the PIXEL_UNPACK_BUFFER
// to be used.
TEST_P(RobustResourceInitTestES3, ReadingOutOfboundsCopiedTextureWithUnpackBuffer)
TEST_P(RobustResourceInitTestES3, ReadingOutOfBoundsCopiedTextureWithUnpackBuffer)
{
ANGLE_SKIP_TEST_IF(!hasGLExtension());
// TODO(geofflang@chromium.org): CopyTexImage from GL_RGBA4444 to GL_ALPHA fails when looking
......@@ -834,7 +835,7 @@ TEST_P(RobustResourceInitTestES3, ReadingOutOfboundsCopiedTextureWithUnpackBuffe
// Reading an uninitialized portion of a texture (copyTexImage2D with negative x and y) should
// succeed with all bytes set to 0.
TEST_P(RobustResourceInitTest, ReadingOutOfboundsCopiedTexture)
TEST_P(RobustResourceInitTest, ReadingOutOfBoundsCopiedTexture)
{
ANGLE_SKIP_TEST_IF(!hasGLExtension());
......@@ -1827,6 +1828,9 @@ TEST_P(RobustResourceInitTestES31, Multisample2DTextureArray)
// Tests that using an out of bounds draw offset with a dynamic array succeeds.
TEST_P(RobustResourceInitTest, DynamicVertexArrayOffsetOutOfBounds)
{
// Not implemented on Vulkan. http://anglebug.com/3350
ANGLE_SKIP_TEST_IF(IsVulkan());
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
glUseProgram(program);
......@@ -1851,7 +1855,8 @@ ANGLE_INSTANTIATE_TEST(RobustResourceInitTest,
ES2_OPENGL(),
ES3_OPENGL(),
ES2_OPENGLES(),
ES3_OPENGLES());
ES3_OPENGLES(),
ES2_VULKAN());
ANGLE_INSTANTIATE_TEST(RobustResourceInitTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
......
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