Commit 91a03bd4 by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Fix render-to-texture simultaneously bound to two FBOs

If a texture is simultaneously attached to two FBOs, one where it's a normal texture and another where it's multisampled-render-to-texture, different render targets must be created for it. If a texture is simultaneously attached to two FBOs, both as multisampled-render-to-texture but with different sample counts, two implicit multisampled images need to be created as well as different render targets. Bug: angleproject:4913 Change-Id: I584ba327e4cb2099ef62f86f5d88719dc156ce13 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2335810 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarTim Van Patten <timvp@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 3a3d419d
...@@ -710,6 +710,26 @@ bool ValidateComponentTypeMasks(unsigned long outputTypes, ...@@ -710,6 +710,26 @@ bool ValidateComponentTypeMasks(unsigned long outputTypes,
unsigned long outputMask, unsigned long outputMask,
unsigned long inputMask); unsigned long inputMask);
enum class RenderToTextureImageIndex
{
// The default image of the texture, where data is expected to be.
Default = 0,
// Intermediate multisampled images for EXT_multisampled_render_to_texture.
// These values must match log2(SampleCount).
IntermediateImage2xMultisampled = 1,
IntermediateImage4xMultisampled = 2,
IntermediateImage8xMultisampled = 3,
IntermediateImage16xMultisampled = 4,
// We currently only support up to 16xMSAA in backends that use this enum.
InvalidEnum = 5,
EnumCount = 5,
};
template <typename T>
using RenderToTextureImageMap = angle::PackedEnumMap<RenderToTextureImageIndex, T>;
using ContextID = uintptr_t; using ContextID = uintptr_t;
constexpr size_t kCubeFaceCount = 6; constexpr size_t kCubeFaceCount = 6;
......
...@@ -1243,15 +1243,18 @@ void TextureVk::setImageHelper(ContextVk *contextVk, ...@@ -1243,15 +1243,18 @@ void TextureVk::setImageHelper(ContextVk *contextVk,
updateImageHelper(contextVk, format.getImageCopyBufferAlignment()); updateImageHelper(contextVk, format.getImageCopyBufferAlignment());
// Force re-creation of render targets next time they are needed // Force re-creation of render targets next time they are needed
for (RenderTargetVector &renderTargetLevels : mRenderTargets) for (auto &renderTargets : mRenderTargets)
{ {
renderTargetLevels.clear(); for (RenderTargetVector &renderTargetLevels : renderTargets)
{
renderTargetLevels.clear();
}
renderTargets.clear();
} }
mRenderTargets.clear();
RendererVk *renderer = contextVk->getRenderer(); RendererVk *renderer = contextVk->getRenderer();
mImageViews.init(renderer); getImageViews().init(renderer);
} }
void TextureVk::updateImageHelper(ContextVk *contextVk, size_t imageCopyBufferAlignment) void TextureVk::updateImageHelper(ContextVk *contextVk, size_t imageCopyBufferAlignment)
...@@ -1485,8 +1488,8 @@ angle::Result TextureVk::generateMipmapsWithCompute(ContextVk *contextVk) ...@@ -1485,8 +1488,8 @@ angle::Result TextureVk::generateMipmapsWithCompute(ContextVk *contextVk)
UtilsVk::GenerateMipmapDestLevelViews destLevelViews = {}; UtilsVk::GenerateMipmapDestLevelViews destLevelViews = {};
const uint32_t srcLevel = destBaseLevel - 1; const uint32_t srcLevel = destBaseLevel - 1;
ANGLE_TRY(mImageViews.getLevelLayerDrawImageView(contextVk, *mImage, srcLevel, layer, ANGLE_TRY(getImageViews().getLevelLayerDrawImageView(contextVk, *mImage, srcLevel,
&srcView)); layer, &srcView));
uint32_t destLevelCount = maxGenerateLevels; uint32_t destLevelCount = maxGenerateLevels;
for (uint32_t level = 0; level < maxGenerateLevels; ++level) for (uint32_t level = 0; level < maxGenerateLevels; ++level)
...@@ -1500,8 +1503,8 @@ angle::Result TextureVk::generateMipmapsWithCompute(ContextVk *contextVk) ...@@ -1500,8 +1503,8 @@ angle::Result TextureVk::generateMipmapsWithCompute(ContextVk *contextVk)
break; break;
} }
ANGLE_TRY(mImageViews.getLevelLayerDrawImageView(contextVk, *mImage, destLevel, ANGLE_TRY(getImageViews().getLevelLayerDrawImageView(
layer, &destLevelViews[level])); contextVk, *mImage, destLevel, layer, &destLevelViews[level]));
} }
// If the image has fewer than maximum levels, fill the last views with a dummy view. // If the image has fewer than maximum levels, fill the last views with a dummy view.
...@@ -1586,7 +1589,7 @@ angle::Result TextureVk::generateMipmap(const gl::Context *context) ...@@ -1586,7 +1589,7 @@ angle::Result TextureVk::generateMipmap(const gl::Context *context)
ASSERT((mImageUsageFlags & VK_IMAGE_USAGE_STORAGE_BIT) != 0); ASSERT((mImageUsageFlags & VK_IMAGE_USAGE_STORAGE_BIT) != 0);
mImage->retain(&contextVk->getResourceUseList()); mImage->retain(&contextVk->getResourceUseList());
mImageViews.retain(&contextVk->getResourceUseList()); getImageViews().retain(&contextVk->getResourceUseList());
return generateMipmapsWithCompute(contextVk); return generateMipmapsWithCompute(contextVk);
} }
...@@ -1834,13 +1837,16 @@ angle::Result TextureVk::getAttachmentRenderTarget(const gl::Context *context, ...@@ -1834,13 +1837,16 @@ angle::Result TextureVk::getAttachmentRenderTarget(const gl::Context *context,
// In this case, create a multisampled image that is otherwise identical to the single sampled // In this case, create a multisampled image that is otherwise identical to the single sampled
// image. That multisampled image is used as color or depth/stencil attachment, while the // image. That multisampled image is used as color or depth/stencil attachment, while the
// original image is used as the resolve attachment. // original image is used as the resolve attachment.
if (samples > 1 && !mMultisampledImage.valid()) const gl::RenderToTextureImageIndex renderToTextureIndex =
static_cast<gl::RenderToTextureImageIndex>(PackSampleCount(samples));
if (samples > 1 && !mMultisampledImages[renderToTextureIndex].valid())
{ {
ASSERT(mState.getBaseLevelDesc().samples <= 1); ASSERT(mState.getBaseLevelDesc().samples <= 1);
vk::ImageHelper *multisampledImage = &mMultisampledImages[renderToTextureIndex];
// Ensure the view serial is valid. // Ensure the view serial is valid.
RendererVk *renderer = contextVk->getRenderer(); RendererVk *renderer = contextVk->getRenderer();
mMultisampledImageViews.init(renderer); mMultisampledImageViews[renderToTextureIndex].init(renderer);
// The image is used as either color or depth/stencil attachment. Additionally, its memory // The image is used as either color or depth/stencil attachment. Additionally, its memory
// is lazily allocated as the contents are discarded at the end of the renderpass and with // is lazily allocated as the contents are discarded at the end of the renderpass and with
...@@ -1863,7 +1869,7 @@ angle::Result TextureVk::getAttachmentRenderTarget(const gl::Context *context, ...@@ -1863,7 +1869,7 @@ angle::Result TextureVk::getAttachmentRenderTarget(const gl::Context *context,
: VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT); : VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
constexpr VkImageCreateFlags kMultisampledCreateFlags = 0; constexpr VkImageCreateFlags kMultisampledCreateFlags = 0;
ANGLE_TRY(mMultisampledImage.initExternal( ANGLE_TRY(multisampledImage->initExternal(
contextVk, mState.getType(), mImage->getExtents(), mImage->getFormat(), samples, contextVk, mState.getType(), mImage->getExtents(), mImage->getFormat(), samples,
kMultisampledUsageFlags, kMultisampledCreateFlags, rx::vk::ImageLayout::Undefined, kMultisampledUsageFlags, kMultisampledCreateFlags, rx::vk::ImageLayout::Undefined,
nullptr, mImage->getBaseLevel(), mImage->getMaxLevel(), mImage->getLevelCount(), nullptr, mImage->getBaseLevel(), mImage->getMaxLevel(), mImage->getLevelCount(),
...@@ -1873,13 +1879,13 @@ angle::Result TextureVk::getAttachmentRenderTarget(const gl::Context *context, ...@@ -1873,13 +1879,13 @@ angle::Result TextureVk::getAttachmentRenderTarget(const gl::Context *context,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
(hasLazilyAllocatedMemory ? VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT : 0); (hasLazilyAllocatedMemory ? VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT : 0);
ANGLE_TRY(mMultisampledImage.initMemory(contextVk, renderer->getMemoryProperties(), ANGLE_TRY(multisampledImage->initMemory(contextVk, renderer->getMemoryProperties(),
kMultisampledMemoryFlags)); kMultisampledMemoryFlags));
// Remove the emulated format clear from the multisampled image if any. There is one // Remove the emulated format clear from the multisampled image if any. There is one
// already staged on the resolve image if needed. // already staged on the resolve image if needed.
mMultisampledImage.removeStagedUpdates(contextVk, mMultisampledImage.getBaseLevel(), multisampledImage->removeStagedUpdates(contextVk, multisampledImage->getBaseLevel(),
mMultisampledImage.getMaxLevel()); multisampledImage->getMaxLevel());
} }
// Don't flush staged updates here. We'll handle that in FramebufferVk so it can defer clears. // Don't flush staged updates here. We'll handle that in FramebufferVk so it can defer clears.
...@@ -1887,10 +1893,12 @@ angle::Result TextureVk::getAttachmentRenderTarget(const gl::Context *context, ...@@ -1887,10 +1893,12 @@ angle::Result TextureVk::getAttachmentRenderTarget(const gl::Context *context,
GLuint layerIndex = 0, layerCount = 0; GLuint layerIndex = 0, layerCount = 0;
GetRenderTargetLayerCountAndIndex(mImage, imageIndex, &layerCount, &layerIndex); GetRenderTargetLayerCountAndIndex(mImage, imageIndex, &layerCount, &layerIndex);
ANGLE_TRY(initRenderTargets(contextVk, layerCount, imageIndex.getLevelIndex())); ANGLE_TRY(
initRenderTargets(contextVk, layerCount, imageIndex.getLevelIndex(), renderToTextureIndex));
ASSERT(imageIndex.getLevelIndex() < static_cast<int32_t>(mRenderTargets.size())); ASSERT(imageIndex.getLevelIndex() <
*rtOut = &mRenderTargets[imageIndex.getLevelIndex()][layerIndex]; static_cast<int32_t>(mRenderTargets[renderToTextureIndex].size()));
*rtOut = &mRenderTargets[renderToTextureIndex][imageIndex.getLevelIndex()][layerIndex];
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -1939,36 +1947,44 @@ angle::Result TextureVk::flushImageStagedUpdates(ContextVk *contextVk) ...@@ -1939,36 +1947,44 @@ angle::Result TextureVk::flushImageStagedUpdates(ContextVk *contextVk)
angle::Result TextureVk::initRenderTargets(ContextVk *contextVk, angle::Result TextureVk::initRenderTargets(ContextVk *contextVk,
GLuint layerCount, GLuint layerCount,
GLuint levelIndex) GLuint levelIndex,
gl::RenderToTextureImageIndex renderToTextureIndex)
{ {
if (mRenderTargets.size() <= levelIndex) std::vector<RenderTargetVector> &renderTargets = mRenderTargets[renderToTextureIndex];
if (renderTargets.size() <= levelIndex)
{ {
mRenderTargets.resize(levelIndex + 1); renderTargets.resize(levelIndex + 1);
} }
// Lazy init. Check if already initialized. // Lazy init. Check if already initialized.
if (!mRenderTargets[levelIndex].empty()) if (!renderTargets[levelIndex].empty())
{ {
return angle::Result::Continue; return angle::Result::Continue;
} }
mRenderTargets[levelIndex].resize(layerCount); renderTargets[levelIndex].resize(layerCount);
for (uint32_t layerIndex = 0; layerIndex < layerCount; ++layerIndex) for (uint32_t layerIndex = 0; layerIndex < layerCount; ++layerIndex)
{ {
vk::ImageHelper *drawImage = mImage; vk::ImageHelper *drawImage = mImage;
vk::ImageViewHelper *drawImageViews = &mImageViews; vk::ImageViewHelper *drawImageViews = &getImageViews();
vk::ImageHelper *resolveImage = nullptr; vk::ImageHelper *resolveImage = nullptr;
vk::ImageViewHelper *resolveImageViews = nullptr; vk::ImageViewHelper *resolveImageViews = nullptr;
const bool isMultisampledRenderToTexture =
renderToTextureIndex != gl::RenderToTextureImageIndex::Default;
// If multisampled render to texture, use the multisampled image as draw image instead, and // If multisampled render to texture, use the multisampled image as draw image instead, and
// resolve into the texture's image automatically. // resolve into the texture's image automatically.
if (mMultisampledImage.valid()) if (isMultisampledRenderToTexture)
{ {
drawImage = &mMultisampledImage; ASSERT(mMultisampledImages[renderToTextureIndex].valid());
drawImageViews = &mMultisampledImageViews;
resolveImage = mImage; resolveImage = drawImage;
resolveImageViews = &mImageViews; resolveImageViews = drawImageViews;
drawImage = &mMultisampledImages[renderToTextureIndex];
drawImageViews = &mMultisampledImageViews[renderToTextureIndex];
// If the texture is depth/stencil, GL_EXT_multisampled_render_to_texture2 explicitly // If the texture is depth/stencil, GL_EXT_multisampled_render_to_texture2 explicitly
// indicates that there is no need for the image to be resolved. In that case, don't // indicates that there is no need for the image to be resolved. In that case, don't
...@@ -1981,10 +1997,10 @@ angle::Result TextureVk::initRenderTargets(ContextVk *contextVk, ...@@ -1981,10 +1997,10 @@ angle::Result TextureVk::initRenderTargets(ContextVk *contextVk,
} }
} }
mRenderTargets[levelIndex][layerIndex].init( renderTargets[levelIndex][layerIndex].init(
drawImage, drawImageViews, resolveImage, resolveImageViews, drawImage, drawImageViews, resolveImage, resolveImageViews,
getNativeImageLevel(levelIndex), getNativeImageLayer(layerIndex), getNativeImageLevel(levelIndex), getNativeImageLayer(layerIndex),
mMultisampledImage.valid()); isMultisampledRenderToTexture);
} }
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -2150,7 +2166,7 @@ angle::Result TextureVk::syncState(const gl::Context *context, ...@@ -2150,7 +2166,7 @@ angle::Result TextureVk::syncState(const gl::Context *context,
uint32_t layerCount = uint32_t layerCount =
mState.getType() == gl::TextureType::_2D ? 1 : mImage->getLayerCount(); mState.getType() == gl::TextureType::_2D ? 1 : mImage->getLayerCount();
mImageViews.release(renderer); getImageViews().release(renderer);
const gl::ImageDesc &baseLevelDesc = mState.getBaseLevelDesc(); const gl::ImageDesc &baseLevelDesc = mState.getBaseLevelDesc();
ANGLE_TRY(initImageViews(contextVk, mImage->getFormat(), baseLevelDesc.format.info->sized, ANGLE_TRY(initImageViews(contextVk, mImage->getFormat(), baseLevelDesc.format.info->sized,
...@@ -2190,53 +2206,56 @@ const vk::ImageView &TextureVk::getReadImageViewAndRecordUse(ContextVk *contextV ...@@ -2190,53 +2206,56 @@ const vk::ImageView &TextureVk::getReadImageViewAndRecordUse(ContextVk *contextV
{ {
ASSERT(mImage->valid()); ASSERT(mImage->valid());
mImageViews.retain(&contextVk->getResourceUseList()); const vk::ImageViewHelper &imageViews = getImageViews();
imageViews.retain(&contextVk->getResourceUseList());
if (mState.isStencilMode() && mImageViews.hasStencilReadImageView()) if (mState.isStencilMode() && imageViews.hasStencilReadImageView())
{ {
return mImageViews.getStencilReadImageView(); return imageViews.getStencilReadImageView();
} }
if (mState.getSRGBOverride() == gl::SrgbOverride::Enabled) if (mState.getSRGBOverride() == gl::SrgbOverride::Enabled)
{ {
ASSERT(mImageViews.getNonLinearReadImageView().valid()); ASSERT(imageViews.getNonLinearReadImageView().valid());
return mImageViews.getNonLinearReadImageView(); return imageViews.getNonLinearReadImageView();
} }
return mImageViews.getReadImageView(); return imageViews.getReadImageView();
} }
const vk::ImageView &TextureVk::getFetchImageViewAndRecordUse(ContextVk *contextVk) const const vk::ImageView &TextureVk::getFetchImageViewAndRecordUse(ContextVk *contextVk) const
{ {
ASSERT(mImage->valid()); ASSERT(mImage->valid());
mImageViews.retain(&contextVk->getResourceUseList()); const vk::ImageViewHelper &imageViews = getImageViews();
imageViews.retain(&contextVk->getResourceUseList());
// We don't currently support fetch for depth/stencil cube map textures. // We don't currently support fetch for depth/stencil cube map textures.
ASSERT(!mImageViews.hasStencilReadImageView() || !mImageViews.hasFetchImageView()); ASSERT(!imageViews.hasStencilReadImageView() || !imageViews.hasFetchImageView());
if (mState.getSRGBOverride() == gl::SrgbOverride::Enabled) if (mState.getSRGBOverride() == gl::SrgbOverride::Enabled)
{ {
return (mImageViews.hasFetchImageView() ? mImageViews.getNonLinearFetchImageView() return (imageViews.hasFetchImageView() ? imageViews.getNonLinearFetchImageView()
: mImageViews.getNonLinearReadImageView()); : imageViews.getNonLinearReadImageView());
} }
return (mImageViews.hasFetchImageView() ? mImageViews.getFetchImageView() return (imageViews.hasFetchImageView() ? imageViews.getFetchImageView()
: mImageViews.getReadImageView()); : imageViews.getReadImageView());
} }
const vk::ImageView &TextureVk::getCopyImageViewAndRecordUse(ContextVk *contextVk) const const vk::ImageView &TextureVk::getCopyImageViewAndRecordUse(ContextVk *contextVk) const
{ {
ASSERT(mImage->valid()); ASSERT(mImage->valid());
mImageViews.retain(&contextVk->getResourceUseList()); const vk::ImageViewHelper &imageViews = getImageViews();
imageViews.retain(&contextVk->getResourceUseList());
if (mState.getSRGBOverride() == gl::SrgbOverride::Enabled) if (mState.getSRGBOverride() == gl::SrgbOverride::Enabled)
{ {
return mImageViews.getNonLinearCopyImageView(); return imageViews.getNonLinearCopyImageView();
} }
return mImageViews.getCopyImageView(); return imageViews.getCopyImageView();
} }
angle::Result TextureVk::getLevelLayerImageView(ContextVk *contextVk, angle::Result TextureVk::getLevelLayerImageView(ContextVk *contextVk,
...@@ -2250,8 +2269,8 @@ angle::Result TextureVk::getLevelLayerImageView(ContextVk *contextVk, ...@@ -2250,8 +2269,8 @@ angle::Result TextureVk::getLevelLayerImageView(ContextVk *contextVk,
uint32_t nativeLayer = getNativeImageLayer(static_cast<uint32_t>(layer)); uint32_t nativeLayer = getNativeImageLayer(static_cast<uint32_t>(layer));
uint32_t levelVK = levelGL - mImage->getBaseLevel(); uint32_t levelVK = levelGL - mImage->getBaseLevel();
return mImageViews.getLevelLayerDrawImageView(contextVk, *mImage, levelVK, nativeLayer, return getImageViews().getLevelLayerDrawImageView(contextVk, *mImage, levelVK, nativeLayer,
imageViewOut); imageViewOut);
} }
angle::Result TextureVk::getStorageImageView(ContextVk *contextVk, angle::Result TextureVk::getStorageImageView(ContextVk *contextVk,
...@@ -2268,7 +2287,7 @@ angle::Result TextureVk::getStorageImageView(ContextVk *contextVk, ...@@ -2268,7 +2287,7 @@ angle::Result TextureVk::getStorageImageView(ContextVk *contextVk,
uint32_t nativeLevel = getNativeImageLevel(static_cast<uint32_t>(binding.level)); uint32_t nativeLevel = getNativeImageLevel(static_cast<uint32_t>(binding.level));
uint32_t nativeLayer = getNativeImageLayer(0); uint32_t nativeLayer = getNativeImageLayer(0);
return mImageViews.getLevelDrawImageView( return getImageViews().getLevelDrawImageView(
contextVk, mState.getType(), *mImage, nativeLevel, nativeLayer, contextVk, mState.getType(), *mImage, nativeLevel, nativeLayer,
VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT, format.vkImageFormat, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT, format.vkImageFormat,
imageViewOut); imageViewOut);
...@@ -2315,10 +2334,10 @@ angle::Result TextureVk::initImageViews(ContextVk *contextVk, ...@@ -2315,10 +2334,10 @@ angle::Result TextureVk::initImageViews(ContextVk *contextVk,
gl::SwizzleState formatSwizzle = GetFormatSwizzle(contextVk, format, sized); gl::SwizzleState formatSwizzle = GetFormatSwizzle(contextVk, format, sized);
gl::SwizzleState readSwizzle = ApplySwizzle(formatSwizzle, mState.getSwizzleState()); gl::SwizzleState readSwizzle = ApplySwizzle(formatSwizzle, mState.getSwizzleState());
ANGLE_TRY(mImageViews.initReadViews(contextVk, mState.getType(), *mImage, format, formatSwizzle, ANGLE_TRY(getImageViews().initReadViews(contextVk, mState.getType(), *mImage, format,
readSwizzle, baseLevel, levelCount, baseLayer, layerCount, formatSwizzle, readSwizzle, baseLevel, levelCount,
mRequiresSRGBViews, baseLayer, layerCount, mRequiresSRGBViews,
mImageUsageFlags & ~VK_IMAGE_USAGE_STORAGE_BIT)); mImageUsageFlags & ~VK_IMAGE_USAGE_STORAGE_BIT));
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -2339,21 +2358,30 @@ void TextureVk::releaseImage(ContextVk *contextVk) ...@@ -2339,21 +2358,30 @@ void TextureVk::releaseImage(ContextVk *contextVk)
mImage = nullptr; mImage = nullptr;
} }
} }
if (mMultisampledImage.valid())
for (vk::ImageHelper &image : mMultisampledImages)
{ {
mMultisampledImage.releaseImage(renderer); if (image.valid())
{
image.releaseImage(renderer);
}
} }
mImageViews.release(renderer); for (vk::ImageViewHelper &imageViews : mMultisampledImageViews)
mMultisampledImageViews.release(renderer); {
imageViews.release(renderer);
}
for (RenderTargetVector &renderTargetLevels : mRenderTargets) for (auto &renderTargets : mRenderTargets)
{ {
// Clear the layers tracked for each level for (RenderTargetVector &renderTargetLevels : renderTargets)
renderTargetLevels.clear(); {
// Clear the layers tracked for each level
renderTargetLevels.clear();
}
// Then clear the levels
renderTargets.clear();
} }
// Then clear the levels
mRenderTargets.clear();
onStateChange(angle::SubjectMessage::SubjectChanged); onStateChange(angle::SubjectMessage::SubjectChanged);
mRedefinedLevels.reset(); mRedefinedLevels.reset();
...@@ -2522,6 +2550,6 @@ vk::ImageViewSubresourceSerial TextureVk::getImageViewSubresourceSerial() const ...@@ -2522,6 +2550,6 @@ vk::ImageViewSubresourceSerial TextureVk::getImageViewSubresourceSerial() const
uint32_t baseLevel = mState.getEffectiveBaseLevel(); uint32_t baseLevel = mState.getEffectiveBaseLevel();
// getMipmapMaxLevel will clamp to the max level if it is smaller than the number of mips. // getMipmapMaxLevel will clamp to the max level if it is smaller than the number of mips.
uint32_t levelCount = mState.getMipmapMaxLevel() - baseLevel + 1; uint32_t levelCount = mState.getMipmapMaxLevel() - baseLevel + 1;
return mImageViews.getSubresourceSerial(baseLevel, levelCount, 0, vk::LayerMode::All); return getImageViews().getSubresourceSerial(baseLevel, levelCount, 0, vk::LayerMode::All);
} }
} // namespace rx } // namespace rx
...@@ -169,7 +169,7 @@ class TextureVk : public TextureImpl, public angle::ObserverInterface ...@@ -169,7 +169,7 @@ class TextureVk : public TextureImpl, public angle::ObserverInterface
void retainImageViews(vk::ResourceUseList *resourceUseList) void retainImageViews(vk::ResourceUseList *resourceUseList)
{ {
mImageViews.retain(resourceUseList); getImageViews().retain(resourceUseList);
} }
void releaseOwnershipOfImage(const gl::Context *context); void releaseOwnershipOfImage(const gl::Context *context);
...@@ -235,6 +235,14 @@ class TextureVk : public TextureImpl, public angle::ObserverInterface ...@@ -235,6 +235,14 @@ class TextureVk : public TextureImpl, public angle::ObserverInterface
uint32_t imageBaseLevel, uint32_t imageBaseLevel,
bool selfOwned); bool selfOwned);
void updateImageHelper(ContextVk *contextVk, size_t imageCopyBufferAlignment); void updateImageHelper(ContextVk *contextVk, size_t imageCopyBufferAlignment);
vk::ImageViewHelper &getImageViews()
{
return mMultisampledImageViews[gl::RenderToTextureImageIndex::Default];
}
const vk::ImageViewHelper &getImageViews() const
{
return mMultisampledImageViews[gl::RenderToTextureImageIndex::Default];
}
// Redefine a mip level of the texture. If the new size and format don't match the allocated // Redefine a mip level of the texture. If the new size and format don't match the allocated
// image, the image may be released. When redefining a mip of a multi-level image, updates are // image, the image may be released. When redefining a mip of a multi-level image, updates are
...@@ -364,7 +372,10 @@ class TextureVk : public TextureImpl, public angle::ObserverInterface ...@@ -364,7 +372,10 @@ class TextureVk : public TextureImpl, public angle::ObserverInterface
const bool sized, const bool sized,
uint32_t levelCount, uint32_t levelCount,
uint32_t layerCount); uint32_t layerCount);
angle::Result initRenderTargets(ContextVk *contextVk, GLuint layerCount, GLuint levelIndex); angle::Result initRenderTargets(ContextVk *contextVk,
GLuint layerCount,
GLuint levelIndex,
gl::RenderToTextureImageIndex renderToTextureIndex);
angle::Result getLevelLayerImageView(ContextVk *contextVk, angle::Result getLevelLayerImageView(ContextVk *contextVk,
size_t level, size_t level,
size_t layer, size_t layer,
...@@ -412,29 +423,42 @@ class TextureVk : public TextureImpl, public angle::ObserverInterface ...@@ -412,29 +423,42 @@ class TextureVk : public TextureImpl, public angle::ObserverInterface
// mImage. // mImage.
uint32_t mImageLevelOffset; uint32_t mImageLevelOffset;
// If multisampled rendering to texture, an intermediate multisampled image is created for use
// as renderpass color attachment. An array of images and image views are used based on the
// number of samples used with multisampled rendering to texture. Index 0 corresponds to the
// non-multisampled-render-to-texture usage of the texture.
// - index 0: Unused. See description of |mImage|.
// - index N: intermediate multisampled image used for multisampled rendering to texture with
// 1 << N samples
gl::RenderToTextureImageMap<vk::ImageHelper> mMultisampledImages;
// |ImageViewHelper| contains all the current views for the Texture. The views are always owned
// by the Texture and are not shared like |mImage|. They also have different lifetimes and can
// be reallocated independently of |mImage| on state changes.
//
// - index 0: views for the texture's image (regardless of |mOwnsImage|).
// - index N: views for mMultisampledImages[N]
gl::RenderToTextureImageMap<vk::ImageViewHelper> mMultisampledImageViews;
// Render targets stored as array of vector of vectors
//
// - First dimension: index N contains render targets with views from mMultisampledImageViews[N]
// - Second dimension: level
// - Third dimension: layer
gl::RenderToTextureImageMap<std::vector<RenderTargetVector>> mRenderTargets;
// |mImage| wraps a VkImage and VkDeviceMemory that represents the gl::Texture. |mOwnsImage| // |mImage| wraps a VkImage and VkDeviceMemory that represents the gl::Texture. |mOwnsImage|
// indicates that |TextureVk| owns the image. Otherwise it is a weak pointer shared with another // indicates that |TextureVk| owns the image. Otherwise it is a weak pointer shared with another
// class. // class. Due to this sharing, for example through EGL images, the image must always be
// dynamically allocated as the texture can release ownership for example and it can be
// transferred to another |TextureVk|.
vk::ImageHelper *mImage; vk::ImageHelper *mImage;
// |mImageViews| contains all the current views for the Texture. The views are always owned by
// the Texture and are not shared like |mImage|. They also have different lifetimes and can be
// reallocated independently of |mImage| on state changes.
vk::ImageViewHelper mImageViews;
// If multisampled rendering to texture, an intermediate multisampled image is created for use
// as renderpass color or depth/stencil attachment.
vk::ImageHelper mMultisampledImage;
vk::ImageViewHelper mMultisampledImageViews;
// |mSampler| contains the relevant Vulkan sampler states representing the OpenGL Texture // |mSampler| contains the relevant Vulkan sampler states representing the OpenGL Texture
// sampling states for the Texture. // sampling states for the Texture.
vk::SamplerBinding mSampler; vk::SamplerBinding mSampler;
// Render targets stored as vector of vectors
// Level is first dimension, layer is second
std::vector<RenderTargetVector> mRenderTargets;
// Overridden in some tests. // Overridden in some tests.
size_t mStagingBufferInitialSize; size_t mStagingBufferInitialSize;
......
...@@ -463,9 +463,7 @@ RenderPassDesc::RenderPassDesc(const RenderPassDesc &other) ...@@ -463,9 +463,7 @@ RenderPassDesc::RenderPassDesc(const RenderPassDesc &other)
void RenderPassDesc::setSamples(GLint samples) void RenderPassDesc::setSamples(GLint samples)
{ {
ASSERT(samples < std::numeric_limits<uint8_t>::max()); SetBitField(mLogSamples, PackSampleCount(samples));
ASSERT(gl::isPow2(samples));
SetBitField(mLogSamples, gl::ScanForward(static_cast<uint8_t>(samples)));
} }
void RenderPassDesc::packColorAttachment(size_t colorIndexGL, angle::FormatID formatID) void RenderPassDesc::packColorAttachment(size_t colorIndexGL, angle::FormatID formatID)
......
...@@ -954,6 +954,21 @@ GLenum CalculateGenerateMipmapFilter(ContextVk *contextVk, const vk::Format &for ...@@ -954,6 +954,21 @@ GLenum CalculateGenerateMipmapFilter(ContextVk *contextVk, const vk::Format &for
return formatSupportsLinearFiltering && !hintFastest ? GL_LINEAR : GL_NEAREST; return formatSupportsLinearFiltering && !hintFastest ? GL_LINEAR : GL_NEAREST;
} }
// Return the log of samples. Assumes |sampleCount| is a power of 2. The result can be used to
// index an array based on sample count. See for example TextureVk::PerSampleCountArray.
size_t PackSampleCount(GLint sampleCount)
{
if (sampleCount == 0)
{
sampleCount = 1;
}
// We currently only support up to 16xMSAA.
ASSERT(sampleCount <= VK_SAMPLE_COUNT_16_BIT);
ASSERT(gl::isPow2(sampleCount));
return gl::ScanForward(static_cast<uint32_t>(sampleCount));
}
namespace gl_vk namespace gl_vk
{ {
......
...@@ -796,6 +796,7 @@ void InitExternalSemaphoreCapabilitiesFunctions(VkInstance instance); ...@@ -796,6 +796,7 @@ void InitExternalSemaphoreCapabilitiesFunctions(VkInstance instance);
#endif // !defined(ANGLE_SHARED_LIBVULKAN) #endif // !defined(ANGLE_SHARED_LIBVULKAN)
GLenum CalculateGenerateMipmapFilter(ContextVk *contextVk, const vk::Format &format); GLenum CalculateGenerateMipmapFilter(ContextVk *contextVk, const vk::Format &format);
size_t PackSampleCount(GLint sampleCount);
namespace gl_vk namespace gl_vk
{ {
......
...@@ -1292,6 +1292,161 @@ TEST_P(MultisampledRenderToTextureES3Test, BlitFramebufferMixedColorAndDepth) ...@@ -1292,6 +1292,161 @@ TEST_P(MultisampledRenderToTextureES3Test, BlitFramebufferMixedColorAndDepth)
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
} }
// Draw non-multisampled, draw multisampled, repeat. This tests the same texture being bound
// differently to two FBOs.
TEST_P(MultisampledRenderToTextureTest, DrawNonMultisampledThenMultisampled)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
constexpr GLsizei kSize = 64;
// http://anglebug.com/4935
ANGLE_SKIP_TEST_IF(IsD3D11());
// Texture attachment to the two framebuffers.
GLTexture color;
glBindTexture(GL_TEXTURE_2D, color);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
// Create singlesampled framebuffer.
GLFramebuffer fboSS;
glBindFramebuffer(GL_FRAMEBUFFER, fboSS);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0);
ASSERT_GL_NO_ERROR();
// Create multisampled framebuffer.
GLFramebuffer fboMS;
glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color,
0, 4);
ASSERT_GL_NO_ERROR();
// Draw red into the multisampled color buffer.
ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
glUseProgram(drawColor);
GLint colorUniformLocation =
glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
ASSERT_NE(colorUniformLocation, -1);
glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
// Draw green into the singlesampled color buffer
glBindFramebuffer(GL_FRAMEBUFFER, fboSS);
glEnable(GL_SCISSOR_TEST);
glScissor(kSize / 8, kSize / 8, 3 * kSize / 4, 3 * kSize / 4);
glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f);
drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
// Draw blue into the multisampled color buffer
glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
glScissor(kSize / 4, kSize / 4, kSize / 2, kSize / 2);
glUniform4f(colorUniformLocation, 0.0f, 0.0f, 1.0f, 1.0f);
drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
// Verify that the texture is red on the border, blue in the middle and green in between.
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboSS);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(3 * kSize / 16, 3 * kSize / 16, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(13 * kSize / 16, 3 * kSize / 16, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(3 * kSize / 16, 13 * kSize / 16, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(13 * kSize / 16, 13 * kSize / 16, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(3 * kSize / 8, 3 * kSize / 8, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(5 * kSize / 8, 3 * kSize / 8, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(3 * kSize / 8, 5 * kSize / 8, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(5 * kSize / 8, 5 * kSize / 8, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::blue);
ASSERT_GL_NO_ERROR();
}
// Draw multisampled, draw multisampled with another sample count, repeat. This tests the same
// texture being bound as multisampled-render-to-texture with different sample counts to two FBOs.
TEST_P(MultisampledRenderToTextureTest, DrawMultisampledDifferentSamples)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
constexpr GLsizei kSize = 64;
GLsizei maxSamples = 0;
glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
ASSERT_GE(maxSamples, 4);
// Texture attachment to the two framebuffers.
GLTexture color;
glBindTexture(GL_TEXTURE_2D, color);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
// Create two multisampled framebuffers.
GLFramebuffer fboMS1;
glBindFramebuffer(GL_FRAMEBUFFER, fboMS1);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color,
0, 4);
GLFramebuffer fboMS2;
glBindFramebuffer(GL_FRAMEBUFFER, fboMS2);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color,
0, maxSamples);
ASSERT_GL_NO_ERROR();
// Draw red into the first multisampled color buffer.
ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
glUseProgram(drawColor);
GLint colorUniformLocation =
glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
ASSERT_NE(colorUniformLocation, -1);
glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
// Draw green into the second multisampled color buffer
glBindFramebuffer(GL_FRAMEBUFFER, fboMS1);
glEnable(GL_SCISSOR_TEST);
glScissor(kSize / 8, kSize / 8, 3 * kSize / 4, 3 * kSize / 4);
glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f);
drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
// Draw blue into the first multisampled color buffer
glBindFramebuffer(GL_FRAMEBUFFER, fboMS2);
glScissor(kSize / 4, kSize / 4, kSize / 2, kSize / 2);
glUniform4f(colorUniformLocation, 0.0f, 0.0f, 1.0f, 1.0f);
drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
// Verify that the texture is red on the border, blue in the middle and green in between.
GLFramebuffer fboSS;
glBindFramebuffer(GL_FRAMEBUFFER, fboSS);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(3 * kSize / 16, 3 * kSize / 16, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(13 * kSize / 16, 3 * kSize / 16, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(3 * kSize / 16, 13 * kSize / 16, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(13 * kSize / 16, 13 * kSize / 16, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(3 * kSize / 8, 3 * kSize / 8, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(5 * kSize / 8, 3 * kSize / 8, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(3 * kSize / 8, 5 * kSize / 8, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(5 * kSize / 8, 5 * kSize / 8, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::blue);
ASSERT_GL_NO_ERROR();
}
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(MultisampledRenderToTextureTest); ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(MultisampledRenderToTextureTest);
ANGLE_INSTANTIATE_TEST_ES3(MultisampledRenderToTextureES3Test); ANGLE_INSTANTIATE_TEST_ES3(MultisampledRenderToTextureES3Test);
ANGLE_INSTANTIATE_TEST_ES31(MultisampledRenderToTextureES31Test); ANGLE_INSTANTIATE_TEST_ES31(MultisampledRenderToTextureES31Test);
......
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