Edit

kc3-lang/angle/src/tests/gl_tests/VulkanPerformanceCounterTest.cpp

Branch :

  • Show log

    Commit

  • Author : Jamie Madill
    Date : 2021-03-09 18:37:57
    Hash : 89d2a96a
    Message : Vulkan: Add test for UBO descriptor allocations. This performance counter test verifies that re-binding the same two buffers repeatedly doesn't allocate new descriptor sets. Currently the test fails because we don't cache descriptor sets for UBOs. Covers equivalent code patterns in Asphalt 9. Reorganizes the perf counters collected for the program objects. Now they are per-frame reset instead of cumulative. This tracking is now consistent for the different counter types. In the future we can add cumulative tracking for all per-object and global perf counters. Bug: angleproject:5736 Change-Id: I23d04b6453e38af1cf4af7274d24382d136efad3 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2746176 Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: Tim Van Patten <timvp@google.com> Commit-Queue: Jamie Madill <jmadill@chromium.org>

  • src/tests/gl_tests/VulkanPerformanceCounterTest.cpp
  • //
    // Copyright 2020 The ANGLE Project Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    //
    // VulkanPerformanceCounterTest:
    //   Validates specific GL call patterns with ANGLE performance counters.
    //   For example we can verify a certain call set doesn't break the RenderPass.
    //
    // TODO(jmadill): Move to a GL extension. http://anglebug.com/4918
    
    #include "test_utils/ANGLETest.h"
    #include "test_utils/angle_test_instantiate.h"
    // 'None' is defined as 'struct None {};' in
    // third_party/googletest/src/googletest/include/gtest/internal/gtest-type-util.h.
    // But 'None' is also defined as a numeric constant 0L in <X11/X.h>.
    // So we need to include ANGLETest.h first to avoid this conflict.
    
    #include "libANGLE/Context.h"
    #include "libANGLE/angletypes.h"
    #include "libANGLE/renderer/vulkan/ContextVk.h"
    #include "test_utils/gl_raii.h"
    
    using namespace angle;
    
    namespace
    {
    class VulkanPerformanceCounterTest : public ANGLETest
    {
      protected:
        VulkanPerformanceCounterTest()
        {
            // Depth required for SwapShouldInvalidateDepthAfterClear.
            // Also RGBA8 is required to avoid the clear for emulated alpha.
            setConfigRedBits(8);
            setConfigGreenBits(8);
            setConfigBlueBits(8);
            setConfigAlphaBits(8);
            setConfigDepthBits(24);
        }
    
        const rx::vk::PerfCounters &hackANGLE() const
        {
            // Hack the angle!
            const gl::Context *context = static_cast<const gl::Context *>(getEGLWindow()->getContext());
            rx::ContextVk *contextVk   = rx::GetImplAs<rx::ContextVk>(context);
            // This will be implicitly called when using the extension.
            contextVk->syncObjectPerfCounters();
            return contextVk->getPerfCounters();
        }
    
        static constexpr GLsizei kInvalidateTestSize = 16;
    
        void setupClearAndDrawForInvalidateTest(GLProgram *program,
                                                GLFramebuffer *framebuffer,
                                                GLTexture *texture,
                                                GLRenderbuffer *renderbuffer,
                                                bool clearStencil)
        {
            glUseProgram(*program);
    
            // Setup to draw to color, depth, and stencil
            glBindFramebuffer(GL_FRAMEBUFFER, *framebuffer);
            glBindTexture(GL_TEXTURE_2D, *texture);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kInvalidateTestSize, kInvalidateTestSize, 0,
                         GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *texture, 0);
            glBindRenderbuffer(GL_RENDERBUFFER, *renderbuffer);
            glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, kInvalidateTestSize,
                                  kInvalidateTestSize);
            glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
                                      *renderbuffer);
            ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
            // Clear and draw with depth and stencil buffer enabled
            glEnable(GL_DEPTH_TEST);
            glDepthMask(GL_TRUE);
            glDepthFunc(GL_GEQUAL);
            glClearDepthf(0.99f);
            glEnable(GL_STENCIL_TEST);
            glClearStencil(0xAA);
            glViewport(0, 0, kInvalidateTestSize, kInvalidateTestSize);
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |
                    (clearStencil ? GL_STENCIL_BUFFER_BIT : 0));
            drawQuad(*program, essl1_shaders::PositionAttrib(), 0.5f);
            ASSERT_GL_NO_ERROR();
        }
    
        void setExpectedCountersForInvalidateTest(const rx::vk::PerfCounters &counters,
                                                  uint32_t incrementalRenderPasses,
                                                  uint32_t incrementalDepthClears,
                                                  uint32_t incrementalDepthLoads,
                                                  uint32_t incrementalDepthStores,
                                                  uint32_t incrementalStencilClears,
                                                  uint32_t incrementalStencilLoads,
                                                  uint32_t incrementalStencilStores,
                                                  rx::vk::PerfCounters *expected)
        {
            expected->renderPasses  = counters.renderPasses + incrementalRenderPasses;
            expected->depthClears   = counters.depthClears + incrementalDepthClears;
            expected->depthLoads    = counters.depthLoads + incrementalDepthLoads;
            expected->depthStores   = counters.depthStores + incrementalDepthStores;
            expected->stencilClears = counters.stencilClears + incrementalStencilClears;
            expected->stencilLoads  = counters.stencilLoads + incrementalStencilLoads;
            expected->stencilStores = counters.stencilStores + incrementalStencilStores;
        }
    
        void compareDepthStencilCountersForInvalidateTest(const rx::vk::PerfCounters &counters,
                                                          const rx::vk::PerfCounters &expected)
        {
            EXPECT_EQ(expected.depthClears, counters.depthClears);
            EXPECT_EQ(expected.depthLoads, counters.depthLoads);
            EXPECT_EQ(expected.depthStores, counters.depthStores);
            EXPECT_EQ(expected.stencilClears, counters.stencilClears);
            EXPECT_EQ(expected.stencilLoads, counters.stencilLoads);
            EXPECT_EQ(expected.stencilStores, counters.stencilStores);
        }
    
        void setAndIncrementLoadCountersForInvalidateTest(const rx::vk::PerfCounters &counters,
                                                          uint32_t incrementalDepthLoads,
                                                          uint32_t incrementalStencilLoads,
                                                          rx::vk::PerfCounters *expected)
        {
            expected->depthLoads   = counters.depthLoads + incrementalDepthLoads;
            expected->stencilLoads = counters.stencilLoads + incrementalStencilLoads;
        }
    
        void compareLoadCountersForInvalidateTest(const rx::vk::PerfCounters &counters,
                                                  const rx::vk::PerfCounters &expected)
        {
            EXPECT_EQ(expected.depthLoads, counters.depthLoads);
            EXPECT_EQ(expected.stencilLoads, counters.stencilLoads);
        }
    
        void setExpectedCountersForUnresolveResolveTest(const rx::vk::PerfCounters &counters,
                                                        uint32_t incrementalColorAttachmentUnresolves,
                                                        uint32_t incrementalDepthAttachmentUnresolves,
                                                        uint32_t incrementalStencilAttachmentUnresolves,
                                                        uint32_t incrementalColorAttachmentResolves,
                                                        uint32_t incrementalDepthAttachmentResolves,
                                                        uint32_t incrementalStencilAttachmentResolves,
                                                        rx::vk::PerfCounters *expected)
        {
            expected->colorAttachmentUnresolves =
                counters.colorAttachmentUnresolves + incrementalColorAttachmentUnresolves;
            expected->depthAttachmentUnresolves =
                counters.depthAttachmentUnresolves + incrementalDepthAttachmentUnresolves;
            expected->stencilAttachmentUnresolves =
                counters.stencilAttachmentUnresolves + incrementalStencilAttachmentUnresolves;
            expected->colorAttachmentResolves =
                counters.colorAttachmentResolves + incrementalColorAttachmentResolves;
            expected->depthAttachmentResolves =
                counters.depthAttachmentResolves + incrementalDepthAttachmentResolves;
            expected->stencilAttachmentResolves =
                counters.stencilAttachmentResolves + incrementalStencilAttachmentResolves;
        }
    
        void compareCountersForUnresolveResolveTest(const rx::vk::PerfCounters &counters,
                                                    const rx::vk::PerfCounters &expected)
        {
            EXPECT_EQ(expected.colorAttachmentUnresolves, counters.colorAttachmentUnresolves);
            EXPECT_EQ(expected.depthAttachmentUnresolves, counters.depthAttachmentUnresolves);
            if (counters.stencilAttachmentUnresolves != 0)
            {
                // Allow stencil unresolves to be 0.  If VK_EXT_shader_stencil_export is not supported,
                // stencil unresolve is impossible.
                EXPECT_EQ(expected.stencilAttachmentUnresolves, counters.stencilAttachmentUnresolves);
            }
            EXPECT_EQ(expected.colorAttachmentResolves, counters.colorAttachmentResolves);
            EXPECT_EQ(expected.depthAttachmentResolves, counters.depthAttachmentResolves);
            EXPECT_EQ(expected.stencilAttachmentResolves, counters.stencilAttachmentResolves);
        }
    };
    
    class VulkanPerformanceCounterTest_ES31 : public VulkanPerformanceCounterTest
    {};
    
    // Tests that texture updates to unused textures don't break the RP.
    TEST_P(VulkanPerformanceCounterTest, NewTextureDoesNotBreakRenderPass)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
    
        GLColor kInitialData[4] = {GLColor::red, GLColor::blue, GLColor::green, GLColor::yellow};
    
        // Step 1: Set up a simple 2D Texture rendering loop.
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, kInitialData);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
        auto quadVerts = GetQuadVertices();
    
        GLBuffer vertexBuffer;
        glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
        glBufferData(GL_ARRAY_BUFFER, quadVerts.size() * sizeof(quadVerts[0]), quadVerts.data(),
                     GL_STATIC_DRAW);
    
        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
        glUseProgram(program);
    
        GLint posLoc = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
        ASSERT_NE(-1, posLoc);
    
        glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
        glEnableVertexAttribArray(posLoc);
        ASSERT_GL_NO_ERROR();
    
        glDrawArrays(GL_TRIANGLES, 0, 6);
        ASSERT_GL_NO_ERROR();
        uint32_t expectedRenderPassCount = counters.renderPasses;
    
        // Step 2: Introduce a new 2D Texture with the same Program and Framebuffer.
        GLTexture newTexture;
        glBindTexture(GL_TEXTURE_2D, newTexture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, kInitialData);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
        glDrawArrays(GL_TRIANGLES, 0, 6);
        ASSERT_GL_NO_ERROR();
    
        uint32_t actualRenderPassCount = counters.renderPasses;
        EXPECT_EQ(expectedRenderPassCount, actualRenderPassCount);
    }
    
    // Tests that RGB texture should not break renderpass.
    TEST_P(VulkanPerformanceCounterTest, SampleFromRGBTextureDoesNotBreakRenderPass)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
    
        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
        glUseProgram(program);
        GLint textureLoc = glGetUniformLocation(program, essl1_shaders::Texture2DUniform());
        ASSERT_NE(-1, textureLoc);
    
        GLTexture textureRGBA;
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, textureRGBA);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    
        GLTexture textureRGB;
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, textureRGB);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    
        uint32_t expectedRenderPassCount = counters.renderPasses + 1;
    
        // First draw with textureRGBA which should start the renderpass
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glUniform1i(textureLoc, 0);
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Next draw with textureRGB which should not end the renderpass
        glUniform1i(textureLoc, 1);
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        uint32_t actualRenderPassCount = counters.renderPasses;
        EXPECT_EQ(expectedRenderPassCount, actualRenderPassCount);
    }
    
    // Tests that RGB texture should not break renderpass.
    TEST_P(VulkanPerformanceCounterTest, renderToRGBTextureDoesNotBreakRenderPass)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
    
        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Passthrough(), essl1_shaders::fs::UniformColor());
        glUseProgram(program);
        GLint colorUniformLocation =
            glGetUniformLocation(program, angle::essl1_shaders::ColorUniform());
        ASSERT_NE(-1, colorUniformLocation);
        ASSERT_GL_NO_ERROR();
    
        GLTexture textureRGB;
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, textureRGB);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 256, 256, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    
        GLFramebuffer framebuffer;
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureRGB, 0);
        ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
        ASSERT_GL_NO_ERROR();
    
        uint32_t expectedRenderPassCount = counters.renderPasses + 1;
    
        // Draw into FBO
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
        glClearColor(0.0f, 1.0f, 0.0f, 1.0f);  // clear to green
        glClear(GL_COLOR_BUFFER_BIT);
        glViewport(0, 0, 256, 256);
        glUniform4fv(colorUniformLocation, 1, GLColor::blue.toNormalizedVector().data());
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
    
        uint32_t actualRenderPassCount = counters.renderPasses;
        EXPECT_EQ(expectedRenderPassCount, actualRenderPassCount);
    }
    
    // Tests that changing a Texture's max level hits the descriptor set cache.
    TEST_P(VulkanPerformanceCounterTest, ChangingMaxLevelHitsDescriptorCache)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
    
        GLColor kInitialData[4] = {GLColor::red, GLColor::blue, GLColor::green, GLColor::yellow};
    
        // Step 1: Set up a simple mipped 2D Texture rendering loop.
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, kInitialData);
        glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, kInitialData);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
    
        auto quadVerts = GetQuadVertices();
    
        GLBuffer vertexBuffer;
        glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
        glBufferData(GL_ARRAY_BUFFER, quadVerts.size() * sizeof(quadVerts[0]), quadVerts.data(),
                     GL_STATIC_DRAW);
    
        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
        glUseProgram(program);
    
        GLint posLoc = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
        ASSERT_NE(-1, posLoc);
    
        glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
        glEnableVertexAttribArray(posLoc);
        ASSERT_GL_NO_ERROR();
    
        glDrawArrays(GL_TRIANGLES, 0, 6);
        ASSERT_GL_NO_ERROR();
    
        // Step 2: Change max level and draw.
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
        glDrawArrays(GL_TRIANGLES, 0, 6);
        ASSERT_GL_NO_ERROR();
    
        uint32_t expectedWriteDescriptorSetCount = counters.writeDescriptorSets;
    
        // Step 3: Change max level back to original value and verify we hit the cache.
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
        glDrawArrays(GL_TRIANGLES, 0, 6);
        ASSERT_GL_NO_ERROR();
    
        uint32_t actualWriteDescriptorSetCount = counters.writeDescriptorSets;
        EXPECT_EQ(expectedWriteDescriptorSetCount, actualWriteDescriptorSetCount);
    }
    
    // Tests that two glCopyBufferSubData commands can share a barrier.
    TEST_P(VulkanPerformanceCounterTest, IndependentBufferCopiesShareSingleBarrier)
    {
        constexpr GLint srcDataA[] = {1, 2, 3, 4};
        constexpr GLint srcDataB[] = {5, 6, 7, 8};
    
        // Step 1: Set up four buffers for two copies.
        GLBuffer srcA;
        glBindBuffer(GL_COPY_READ_BUFFER, srcA);
        glBufferData(GL_COPY_READ_BUFFER, sizeof(srcDataA), srcDataA, GL_STATIC_COPY);
    
        GLBuffer dstA;
        glBindBuffer(GL_COPY_WRITE_BUFFER, dstA);
        glBufferData(GL_COPY_WRITE_BUFFER, sizeof(srcDataA[0]) * 2, nullptr, GL_STATIC_COPY);
    
        GLBuffer srcB;
        glBindBuffer(GL_COPY_READ_BUFFER, srcB);
        glBufferData(GL_COPY_READ_BUFFER, sizeof(srcDataB), srcDataB, GL_STATIC_COPY);
    
        GLBuffer dstB;
        glBindBuffer(GL_COPY_WRITE_BUFFER, dstB);
        glBufferData(GL_COPY_WRITE_BUFFER, sizeof(srcDataB[0]) * 2, nullptr, GL_STATIC_COPY);
    
        // We expect that ANGLE generate zero additional command buffers.
        const rx::vk::PerfCounters &counters = hackANGLE();
        uint32_t expectedFlushCount          = counters.flushedOutsideRenderPassCommandBuffers;
    
        // Step 2: Do the two copies.
        glBindBuffer(GL_COPY_READ_BUFFER, srcA);
        glBindBuffer(GL_COPY_WRITE_BUFFER, dstA);
        glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, sizeof(srcDataB[0]), 0,
                            sizeof(srcDataA[0]) * 2);
    
        glBindBuffer(GL_COPY_READ_BUFFER, srcB);
        glBindBuffer(GL_COPY_WRITE_BUFFER, dstB);
        glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, sizeof(srcDataB[0]), 0,
                            sizeof(srcDataB[0]) * 2);
    
        ASSERT_GL_NO_ERROR();
    
        uint32_t actualFlushCount = counters.flushedOutsideRenderPassCommandBuffers;
        EXPECT_EQ(expectedFlushCount, actualFlushCount);
    }
    
    // Test resolving a multisampled texture with blit doesn't break the render pass so a subpass can be
    // used
    TEST_P(VulkanPerformanceCounterTest_ES31, MultisampleResolveWithBlit)
    {
        constexpr int kSize = 16;
        glViewport(0, 0, kSize, kSize);
    
        GLFramebuffer msaaFBO;
        glBindFramebuffer(GL_FRAMEBUFFER, msaaFBO.get());
    
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texture.get());
        glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, kSize, kSize, false);
        ASSERT_GL_NO_ERROR();
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE,
                               texture.get(), 0);
        ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        ANGLE_GL_PROGRAM(gradientProgram, essl31_shaders::vs::Passthrough(),
                         essl31_shaders::fs::RedGreenGradient());
        drawQuad(gradientProgram, essl31_shaders::PositionAttrib(), 0.5f, 1.0f, true);
        ASSERT_GL_NO_ERROR();
    
        // Create another FBO to resolve the multisample buffer into.
        GLTexture resolveTexture;
        GLFramebuffer resolveFBO;
        glBindTexture(GL_TEXTURE_2D, resolveTexture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glBindFramebuffer(GL_FRAMEBUFFER, resolveFBO);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, resolveTexture, 0);
        EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
    
        glBindFramebuffer(GL_READ_FRAMEBUFFER, msaaFBO);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFBO);
        glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
        ASSERT_GL_NO_ERROR();
    
        const rx::vk::PerfCounters &counters = hackANGLE();
        EXPECT_EQ(counters.resolveImageCommands, 0u);
    
        glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFBO);
        constexpr uint8_t kHalfPixelGradient = 256 / kSize / 2;
        EXPECT_PIXEL_NEAR(0, 0, kHalfPixelGradient, kHalfPixelGradient, 0, 255, 1.0);
        EXPECT_PIXEL_NEAR(kSize - 1, 0, 255 - kHalfPixelGradient, kHalfPixelGradient, 0, 255, 1.0);
        EXPECT_PIXEL_NEAR(0, kSize - 1, kHalfPixelGradient, 255 - kHalfPixelGradient, 0, 255, 1.0);
        EXPECT_PIXEL_NEAR(kSize - 1, kSize - 1, 255 - kHalfPixelGradient, 255 - kHalfPixelGradient, 0,
                          255, 1.0);
    }
    
    // Ensures a read-only depth-stencil feedback loop works in a single RenderPass.
    TEST_P(VulkanPerformanceCounterTest, ReadOnlyDepthStencilFeedbackLoopUsesSingleRenderPass)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
    
        constexpr GLsizei kSize = 4;
    
        ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
        ANGLE_GL_PROGRAM(texProgram, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
    
        GLTexture colorTexture;
        glBindTexture(GL_TEXTURE_2D, colorTexture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        setupQuadVertexBuffer(0.5f, 1.0f);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
        glEnableVertexAttribArray(0);
    
        // Set up a depth texture and fill it with an arbitrary initial value.
        GLTexture depthTexture;
        glBindTexture(GL_TEXTURE_2D, depthTexture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, kSize, kSize, 0, GL_DEPTH_COMPONENT,
                     GL_UNSIGNED_INT, nullptr);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glBindTexture(GL_TEXTURE_2D, 0);
    
        GLFramebuffer depthAndColorFBO;
        glBindFramebuffer(GL_FRAMEBUFFER, depthAndColorFBO);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0);
        ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        GLFramebuffer depthOnlyFBO;
        glBindFramebuffer(GL_FRAMEBUFFER, depthOnlyFBO);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0);
        ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Draw to a first FBO to initialize the depth buffer.
        glBindFramebuffer(GL_FRAMEBUFFER, depthOnlyFBO);
        glEnable(GL_DEPTH_TEST);
        glUseProgram(redProgram);
        glDrawArrays(GL_TRIANGLES, 0, 6);
        ASSERT_GL_NO_ERROR();
    
        uint32_t expectedRenderPassCount = counters.renderPasses + 1;
    
        // Start new RenderPass with depth write disabled and no loop.
        glBindFramebuffer(GL_FRAMEBUFFER, depthAndColorFBO);
        glDepthMask(false);
        glDrawArrays(GL_TRIANGLES, 0, 6);
        ASSERT_GL_NO_ERROR();
    
        // Now set up the read-only feedback loop.
        glBindTexture(GL_TEXTURE_2D, depthTexture);
        glUseProgram(texProgram);
        glDrawArrays(GL_TRIANGLES, 0, 6);
        ASSERT_GL_NO_ERROR();
    
        // Tweak the bits to keep it read-only.
        glEnable(GL_DEPTH_TEST);
        glDrawArrays(GL_TRIANGLES, 0, 6);
        ASSERT_GL_NO_ERROR();
    
        // Render with just the depth attachment.
        glUseProgram(redProgram);
        glBindTexture(GL_TEXTURE_2D, 0);
        glDrawArrays(GL_TRIANGLES, 0, 6);
        ASSERT_GL_NO_ERROR();
    
        // Rebind the depth texture.
        glUseProgram(texProgram);
        glDepthMask(GL_FALSE);
        glEnable(GL_DEPTH_TEST);
        glBindTexture(GL_TEXTURE_2D, depthTexture);
        glDrawArrays(GL_TRIANGLES, 0, 6);
        ASSERT_GL_NO_ERROR();
    
        uint32_t actualRenderPassCount = counters.renderPasses;
        EXPECT_EQ(expectedRenderPassCount, actualRenderPassCount);
    
        // Do a final write to depth to make sure we can switch out of read-only mode.
        glBindTexture(GL_TEXTURE_2D, 0);
        glDepthMask(GL_TRUE);
        glDrawArrays(GL_TRIANGLES, 0, 6);
        ASSERT_GL_NO_ERROR();
    }
    
    // Tests that common PUBG MOBILE case does not break render pass, and that counts are correct:
    //
    // - Scenario: invalidate, disable, draw
    TEST_P(VulkanPerformanceCounterTest, InvalidateDisableDraw)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
        rx::vk::PerfCounters expected;
    
        // Expect rpCount+1, depth(Clears+1, Loads+0, Stores+0), stencil(Clears+0, Load+0, Stores+0)
        setExpectedCountersForInvalidateTest(counters, 1, 1, 0, 0, 0, 0, 0, &expected);
    
        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
        GLFramebuffer framebuffer;
        GLTexture texture;
        GLRenderbuffer renderbuffer;
        setupClearAndDrawForInvalidateTest(&program, &framebuffer, &texture, &renderbuffer, false);
    
        // Execute the scenario that this test is for:
    
        // Invalidate (storeOp = DONT_CARE; mContentDefined = false)
        const GLenum discards[] = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT};
        glInvalidateFramebuffer(GL_FRAMEBUFFER, 2, discards);
        ASSERT_GL_NO_ERROR();
    
        // Disable (shouldn't change result)
        glDisable(GL_DEPTH_TEST);
        glDisable(GL_STENCIL_TEST);
    
        // Draw (since disabled, shouldn't change result)
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Ensure that the render pass wasn't broken
        EXPECT_EQ(expected.renderPasses, counters.renderPasses);
    
        // Use swapBuffers and then check how many loads and stores were actually done
        swapBuffers();
        compareDepthStencilCountersForInvalidateTest(counters, expected);
    
        // Start and end another render pass, to check that the load ops are as expected
        setAndIncrementLoadCountersForInvalidateTest(counters, 0, 0, &expected);
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
        swapBuffers();
        compareLoadCountersForInvalidateTest(counters, expected);
    }
    
    // Tests that alternative PUBG MOBILE case does not break render pass, and that counts are correct:
    //
    // - Scenario: disable, invalidate, draw
    TEST_P(VulkanPerformanceCounterTest, DisableInvalidateDraw)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
        rx::vk::PerfCounters expected;
    
        // Expect rpCount+1, depth(Clears+1, Loads+0, Stores+0), stencil(Clears+0, Load+0, Stores+0)
        setExpectedCountersForInvalidateTest(counters, 1, 1, 0, 0, 0, 0, 0, &expected);
    
        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
        GLFramebuffer framebuffer;
        GLTexture texture;
        GLRenderbuffer renderbuffer;
        setupClearAndDrawForInvalidateTest(&program, &framebuffer, &texture, &renderbuffer, false);
    
        // Execute the scenario that this test is for:
    
        // Disable (shouldn't change result)
        glDisable(GL_DEPTH_TEST);
        glDisable(GL_STENCIL_TEST);
    
        // Invalidate (storeOp = DONT_CARE; mContentDefined = false)
        const GLenum discards[] = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT};
        glInvalidateFramebuffer(GL_FRAMEBUFFER, 2, discards);
        ASSERT_GL_NO_ERROR();
    
        // Draw (since disabled, shouldn't change result)
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Ensure that the render pass wasn't broken
        EXPECT_EQ(expected.renderPasses, counters.renderPasses);
    
        // Use swapBuffers and then check how many loads and stores were actually done
        swapBuffers();
        compareDepthStencilCountersForInvalidateTest(counters, expected);
    
        // Start and end another render pass, to check that the load ops are as expected
        setAndIncrementLoadCountersForInvalidateTest(counters, 0, 0, &expected);
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
        swapBuffers();
        compareLoadCountersForInvalidateTest(counters, expected);
    }
    
    // Tests that another case does not break render pass, and that counts are correct:
    //
    // - Scenario: disable, draw, invalidate, enable
    TEST_P(VulkanPerformanceCounterTest, DisableDrawInvalidateEnable)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
        rx::vk::PerfCounters expected;
    
        // Expect rpCount+1, depth(Clears+1, Loads+0, Stores+0), stencil(Clears+0, Load+0, Stores+0)
        setExpectedCountersForInvalidateTest(counters, 1, 1, 0, 0, 0, 0, 0, &expected);
    
        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
        GLFramebuffer framebuffer;
        GLTexture texture;
        GLRenderbuffer renderbuffer;
        setupClearAndDrawForInvalidateTest(&program, &framebuffer, &texture, &renderbuffer, false);
    
        // Execute the scenario that this test is for:
    
        // Note: setupClearAndDrawForInvalidateTest() did an enable and draw
    
        // Disable (since not invalidated, shouldn't change result)
        glDisable(GL_DEPTH_TEST);
        glDisable(GL_STENCIL_TEST);
    
        // Draw (since not invalidated, shouldn't change result)
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Invalidate (should result: in storeOp = DONT_CARE; mContentDefined = false)
        const GLenum discards[] = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT};
        glInvalidateFramebuffer(GL_FRAMEBUFFER, 2, discards);
        ASSERT_GL_NO_ERROR();
    
        // Enable (shouldn't change result)
        glEnable(GL_DEPTH_TEST);
        glEnable(GL_STENCIL_TEST);
        // Note: The above enable calls will be ignored, since no drawing was done to force the enable
        // dirty bit to be processed
    
        // Ensure that the render pass wasn't broken
        EXPECT_EQ(expected.renderPasses, counters.renderPasses);
    
        // Break the render pass by reading back a pixel.
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
    
        // Use swapBuffers and then check how many loads and stores were actually done
        swapBuffers();
        compareDepthStencilCountersForInvalidateTest(counters, expected);
    
        // Start and end another render pass, to check that the load ops are as expected
        setAndIncrementLoadCountersForInvalidateTest(counters, 0, 0, &expected);
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
        swapBuffers();
        compareLoadCountersForInvalidateTest(counters, expected);
    }
    
    // Tests that common TRex case does not break render pass, and that counts are correct:
    //
    // - Scenario: invalidate
    TEST_P(VulkanPerformanceCounterTest, Invalidate)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
        rx::vk::PerfCounters expected;
    
        // Expect rpCount+1, depth(Clears+1, Loads+0, Stores+0), stencil(Clears+0, Load+0, Stores+0)
        setExpectedCountersForInvalidateTest(counters, 1, 1, 0, 0, 0, 0, 0, &expected);
    
        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
        GLFramebuffer framebuffer;
        GLTexture texture;
        GLRenderbuffer renderbuffer;
        setupClearAndDrawForInvalidateTest(&program, &framebuffer, &texture, &renderbuffer, false);
    
        // Execute the scenario that this test is for:
    
        // Invalidate (storeOp = DONT_CARE; mContentDefined = false)
        const GLenum discards[] = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT};
        glInvalidateFramebuffer(GL_FRAMEBUFFER, 2, discards);
        ASSERT_GL_NO_ERROR();
    
        // Ensure that the render pass wasn't broken
        EXPECT_EQ(expected.renderPasses, counters.renderPasses);
    
        // Use swapBuffers and then check how many loads and stores were actually done
        swapBuffers();
        compareDepthStencilCountersForInvalidateTest(counters, expected);
    
        // Start and end another render pass, to check that the load ops are as expected
        setAndIncrementLoadCountersForInvalidateTest(counters, 0, 0, &expected);
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
        swapBuffers();
        compareLoadCountersForInvalidateTest(counters, expected);
    }
    
    // Similar to Invalidate, but uses glInvalidateSubFramebuffer such that the given area covers the
    // whole framebuffer.
    TEST_P(VulkanPerformanceCounterTest, InvalidateSub)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
        rx::vk::PerfCounters expected;
    
        // Expect rpCount+1, depth(Clears+1, Loads+0, Stores+0), stencil(Clears+0, Load+0, Stores+0)
        setExpectedCountersForInvalidateTest(counters, 1, 1, 0, 0, 0, 0, 0, &expected);
    
        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
        GLFramebuffer framebuffer;
        GLTexture texture;
        GLRenderbuffer renderbuffer;
        setupClearAndDrawForInvalidateTest(&program, &framebuffer, &texture, &renderbuffer, false);
    
        // Execute the scenario that this test is for:
    
        // Invalidate (storeOp = DONT_CARE; mContentDefined = false)
        const GLenum discards[] = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT};
        glInvalidateSubFramebuffer(GL_FRAMEBUFFER, 2, discards, -100, -100, kInvalidateTestSize + 200,
                                   kInvalidateTestSize + 200);
        ASSERT_GL_NO_ERROR();
    
        // Ensure that the render pass wasn't broken
        EXPECT_EQ(expected.renderPasses, counters.renderPasses);
    
        // Use swapBuffers and then check how many loads and stores were actually done
        swapBuffers();
        compareDepthStencilCountersForInvalidateTest(counters, expected);
    
        // Start and end another render pass, to check that the load ops are as expected
        setAndIncrementLoadCountersForInvalidateTest(counters, 0, 0, &expected);
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
        swapBuffers();
        compareLoadCountersForInvalidateTest(counters, expected);
    }
    
    // Tests that another case does not break render pass, and that counts are correct:
    //
    // - Scenario: invalidate, draw
    TEST_P(VulkanPerformanceCounterTest, InvalidateDraw)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
        rx::vk::PerfCounters expected;
    
        // Expect rpCount+1, depth(Clears+1, Loads+0, Stores+1), stencil(Clears+0, Load+0, Stores+0)
        setExpectedCountersForInvalidateTest(counters, 1, 1, 0, 1, 0, 0, 0, &expected);
    
        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
        GLFramebuffer framebuffer;
        GLTexture texture;
        GLRenderbuffer renderbuffer;
        setupClearAndDrawForInvalidateTest(&program, &framebuffer, &texture, &renderbuffer, false);
    
        // Execute the scenario that this test is for:
    
        // Invalidate (should result: in storeOp = DONT_CARE; mContentDefined = false)
        const GLenum discards[] = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT};
        glInvalidateFramebuffer(GL_FRAMEBUFFER, 2, discards);
        ASSERT_GL_NO_ERROR();
    
        // Draw (since enabled, should result: in storeOp = STORE; mContentDefined = true)
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Ensure that the render pass wasn't broken
        EXPECT_EQ(expected.renderPasses, counters.renderPasses);
    
        // Use swapBuffers and then check how many loads and stores were actually done
        swapBuffers();
        compareDepthStencilCountersForInvalidateTest(counters, expected);
    
        // Start and end another render pass, to check that the load ops are as expected
        setAndIncrementLoadCountersForInvalidateTest(counters, 1, 0, &expected);
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
        swapBuffers();
        compareLoadCountersForInvalidateTest(counters, expected);
    }
    
    // Tests that another case does not break render pass, and that counts are correct:
    //
    // - Scenario: invalidate, draw, disable
    TEST_P(VulkanPerformanceCounterTest, InvalidateDrawDisable)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
        rx::vk::PerfCounters expected;
    
        // Expect rpCount+1, depth(Clears+1, Loads+0, Stores+1), stencil(Clears+0, Load+0, Stores+0)
        setExpectedCountersForInvalidateTest(counters, 1, 1, 0, 1, 0, 0, 0, &expected);
    
        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
        GLFramebuffer framebuffer;
        GLTexture texture;
        GLRenderbuffer renderbuffer;
        setupClearAndDrawForInvalidateTest(&program, &framebuffer, &texture, &renderbuffer, false);
    
        // Execute the scenario that this test is for:
    
        // Invalidate (should result: in storeOp = DONT_CARE; mContentDefined = false)
        const GLenum discards[] = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT};
        glInvalidateFramebuffer(GL_FRAMEBUFFER, 2, discards);
        ASSERT_GL_NO_ERROR();
    
        // Draw (since enabled, should result: in storeOp = STORE; mContentDefined = true)
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Disable (shouldn't change result)
        glDisable(GL_DEPTH_TEST);
        glDisable(GL_STENCIL_TEST);
        // Note: this draw is just so that the disable dirty bits will be processed
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Ensure that the render pass wasn't broken
        EXPECT_EQ(expected.renderPasses, counters.renderPasses);
    
        // Use swapBuffers and then check how many loads and stores were actually done
        swapBuffers();
        compareDepthStencilCountersForInvalidateTest(counters, expected);
    
        // Start and end another render pass, to check that the load ops are as expected
        setAndIncrementLoadCountersForInvalidateTest(counters, 1, 1, &expected);
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
        swapBuffers();
        compareLoadCountersForInvalidateTest(counters, expected);
    }
    
    // Tests that another case does not break render pass, and that counts are correct:
    //
    // - Scenario: invalidate, disable, draw, enable
    TEST_P(VulkanPerformanceCounterTest, InvalidateDisableDrawEnable)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
        rx::vk::PerfCounters expected;
    
        // Expect rpCount+1, depth(Clears+1, Loads+0, Stores+0), stencil(Clears+0, Load+0, Stores+0)
        setExpectedCountersForInvalidateTest(counters, 1, 1, 0, 0, 0, 0, 0, &expected);
    
        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
        GLFramebuffer framebuffer;
        GLTexture texture;
        GLRenderbuffer renderbuffer;
        setupClearAndDrawForInvalidateTest(&program, &framebuffer, &texture, &renderbuffer, false);
    
        // Execute the scenario that this test is for:
    
        // Invalidate (should result: in storeOp = DONT_CARE; mContentDefined = false)
        const GLenum discards[] = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT};
        glInvalidateFramebuffer(GL_FRAMEBUFFER, 2, discards);
        ASSERT_GL_NO_ERROR();
    
        // Disable (shouldn't change result)
        glDisable(GL_DEPTH_TEST);
        glDisable(GL_STENCIL_TEST);
    
        // Draw (since disabled, shouldn't change result)
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Enable (shouldn't change result)
        glEnable(GL_DEPTH_TEST);
        glEnable(GL_STENCIL_TEST);
        // Note: The above enable calls will be ignored, since no drawing was done to force the enable
        // dirty bit to be processed
    
        // Ensure that the render pass wasn't broken
        EXPECT_EQ(expected.renderPasses, counters.renderPasses);
    
        // Use swapBuffers and then check how many loads and stores were actually done
        swapBuffers();
        compareDepthStencilCountersForInvalidateTest(counters, expected);
    
        // Start and end another render pass, to check that the load ops are as expected
        setAndIncrementLoadCountersForInvalidateTest(counters, 0, 0, &expected);
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
        swapBuffers();
        compareLoadCountersForInvalidateTest(counters, expected);
    }
    
    // Tests that another case does not break render pass, and that counts are correct:
    //
    // - Scenario: invalidate, disable, draw, enable, draw
    TEST_P(VulkanPerformanceCounterTest, InvalidateDisableDrawEnableDraw)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
        rx::vk::PerfCounters expected;
    
        // Expect rpCount+1, depth(Clears+1, Loads+0, Stores+1), stencil(Clears+0, Load+0, Stores+0)
        setExpectedCountersForInvalidateTest(counters, 1, 1, 0, 1, 0, 0, 0, &expected);
    
        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
        GLFramebuffer framebuffer;
        GLTexture texture;
        GLRenderbuffer renderbuffer;
        setupClearAndDrawForInvalidateTest(&program, &framebuffer, &texture, &renderbuffer, false);
    
        // Execute the scenario that this test is for:
    
        // Invalidate (should result: in storeOp = DONT_CARE; mContentDefined = false)
        const GLenum discards[] = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT};
        glInvalidateFramebuffer(GL_FRAMEBUFFER, 2, discards);
        ASSERT_GL_NO_ERROR();
    
        // Disable (shouldn't change result)
        glDisable(GL_DEPTH_TEST);
        glDisable(GL_STENCIL_TEST);
    
        // Draw (since disabled, shouldn't change result)
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Enable (shouldn't change result)
        glEnable(GL_DEPTH_TEST);
        glEnable(GL_STENCIL_TEST);
    
        // Draw (since enabled, should result: in storeOp = STORE; mContentDefined = true)
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Ensure that the render pass wasn't broken
        EXPECT_EQ(expected.renderPasses, counters.renderPasses);
    
        // Use swapBuffers and then check how many loads and stores were actually done
        swapBuffers();
        compareDepthStencilCountersForInvalidateTest(counters, expected);
    
        // Start and end another render pass, to check that the load ops are as expected
        setAndIncrementLoadCountersForInvalidateTest(counters, 1, 1, &expected);
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
        swapBuffers();
        compareLoadCountersForInvalidateTest(counters, expected);
    }
    
    // Tests that another case does not break render pass, and that counts are correct:
    //
    // - Scenario: invalidate, draw, disable, enable
    TEST_P(VulkanPerformanceCounterTest, InvalidateDrawDisableEnable)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
        rx::vk::PerfCounters expected;
    
        // Expect rpCount+1, depth(Clears+1, Loads+0, Stores+1), stencil(Clears+0, Load+0, Stores+0)
        setExpectedCountersForInvalidateTest(counters, 1, 1, 0, 1, 0, 0, 0, &expected);
    
        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
        GLFramebuffer framebuffer;
        GLTexture texture;
        GLRenderbuffer renderbuffer;
        setupClearAndDrawForInvalidateTest(&program, &framebuffer, &texture, &renderbuffer, false);
    
        // Execute the scenario that this test is for:
    
        // Invalidate (should result: in storeOp = DONT_CARE; mContentDefined = false)
        const GLenum discards[] = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT};
        glInvalidateFramebuffer(GL_FRAMEBUFFER, 2, discards);
        ASSERT_GL_NO_ERROR();
    
        // Draw (since enabled, should result: in storeOp = STORE; mContentDefined = true)
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Disable (shouldn't change result)
        glDisable(GL_DEPTH_TEST);
        glDisable(GL_STENCIL_TEST);
        // Note: this draw is just so that the disable dirty bits will be processed
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Enable (shouldn't change result)
        glEnable(GL_DEPTH_TEST);
        glEnable(GL_STENCIL_TEST);
        // Note: The above enable calls will be ignored, since no drawing was done to force the enable
        // dirty bit to be processed
    
        // Ensure that the render pass wasn't broken
        EXPECT_EQ(expected.renderPasses, counters.renderPasses);
    
        // Use swapBuffers and then check how many loads and stores were actually done
        swapBuffers();
        compareDepthStencilCountersForInvalidateTest(counters, expected);
    
        // Start and end another render pass, to check that the load ops are as expected
        setAndIncrementLoadCountersForInvalidateTest(counters, 1, 1, &expected);
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
        swapBuffers();
        compareLoadCountersForInvalidateTest(counters, expected);
    }
    
    // Tests that another case does not break render pass, and that counts are correct:
    //
    // - Scenario: invalidate, draw, disable, enable, invalidate
    TEST_P(VulkanPerformanceCounterTest, InvalidateDrawDisableEnableInvalidate)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
        rx::vk::PerfCounters expected;
    
        // Expect rpCount+1, depth(Clears+1, Loads+0, Stores+0), stencil(Clears+0, Load+0, Stores+0)
        setExpectedCountersForInvalidateTest(counters, 1, 1, 0, 0, 0, 0, 0, &expected);
    
        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
        GLFramebuffer framebuffer;
        GLTexture texture;
        GLRenderbuffer renderbuffer;
        setupClearAndDrawForInvalidateTest(&program, &framebuffer, &texture, &renderbuffer, false);
    
        // Execute the scenario that this test is for:
    
        // Invalidate (should result: in storeOp = DONT_CARE; mContentDefined = false)
        const GLenum discards[] = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT};
        glInvalidateFramebuffer(GL_FRAMEBUFFER, 2, discards);
        ASSERT_GL_NO_ERROR();
    
        // Draw (since enabled, should result: in storeOp = STORE; mContentDefined = true)
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Disable (shouldn't change result)
        glDisable(GL_DEPTH_TEST);
        glDisable(GL_STENCIL_TEST);
        // Note: this draw is just so that the disable dirty bits will be processed
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Enable (shouldn't change result)
        glEnable(GL_DEPTH_TEST);
        glEnable(GL_STENCIL_TEST);
    
        // Invalidate (should result: in storeOp = DONT_CARE; mContentDefined = false)
        glInvalidateFramebuffer(GL_FRAMEBUFFER, 2, discards);
        ASSERT_GL_NO_ERROR();
    
        // Ensure that the render pass wasn't broken
        EXPECT_EQ(expected.renderPasses, counters.renderPasses);
    
        // Use swapBuffers and then check how many loads and stores were actually done
        swapBuffers();
        compareDepthStencilCountersForInvalidateTest(counters, expected);
    
        // Start and end another render pass, to check that the load ops are as expected
        setAndIncrementLoadCountersForInvalidateTest(counters, 0, 0, &expected);
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
        swapBuffers();
        compareLoadCountersForInvalidateTest(counters, expected);
    }
    
    // Tests that another case does not break render pass, and that counts are correct:
    //
    // - Scenario: invalidate, draw, disable, enable, invalidate, draw
    TEST_P(VulkanPerformanceCounterTest, InvalidateDrawDisableEnableInvalidateDraw)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
        rx::vk::PerfCounters expected;
    
        // Expect rpCount+1, depth(Clears+1, Loads+0, Stores+1), stencil(Clears+0, Load+0, Stores+0)
        setExpectedCountersForInvalidateTest(counters, 1, 1, 0, 1, 0, 0, 0, &expected);
    
        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
        GLFramebuffer framebuffer;
        GLTexture texture;
        GLRenderbuffer renderbuffer;
        setupClearAndDrawForInvalidateTest(&program, &framebuffer, &texture, &renderbuffer, false);
    
        // Execute the scenario that this test is for:
    
        // Invalidate (should result: in storeOp = DONT_CARE; mContentDefined = false)
        const GLenum discards[] = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT};
        glInvalidateFramebuffer(GL_FRAMEBUFFER, 2, discards);
        ASSERT_GL_NO_ERROR();
    
        // Draw (since enabled, should result: in storeOp = STORE; mContentDefined = true)
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Disable (shouldn't change result)
        glDisable(GL_DEPTH_TEST);
        glDisable(GL_STENCIL_TEST);
        // Note: this draw is just so that the disable dirty bits will be processed
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Enable (shouldn't change result)
        glEnable(GL_DEPTH_TEST);
        glEnable(GL_STENCIL_TEST);
    
        // Invalidate (should result: in storeOp = DONT_CARE; mContentDefined = false)
        glInvalidateFramebuffer(GL_FRAMEBUFFER, 2, discards);
        ASSERT_GL_NO_ERROR();
    
        // Draw (since enabled, should result: in storeOp = STORE; mContentDefined = true)
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Ensure that the render pass wasn't broken
        EXPECT_EQ(expected.renderPasses, counters.renderPasses);
    
        // Use swapBuffers and then check how many loads and stores were actually done
        swapBuffers();
        compareDepthStencilCountersForInvalidateTest(counters, expected);
    
        // Start and end another render pass, to check that the load ops are as expected
        setAndIncrementLoadCountersForInvalidateTest(counters, 1, 0, &expected);
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
        swapBuffers();
        compareLoadCountersForInvalidateTest(counters, expected);
    }
    
    // Tests that another common (dEQP) case does not break render pass, and that counts are correct:
    //
    // - Scenario: invalidate, disable, enable, draw
    TEST_P(VulkanPerformanceCounterTest, InvalidateDisableEnableDraw)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
        rx::vk::PerfCounters expected;
    
        // Expect rpCount+1, depth(Clears+1, Loads+0, Stores+1), stencil(Clears+0, Load+0, Stores+0)
        setExpectedCountersForInvalidateTest(counters, 1, 1, 0, 1, 0, 0, 0, &expected);
    
        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
        GLFramebuffer framebuffer;
        GLTexture texture;
        GLRenderbuffer renderbuffer;
        setupClearAndDrawForInvalidateTest(&program, &framebuffer, &texture, &renderbuffer, false);
    
        // Execute the scenario that this test is for:
    
        // Invalidate (should result: in storeOp = DONT_CARE; mContentDefined = false)
        const GLenum discards[] = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT};
        glInvalidateFramebuffer(GL_FRAMEBUFFER, 2, discards);
        ASSERT_GL_NO_ERROR();
    
        // Disable (shouldn't change result)
        glDisable(GL_DEPTH_TEST);
        glDisable(GL_STENCIL_TEST);
        // Note: this draw is just so that the disable dirty bits will be processed
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Enable (shouldn't change result)
        glEnable(GL_DEPTH_TEST);
        glEnable(GL_STENCIL_TEST);
    
        // Draw (since enabled, should result: in storeOp = STORE; mContentDefined = true)
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Ensure that the render pass wasn't broken
        EXPECT_EQ(expected.renderPasses, counters.renderPasses);
    
        // Use swapBuffers and then check how many loads and stores were actually done
        swapBuffers();
        compareDepthStencilCountersForInvalidateTest(counters, expected);
    
        // Start and end another render pass, to check that the load ops are as expected
        setAndIncrementLoadCountersForInvalidateTest(counters, 1, 1, &expected);
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
        swapBuffers();
        compareLoadCountersForInvalidateTest(counters, expected);
    }
    
    // Tests that an in renderpass clear after invalidate keeps content stored.
    TEST_P(VulkanPerformanceCounterTest, InvalidateAndClear)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
        rx::vk::PerfCounters expected;
    
        // Expect rpCount+1, depth(Clears+1, Loads+0, Stores+1), stencil(Clears+0, Load+0, Stores+0)
        setExpectedCountersForInvalidateTest(counters, 1, 1, 0, 1, 0, 0, 0, &expected);
    
        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
        GLFramebuffer framebuffer;
        GLTexture texture;
        GLRenderbuffer renderbuffer;
        setupClearAndDrawForInvalidateTest(&program, &framebuffer, &texture, &renderbuffer, false);
    
        // Disable depth test but with depth mask enabled so that clear should still work.
        glDisable(GL_DEPTH_TEST);
        glDepthMask(GL_TRUE);
    
        // Invalidate (should result: in storeOp = DONT_CARE; mContentDefined = false)
        const GLenum discards[] = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT};
        glInvalidateFramebuffer(GL_FRAMEBUFFER, 2, discards);
        ASSERT_GL_NO_ERROR();
    
        // Do in-renderpass clear. This should result in StoreOp=STORE; mContentDefined = true.
        glClearDepthf(1.0f);
        glClear(GL_DEPTH_BUFFER_BIT);
        ASSERT_GL_NO_ERROR();
    
        // Use swapBuffers and then check how many loads and stores were actually done
        swapBuffers();
        compareDepthStencilCountersForInvalidateTest(counters, expected);
    
        // Expect rpCount+1, depth(Clears+0, Loads+1, Stores+1), stencil(Clears+0, Load+0, Stores+0)
        setExpectedCountersForInvalidateTest(counters, 0, 0, 1, 1, 0, 0, 0, &expected);
    
        // Bind FBO again and try to use the depth buffer without clear. This should result in
        // loadOp=LOAD and StoreOP=STORE
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LESS);
        glDisable(GL_STENCIL_TEST);
        ANGLE_GL_PROGRAM(blueProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue());
        // Should pass depth test: (0.5+1.0)/2.0=0.75 < 1.0
        drawQuad(blueProgram, essl1_shaders::PositionAttrib(), 0.5f);
        EXPECT_PIXEL_COLOR_EQ(kInvalidateTestSize / 2, kInvalidateTestSize / 2, GLColor::blue);
        compareDepthStencilCountersForInvalidateTest(counters, expected);
    }
    
    // Tests that the draw path for clear after invalidate and disabling depth/stencil test keeps
    // content stored.
    TEST_P(VulkanPerformanceCounterTest, InvalidateAndMaskedClear)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
        rx::vk::PerfCounters expected;
    
        // Expect rpCount+1, depth(Clears+1, Loads+0, Stores+1), stencil(Clears+1, Load+0, Stores+1)
        setExpectedCountersForInvalidateTest(counters, 1, 1, 0, 1, 1, 0, 1, &expected);
    
        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
        GLFramebuffer framebuffer;
        GLTexture texture;
        GLRenderbuffer renderbuffer;
        setupClearAndDrawForInvalidateTest(&program, &framebuffer, &texture, &renderbuffer, true);
    
        // Invalidate (should result: in storeOp = DONT_CARE; mContentDefined = false)
        const GLenum discards[] = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT};
        glInvalidateFramebuffer(GL_FRAMEBUFFER, 2, discards);
        ASSERT_GL_NO_ERROR();
    
        // Disable depth/stencil test but make stencil masked
        glDisable(GL_DEPTH_TEST);
        glDisable(GL_STENCIL_TEST);
        glDepthMask(GL_TRUE);
        glStencilMask(0xF0);
    
        // Enable scissor for the draw path to be taken.
        glEnable(GL_SCISSOR_TEST);
        glScissor(kInvalidateTestSize / 4, kInvalidateTestSize / 4, kInvalidateTestSize / 2,
                  kInvalidateTestSize / 2);
    
        // Do in-renderpass clear. This should result in StoreOp=STORE
        glClearDepthf(1.0f);
        glClearStencil(0x55);
        glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
        ASSERT_GL_NO_ERROR();
    
        // Use swapBuffers and then check how many loads and stores were actually done
        swapBuffers();
        compareDepthStencilCountersForInvalidateTest(counters, expected);
    
        // Expect rpCount+1, depth(Clears+0, Loads+1, Stores+1), stencil(Clears+0, Load+1, Stores+1)
        setExpectedCountersForInvalidateTest(counters, 0, 0, 1, 1, 0, 1, 1, &expected);
    
        // Bind FBO again and try to use the depth buffer without clear. This should result in
        // loadOp=LOAD and StoreOP=STORE
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LESS);
        glEnable(GL_STENCIL_TEST);
        glStencilFunc(GL_EQUAL, 0x50, 0xF0);
        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
        glStencilMask(0xFF);
        ANGLE_GL_PROGRAM(blueProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue());
        drawQuad(blueProgram, essl1_shaders::PositionAttrib(), 0.95f);
        EXPECT_PIXEL_COLOR_EQ(kInvalidateTestSize / 2, kInvalidateTestSize / 2, GLColor::blue);
        compareDepthStencilCountersForInvalidateTest(counters, expected);
    }
    
    // Tests whether depth-stencil ContentDefined will be correct when:
    //
    // - Scenario: invalidate, detach D/S texture and modify it, attach D/S texture, draw with blend
    TEST_P(VulkanPerformanceCounterTest, InvalidateDetachModifyTexAttachDrawWithBlend)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
        rx::vk::PerfCounters expected;
    
        // Expect rpCount+1, depth(Clears+1, Loads+0, Stores+1), stencil(Clears+0, Load+0, Stores+1)
        setExpectedCountersForInvalidateTest(counters, 1, 1, 0, 0, 0, 0, 0, &expected);
    
        ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
        ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
        GLFramebuffer framebuffer;
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
    
        GLTexture colorTexture;
        glBindTexture(GL_TEXTURE_2D, colorTexture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0);
    
        GLTexture depthStencilTexture;
        glBindTexture(GL_TEXTURE_2D, depthStencilTexture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, 2, 2, 0, GL_DEPTH_STENCIL,
                     GL_UNSIGNED_INT_24_8, nullptr);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
                               depthStencilTexture, 0);
        ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Clear and draw with depth-stencil enabled
        glEnable(GL_DEPTH_TEST);
        glDepthMask(GL_TRUE);
        glDepthFunc(GL_LEQUAL);
        glClearDepthf(0.99f);
        glEnable(GL_STENCIL_TEST);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Invalidate depth & stencil (should result: in storeOp = DONT_CARE; mContentDefined = false)
        const GLenum discards[] = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT};
        glInvalidateFramebuffer(GL_FRAMEBUFFER, 2, discards);
        ASSERT_GL_NO_ERROR();
    
        // Check for the expected number of render passes, expected color, and other expected counters
        EXPECT_EQ(expected.renderPasses, counters.renderPasses);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
        compareDepthStencilCountersForInvalidateTest(counters, expected);
    
        // Detach depth-stencil attachment
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
        ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Modify depth-stencil
        constexpr uint32_t kDepthStencilInitialValue = 0xafffff00;
        uint32_t depthStencilData[4] = {kDepthStencilInitialValue, kDepthStencilInitialValue,
                                        kDepthStencilInitialValue, kDepthStencilInitialValue};
        glBindTexture(GL_TEXTURE_2D, depthStencilTexture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, 2, 2, 0, GL_DEPTH_STENCIL,
                     GL_UNSIGNED_INT_24_8, depthStencilData);
    
        // Re-attach depth-stencil
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
                               depthStencilTexture, 0);
        ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Draw again, showing that the modified depth-stencil value prevents a new color value
        //
        // Expect rpCount+1, depth(Clears+0, Loads+1, Stores+1), stencil(Clears+0, Load+1, Stores+1)
        setExpectedCountersForInvalidateTest(counters, 1, 0, 1, 1, 0, 1, 1, &expected);
        drawQuad(greenProgram, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
        // Check for the expected number of render passes, expected color, and other expected counters
        EXPECT_EQ(expected.renderPasses, counters.renderPasses);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
        compareDepthStencilCountersForInvalidateTest(counters, expected);
    
        // Draw again, using a different depth value, so that the drawing takes place
        //
        // Expect rpCount+1, depth(Clears+0, Loads+1, Stores+1), stencil(Clears+0, Load+1, Stores+1)
        setExpectedCountersForInvalidateTest(counters, 1, 0, 1, 1, 0, 1, 1, &expected);
        drawQuad(greenProgram, essl1_shaders::PositionAttrib(), 0.2f);
        ASSERT_GL_NO_ERROR();
        // Check for the expected number of render passes, expected color, and other expected counters
        EXPECT_EQ(expected.renderPasses, counters.renderPasses);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
        compareDepthStencilCountersForInvalidateTest(counters, expected);
    }
    
    // Tests that a GLRenderbuffer can be deleted before the render pass ends, and that everything
    // still works.
    //
    // - Scenario: invalidate
    TEST_P(VulkanPerformanceCounterTest, InvalidateDrawAndDeleteRenderbuffer)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
        rx::vk::PerfCounters expected;
    
        // Expect rpCount+1, depth(Clears+1, Loads+0, Stores+1), stencil(Clears+0, Load+0, Stores+0)
        setExpectedCountersForInvalidateTest(counters, 1, 1, 0, 1, 0, 0, 0, &expected);
    
        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
        GLFramebuffer framebuffer;
        GLTexture texture;
        {
            // Declare the RAII-based GLRenderbuffer object within this set of curly braces, so that it
            // will be deleted early (at the close-curly-brace)
            GLRenderbuffer renderbuffer;
            setupClearAndDrawForInvalidateTest(&program, &framebuffer, &texture, &renderbuffer, false);
    
            // Invalidate (storeOp = DONT_CARE; mContentDefined = false)
            const GLenum discards[] = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT};
            glInvalidateFramebuffer(GL_FRAMEBUFFER, 2, discards);
            ASSERT_GL_NO_ERROR();
    
            // Draw (since enabled, should result: in storeOp = STORE; mContentDefined = true)
            drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
            ASSERT_GL_NO_ERROR();
    
            // Ensure that the render pass wasn't broken
            EXPECT_EQ(expected.renderPasses, counters.renderPasses);
        }
    
        // The renderbuffer should now be deleted.
    
        // Use swapBuffers and then check how many loads and stores were actually done
        swapBuffers();
        compareDepthStencilCountersForInvalidateTest(counters, expected);
    
        // Start and end another render pass, to check that the load ops are as expected
        setAndIncrementLoadCountersForInvalidateTest(counters, 0, 0, &expected);
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
        swapBuffers();
        compareLoadCountersForInvalidateTest(counters, expected);
    }
    
    // Tests that even if the app clears depth, it should be invalidated if there is no read.
    TEST_P(VulkanPerformanceCounterTest, SwapShouldInvalidateDepthAfterClear)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
    
        ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
    
        // Clear depth.
        glClear(GL_DEPTH_BUFFER_BIT);
    
        // Ensure we never read from depth.
        glDisable(GL_DEPTH_TEST);
    
        // Do one draw, then swap.
        drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        uint32_t expectedDepthClears = counters.depthClears;
    
        swapBuffers();
    
        uint32_t actualDepthClears = counters.depthClears;
        EXPECT_EQ(expectedDepthClears, actualDepthClears);
    }
    
    // Tests that masked color clears don't break the RP.
    TEST_P(VulkanPerformanceCounterTest, MaskedColorClearDoesNotBreakRenderPass)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
    
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        GLFramebuffer framebuffer;
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
        ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
        ASSERT_GL_NO_ERROR();
    
        uint32_t expectedRenderPassCount = counters.renderPasses + 1;
    
        // Mask color channels and clear the framebuffer multiple times.
        glClearColor(0.25f, 0.25f, 0.25f, 0.25f);
        glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_FALSE);
        glClear(GL_COLOR_BUFFER_BIT);
    
        glClearColor(0.5f, 0.5f, 0.5f, 0.5f);
        glColorMask(GL_FALSE, GL_TRUE, GL_FALSE, GL_FALSE);
        glClear(GL_COLOR_BUFFER_BIT);
    
        glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
        glColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_FALSE);
        glClear(GL_COLOR_BUFFER_BIT);
    
        glClearColor(0.75f, 0.75f, 0.75f, 0.75f);
        glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
        glClear(GL_COLOR_BUFFER_BIT);
    
        uint32_t actualRenderPassCount = counters.renderPasses;
        EXPECT_EQ(expectedRenderPassCount, actualRenderPassCount);
    
        EXPECT_PIXEL_NEAR(0, 0, 63, 127, 255, 191, 1);
    }
    
    // Tests that masked color/depth/stencil clears don't break the RP.
    TEST_P(VulkanPerformanceCounterTest, MaskedClearDoesNotBreakRenderPass)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
    
        constexpr GLsizei kSize = 64;
    
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        GLRenderbuffer renderbuffer;
        glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, kSize, kSize);
    
        GLFramebuffer framebuffer;
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
                                  renderbuffer);
        ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
        ASSERT_GL_NO_ERROR();
    
        uint32_t expectedRenderPassCount = counters.renderPasses + 1;
    
        glEnable(GL_DEPTH_TEST);
        glEnable(GL_STENCIL_TEST);
    
        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Passthrough(), essl1_shaders::fs::UniformColor());
        glUseProgram(program);
        GLint colorUniformLocation =
            glGetUniformLocation(program, angle::essl1_shaders::ColorUniform());
        ASSERT_NE(-1, colorUniformLocation);
        ASSERT_GL_NO_ERROR();
    
        // Clear the framebuffer with a draw call to start a render pass.
        glViewport(0, 0, kSize, kSize);
        glDepthFunc(GL_ALWAYS);
        glStencilFunc(GL_ALWAYS, 0x55, 0xFF);
        glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        drawQuad(program, essl1_shaders::PositionAttrib(), 1.0f);
    
        // Issue a masked clear.
        glClearColor(0.25f, 1.0f, 0.25f, 1.25f);
        glClearDepthf(0.0f);
        glClearStencil(0x3F);
        glColorMask(GL_FALSE, GL_TRUE, GL_FALSE, GL_FALSE);
        glStencilMask(0xF0);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
        // Make sure the render pass wasn't broken.
        EXPECT_EQ(expectedRenderPassCount, counters.renderPasses);
    
        // Verify that clear was done correctly.
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::yellow);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::yellow);
        EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::yellow);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::yellow);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::yellow);
    
        glDisable(GL_SCISSOR_TEST);
        glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
        glStencilMask(0xFF);
    
        // Make sure depth = 0.0f, stencil = 0x35
        glDepthFunc(GL_GREATER);
        glStencilFunc(GL_EQUAL, 0x35, 0xFF);
        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
    
        glUniform4f(colorUniformLocation, 0.0f, 0.0f, 1.0f, 1.0f);
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.05f);
        ASSERT_GL_NO_ERROR();
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::blue);
    }
    
    // Tests that clear followed by scissored draw uses loadOp to clear.
    TEST_P(VulkanPerformanceCounterTest, ClearThenScissoredDraw)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
    
        uint32_t expectedRenderPassCount = counters.renderPasses + 1;
        uint32_t expectedDepthClears     = counters.depthClears + 1;
        uint32_t expectedStencilClears   = counters.stencilClears + 1;
    
        constexpr GLsizei kSize = 64;
    
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        GLRenderbuffer renderbuffer;
        glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, kSize, kSize);
    
        GLFramebuffer framebuffer;
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
                                  renderbuffer);
        ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
        ASSERT_GL_NO_ERROR();
    
        // Clear depth/stencil
        glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
        glClearDepthf(1.0f);
        glClearStencil(0x55);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
        // Issue a scissored draw call, expecting depth/stencil to be 1.0 and 0x55.
        glViewport(0, 0, kSize, kSize);
        glScissor(0, 0, kSize / 2, kSize);
        glEnable(GL_SCISSOR_TEST);
    
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LESS);
    
        glEnable(GL_STENCIL_TEST);
        glStencilFunc(GL_EQUAL, 0x55, 0xFF);
        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
        glStencilMask(0xFF);
    
        ANGLE_GL_PROGRAM(drawGreen, essl1_shaders::vs::Passthrough(), essl1_shaders::fs::Green());
        drawQuad(drawGreen, essl1_shaders::PositionAttrib(), 0.95f);
        ASSERT_GL_NO_ERROR();
    
        // Break the render pass.
        GLTexture copyTex;
        glBindTexture(GL_TEXTURE_2D, copyTex);
        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, kSize, kSize, 0);
        ASSERT_GL_NO_ERROR();
    
        // Make sure a single render pass was used and depth/stencil clear used loadOp=CLEAR.
        EXPECT_EQ(expectedRenderPassCount, counters.renderPasses);
        EXPECT_EQ(expectedDepthClears, counters.depthClears);
        EXPECT_EQ(expectedStencilClears, counters.stencilClears);
    
        // Verify correctness.
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2 - 1, 0, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2 - 1, kSize - 1, GLColor::green);
    
        EXPECT_PIXEL_COLOR_EQ(kSize / 2, 0, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize - 1, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::red);
    }
    
    // Tests that scissored clears don't break the RP.
    TEST_P(VulkanPerformanceCounterTest, ScissoredClearDoesNotBreakRenderPass)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
    
        constexpr GLsizei kSize = 64;
    
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        GLRenderbuffer renderbuffer;
        glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, kSize, kSize);
    
        GLFramebuffer framebuffer;
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
                                  renderbuffer);
        ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
        ASSERT_GL_NO_ERROR();
    
        uint32_t expectedRenderPassCount = counters.renderPasses + 1;
    
        glEnable(GL_DEPTH_TEST);
        glEnable(GL_STENCIL_TEST);
    
        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Passthrough(), essl1_shaders::fs::UniformColor());
        glUseProgram(program);
        GLint colorUniformLocation =
            glGetUniformLocation(program, angle::essl1_shaders::ColorUniform());
        ASSERT_NE(-1, colorUniformLocation);
        ASSERT_GL_NO_ERROR();
    
        // Clear the framebuffer with a draw call to start a render pass.
        glViewport(0, 0, kSize, kSize);
        glDepthFunc(GL_ALWAYS);
        glStencilFunc(GL_ALWAYS, 0x55, 0xFF);
        glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        drawQuad(program, essl1_shaders::PositionAttrib(), 1.0f);
    
        // Issue a scissored clear.
        glEnable(GL_SCISSOR_TEST);
        glScissor(kSize / 4, kSize / 4, kSize / 2, kSize / 2);
        glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
        glClearDepthf(0.0f);
        glClearStencil(0x3F);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
        // Make sure the render pass wasn't broken.
        EXPECT_EQ(expectedRenderPassCount, counters.renderPasses);
    
        // Verify that clear was done correctly.
        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 / 4, kSize / 4, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(3 * kSize / 4 - 1, kSize / 4, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(kSize / 4, 3 * kSize / 4 - 1, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(3 * kSize / 4 - 1, 3 * kSize / 4 - 1, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::green);
    
        glDisable(GL_SCISSOR_TEST);
    
        // Make sure the border has depth = 1.0f, stencil = 0x55
        glDepthFunc(GL_LESS);
        glStencilFunc(GL_EQUAL, 0x55, 0xFF);
        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
    
        glUniform4f(colorUniformLocation, 0.0f, 0.0f, 1.0f, 1.0f);
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.95f);
        ASSERT_GL_NO_ERROR();
    
        // Make sure the center has depth = 0.0f, stencil = 0x3F
        glDepthFunc(GL_GREATER);
        glStencilFunc(GL_EQUAL, 0x3F, 0xFF);
        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
    
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 1.0f, 1.0f);
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.05f);
        ASSERT_GL_NO_ERROR();
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::blue);
    
        EXPECT_PIXEL_COLOR_EQ(kSize / 4, kSize / 4, GLColor::magenta);
        EXPECT_PIXEL_COLOR_EQ(3 * kSize / 4 - 1, kSize / 4, GLColor::magenta);
        EXPECT_PIXEL_COLOR_EQ(kSize / 4, 3 * kSize / 4 - 1, GLColor::magenta);
        EXPECT_PIXEL_COLOR_EQ(3 * kSize / 4 - 1, 3 * kSize / 4 - 1, GLColor::magenta);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::magenta);
    }
    
    // Tests that draw buffer change with all color channel mask off should not break renderpass
    TEST_P(VulkanPerformanceCounterTest, DrawbufferChangeWithAllColorMaskDisabled)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
    
        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Passthrough(), essl1_shaders::fs::UniformColor());
        glUseProgram(program);
        GLint colorUniformLocation =
            glGetUniformLocation(program, angle::essl1_shaders::ColorUniform());
        ASSERT_NE(-1, colorUniformLocation);
        ASSERT_GL_NO_ERROR();
    
        GLTexture textureRGBA;
        glBindTexture(GL_TEXTURE_2D, textureRGBA);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    
        GLTexture textureDepth;
        glBindTexture(GL_TEXTURE_2D, textureDepth);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, 64, 64, 0, GL_DEPTH_COMPONENT,
                     GL_UNSIGNED_INT, nullptr);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
        GLFramebuffer framebuffer;
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureRGBA, 0);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, textureDepth, 0);
        ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
        ASSERT_GL_NO_ERROR();
    
        uint32_t expectedRenderPassCount = counters.renderPasses + 1;
    
        // Draw into FBO
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
        glClearColor(0.0f, 1.0f, 0.0f, 1.0f);  // clear to green
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glViewport(0, 0, 256, 256);
        glUniform4fv(colorUniformLocation, 1, GLColor::blue.toNormalizedVector().data());
        GLenum glDrawBuffers_bufs_1[] = {GL_COLOR_ATTACHMENT0};
        glDrawBuffers(1, glDrawBuffers_bufs_1);
        glEnable(GL_DEPTH_TEST);
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
        // Change draw buffer state and color mask
        GLenum glDrawBuffers_bufs_0[] = {GL_NONE};
        glDrawBuffers(1, glDrawBuffers_bufs_0);
        glColorMask(false, false, false, false);
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.6f);
        // Change back draw buffer state and color mask
        glDrawBuffers(1, glDrawBuffers_bufs_1);
        glColorMask(true, true, true, true);
        glUniform4fv(colorUniformLocation, 1, GLColor::red.toNormalizedVector().data());
        drawQuad(program, essl1_shaders::PositionAttrib(), 0.7f);
    
        uint32_t actualRenderPassCount = counters.renderPasses;
        EXPECT_EQ(expectedRenderPassCount, actualRenderPassCount);
    }
    
    // Tests the optimization that a glFlush call issued inside a renderpass will be skipped.
    TEST_P(VulkanPerformanceCounterTest, InRenderpassFlushShouldNotBreakRenderpass)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
        uint32_t expectedRenderPassCount     = counters.renderPasses + 1;
    
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        GLFramebuffer framebuffer;
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
        ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
        ASSERT_GL_NO_ERROR();
    
        ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
        drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.5f);
        glFlush();
        ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
        drawQuad(greenProgram, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        uint32_t actualRenderPassCount = counters.renderPasses;
        EXPECT_EQ(expectedRenderPassCount, actualRenderPassCount);
    }
    
    // Tests that depth/stencil texture clear/load works correctly.
    TEST_P(VulkanPerformanceCounterTest, DepthStencilTextureClearAndLoad)
    {
        // TODO: http://anglebug.com/5329 Flaky test
        ANGLE_SKIP_TEST_IF(IsWindows() && IsAMD() && IsVulkan());
    
        const rx::vk::PerfCounters &counters = hackANGLE();
        uint32_t expectedDepthClearCount     = counters.depthClears + 1;
        uint32_t expectedDepthLoadCount      = counters.depthLoads + 3;
        uint32_t expectedStencilClearCount   = counters.stencilClears + 1;
        uint32_t expectedStencilLoadCount    = counters.stencilLoads + 3;
    
        constexpr GLsizei kSize = 6;
    
        // Create framebuffer to draw into, with both color and depth attachments.
        GLTexture color;
        glBindTexture(GL_TEXTURE_2D, color);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        GLTexture depth;
        glBindTexture(GL_TEXTURE_2D, depth);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, kSize, kSize, 0, GL_DEPTH_STENCIL,
                     GL_UNSIGNED_INT_24_8_OES, nullptr);
    
        GLFramebuffer fbo;
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depth, 0);
        ASSERT_GL_NO_ERROR();
    
        // Set up texture for copy operation that breaks the render pass
        GLTexture copyTex;
        glBindTexture(GL_TEXTURE_2D, copyTex);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        // Set viewport and clear depth/stencil
        glViewport(0, 0, kSize, kSize);
        glClearDepthf(1);
        glClearStencil(0x55);
        glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
        // If depth is not cleared to 1, rendering would fail.
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LESS);
        glDepthMask(GL_FALSE);
    
        // If stencil is not clear to 0x55, rendering would fail.
        glEnable(GL_STENCIL_TEST);
        glStencilFunc(GL_EQUAL, 0x55, 0xFF);
        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
        glStencilMask(0xFF);
    
        // Set up 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);
    
        // Draw red
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.0f);
        ASSERT_GL_NO_ERROR();
    
        // Break the render pass
        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, kSize / 2, kSize / 2);
        ASSERT_GL_NO_ERROR();
    
        // Draw green
        glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.0f);
        ASSERT_GL_NO_ERROR();
    
        // Break the render pass
        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, kSize / 2, 0, 0, 0, kSize / 2, kSize / 2);
        ASSERT_GL_NO_ERROR();
    
        // Draw blue
        glUniform4f(colorUniformLocation, 0.0f, 0.0f, 1.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.0f);
        ASSERT_GL_NO_ERROR();
    
        // Break the render pass
        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, kSize / 2, 0, 0, kSize / 2, kSize / 2);
        ASSERT_GL_NO_ERROR();
    
        // Draw yellow
        glUniform4f(colorUniformLocation, 1.0f, 1.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.0f);
        ASSERT_GL_NO_ERROR();
    
        // Break the render pass
        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, kSize / 2, kSize / 2, 0, 0, kSize / 2, kSize / 2);
        ASSERT_GL_NO_ERROR();
    
        // Verify the counters
        EXPECT_EQ(counters.depthClears, expectedDepthClearCount);
        EXPECT_EQ(counters.depthLoads, expectedDepthLoadCount);
        EXPECT_EQ(counters.stencilClears, expectedStencilClearCount);
        EXPECT_EQ(counters.stencilLoads, expectedStencilLoadCount);
    
        // Verify that copies were done correctly.
        GLFramebuffer verifyFBO;
        glBindFramebuffer(GL_FRAMEBUFFER, verifyFBO);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, copyTex, 0);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2, 0, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(0, kSize / 2, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::yellow);
    }
    
    // Tests that multisampled-render-to-texture depth/stencil textures don't ever load data.
    TEST_P(VulkanPerformanceCounterTest, RenderToTextureDepthStencilTextureShouldNotLoad)
    {
        // http://anglebug.com/5083
        ANGLE_SKIP_TEST_IF(IsWindows() && IsAMD() && IsVulkan());
    
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture2"));
    
        const rx::vk::PerfCounters &counters = hackANGLE();
        uint32_t expectedDepthClearCount     = counters.depthClears + 1;
        uint32_t expectedDepthLoadCount      = counters.depthLoads;
        uint32_t expectedStencilClearCount   = counters.stencilClears + 1;
        uint32_t expectedStencilLoadCount    = counters.stencilLoads;
    
        constexpr GLsizei kSize = 6;
    
        // 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, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        GLTexture depthMS;
        glBindTexture(GL_TEXTURE_2D, depthMS);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, kSize, kSize, 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();
    
        // Set up texture for copy operation that breaks the render pass
        GLTexture copyTex;
        glBindTexture(GL_TEXTURE_2D, copyTex);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        // Set viewport and clear depth
        glViewport(0, 0, kSize, kSize);
        glClearDepthf(1);
        glClearStencil(0x55);
        glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
        // If depth is not cleared to 1, rendering would fail.
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LESS);
    
        // If stencil is not clear to 0x55, rendering would fail.
        glEnable(GL_STENCIL_TEST);
        glStencilFunc(GL_EQUAL, 0x55, 0xFF);
        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
        glStencilMask(0xFF);
    
        // Set up 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);
    
        // Draw red
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.0f);
        ASSERT_GL_NO_ERROR();
    
        // Break the render pass
        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, kSize / 2, kSize / 2);
        ASSERT_GL_NO_ERROR();
    
        // Draw green
        glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.0f);
        ASSERT_GL_NO_ERROR();
    
        // Break the render pass
        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, kSize / 2, 0, 0, 0, kSize / 2, kSize / 2);
        ASSERT_GL_NO_ERROR();
    
        // Draw blue
        glUniform4f(colorUniformLocation, 0.0f, 0.0f, 1.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.0f);
        ASSERT_GL_NO_ERROR();
    
        // Break the render pass
        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, kSize / 2, 0, 0, kSize / 2, kSize / 2);
        ASSERT_GL_NO_ERROR();
    
        // Draw yellow
        glUniform4f(colorUniformLocation, 1.0f, 1.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.0f);
        ASSERT_GL_NO_ERROR();
    
        // Break the render pass
        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, kSize / 2, kSize / 2, 0, 0, kSize / 2, kSize / 2);
        ASSERT_GL_NO_ERROR();
    
        // Verify the counters
        EXPECT_EQ(counters.depthClears, expectedDepthClearCount);
        EXPECT_EQ(counters.depthLoads, expectedDepthLoadCount);
        EXPECT_EQ(counters.stencilClears, expectedStencilClearCount);
        EXPECT_EQ(counters.stencilLoads, expectedStencilLoadCount);
    
        // Verify that copies were done correctly.  Only the first copy can be verified because the
        // contents of the depth/stencil buffer is undefined after the first render pass break, meaning
        // it is unknown whether the three subsequent draw calls passed the depth or stencil tests.
        GLFramebuffer verifyFBO;
        glBindFramebuffer(GL_FRAMEBUFFER, verifyFBO);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, copyTex, 0);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2 - 1, 0, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(0, kSize / 2 - 1, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2 - 1, kSize / 2 - 1, GLColor::red);
    }
    
    // Tests that multisampled-render-to-texture depth/stencil renderbuffers don't ever load depth data.
    // Stencil data may still be loaded if VK_EXT_shader_stencil_export is not supported.
    TEST_P(VulkanPerformanceCounterTest, RenderToTextureDepthStencilRenderbufferShouldNotLoad)
    {
        // http://anglebug.com/5083
        ANGLE_SKIP_TEST_IF(IsWindows() && IsAMD() && IsVulkan());
        // http://anglebug.com/5380
        ANGLE_SKIP_TEST_IF(IsLinux() && IsAMD() && IsVulkan());
    
        // http://crbug.com/1134286
        ANGLE_SKIP_TEST_IF(IsWindows7() && IsNVIDIA() && IsVulkan());
    
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
    
        const rx::vk::PerfCounters &counters = hackANGLE();
        rx::vk::PerfCounters expected;
    
        // This test creates 4 render passes. In the first render pass, color, depth and stencil are
        // cleared.  In the following render passes, they must be loaded.  However, given that the
        // attachments are multisampled-render-to-texture, loads are done through an unresolve
        // operation.  All 4 render passes resolve the attachments.
    
        // Expect rpCount+4, depth(Clears+1, Loads+3, Stores+3), stencil(Clears+1, Load+3, Stores+3).
        // Note that the Loads and Stores are from the resolve attachments.
        setExpectedCountersForInvalidateTest(counters, 4, 1, 3, 3, 1, 3, 3, &expected);
    
        // Additionally, expect 4 resolves and 3 unresolves.
        setExpectedCountersForUnresolveResolveTest(counters, 3, 3, 3, 4, 4, 4, &expected);
    
        constexpr GLsizei kSize = 6;
    
        // 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, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        GLRenderbuffer depthStencilMS;
        glBindRenderbuffer(GL_RENDERBUFFER, depthStencilMS);
        glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, kSize, kSize);
    
        GLFramebuffer fboMS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
        glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                                             colorMS, 0, 4);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
                                  depthStencilMS);
        ASSERT_GL_NO_ERROR();
    
        // Set up texture for copy operation that breaks the render pass
        GLTexture copyTex;
        glBindTexture(GL_TEXTURE_2D, copyTex);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        // Set viewport and clear color, depth and stencil
        glViewport(0, 0, kSize, kSize);
        glClearDepthf(1);
        glClearStencil(0x55);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
        // If depth is not cleared to 1, rendering would fail.
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LESS);
    
        // If stencil is not clear to 0x55, rendering would fail.
        glEnable(GL_STENCIL_TEST);
        glStencilFunc(GL_EQUAL, 0x55, 0xFF);
        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
        glStencilMask(0xFF);
    
        // Set up 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);
    
        // Draw red
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.75f);
        ASSERT_GL_NO_ERROR();
    
        // Break the render pass
        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, kSize / 2, kSize / 2);
        ASSERT_GL_NO_ERROR();
    
        // Draw green
        glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Break the render pass
        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, kSize / 2, 0, 0, 0, kSize / 2, kSize / 2);
        ASSERT_GL_NO_ERROR();
    
        // Draw blue
        glUniform4f(colorUniformLocation, 0.0f, 0.0f, 1.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.25f);
        ASSERT_GL_NO_ERROR();
    
        // Break the render pass
        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, kSize / 2, 0, 0, kSize / 2, kSize / 2);
        ASSERT_GL_NO_ERROR();
    
        // Draw yellow
        glUniform4f(colorUniformLocation, 1.0f, 1.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.0f);
        ASSERT_GL_NO_ERROR();
    
        // Break the render pass
        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, kSize / 2, kSize / 2, 0, 0, kSize / 2, kSize / 2);
        ASSERT_GL_NO_ERROR();
    
        // Verify the counters
        compareLoadCountersForInvalidateTest(counters, expected);
        compareCountersForUnresolveResolveTest(counters, expected);
    
        // Verify that copies were done correctly.
        GLFramebuffer verifyFBO;
        glBindFramebuffer(GL_FRAMEBUFFER, verifyFBO);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, copyTex, 0);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2, 0, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(0, kSize / 2, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::yellow);
    }
    
    // Tests counters when multisampled-render-to-texture color/depth/stencil renderbuffers are
    // invalidated.
    TEST_P(VulkanPerformanceCounterTest, RenderToTextureInvalidate)
    {
        // http://anglebug.com/5083
        ANGLE_SKIP_TEST_IF(IsWindows() && IsAMD() && IsVulkan());
    
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
    
        const rx::vk::PerfCounters &counters = hackANGLE();
        rx::vk::PerfCounters expected;
    
        // This test creates 4 render passes. In the first render pass, color, depth and stencil are
        // cleared.  After every render pass, the attachments are invalidated.  In the following render
        // passes thus they are not loaded (rather unresolved, as the attachments are
        // multisampled-render-to-texture).  Due to the invalidate call, neither of the 4 render passes
        // should resolve the attachments.
    
        // Expect rpCount+4, depth(Clears+1, Loads+0, Stores+0), stencil(Clears+1, Load+0, Stores+0)
        setExpectedCountersForInvalidateTest(counters, 4, 1, 0, 0, 1, 0, 0, &expected);
    
        // Additionally, expect no resolve and unresolve.
        setExpectedCountersForUnresolveResolveTest(counters, 0, 0, 0, 0, 0, 0, &expected);
    
        constexpr GLsizei kSize = 6;
    
        // 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, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        GLRenderbuffer depthStencilMS;
        glBindRenderbuffer(GL_RENDERBUFFER, depthStencilMS);
        glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, kSize, kSize);
    
        GLFramebuffer fboMS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
        glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                                             colorMS, 0, 4);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
                                  depthStencilMS);
        ASSERT_GL_NO_ERROR();
    
        // Set up texture for copy operation that breaks the render pass
        GLTexture copyTex;
        glBindTexture(GL_TEXTURE_2D, copyTex);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        // Set viewport and clear color, depth and stencil
        glViewport(0, 0, kSize, kSize);
        glClearColor(0, 0, 0, 1.0f);
        glClearDepthf(1);
        glClearStencil(0x55);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
        // Output depth/stencil, but disable testing so all draw calls succeed
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_ALWAYS);
    
        glEnable(GL_STENCIL_TEST);
        glStencilFunc(GL_ALWAYS, 0x55, 0xFF);
        glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
        glStencilMask(0xFF);
    
        // Set up 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);
    
        // Draw red
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.75f);
        ASSERT_GL_NO_ERROR();
    
        // Invalidate everything
        const GLenum discards[] = {GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT};
        glInvalidateFramebuffer(GL_FRAMEBUFFER, 3, discards);
    
        // Break the render pass
        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, kSize / 2, kSize / 2);
        ASSERT_GL_NO_ERROR();
    
        // Draw green
        glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Invalidate everything
        glInvalidateFramebuffer(GL_FRAMEBUFFER, 3, discards);
    
        // Break the render pass
        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, kSize / 2, 0, 0, 0, kSize / 2, kSize / 2);
        ASSERT_GL_NO_ERROR();
    
        // Draw blue
        glUniform4f(colorUniformLocation, 0.0f, 0.0f, 1.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.25f);
        ASSERT_GL_NO_ERROR();
    
        // Invalidate everything
        glInvalidateFramebuffer(GL_FRAMEBUFFER, 3, discards);
    
        // Break the render pass
        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, kSize / 2, 0, 0, kSize / 2, kSize / 2);
        ASSERT_GL_NO_ERROR();
    
        // Draw yellow
        glUniform4f(colorUniformLocation, 1.0f, 1.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.0f);
        ASSERT_GL_NO_ERROR();
    
        // Invalidate everything
        glInvalidateFramebuffer(GL_FRAMEBUFFER, 3, discards);
    
        // Break the render pass
        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, kSize / 2, kSize / 2, 0, 0, kSize / 2, kSize / 2);
        ASSERT_GL_NO_ERROR();
    
        // Verify the counters
        compareLoadCountersForInvalidateTest(counters, expected);
        compareCountersForUnresolveResolveTest(counters, expected);
    }
    
    // Tests counters when uninitialized multisampled-render-to-texture depth/stencil renderbuffers are
    // unused but not invalidated.
    TEST_P(VulkanPerformanceCounterTest, RenderToTextureUninitializedAndUnusedDepthStencil)
    {
        // http://anglebug.com/5083
        ANGLE_SKIP_TEST_IF(IsWindows() && IsAMD() && IsVulkan());
    
        ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
    
        const rx::vk::PerfCounters &counters = hackANGLE();
        rx::vk::PerfCounters expected;
    
        // Expect rpCount+1, no depth/stencil clear, load or store.
        setExpectedCountersForInvalidateTest(counters, 1, 0, 0, 0, 0, 0, 0, &expected);
    
        // Additionally, expect only color resolve.
        setExpectedCountersForUnresolveResolveTest(counters, 0, 0, 0, 1, 0, 0, &expected);
    
        constexpr GLsizei kSize = 6;
    
        // 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, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        GLRenderbuffer depthStencilMS;
        glBindRenderbuffer(GL_RENDERBUFFER, depthStencilMS);
        glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, kSize, kSize);
    
        GLFramebuffer fboMS;
        glBindFramebuffer(GL_FRAMEBUFFER, fboMS);
        glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                                             colorMS, 0, 4);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
                                  depthStencilMS);
        ASSERT_GL_NO_ERROR();
    
        // Set up texture for copy operation that breaks the render pass
        GLTexture copyTex;
        glBindTexture(GL_TEXTURE_2D, copyTex);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        // Set viewport and clear color only
        glViewport(0, 0, kSize, kSize);
        glClearColor(0, 0, 0, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Disable depth/stencil testing.
        glDisable(GL_DEPTH_TEST);
        glDisable(GL_STENCIL_TEST);
    
        // Set up 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);
    
        // Draw red
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.75f);
        ASSERT_GL_NO_ERROR();
    
        // Break the render pass
        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, kSize / 2, kSize / 2);
        ASSERT_GL_NO_ERROR();
    
        // Verify the counters
        compareLoadCountersForInvalidateTest(counters, expected);
        compareCountersForUnresolveResolveTest(counters, expected);
    }
    
    // Ensures we use read-only depth layout when there is no write
    TEST_P(VulkanPerformanceCounterTest, ReadOnlyDepthBufferLayout)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
    
        constexpr GLsizei kSize = 64;
    
        // Create depth only FBO and fill depth texture to leftHalf=0.0 and rightHalf=1.0. This should
        // use writeable layout
        uint32_t expectedReadOnlyDepthStencilCount = counters.readOnlyDepthStencilRenderPasses;
        GLTexture depthTexture;
        glBindTexture(GL_TEXTURE_2D, depthTexture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, kSize, kSize, 0, GL_DEPTH_COMPONENT,
                     GL_UNSIGNED_INT, nullptr);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
        GLFramebuffer depthOnlyFBO;
        glBindFramebuffer(GL_FRAMEBUFFER, depthOnlyFBO);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0);
        ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_ALWAYS);
        glDepthMask(GL_TRUE);
        ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
        glViewport(0, 0, kSize / 2, kSize);
        drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.0f);
        glViewport(kSize / 2, 0, kSize / 2, kSize);
        drawQuad(redProgram, essl1_shaders::PositionAttrib(), 1.0f);
        glViewport(0, 0, kSize, kSize);
        ASSERT_GL_NO_ERROR();
    
        // Because the layout counter is updated at end of renderpass, we need to issue a finish call
        // here to end the renderpass.
        glFinish();
    
        uint32_t actualReadOnlyDepthStencilCount = counters.readOnlyDepthStencilRenderPasses;
        EXPECT_EQ(expectedReadOnlyDepthStencilCount, actualReadOnlyDepthStencilCount);
    
        // Create a color+depth FBO and use depth as read only. This should use read only layout
        expectedReadOnlyDepthStencilCount = counters.readOnlyDepthStencilRenderPasses + 1;
        GLTexture colorTexture;
        glBindTexture(GL_TEXTURE_2D, colorTexture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
        GLFramebuffer depthAndColorFBO;
        glBindFramebuffer(GL_FRAMEBUFFER, depthAndColorFBO);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0);
        ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
    
        // Clear color to blue and draw a green quad with depth=0.5
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LESS);
        glDepthMask(GL_FALSE);
        GLfloat *clearColor = GLColor::blue.toNormalizedVector().data();
        glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
        glClear(GL_COLOR_BUFFER_BIT);
        drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.5f);
        ASSERT_GL_NO_ERROR();
        // The pixel check will end renderpass.
        EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(1 + kSize / 2, 1, GLColor::red);
        actualReadOnlyDepthStencilCount = counters.readOnlyDepthStencilRenderPasses;
        EXPECT_EQ(expectedReadOnlyDepthStencilCount, actualReadOnlyDepthStencilCount);
    }
    
    // Ensures depth/stencil is not loaded after storeOp=DONT_CARE due to optimization (as opposed to
    // invalidate)
    TEST_P(VulkanPerformanceCounterTest, RenderPassAfterRenderPassWithoutDepthStencilWrite)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
        rx::vk::PerfCounters expected;
    
        // Expect rpCount+1, depth(Clears+0, Loads+0, Stores+0), stencil(Clears+0, Load+0, Stores+0)
        setExpectedCountersForInvalidateTest(counters, 1, 0, 0, 0, 0, 0, 0, &expected);
    
        constexpr GLsizei kSize = 64;
    
        // Create FBO with color, depth and stencil.  Leave depth/stencil uninitialized.
        GLTexture texture;
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        GLRenderbuffer renderbuffer;
        glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, kSize, kSize);
    
        GLFramebuffer framebuffer;
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
                                  renderbuffer);
        ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
        ASSERT_GL_NO_ERROR();
    
        // Draw to the FBO, without enabling depth/stencil.
        ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Passthrough(), essl1_shaders::fs::UniformColor());
        glUseProgram(program);
        GLint colorUniformLocation =
            glGetUniformLocation(program, angle::essl1_shaders::ColorUniform());
        ASSERT_NE(-1, colorUniformLocation);
        ASSERT_GL_NO_ERROR();
    
        glViewport(0, 0, kSize, kSize);
        glUniform4f(colorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        drawQuad(program, essl1_shaders::PositionAttrib(), 1.0f);
    
        // Break the render pass and ensure no depth/stencil load/store was done.
        swapBuffers();
        compareDepthStencilCountersForInvalidateTest(counters, expected);
    
        // Expect rpCount+1, depth(Clears+0, Loads+0, Stores+0), stencil(Clears+0, Load+0, Stores+0)
        setExpectedCountersForInvalidateTest(counters, 1, 0, 0, 0, 0, 0, 0, &expected);
    
        // Draw again with similar conditions, and again make sure no load/store is done.
        glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f);
        drawQuad(program, essl1_shaders::PositionAttrib(), 1.0f);
    
        // Break the render pass and ensure no depth/stencil load/store was done.
        swapBuffers();
        compareDepthStencilCountersForInvalidateTest(counters, expected);
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }
    
    // Ensures repeated clears of various kind (all attachments, some attachments, scissored, masked
    // etc) don't break the render pass.
    TEST_P(VulkanPerformanceCounterTest, ClearAfterClearDoesNotBreakRenderPass)
    {
        const rx::vk::PerfCounters &counters = hackANGLE();
        uint32_t expectedRenderPassCount     = counters.renderPasses + 1;
    
        constexpr GLsizei kSize = 6;
    
        // Create a framebuffer to clear with both color and depth/stencil attachments.
        GLTexture color;
        glBindTexture(GL_TEXTURE_2D, color);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        GLTexture depth;
        glBindTexture(GL_TEXTURE_2D, depth);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, kSize, kSize, 0, GL_DEPTH_STENCIL,
                     GL_UNSIGNED_INT_24_8_OES, nullptr);
    
        GLFramebuffer fbo;
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depth, 0);
        ASSERT_GL_NO_ERROR();
    
        // Clear color and depth, but not stencil.
        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        glClearDepthf(0.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
        // Clear color and stencil, but not depth.
        glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
        glClearStencil(0x11);
        glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
        // Clear depth and stencil, but not color.
        glClearDepthf(0.1f);
        glClearStencil(0x22);
        glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
        // Clear masked color, and unmasked depth.
        glClearDepthf(0.2f);
        glClearColor(0.1f, 1.0f, 0.0f, 1.0f);
        glColorMask(GL_FALSE, GL_TRUE, GL_FALSE, GL_FALSE);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
        // Clear unmasked color, and masked stencil.
        glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
        glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
        glClearStencil(0x33);
        glStencilMask(0xF0);
        glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
        // Clear unmasked depth and stencil.
        glClearDepthf(0.3f);
        glClearStencil(0x44);
        glStencilMask(0xFF);
        glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
        // Clear with scissor.
        glEnable(GL_SCISSOR_TEST);
        glScissor(kSize / 3, kSize / 3, kSize / 3, kSize / 3);
        glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
        glClearDepthf(1.0f);
        glClearStencil(0x55);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
        // Verify render pass count.
        EXPECT_EQ(counters.renderPasses, expectedRenderPassCount);
    
        // Make sure the result is correct.  The border of the image should be blue with depth 0.3f and
        // stencil 0x44.  The center is red with depth 1.0f and stencil 0x55.
    
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::blue);
    
        EXPECT_PIXEL_COLOR_EQ(kSize / 3, kSize / 3, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(2 * kSize / 3 - 1, kSize / 3, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kSize / 3, 2 * kSize / 3 - 1, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(2 * kSize / 3 - 1, 2 * kSize / 3 - 1, GLColor::red);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::red);
    
        glViewport(0, 0, kSize, kSize);
        glDisable(GL_SCISSOR_TEST);
    
        // Center: If depth is not cleared to 1, rendering would fail.
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LESS);
    
        // Center: If stencil is not clear to 0x55, rendering would fail.
        glEnable(GL_STENCIL_TEST);
        glStencilFunc(GL_EQUAL, 0x55, 0xFF);
        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
        glStencilMask(0xFF);
    
        // Set up 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);
    
        // Draw green
        glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.95f);
        ASSERT_GL_NO_ERROR();
    
        // Verify that only the center has changed
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::blue);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::blue);
    
        EXPECT_PIXEL_COLOR_EQ(kSize / 3, kSize / 3, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(2 * kSize / 3 - 1, kSize / 3, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(kSize / 3, 2 * kSize / 3 - 1, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(2 * kSize / 3 - 1, 2 * kSize / 3 - 1, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::green);
    
        // Border: If depth is not cleared to 0.3f, rendering would fail.
        glDepthFunc(GL_LESS);
    
        // Center: If stencil is not clear to 0x44, rendering would fail.
        glStencilFunc(GL_EQUAL, 0x44, 0xFF);
    
        // Draw yellow
        glUniform4f(colorUniformLocation, 1.0f, 1.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), -0.5f);
        ASSERT_GL_NO_ERROR();
    
        // Verify that only the border has changed
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::yellow);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::yellow);
        EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::yellow);
        EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::yellow);
    
        EXPECT_PIXEL_COLOR_EQ(kSize / 3, kSize / 3, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(2 * kSize / 3 - 1, kSize / 3, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(kSize / 3, 2 * kSize / 3 - 1, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(2 * kSize / 3 - 1, 2 * kSize / 3 - 1, GLColor::green);
        EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, GLColor::green);
    }
    
    // Ensures that changing the scissor size doesn't break the render pass.
    TEST_P(VulkanPerformanceCounterTest, ScissorDoesNotBreakRenderPass)
    {
        constexpr GLsizei kSize = 16;
    
        // Create a framebuffer with a color attachment.
        GLTexture color;
        glBindTexture(GL_TEXTURE_2D, color);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
    
        GLFramebuffer fbo;
        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0);
        ASSERT_GL_NO_ERROR();
    
        // First, issue a clear and make sure it's done.  Later we can verify that areas outside
        // scissors are not rendered to.
        glClearColor(0, 0, 0, 1);
        glClear(GL_COLOR_BUFFER_BIT);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black);
    
        const rx::vk::PerfCounters &counters = hackANGLE();
        uint32_t expectedRenderPassCount     = counters.renderPasses + 1;
    
        // This test starts with a small scissor and gradually grows it and issues draw calls and
        // various kinds of clears:
        //
        // - Clear the center to red
        //
        //     +----------------------+
        //     | K                    |
        //     |                      |
        //     |       +-----+        |
        //     |       |     |        |
        //     |       |  R  |        |
        //     |       |     |        |
        //     |       +-----+        |
        //     |                      |
        //     |                      |
        //     |                      |
        //     |                      |
        //     +----------------------+
        //
        // - Draw green to center right
        //
        //     +----------------------+
        //     |                      |
        //     |              +-------+
        //     |       +-----+|       |
        //     |       |     ||       |
        //     |       |  R  ||  G    |
        //     |       |     ||       |
        //     |       +-----+|       |
        //     |              |       |
        //     |              |       |
        //     |              |       |
        //     |              +-------+
        //     +----------------------+
        //
        // - Masked clear of center column, only outputting to the blue channel
        //
        //     +----------------------+
        //     |      +---+           |
        //     |      | B |   +-------+
        //     |      |+--+--+|       |
        //     |      ||  |  ||       |
        //     |      ||M |R ||  G    |
        //     |      ||  |  ||       |
        //     |      |+--+--+|       |
        //     |      |   |   |       |
        //     |      |   |   |       |
        //     |      |   |   |       |
        //     |      |   |   +-------+
        //     +------+---+-----------+
        //
        // - Masked draw of center row, only outputting to alpha.
        //
        //     +----------------------+
        //     | K    +---+ K         |
        //     |      | B |   +-------+
        //     |      |+--+--+|       |
        //     |      ||M |R ||   G   |
        //     | +----++--+--++-----+ |
        //     | |    ||TM|TR||     | |
        //     | | TK |+--+--+|  TG | |
        //     | |    |TB |TK |     | |
        //     | +----+---+---+-----+ |
        //     |      |   |   |   G   |
        //     | K    | B | K +-------+
        //     +------+---+-----------+
        //
        // Where: K=Black, R=Red, G=Green, B=Blue, M=Magenta, T=Transparent
    
        constexpr GLsizei kClearX      = kSize / 3;
        constexpr GLsizei kClearY      = kSize / 3;
        constexpr GLsizei kClearWidth  = kSize / 3;
        constexpr GLsizei kClearHeight = kSize / 3;
    
        constexpr GLsizei kDrawX      = kClearX + kClearWidth + 2;
        constexpr GLsizei kDrawY      = kSize / 5;
        constexpr GLsizei kDrawWidth  = kSize - kDrawX;
        constexpr GLsizei kDrawHeight = 7 * kSize / 10;
    
        constexpr GLsizei kMaskedClearX      = kSize / 4;
        constexpr GLsizei kMaskedClearY      = kSize / 8;
        constexpr GLsizei kMaskedClearWidth  = kSize / 4;
        constexpr GLsizei kMaskedClearHeight = 7 * kSize / 8;
    
        constexpr GLsizei kMaskedDrawX      = kSize / 8;
        constexpr GLsizei kMaskedDrawY      = kSize / 2;
        constexpr GLsizei kMaskedDrawWidth  = 6 * kSize / 8;
        constexpr GLsizei kMaskedDrawHeight = kSize / 4;
    
        glEnable(GL_SCISSOR_TEST);
    
        // Clear center to red
        glScissor(kClearX, kClearY, kClearWidth, kClearHeight);
        glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
    
        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);
    
        // Draw green to center right
        glScissor(kDrawX, kDrawY, kDrawWidth, kDrawHeight);
        glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0);
        ASSERT_GL_NO_ERROR();
    
        // Masked blue-channel clear of center column
        glColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_FALSE);
        glScissor(kMaskedClearX, kMaskedClearY, kMaskedClearWidth, kMaskedClearHeight);
        glClearColor(0.5f, 0.5f, 1.0f, 0.5f);
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Masked alpha-channel draw of center row
        glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
        glScissor(kMaskedDrawX, kMaskedDrawY, kMaskedDrawWidth, kMaskedDrawHeight);
        glUniform4f(colorUniformLocation, 0.5f, 0.5f, 0.5f, 0.0f);
        drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0);
        ASSERT_GL_NO_ERROR();
    
        // Verify render pass count.
        EXPECT_EQ(counters.renderPasses, expectedRenderPassCount);
    
        // Make sure the result is correct:
        //
        //     +----------------------+  <-- 0
        //     | K    +---+ K         |  <-- kMaskedClearY
        //     |      | B |   +-------+  <-- kDrawY
        //     |      |+--+--+|       |  <-- kClearY
        //     |      ||M |R ||   G   |
        //     | +----++--+--++-----+ |  <-- kMaskedDrawY
        //     | |    ||TM|TR||     | |
        //     | | TK |+--+--+|  TG | |  <-- kClearY + kClearHeight
        //     | |    |TB |TK |     | |
        //     | +----+---+---+-----+ |  <-- kMaskedDrawY + kMaskedDrawHeight
        //     |      |   |   |   G   |
        //     | K    | B | K +-------+  <-- kDrawY + kDrawHeight
        //     +------+---+-----------+  <-- kSize == kMaskedClearY + kMaskedClearHeight
        //     | |    ||  |  ||     | |
        //     | |    ||  |  ||     |  \---> kSize == kDrawX + kDrawWidth
        //     | |    ||  |  ||      \-----> kMaskedDrawX + kMaskedDrawWidth
        //     | |    ||  |  | \-----------> kDrawX
        //     | |    ||  |   \------------> kClearX + kClearWidth
        //     | |    ||   \---------------> kMaskedClearX + kMaskedClearWidth
        //     | |    | \------------------> kClearX
        //     | |     \-------------------> kMaskedClearX
        //     |  \------------------------> kMaskedDrawX
        //      \--------------------------> 0
    
        constexpr GLsizei kClearX2       = kClearX + kClearWidth;
        constexpr GLsizei kClearY2       = kClearY + kClearHeight;
        constexpr GLsizei kDrawX2        = kDrawX + kDrawWidth;
        constexpr GLsizei kDrawY2        = kDrawY + kDrawHeight;
        constexpr GLsizei kMaskedClearX2 = kMaskedClearX + kMaskedClearWidth;
        constexpr GLsizei kMaskedClearY2 = kMaskedClearY + kMaskedClearHeight;
        constexpr GLsizei kMaskedDrawX2  = kMaskedDrawX + kMaskedDrawWidth;
        constexpr GLsizei kMaskedDrawY2  = kMaskedDrawY + kMaskedDrawHeight;
    
        constexpr GLColor kTransparentRed(255, 0, 0, 0);
        constexpr GLColor kTransparentGreen(0, 255, 0, 0);
        constexpr GLColor kTransparentBlue(0, 0, 255, 0);
        constexpr GLColor kTransparentMagenta(255, 0, 255, 0);
    
        // Verify the black areas.
        EXPECT_PIXEL_RECT_EQ(0, 0, kMaskedClearX, kMaskedDrawY, GLColor::black);
        EXPECT_PIXEL_RECT_EQ(0, kMaskedDrawY2, kMaskedClearX, kSize - kMaskedDrawY2, GLColor::black);
        EXPECT_PIXEL_RECT_EQ(kMaskedClearX2, 0, kSize - kMaskedClearX2, kDrawY, GLColor::black);
        EXPECT_PIXEL_RECT_EQ(kMaskedClearX2, kDrawY2, kSize - kMaskedClearX2, kSize - kDrawY2,
                             GLColor::black);
        EXPECT_PIXEL_RECT_EQ(kMaskedClearX, 0, kMaskedClearWidth, kMaskedClearY, GLColor::black);
        EXPECT_PIXEL_RECT_EQ(kMaskedClearX2, kDrawY, kDrawX - kMaskedClearX2, kClearY - kDrawY,
                             GLColor::black);
        EXPECT_PIXEL_RECT_EQ(kClearX2, kClearY, kDrawX - kClearX2, kMaskedDrawY - kClearY,
                             GLColor::black);
        EXPECT_PIXEL_RECT_EQ(0, kMaskedDrawY, kMaskedDrawX, kMaskedDrawHeight, GLColor::black);
        EXPECT_PIXEL_RECT_EQ(kMaskedClearX2, kMaskedDrawY2, kDrawX - kMaskedClearX2,
                             kSize - kMaskedDrawY2, GLColor::black);
    
        // Verify the red area:
        EXPECT_PIXEL_RECT_EQ(kMaskedClearX2, kClearY, kClearX2 - kMaskedClearX2, kMaskedDrawY - kClearY,
                             GLColor::red);
        // Verify the transparent red area:
        EXPECT_PIXEL_RECT_EQ(kMaskedClearX2, kMaskedDrawY, kClearX2 - kMaskedClearX2,
                             kClearY2 - kMaskedDrawY, kTransparentRed);
        // Verify the magenta area:
        EXPECT_PIXEL_RECT_EQ(kClearX, kClearY, kMaskedClearX2 - kClearX, kMaskedDrawY - kClearY,
                             GLColor::magenta);
        // Verify the transparent magenta area:
        EXPECT_PIXEL_RECT_EQ(kClearX, kMaskedDrawY, kMaskedClearX2 - kClearX, kClearY2 - kMaskedDrawY,
                             kTransparentMagenta);
        // Verify the green area:
        EXPECT_PIXEL_RECT_EQ(kDrawX, kDrawY, kDrawWidth, kMaskedDrawY - kDrawY, GLColor::green);
        EXPECT_PIXEL_RECT_EQ(kDrawX, kMaskedDrawY2, kDrawWidth, kDrawY2 - kMaskedDrawY2,
                             GLColor::green);
        EXPECT_PIXEL_RECT_EQ(kMaskedDrawX2, kMaskedDrawY, kDrawX2 - kMaskedDrawX2, kMaskedDrawHeight,
                             GLColor::green);
        // Verify the transparent green area:
        EXPECT_PIXEL_RECT_EQ(kDrawX, kMaskedDrawY, kMaskedDrawX2 - kDrawX, kMaskedDrawHeight,
                             kTransparentGreen);
        // Verify the blue area:
        EXPECT_PIXEL_RECT_EQ(kMaskedClearX, kMaskedClearY, kMaskedClearWidth, kClearY - kMaskedClearY,
                             GLColor::blue);
        EXPECT_PIXEL_RECT_EQ(kMaskedClearX, kMaskedDrawY2, kMaskedClearWidth,
                             kMaskedClearY2 - kMaskedDrawY2, GLColor::blue);
        EXPECT_PIXEL_RECT_EQ(kMaskedClearX, kClearY, kClearX - kMaskedClearX, kMaskedDrawY - kClearY,
                             GLColor::blue);
        // Verify the transparent blue area:
        EXPECT_PIXEL_RECT_EQ(kMaskedClearX, kClearY2, kMaskedClearWidth, kMaskedDrawY2 - kClearY2,
                             kTransparentBlue);
        EXPECT_PIXEL_RECT_EQ(kMaskedClearX, kMaskedDrawY, kClearX - kMaskedClearX,
                             kClearY2 - kMaskedDrawY, kTransparentBlue);
        // Verify the transparent black area:
        EXPECT_PIXEL_RECT_EQ(kMaskedDrawX, kMaskedDrawY, kMaskedClearX - kMaskedDrawX,
                             kMaskedDrawHeight, GLColor::transparentBlack);
        EXPECT_PIXEL_RECT_EQ(kMaskedClearX2, kClearY2, kDrawX - kMaskedClearX2,
                             kMaskedDrawY2 - kClearY2, GLColor::transparentBlack);
        EXPECT_PIXEL_RECT_EQ(kClearX2, kMaskedDrawY, kDrawX - kClearX2, kMaskedDrawHeight,
                             GLColor::transparentBlack);
    }
    
    // Tests that changing UBO bindings does not allocate new descriptor sets.
    TEST_P(VulkanPerformanceCounterTest, ChangingUBOsHitsDescriptorSetCache)
    {
        // Set up two UBOs, one filled with "1" and the second with "2".
        constexpr GLsizei kCount = 64;
        std::vector<GLint> data1(kCount, 1);
        std::vector<GLint> data2(kCount, 2);
    
        GLBuffer ubo1;
        glBindBuffer(GL_UNIFORM_BUFFER, ubo1);
        glBufferData(GL_UNIFORM_BUFFER, kCount * sizeof(data1[0]), data1.data(), GL_STATIC_DRAW);
    
        GLBuffer ubo2;
        glBindBuffer(GL_UNIFORM_BUFFER, ubo2);
        glBufferData(GL_UNIFORM_BUFFER, kCount * sizeof(data2[0]), data2.data(), GL_STATIC_DRAW);
    
        // Set up a program that verifies the contents of uniform blocks.
        constexpr char kVS[] = R"(#version 300 es
    precision mediump float;
    in vec4 position;
    void main()
    {
        gl_Position = position;
    })";
    
        constexpr char kFS[] = R"(#version 300 es
    precision mediump float;
    uniform buf {
        int data[64/4];
    };
    uniform int checkValue;
    out vec4 outColor;
    
    void main()
    {
        for (int i = 0; i < 64/4; ++i) {
            if (data[i] != checkValue) {
                outColor = vec4(1, 0, 0, 1);
                return;
            }
        }
        outColor = vec4(0, 1, 0, 1);
    })";
    
        ANGLE_GL_PROGRAM(program, kVS, kFS);
        glUseProgram(program);
        ASSERT_GL_NO_ERROR();
    
        GLint uniLoc = glGetUniformLocation(program, "checkValue");
        ASSERT_NE(-1, uniLoc);
    
        GLuint blockIndex = glGetUniformBlockIndex(program, "buf");
        ASSERT_NE(blockIndex, GL_INVALID_INDEX);
    
        glUniformBlockBinding(program, blockIndex, 0);
        ASSERT_GL_NO_ERROR();
    
        // Set up the rest of the GL state.
        auto quadVerts = GetQuadVertices();
        GLBuffer vertexBuffer;
        glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
        glBufferData(GL_ARRAY_BUFFER, quadVerts.size() * sizeof(quadVerts[0]), quadVerts.data(),
                     GL_STATIC_DRAW);
    
        GLint posLoc = glGetAttribLocation(program, "position");
        ASSERT_NE(-1, posLoc);
    
        glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
        glEnableVertexAttribArray(posLoc);
    
        // Draw a few times with each UBO. Stream out one pixel for post-render verification.
        constexpr int kIterations         = 5;
        constexpr GLsizei kPackBufferSize = sizeof(GLColor) * kIterations * 2;
    
        GLBuffer packBuffer;
        glBindBuffer(GL_PIXEL_PACK_BUFFER, packBuffer);
        glBufferData(GL_PIXEL_PACK_BUFFER, kPackBufferSize, nullptr, GL_STREAM_READ);
    
        GLsizei offset = 0;
    
        uint32_t descriptorSetAllocationsBefore = 0;
    
        for (int iteration = 0; iteration < kIterations; ++iteration)
        {
            glUniform1i(uniLoc, 1);
            glBindBufferBase(GL_UNIFORM_BUFFER, 0, ubo1);
            glDrawArrays(GL_TRIANGLES, 0, 6);
            glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, reinterpret_cast<GLvoid *>(offset));
            offset += sizeof(GLColor);
            glUniform1i(uniLoc, 2);
            glBindBufferBase(GL_UNIFORM_BUFFER, 0, ubo2);
            glDrawArrays(GL_TRIANGLES, 0, 6);
            glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, reinterpret_cast<GLvoid *>(offset));
            offset += sizeof(GLColor);
    
            // Capture the allocations counter after the first run.
            if (iteration == 0)
            {
                descriptorSetAllocationsBefore = hackANGLE().descriptorSetAllocations;
            }
        }
    
        ASSERT_GL_NO_ERROR();
    
        // Verify correctness first.
        std::vector<GLColor> expectedData(kIterations * 2, GLColor::green);
        std::vector<GLColor> actualData(kIterations * 2, GLColor::black);
    
        void *mapPtr = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, kPackBufferSize, GL_MAP_READ_BIT);
        ASSERT_NE(nullptr, mapPtr);
        memcpy(actualData.data(), mapPtr, kPackBufferSize);
    
        glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
    
        EXPECT_EQ(expectedData, actualData);
    
        // Check for unnecessary descriptor set allocations.
        uint32_t descriptorSetAllocationsAfter = hackANGLE().descriptorSetAllocations;
    
        // TODO(jmadill): http://anglebug.com/5736 change to EXPECT_EQ.
        EXPECT_NE(descriptorSetAllocationsBefore, descriptorSetAllocationsAfter);
    }
    
    ANGLE_INSTANTIATE_TEST(VulkanPerformanceCounterTest, ES3_VULKAN());
    ANGLE_INSTANTIATE_TEST(VulkanPerformanceCounterTest_ES31, ES31_VULKAN());
    
    }  // anonymous namespace