diff --git a/src/tests/gl_tests/MultisampledRenderToTextureTest.cpp b/src/tests/gl_tests/MultisampledRenderToTextureTest.cpp index 37e5c67d35..834e4da732 100644 --- a/src/tests/gl_tests/MultisampledRenderToTextureTest.cpp +++ b/src/tests/gl_tests/MultisampledRenderToTextureTest.cpp @@ -4717,6 +4717,82 @@ TEST_P(MSRTTES3Test, UnresolveWithAndWithoutStencil) EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white); } +// Regression test: with an MSRTT color-only FBO (no depth/stencil), setting +// glDrawBuffers(GL_NONE) and then drawing causes FramebufferVk::getSamplesImpl() +// to derive samples=1 (because it iterates only enabled draw buffers), while +// updateRenderPassDesc()/updateAttachmentsAndRenderTargets() iterate the +// unfiltered color attachment mask and bind the implicit 4-sample VkImageView. +// Under MSRTT emulation (no VK_EXT_multisampled_render_to_single_sampled), this +// produces a VkRenderPass whose attachment[0].samples=VK_SAMPLE_COUNT_1_BIT and +// a resolve attachment, but binds a VK_SAMPLE_COUNT_4_BIT VkImage in +// vkCreateFramebuffer, violating VUID-VkFramebufferCreateInfo-pAttachments-00881, +// VUID-VkSubpassDescription2-pResolveAttachments-03067, and +// VUID-...-rasterizationSamples-06853. +TEST_P(MSRTTES3Test, DrawBuffersNoneWithMSRTTColorOnly) +{ + ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture")); + + constexpr GLsizei kSize = 64; + + // Create a 4-sample MSRTT color attachment with NO depth/stencil. + GLFramebuffer fboMS; + glBindFramebuffer(GL_FRAMEBUFFER, fboMS); + + GLTexture textureMS; + glBindTexture(GL_TEXTURE_2D, textureMS); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + textureMS, 0, mTestSampleCount); + ASSERT_GL_NO_ERROR(); + EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); + + glViewport(0, 0, kSize, kSize); + + 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); + + // First draw: populates a render pass with samples=4. This ensures the + // subsequent syncState early-return at FramebufferVk.cpp:~2847 fails because + // mRenderPassDesc.samples()(=4) != getSamples()(=1), forcing + // updateRenderPassDesc() to re-run with the divergent state. + drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f); + ASSERT_GL_NO_ERROR(); + // Break the render pass so the next draw starts a new one. + glFinish(); + + // Disable all draw buffers. The MSRTT attachment is still bound to + // GL_COLOR_ATTACHMENT0, so the FBO remains complete, but + // mState.getEnabledDrawBuffers() is now empty. + const GLenum kNone[] = {GL_NONE}; + glDrawBuffers(1, kNone); + ASSERT_GL_NO_ERROR(); + + // FBO must still be complete (frontend completeness ignores draw-buffer state). + EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); + + // Second draw: triggers FramebufferVk::syncState -> + // mRasterizationSamples = max(getSamplesImpl(),1) = 1 + // updateRenderPassDesc(): + // setSamples(1) + // packs color0 + resolve from UNFILTERED getColorAttachmentsMask() + // isRenderToTexture derived from FILTERED getEnabledDrawBuffers() = false + // updateAttachmentsAndRenderTargets(): binds 4-sample VkImageView + // -> startNewRenderPass -> vkCreateRenderPass2 (attach[0].samples=1, resolve + // present) + vkCreateFramebuffer (pAttachments[0]=4-sample view) + drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f); + ASSERT_GL_NO_ERROR(); + + // Force submission so any driver-side undefined behavior surfaces. + glFinish(); + ASSERT_GL_NO_ERROR(); +} + ANGLE_INSTANTIATE_TEST_COMBINE_1( MSRTTTest, PrintToStringParamName,