// // 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 . // 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(getEGLWindow()->getContext()); rx::ContextVk *contextVk = rx::GetImplAs(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 data1(kCount, 1); std::vector 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(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(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 expectedData(kIterations * 2, GLColor::green); std::vector 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; EXPECT_EQ(descriptorSetAllocationsAfter, 0u); } ANGLE_INSTANTIATE_TEST(VulkanPerformanceCounterTest, ES3_VULKAN()); ANGLE_INSTANTIATE_TEST(VulkanPerformanceCounterTest_ES31, ES31_VULKAN()); } // anonymous namespace