Commit f776eb9c by Shahbaz Youssefi Committed by Commit Bot

Vulkan: EXT_multisampled_render_to_texture2 support

The previous change that implemented EXT_multisampled_render_to_texture already provisioned this extension in the Vulkan backend. This change implements the front-end for this extension and enables it in the Vulkan backend. Bug: angleproject:4836 Change-Id: I7080260972e61727c5716051c236f635668cb67b Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2330510 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarJonah Ryan-Davis <jonahr@google.com>
parent 47207e42
...@@ -1576,37 +1576,34 @@ angle::Result Framebuffer::blit(const Context *context, ...@@ -1576,37 +1576,34 @@ angle::Result Framebuffer::blit(const Context *context,
int Framebuffer::getSamples(const Context *context) const int Framebuffer::getSamples(const Context *context) const
{ {
return (isComplete(context) ? getCachedSamples(context, AttachmentSampleType::Emulated) : 0); if (!isComplete(context))
} {
return 0;
int Framebuffer::getResourceSamples(const Context *context) const }
{
return (isComplete(context) ? getCachedSamples(context, AttachmentSampleType::Resource) : 0);
}
int Framebuffer::getCachedSamples(const Context *context, AttachmentSampleType sampleType) const
{
ASSERT(mCachedStatus.valid() && mCachedStatus.value() == GL_FRAMEBUFFER_COMPLETE); ASSERT(mCachedStatus.valid() && mCachedStatus.value() == GL_FRAMEBUFFER_COMPLETE);
// For a complete framebuffer, all attachments must have the same sample count. // For a complete framebuffer, all attachments must have the same sample count.
// In this case return the first nonzero sample size. // In this case return the first nonzero sample size.
const auto *firstNonNullAttachment = mState.getFirstNonNullAttachment(); const FramebufferAttachment *firstNonNullAttachment = mState.getFirstNonNullAttachment();
if (firstNonNullAttachment) ASSERT(firstNonNullAttachment == nullptr || firstNonNullAttachment->isAttached());
return firstNonNullAttachment ? firstNonNullAttachment->getSamples() : 0;
}
int Framebuffer::getReadBufferResourceSamples(const Context *context) const
{
if (!isComplete(context))
{ {
ASSERT(firstNonNullAttachment->isAttached()); return 0;
if (sampleType == AttachmentSampleType::Resource)
{
return firstNonNullAttachment->getResourceSamples();
}
else
{
ASSERT(sampleType == AttachmentSampleType::Emulated);
return firstNonNullAttachment->getSamples();
}
} }
// No attachments found. ASSERT(mCachedStatus.valid() && mCachedStatus.value() == GL_FRAMEBUFFER_COMPLETE);
return 0;
const FramebufferAttachment *readAttachment = mState.getReadAttachment();
ASSERT(readAttachment == nullptr || readAttachment->isAttached());
return readAttachment ? readAttachment->getResourceSamples() : 0;
} }
angle::Result Framebuffer::getSamplePosition(const Context *context, angle::Result Framebuffer::getSamplePosition(const Context *context,
......
...@@ -49,15 +49,6 @@ class State; ...@@ -49,15 +49,6 @@ class State;
class Texture; class Texture;
class TextureCapsMap; class TextureCapsMap;
enum class AttachmentSampleType
{
// The sample count of the actual resource
Resource,
// If render_to_texture is used, this is the sample count of the multisampled
// texture that is created behind the scenes.
Emulated
};
class FramebufferState final : angle::NonCopyable class FramebufferState final : angle::NonCopyable
{ {
public: public:
...@@ -278,7 +269,7 @@ class Framebuffer final : public angle::ObserverInterface, ...@@ -278,7 +269,7 @@ class Framebuffer final : public angle::ObserverInterface,
// This method calls checkStatus. // This method calls checkStatus.
int getSamples(const Context *context) const; int getSamples(const Context *context) const;
int getResourceSamples(const Context *context) const; int getReadBufferResourceSamples(const Context *context) const;
angle::Result getSamplePosition(const Context *context, size_t index, GLfloat *xy) const; angle::Result getSamplePosition(const Context *context, size_t index, GLfloat *xy) const;
...@@ -309,9 +300,6 @@ class Framebuffer final : public angle::ObserverInterface, ...@@ -309,9 +300,6 @@ class Framebuffer final : public angle::ObserverInterface,
return checkStatusImpl(context); return checkStatusImpl(context);
} }
// For when we don't want to check completeness in getSamples().
int getCachedSamples(const Context *context, AttachmentSampleType sampleType) const;
// Helper for checkStatus == GL_FRAMEBUFFER_COMPLETE. // Helper for checkStatus == GL_FRAMEBUFFER_COMPLETE.
ANGLE_INLINE bool isComplete(const Context *context) const ANGLE_INLINE bool isComplete(const Context *context) const
{ {
......
...@@ -2142,7 +2142,13 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk, ...@@ -2142,7 +2142,13 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk,
VkAttachmentStoreOp depthStoreOp = VK_ATTACHMENT_STORE_OP_STORE; VkAttachmentStoreOp depthStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
VkAttachmentStoreOp stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; VkAttachmentStoreOp stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
if (!depthStencilRenderTarget->hasDefinedContent()) // If the image data was previously discarded (with no update in between), don't attempt to
// load the image. Additionally, if the multisampled image data is transient and there is
// no resolve attachment, there's no data to load. The latter is the case with
// depth/stencil texture attachments per GL_EXT_multisampled_render_to_texture2.
if (!depthStencilRenderTarget->hasDefinedContent() ||
(depthStencilRenderTarget->isImageTransient() &&
!depthStencilRenderTarget->hasResolveAttachment()))
{ {
depthLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; depthLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
......
...@@ -76,18 +76,19 @@ void RendererVk::ensureCapsInitialized() const ...@@ -76,18 +76,19 @@ void RendererVk::ensureCapsInitialized() const
// Enable this for simple buffer readback testing, but some functionality is missing. // Enable this for simple buffer readback testing, but some functionality is missing.
// TODO(jmadill): Support full mapBufferRange extension. // TODO(jmadill): Support full mapBufferRange extension.
mNativeExtensions.mapBufferOES = true; mNativeExtensions.mapBufferOES = true;
mNativeExtensions.mapBufferRange = true; mNativeExtensions.mapBufferRange = true;
mNativeExtensions.textureStorage = true; mNativeExtensions.textureStorage = true;
mNativeExtensions.drawBuffers = true; mNativeExtensions.drawBuffers = true;
mNativeExtensions.fragDepth = true; mNativeExtensions.fragDepth = true;
mNativeExtensions.framebufferBlit = true; mNativeExtensions.framebufferBlit = true;
mNativeExtensions.framebufferMultisample = true; mNativeExtensions.framebufferMultisample = true;
mNativeExtensions.multisampledRenderToTexture = true; mNativeExtensions.multisampledRenderToTexture = true;
mNativeExtensions.copyTexture = true; mNativeExtensions.multisampledRenderToTexture2 = true;
mNativeExtensions.copyTexture3d = true; mNativeExtensions.copyTexture = true;
mNativeExtensions.copyCompressedTexture = true; mNativeExtensions.copyTexture3d = true;
mNativeExtensions.debugMarker = true; mNativeExtensions.copyCompressedTexture = true;
mNativeExtensions.debugMarker = true;
mNativeExtensions.robustness = mNativeExtensions.robustness =
!IsSwiftshader(mPhysicalDeviceProperties.vendorID, mPhysicalDeviceProperties.deviceID) && !IsSwiftshader(mPhysicalDeviceProperties.vendorID, mPhysicalDeviceProperties.deviceID) &&
!IsARM(mPhysicalDeviceProperties.vendorID); !IsARM(mPhysicalDeviceProperties.vendorID);
......
...@@ -1414,7 +1414,7 @@ bool ValidateBlitFramebufferParameters(const Context *context, ...@@ -1414,7 +1414,7 @@ bool ValidateBlitFramebufferParameters(const Context *context,
} }
// Not allow blitting to MS buffers, therefore if renderToTextureSamples exist, // Not allow blitting to MS buffers, therefore if renderToTextureSamples exist,
// consider it MS. needResourceSamples = false // consider it MS. checkReadBufferResourceSamples = false
if (!ValidateFramebufferNotMultisampled(context, drawFramebuffer, false)) if (!ValidateFramebufferNotMultisampled(context, drawFramebuffer, false))
{ {
return false; return false;
...@@ -2596,8 +2596,8 @@ bool ValidateCopyTexImageParametersBase(const Context *context, ...@@ -2596,8 +2596,8 @@ bool ValidateCopyTexImageParametersBase(const Context *context,
return false; return false;
} }
// needResourceSamples = true. Treat renderToTexture textures as single sample since they will // checkReadBufferResourceSamples = true. Treat renderToTexture textures as single sample since
// be resolved before copying // they will be resolved before copying.
if (!readFramebuffer->isDefault() && if (!readFramebuffer->isDefault() &&
!ValidateFramebufferNotMultisampled(context, readFramebuffer, true)) !ValidateFramebufferNotMultisampled(context, readFramebuffer, true))
{ {
...@@ -6632,10 +6632,11 @@ bool ValidateGetInternalFormativBase(const Context *context, ...@@ -6632,10 +6632,11 @@ bool ValidateGetInternalFormativBase(const Context *context,
bool ValidateFramebufferNotMultisampled(const Context *context, bool ValidateFramebufferNotMultisampled(const Context *context,
const Framebuffer *framebuffer, const Framebuffer *framebuffer,
bool needResourceSamples) bool checkReadBufferResourceSamples)
{ {
int samples = needResourceSamples ? framebuffer->getResourceSamples(context) int samples = checkReadBufferResourceSamples
: framebuffer->getSamples(context); ? framebuffer->getReadBufferResourceSamples(context)
: framebuffer->getSamples(context);
if (samples != 0) if (samples != 0)
{ {
context->validationError(GL_INVALID_OPERATION, kInvalidMultisampledFramebufferOperation); context->validationError(GL_INVALID_OPERATION, kInvalidMultisampledFramebufferOperation);
......
...@@ -650,7 +650,7 @@ bool ValidateGetInternalFormativBase(const Context *context, ...@@ -650,7 +650,7 @@ bool ValidateGetInternalFormativBase(const Context *context,
bool ValidateFramebufferNotMultisampled(const Context *context, bool ValidateFramebufferNotMultisampled(const Context *context,
const Framebuffer *framebuffer, const Framebuffer *framebuffer,
bool needResourceSamples); bool checkReadBufferResourceSamples);
bool ValidateMultitextureUnit(const Context *context, GLenum texture); bool ValidateMultitextureUnit(const Context *context, GLenum texture);
......
...@@ -6713,7 +6713,9 @@ bool ValidateFramebufferTexture2DMultisampleEXT(const Context *context, ...@@ -6713,7 +6713,9 @@ bool ValidateFramebufferTexture2DMultisampleEXT(const Context *context,
return false; return false;
} }
if (attachment != GL_COLOR_ATTACHMENT0) // Unless EXT_multisampled_render_to_texture2 is enabled, only color attachment 0 can be used.
if (!context->getExtensions().multisampledRenderToTexture2 &&
attachment != GL_COLOR_ATTACHMENT0)
{ {
context->validationError(GL_INVALID_ENUM, kInvalidAttachment); context->validationError(GL_INVALID_ENUM, kInvalidAttachment);
return false; return false;
......
...@@ -30,6 +30,18 @@ class MultisampledRenderToTextureTest : public ANGLETest ...@@ -30,6 +30,18 @@ class MultisampledRenderToTextureTest : public ANGLETest
void testTearDown() override {} void testTearDown() override {}
void assertErrorIfNotMSRTT2(GLenum error)
{
if (EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture2"))
{
ASSERT_GL_NO_ERROR();
}
else
{
ASSERT_GL_ERROR(error);
}
}
void setupCopyTexProgram() void setupCopyTexProgram()
{ {
mCopyTextureProgram.makeRaster(essl1_shaders::vs::Texture2D(), mCopyTextureProgram.makeRaster(essl1_shaders::vs::Texture2D(),
...@@ -42,6 +54,33 @@ class MultisampledRenderToTextureTest : public ANGLETest ...@@ -42,6 +54,33 @@ class MultisampledRenderToTextureTest : public ANGLETest
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
} }
void setupUniformColorProgramMultiRenderTarget(const bool bufferEnabled[8], GLuint *programOut)
{
std::stringstream fs;
fs << "#extension GL_EXT_draw_buffers : enable\n"
"precision highp float;\n"
"uniform mediump vec4 "
<< essl1_shaders::ColorUniform()
<< ";\n"
"void main()\n"
"{\n";
for (unsigned int index = 0; index < 8; index++)
{
if (bufferEnabled[index])
{
fs << " gl_FragData[" << index << "] = " << essl1_shaders::ColorUniform()
<< ";\n";
}
}
fs << "}\n";
*programOut = CompileProgram(essl1_shaders::vs::Simple(), fs.str().c_str());
ASSERT_NE(*programOut, 0u);
}
void verifyResults(GLuint texture, void verifyResults(GLuint texture,
const GLColor expected, const GLColor expected,
GLint fboSize, GLint fboSize,
...@@ -87,6 +126,9 @@ class MultisampledRenderToTextureTest : public ANGLETest ...@@ -87,6 +126,9 @@ class MultisampledRenderToTextureTest : public ANGLETest
class MultisampledRenderToTextureES3Test : public MultisampledRenderToTextureTest class MultisampledRenderToTextureES3Test : public MultisampledRenderToTextureTest
{}; {};
class MultisampledRenderToTextureES31Test : public MultisampledRenderToTextureTest
{};
// Checking against invalid parameters for RenderbufferStorageMultisampleEXT. // Checking against invalid parameters for RenderbufferStorageMultisampleEXT.
TEST_P(MultisampledRenderToTextureTest, RenderbufferParameterCheck) TEST_P(MultisampledRenderToTextureTest, RenderbufferParameterCheck)
{ {
...@@ -132,23 +174,53 @@ TEST_P(MultisampledRenderToTextureTest, RenderbufferParameterCheck) ...@@ -132,23 +174,53 @@ TEST_P(MultisampledRenderToTextureTest, RenderbufferParameterCheck)
TEST_P(MultisampledRenderToTextureTest, Texture2DParameterCheck) TEST_P(MultisampledRenderToTextureTest, Texture2DParameterCheck)
{ {
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture")); ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
bool isES3 = getClientMajorVersion() >= 3;
GLTexture texture; GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture); glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
GLTexture depthTexture;
if (isES3)
{
glBindTexture(GL_TEXTURE_2D, depthTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, 64, 64, 0, GL_DEPTH_STENCIL,
GL_UNSIGNED_INT_24_8_OES, nullptr);
}
GLFramebuffer fbo; GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo); glBindFramebuffer(GL_FRAMEBUFFER, fbo);
// Positive test case // Positive test case
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
texture, 0, 4); texture, 0, 4);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Attachment not COLOR_ATTACHMENT0 // Attachment not COLOR_ATTACHMENT0. Allowed only in EXT_multisampled_render_to_texture2
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D,
texture, 0, 4); texture, 0, 4);
ASSERT_GL_ERROR(GL_INVALID_ENUM); assertErrorIfNotMSRTT2(GL_INVALID_ENUM);
// Depth/stencil attachment. Allowed only in EXT_multisampled_render_to_texture2
if (isES3)
{
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
depthTexture, 0, 4);
assertErrorIfNotMSRTT2(GL_INVALID_ENUM);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
depthTexture, 0, 4);
assertErrorIfNotMSRTT2(GL_INVALID_ENUM);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
GL_TEXTURE_2D, depthTexture, 0, 4);
assertErrorIfNotMSRTT2(GL_INVALID_ENUM);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D,
depthTexture, 0, 4);
assertErrorIfNotMSRTT2(GL_INVALID_ENUM);
}
// Target not framebuffer // Target not framebuffer
glFramebufferTexture2DMultisampleEXT(GL_RENDERBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glFramebufferTexture2DMultisampleEXT(GL_RENDERBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
...@@ -204,11 +276,12 @@ TEST_P(MultisampledRenderToTextureTest, TextureCubeMapParameterCheck) ...@@ -204,11 +276,12 @@ TEST_P(MultisampledRenderToTextureTest, TextureCubeMapParameterCheck)
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, texture, 0, 4); GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, texture, 0, 4);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Attachment not COLOR_ATTACHMENT0 // Attachment not COLOR_ATTACHMENT0. Allowed only in EXT_multisampled_render_to_texture2
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1,
GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, texture, 0, 4); GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, texture, 0, 4);
ASSERT_GL_ERROR(GL_INVALID_ENUM); assertErrorIfNotMSRTT2(GL_INVALID_ENUM);
// Target not framebuffer // Target not framebuffer
glFramebufferTexture2DMultisampleEXT(GL_RENDERBUFFER, GL_COLOR_ATTACHMENT0, glFramebufferTexture2DMultisampleEXT(GL_RENDERBUFFER, GL_COLOR_ATTACHMENT0,
...@@ -731,6 +804,86 @@ TEST_P(MultisampledRenderToTextureTest, DrawCopyThenBlend) ...@@ -731,6 +804,86 @@ TEST_P(MultisampledRenderToTextureTest, DrawCopyThenBlend)
EXPECT_PIXEL_COLOR_NEAR(kSize - 1, kSize - 1, kExpected, 1); EXPECT_PIXEL_COLOR_NEAR(kSize - 1, kSize - 1, kExpected, 1);
// For completeness, verify that the texture used as copy target is red. // For completeness, verify that the texture used as copy target is red.
ASSERT_GL_NO_ERROR();
const GLColor expectedCopyResult(255, 0, 0, 255);
verifyResults(texture, expectedCopyResult, kSize, 0, 0, kSize, kSize);
ASSERT_GL_NO_ERROR();
}
// Draw, copy, then blend. The copy will make sure an implicit resolve happens. Regardless, the
// following draw should retain the data written by the first draw command.
// Uses color attachment 1.
TEST_P(MultisampledRenderToTextureES3Test, ColorAttachment1)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture2"));
// Qualcomm driver crashes in the presence of VK_ATTACHMENT_UNUSED.
// http://anglebug.com/3423
ANGLE_SKIP_TEST_IF(IsVulkan() && IsAndroid());
// Fails on Intel Ubuntu 19.04 Mesa 19.0.2 Vulkan. http://anglebug.com/3616
ANGLE_SKIP_TEST_IF(IsLinux() && IsIntel() && IsVulkan());
constexpr GLsizei kSize = 64;
setupCopyTexProgram();
// Create multisampled framebuffer to draw into, use color attachment 1
GLTexture colorMS;
glBindTexture(GL_TEXTURE_2D, colorMS);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
GLFramebuffer fboMS;
glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D,
colorMS, 0, 4);
ASSERT_GL_NO_ERROR();
// Setup program to render into attachment 1.
constexpr bool kBuffersEnabled[8] = {false, true};
GLuint drawColor;
setupUniformColorProgramMultiRenderTarget(kBuffersEnabled, &drawColor);
glUseProgram(drawColor);
GLint colorUniformLocation =
glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
ASSERT_NE(colorUniformLocation, -1);
constexpr GLenum kDrawBuffers[] = {GL_NONE, GL_COLOR_ATTACHMENT1};
glDrawBuffers(2, kDrawBuffers);
glReadBuffer(GL_COLOR_ATTACHMENT1);
ASSERT_GL_NO_ERROR();
// Draw red into the multisampled color buffer.
glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
// Create a texture and copy into it.
GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, kSize, kSize, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
ASSERT_GL_NO_ERROR();
// Blend half-transparent green into the multisampled color buffer.
glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 0.5f);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
// Verify that the texture is now yellow
const GLColor kExpected(127, 127, 0, 191);
EXPECT_PIXEL_COLOR_NEAR(0, 0, kExpected, 1);
EXPECT_PIXEL_COLOR_NEAR(kSize - 1, 0, kExpected, 1);
EXPECT_PIXEL_COLOR_NEAR(0, kSize - 1, kExpected, 1);
EXPECT_PIXEL_COLOR_NEAR(kSize - 1, kSize - 1, kExpected, 1);
// For completeness, verify that the texture used as copy target is red.
const GLColor expectedCopyResult(255, 0, 0, 255); const GLColor expectedCopyResult(255, 0, 0, 255);
verifyResults(texture, expectedCopyResult, kSize, 0, 0, kSize, kSize); verifyResults(texture, expectedCopyResult, kSize, 0, 0, kSize, kSize);
...@@ -739,6 +892,325 @@ TEST_P(MultisampledRenderToTextureTest, DrawCopyThenBlend) ...@@ -739,6 +892,325 @@ TEST_P(MultisampledRenderToTextureTest, DrawCopyThenBlend)
glDeleteProgram(drawColor); glDeleteProgram(drawColor);
} }
// Draw, copy, then blend. The copy will make sure an implicit resolve happens. Regardless, the
// following draw should retain the data written by the first draw command.
// Uses color attachments 0 and 3.
TEST_P(MultisampledRenderToTextureES3Test, ColorAttachments0And3)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture2"));
// Qualcomm driver crashes in the presence of VK_ATTACHMENT_UNUSED.
// http://anglebug.com/3423
ANGLE_SKIP_TEST_IF(IsVulkan() && IsAndroid());
constexpr GLsizei kSize = 64;
setupCopyTexProgram();
// Create multisampled framebuffer to draw into, use color attachment 1
GLTexture colorMS0;
glBindTexture(GL_TEXTURE_2D, colorMS0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
GLTexture colorMS3;
glBindTexture(GL_TEXTURE_2D, colorMS3);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
GLFramebuffer fboMS;
glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
colorMS0, 0, 4);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D,
colorMS3, 0, 4);
ASSERT_GL_NO_ERROR();
// Setup program to render into attachments 0 and 3.
constexpr bool kBuffersEnabled[8] = {true, false, false, true};
GLuint drawColor;
setupUniformColorProgramMultiRenderTarget(kBuffersEnabled, &drawColor);
glUseProgram(drawColor);
GLint colorUniformLocation =
glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
ASSERT_NE(colorUniformLocation, -1);
constexpr GLenum kDrawBuffers[] = {GL_COLOR_ATTACHMENT0, GL_NONE, GL_NONE,
GL_COLOR_ATTACHMENT3};
glDrawBuffers(4, kDrawBuffers);
glReadBuffer(GL_COLOR_ATTACHMENT3);
ASSERT_GL_NO_ERROR();
// Draw red into the multisampled color buffers.
glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
// Create a texture and copy from one of them.
GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, kSize, kSize, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
ASSERT_GL_NO_ERROR();
// Blend half-transparent green into the multisampled color buffers.
glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 0.5f);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
// Verify that the textures are now yellow
const GLColor kExpected(127, 127, 0, 191);
EXPECT_PIXEL_COLOR_NEAR(0, 0, kExpected, 1);
EXPECT_PIXEL_COLOR_NEAR(kSize - 1, 0, kExpected, 1);
EXPECT_PIXEL_COLOR_NEAR(0, kSize - 1, kExpected, 1);
EXPECT_PIXEL_COLOR_NEAR(kSize - 1, kSize - 1, kExpected, 1);
glReadBuffer(GL_COLOR_ATTACHMENT0);
EXPECT_PIXEL_COLOR_NEAR(0, 0, kExpected, 1);
EXPECT_PIXEL_COLOR_NEAR(kSize - 1, 0, kExpected, 1);
EXPECT_PIXEL_COLOR_NEAR(0, kSize - 1, kExpected, 1);
EXPECT_PIXEL_COLOR_NEAR(kSize - 1, kSize - 1, kExpected, 1);
// For completeness, verify that the texture used as copy target is red.
const GLColor expectedCopyResult(255, 0, 0, 255);
verifyResults(texture, expectedCopyResult, kSize, 0, 0, kSize, kSize);
ASSERT_GL_NO_ERROR();
glDeleteProgram(drawColor);
}
// Draw with depth buffer. Uses EXT_multisampled_render_to_texture2.
// The test works with a 64x1 texture. The first draw call will render geometry whose depth is
// different between top and bottom. The second draw call will enable depth test and draw with the
// average of the two depths. Only half of the samples will take the new color. Once resolved, the
// expected color would be the average of the two draw colors.
TEST_P(MultisampledRenderToTextureES3Test, DepthStencilAttachment)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture2"));
constexpr GLsizei kWidth = 64;
// Create multisampled framebuffer to draw into, with both color and depth attachments.
GLTexture colorMS;
glBindTexture(GL_TEXTURE_2D, colorMS);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kWidth, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
GLTexture depthMS;
glBindTexture(GL_TEXTURE_2D, depthMS);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, kWidth, 1, 0, GL_DEPTH_STENCIL,
GL_UNSIGNED_INT_24_8_OES, nullptr);
GLFramebuffer fboMS;
glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
colorMS, 0, 4);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
depthMS, 0, 4);
ASSERT_GL_NO_ERROR();
// Setup draw program
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);
GLint positionLocation = glGetAttribLocation(drawColor, essl1_shaders::PositionAttrib());
ASSERT_NE(-1, positionLocation);
// Setup vertices such that depth is varied from top to bottom.
std::array<Vector3, 6> quadVertices = {
Vector3(-1.0f, 1.0f, 0.8f), Vector3(-1.0f, -1.0f, 0.2f), Vector3(1.0f, -1.0f, 0.2f),
Vector3(-1.0f, 1.0f, 0.8f), Vector3(1.0f, -1.0f, 0.2f), Vector3(1.0f, 1.0f, 0.8f),
};
GLBuffer quadVertexBuffer;
glBindBuffer(GL_ARRAY_BUFFER, quadVertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 6, quadVertices.data(), GL_STATIC_DRAW);
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(positionLocation);
// Draw red into the framebuffer.
glViewport(0, 0, kWidth, 1);
glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_ALWAYS);
glDrawArrays(GL_TRIANGLES, 0, 6);
ASSERT_GL_NO_ERROR();
// Draw green such that half the samples of each pixel pass the depth test.
glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f);
glDepthFunc(GL_GREATER);
drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
const GLColor kExpected(127, 127, 0, 255);
EXPECT_PIXEL_COLOR_NEAR(0, 0, kExpected, 1);
EXPECT_PIXEL_COLOR_NEAR(kWidth - 1, 0, kExpected, 1);
EXPECT_PIXEL_COLOR_NEAR(kWidth / 2, 0, kExpected, 1);
glDisableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
// Draw, copy, then blend. The copy will make sure an implicit resolve happens. Regardless, the
// following draw should retain the data written by the first draw command.
// Uses color attachments 0 and 1. Attachment 0 is a normal multisampled texture, while attachment
// 1 is a multisampled-render-to-texture texture.
TEST_P(MultisampledRenderToTextureES31Test, MixedMultisampledAndMultisampledRenderToTexture)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture2"));
constexpr GLsizei kSize = 64;
setupCopyTexProgram();
// Create multisampled framebuffer to draw into, use color attachment 1
GLTexture colorMS0;
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, colorMS0);
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, kSize, kSize, true);
GLTexture colorMS1;
glBindTexture(GL_TEXTURE_2D, colorMS1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
GLFramebuffer fboMS;
glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE,
colorMS0, 0);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D,
colorMS1, 0, 4);
ASSERT_GL_NO_ERROR();
// Setup program to render into attachments 0 and 1.
constexpr bool kBuffersEnabled[8] = {true, true};
GLuint drawColor;
setupUniformColorProgramMultiRenderTarget(kBuffersEnabled, &drawColor);
glUseProgram(drawColor);
GLint colorUniformLocation =
glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
ASSERT_NE(colorUniformLocation, -1);
constexpr GLenum kDrawBuffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
glDrawBuffers(2, kDrawBuffers);
glReadBuffer(GL_COLOR_ATTACHMENT1);
ASSERT_GL_NO_ERROR();
// Draw red into the multisampled color buffers.
glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
// Create a texture and copy from one of them.
GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, kSize, kSize, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
ASSERT_GL_NO_ERROR();
// Blend half-transparent green into the multisampled color buffers.
glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 0.5f);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
// Verify that the textures are now yellow
const GLColor kExpected(127, 127, 0, 191);
EXPECT_PIXEL_COLOR_NEAR(0, 0, kExpected, 1);
EXPECT_PIXEL_COLOR_NEAR(kSize - 1, 0, kExpected, 1);
EXPECT_PIXEL_COLOR_NEAR(0, kSize - 1, kExpected, 1);
EXPECT_PIXEL_COLOR_NEAR(kSize - 1, kSize - 1, kExpected, 1);
// For completeness, verify that the texture used as copy target is red.
const GLColor expectedCopyResult(255, 0, 0, 255);
verifyResults(texture, expectedCopyResult, kSize, 0, 0, kSize, kSize);
ASSERT_GL_NO_ERROR();
glDeleteProgram(drawColor);
}
// BlitFramebuffer functionality test with mixed color attachments where multisampled render to
// texture as attachment 1 and is the read buffer. This test makes sure the fact that attachment 0
// is a true multisampled texture doesn't cause issues.
// Uses EXT_multisampled_render_to_texture2.
TEST_P(MultisampledRenderToTextureES31Test, BlitFramebufferAttachment1)
{
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture2"));
constexpr GLsizei kSize = 16;
// Create multisampled framebuffer to draw into, use color attachment 1
GLTexture colorMS0;
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, colorMS0);
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, kSize, kSize, true);
GLTexture colorMS1;
glBindTexture(GL_TEXTURE_2D, colorMS1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
GLFramebuffer fboMS;
glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE,
colorMS0, 0);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D,
colorMS1, 0, 4);
ASSERT_GL_NO_ERROR();
// Setup program to render into attachments 0 and 1.
constexpr bool kBuffersEnabled[8] = {true, true};
GLuint drawColor;
setupUniformColorProgramMultiRenderTarget(kBuffersEnabled, &drawColor);
glUseProgram(drawColor);
GLint colorUniformLocation =
glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
ASSERT_NE(colorUniformLocation, -1);
constexpr GLenum kDrawBuffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
glDrawBuffers(2, kDrawBuffers);
glReadBuffer(GL_COLOR_ATTACHMENT1);
ASSERT_GL_NO_ERROR();
// Draw red into the multisampled color buffers.
glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
// Create single sampled framebuffer to use as dest.
GLFramebuffer fboSS;
glBindFramebuffer(GL_FRAMEBUFFER, fboSS);
GLTexture colorSS;
glBindTexture(GL_TEXTURE_2D, colorSS);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorSS, 0);
ASSERT_GL_NO_ERROR();
// Bind MS to READ as SS is already bound to DRAW.
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboMS);
glReadBuffer(GL_COLOR_ATTACHMENT1);
glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
ASSERT_GL_NO_ERROR();
// Bind SS to READ so we can readPixels from it
glBindFramebuffer(GL_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(kSize / 2, kSize / 2, GLColor::red);
ASSERT_GL_NO_ERROR();
}
// BlitFramebuffer functionality test with mixed multisampled-render-to-texture color attachment and // BlitFramebuffer functionality test with mixed multisampled-render-to-texture color attachment and
// multisampled depth buffer. This test makes sure that the color attachment is blitted, while // multisampled depth buffer. This test makes sure that the color attachment is blitted, while
// the depth/stencil attachment is resolved. // the depth/stencil attachment is resolved.
...@@ -822,4 +1294,5 @@ TEST_P(MultisampledRenderToTextureES3Test, BlitFramebufferMixedColorAndDepth) ...@@ -822,4 +1294,5 @@ TEST_P(MultisampledRenderToTextureES3Test, BlitFramebufferMixedColorAndDepth)
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);
} // namespace } // namespace
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