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;
......
...@@ -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