| // |
| // 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 render pass. |
| |
| #include "include/platform/Feature.h" |
| #include "test_utils/ANGLETest.h" |
| #include "test_utils/angle_test_instantiate.h" |
| #include "test_utils/gl_raii.h" |
| #include "util/random_utils.h" |
| #include "util/shader_utils.h" |
| #include "util/test_utils.h" |
| |
| using namespace angle; |
| |
| namespace |
| { |
| // Normally, if LOAD_OP_NONE is not supported, LOAD_OP_LOAD is used instead. Similarly, if |
| // STORE_OP_NONE is not supported, STORE_OP_STORE is used instead. |
| // |
| // If an attachment has undefined contents and is unused, LOAD_OP_DONT_CARE and |
| // STORE_OP_DONT_CARE can be used. However, these are write operations for synchronization |
| // purposes, and so ANGLE uses LOAD_OP_NONE and STORE_OP_NONE if available to avoid the |
| // synchronization. When NONE is not available, ANGLE foregoes synchronization, producing |
| // syncval errors. |
| // |
| // For the sake of validation, it's unknown if NONE is turned into LOAD/STORE or DONT_CARE. So |
| // validation allows variations there. |
| #define EXPECT_OP_LOAD_AND_NONE(expectedLoads, actualLoads, expectedNones, actualNones) \ |
| { \ |
| if (hasLoadOpNoneSupport()) \ |
| { \ |
| EXPECT_EQ(expectedLoads, actualLoads); \ |
| EXPECT_EQ(expectedNones, actualNones); \ |
| } \ |
| else \ |
| { \ |
| EXPECT_EQ(actualNones, 0u); \ |
| EXPECT_LE(expectedLoads, actualLoads); \ |
| EXPECT_GE(expectedLoads + expectedNones, actualLoads); \ |
| } \ |
| } |
| #define EXPECT_OP_STORE_AND_NONE(expectedStores, actualStores, expectedNones, actualNones) \ |
| { \ |
| if (hasStoreOpNoneSupport() && hasLoadOpNoneSupport()) \ |
| { \ |
| EXPECT_EQ(expectedStores, actualStores); \ |
| EXPECT_EQ(expectedNones, actualNones); \ |
| } \ |
| else \ |
| { \ |
| if (!hasStoreOpNoneSupport()) \ |
| { \ |
| EXPECT_EQ(actualNones, 0u); \ |
| } \ |
| EXPECT_LE(expectedStores, actualStores); \ |
| EXPECT_GE(expectedStores + expectedNones, actualStores + actualNones); \ |
| } \ |
| } |
| |
| #define EXPECT_DEPTH_OP_COUNTERS(counters, expected) \ |
| { \ |
| EXPECT_EQ(expected.depthLoadOpClears, counters.depthLoadOpClears); \ |
| EXPECT_OP_LOAD_AND_NONE(expected.depthLoadOpLoads, counters.depthLoadOpLoads, \ |
| expected.depthLoadOpNones, counters.depthLoadOpNones); \ |
| EXPECT_OP_STORE_AND_NONE(expected.depthStoreOpStores, counters.depthStoreOpStores, \ |
| expected.depthStoreOpNones, counters.depthStoreOpNones); \ |
| } |
| |
| #define EXPECT_STENCIL_OP_COUNTERS(counters, expected) \ |
| { \ |
| EXPECT_EQ(expected.stencilLoadOpClears, counters.stencilLoadOpClears); \ |
| EXPECT_OP_LOAD_AND_NONE(expected.stencilLoadOpLoads, counters.stencilLoadOpLoads, \ |
| expected.stencilLoadOpNones, counters.stencilLoadOpNones); \ |
| EXPECT_OP_STORE_AND_NONE(expected.stencilStoreOpStores, counters.stencilStoreOpStores, \ |
| expected.stencilStoreOpNones, counters.stencilStoreOpNones); \ |
| } |
| |
| #define EXPECT_DEPTH_STENCIL_OP_COUNTERS(counters, expected) \ |
| { \ |
| EXPECT_DEPTH_OP_COUNTERS(counters, expected); \ |
| EXPECT_STENCIL_OP_COUNTERS(counters, expected); \ |
| } |
| |
| #define EXPECT_COLOR_OP_COUNTERS(counters, expected) \ |
| { \ |
| EXPECT_EQ(expected.colorLoadOpClears, counters.colorLoadOpClears); \ |
| EXPECT_OP_LOAD_AND_NONE(expected.colorLoadOpLoads, counters.colorLoadOpLoads, \ |
| expected.colorLoadOpNones, counters.colorLoadOpNones); \ |
| EXPECT_OP_STORE_AND_NONE(expected.colorStoreOpStores, counters.colorStoreOpStores, \ |
| expected.colorStoreOpNones, counters.colorStoreOpNones); \ |
| } |
| |
| #define EXPECT_DEPTH_STENCIL_LOAD_OP_COUNTERS(counters, expected) \ |
| { \ |
| EXPECT_EQ(expected.depthLoadOpLoads, counters.depthLoadOpLoads); \ |
| EXPECT_EQ(expected.stencilLoadOpLoads, counters.stencilLoadOpLoads); \ |
| } |
| |
| #define EXPECT_COUNTERS_FOR_UNRESOLVE_RESOLVE_TEST(counters, expected) \ |
| { \ |
| EXPECT_EQ(expected.colorAttachmentUnresolves, counters.colorAttachmentUnresolves); \ |
| EXPECT_EQ(expected.depthAttachmentUnresolves, counters.depthAttachmentUnresolves); \ |
| EXPECT_EQ(expected.stencilAttachmentUnresolves, counters.stencilAttachmentUnresolves); \ |
| EXPECT_EQ(expected.colorAttachmentResolves, counters.colorAttachmentResolves); \ |
| EXPECT_EQ(expected.depthAttachmentResolves, counters.depthAttachmentResolves); \ |
| EXPECT_EQ(expected.stencilAttachmentResolves, counters.stencilAttachmentResolves); \ |
| } |
| |
| #define EXPECT_CLEAR_ATTACHMENTS_COUNTER(expected, actual) \ |
| { \ |
| if (hasPreferDrawOverClearAttachments()) \ |
| { \ |
| EXPECT_EQ(actual, 0u); \ |
| } \ |
| else \ |
| { \ |
| EXPECT_EQ(actual, expected); \ |
| } \ |
| } |
| |
| enum class BufferUpdate |
| { |
| SubData, // use glBufferSubData |
| Copy, // use glCopyBufferSubData |
| }; |
| |
| class VulkanPerformanceCounterTest : public ANGLETest<> |
| { |
| protected: |
| VulkanPerformanceCounterTest() |
| { |
| // Depth/Stencil required for SwapShouldInvalidate*. |
| // Also RGBA8 is required to avoid the clear for emulated alpha. |
| setConfigRedBits(8); |
| setConfigGreenBits(8); |
| setConfigBlueBits(8); |
| setConfigAlphaBits(8); |
| setConfigDepthBits(24); |
| setConfigStencilBits(8); |
| } |
| |
| static constexpr GLsizei kOpsTestSize = 16; |
| |
| void setupForColorOpsTest(GLFramebuffer *framebuffer, GLTexture *texture) |
| { |
| // Setup the framebuffer |
| glBindFramebuffer(GL_FRAMEBUFFER, *framebuffer); |
| glBindTexture(GL_TEXTURE_2D, *texture); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kOpsTestSize, kOpsTestSize, 0, GL_RGBA, |
| GL_UNSIGNED_BYTE, nullptr); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *texture, 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| } |
| |
| void setupForColorDepthOpsTest(GLFramebuffer *framebuffer, |
| GLTexture *texture, |
| GLRenderbuffer *renderbuffer) |
| { |
| // Setup color and depth |
| glBindFramebuffer(GL_FRAMEBUFFER, *framebuffer); |
| glBindTexture(GL_TEXTURE_2D, *texture); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kOpsTestSize, kOpsTestSize, 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_DEPTH_COMPONENT24, kOpsTestSize, kOpsTestSize); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, |
| *renderbuffer); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| // Setup depth parameters |
| glEnable(GL_DEPTH_TEST); |
| glDepthMask(GL_TRUE); |
| glDepthFunc(GL_GEQUAL); |
| glClearDepthf(0.99f); |
| glViewport(0, 0, kOpsTestSize, kOpsTestSize); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| void setupForDepthStencilOpsTest(GLFramebuffer *framebuffer, |
| GLTexture *texture, |
| GLRenderbuffer *renderbuffer) |
| { |
| // Setup color, depth, and stencil |
| glBindFramebuffer(GL_FRAMEBUFFER, *framebuffer); |
| glBindTexture(GL_TEXTURE_2D, *texture); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kOpsTestSize, kOpsTestSize, 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, kOpsTestSize, kOpsTestSize); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, |
| *renderbuffer); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| // Setup depth parameters |
| glEnable(GL_DEPTH_TEST); |
| glDepthMask(GL_TRUE); |
| glDepthFunc(GL_GEQUAL); |
| glEnable(GL_STENCIL_TEST); |
| glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); |
| glStencilMask(0xFF); |
| glClearDepthf(0.99f); |
| glClearStencil(0xAA); |
| glViewport(0, 0, kOpsTestSize, kOpsTestSize); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| void setupClearAndDrawForDepthStencilOpsTest(GLProgram *program, |
| GLFramebuffer *framebuffer, |
| GLTexture *texture, |
| GLRenderbuffer *renderbuffer, |
| bool clearStencil) |
| { |
| setupForDepthStencilOpsTest(framebuffer, texture, renderbuffer); |
| |
| // Clear and draw with depth and stencil buffer enabled |
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | |
| (clearStencil ? GL_STENCIL_BUFFER_BIT : 0)); |
| drawQuad(*program, essl1_shaders::PositionAttrib(), 1.f); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| void setExpectedCountersForDepthOps(const angle::VulkanPerfCounters &counters, |
| uint64_t incrementalRenderPasses, |
| uint64_t incrementalDepthLoadOpClears, |
| uint64_t incrementalDepthLoadOpLoads, |
| uint64_t incrementalDepthLoadOpNones, |
| uint64_t incrementalDepthStoreOpStores, |
| uint64_t incrementalDepthStoreOpNones, |
| angle::VulkanPerfCounters *expected) |
| { |
| expected->renderPasses = counters.renderPasses + incrementalRenderPasses; |
| expected->depthLoadOpClears = counters.depthLoadOpClears + incrementalDepthLoadOpClears; |
| expected->depthLoadOpLoads = counters.depthLoadOpLoads + incrementalDepthLoadOpLoads; |
| expected->depthLoadOpNones = counters.depthLoadOpNones + incrementalDepthLoadOpNones; |
| expected->depthStoreOpStores = counters.depthStoreOpStores + incrementalDepthStoreOpStores; |
| expected->depthStoreOpNones = counters.depthStoreOpNones + incrementalDepthStoreOpNones; |
| } |
| |
| void setExpectedCountersForStencilOps(const angle::VulkanPerfCounters &counters, |
| uint64_t incrementalStencilLoadOpClears, |
| uint64_t incrementalStencilLoadOpLoads, |
| uint64_t incrementalStencilLoadOpNones, |
| uint64_t incrementalStencilStoreOpStores, |
| uint64_t incrementalStencilStoreOpNones, |
| angle::VulkanPerfCounters *expected) |
| { |
| expected->stencilLoadOpClears = |
| counters.stencilLoadOpClears + incrementalStencilLoadOpClears; |
| expected->stencilLoadOpLoads = counters.stencilLoadOpLoads + incrementalStencilLoadOpLoads; |
| expected->stencilLoadOpNones = counters.stencilLoadOpNones + incrementalStencilLoadOpNones; |
| expected->stencilStoreOpStores = |
| counters.stencilStoreOpStores + incrementalStencilStoreOpStores; |
| expected->stencilStoreOpNones = |
| counters.stencilStoreOpNones + incrementalStencilStoreOpNones; |
| } |
| |
| void setExpectedCountersForColorOps(const angle::VulkanPerfCounters &counters, |
| uint64_t incrementalRenderPasses, |
| uint64_t incrementalColorLoadOpClears, |
| uint64_t incrementalColorLoadOpLoads, |
| uint64_t incrementalColorLoadOpNones, |
| uint64_t incrementalColorStoreOpStores, |
| uint64_t incrementalColorStoreOpNones, |
| angle::VulkanPerfCounters *expected) |
| { |
| expected->renderPasses = counters.renderPasses + incrementalRenderPasses; |
| expected->colorLoadOpClears = counters.colorLoadOpClears + incrementalColorLoadOpClears; |
| expected->colorLoadOpLoads = counters.colorLoadOpLoads + incrementalColorLoadOpLoads; |
| expected->colorLoadOpNones = counters.colorLoadOpNones + incrementalColorLoadOpNones; |
| expected->colorStoreOpStores = counters.colorStoreOpStores + incrementalColorStoreOpStores; |
| expected->colorStoreOpNones = counters.colorStoreOpNones + incrementalColorStoreOpNones; |
| } |
| |
| void setAndIncrementDepthStencilLoadCountersForOpsTest( |
| const angle::VulkanPerfCounters &counters, |
| uint64_t incrementalDepthLoadOpLoads, |
| uint64_t incrementalStencilLoadOpLoads, |
| angle::VulkanPerfCounters *expected) |
| { |
| expected->depthLoadOpLoads = counters.depthLoadOpLoads + incrementalDepthLoadOpLoads; |
| expected->stencilLoadOpLoads = counters.stencilLoadOpLoads + incrementalStencilLoadOpLoads; |
| } |
| |
| void setExpectedCountersForUnresolveResolveTest(const angle::VulkanPerfCounters &counters, |
| uint64_t incrementalColorAttachmentUnresolves, |
| uint64_t incrementalDepthAttachmentUnresolves, |
| uint64_t incrementalStencilAttachmentUnresolves, |
| uint64_t incrementalColorAttachmentResolves, |
| uint64_t incrementalDepthAttachmentResolves, |
| uint64_t incrementalStencilAttachmentResolves, |
| angle::VulkanPerfCounters *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 maskedFramebufferFetchDraw(const GLColor &clearColor, GLBuffer &buffer); |
| void maskedFramebufferFetchDrawVerify(const GLColor &expectedColor, GLBuffer &buffer); |
| |
| void saveAndReloadBinary(GLProgram *original, GLProgram *reloaded); |
| void testPipelineCacheIsWarm(GLProgram *program, GLColor color); |
| |
| void updateBuffer(BufferUpdate update, |
| GLenum target, |
| GLintptr offset, |
| GLsizeiptr size, |
| const void *data) |
| { |
| if (update == BufferUpdate::SubData) |
| { |
| // If using glBufferSubData, directly upload data on the specified target (where the |
| // buffer is already bound) |
| glBufferSubData(target, offset, size, data); |
| } |
| else |
| { |
| // Otherwise copy through a temp buffer. Use a non-zero offset for more coverage. |
| constexpr GLintptr kStagingOffset = 123; |
| GLBuffer staging; |
| glBindBuffer(GL_COPY_READ_BUFFER, staging); |
| glBufferData(GL_COPY_READ_BUFFER, offset + size + kStagingOffset * 2, nullptr, |
| GL_STATIC_DRAW); |
| glBufferSubData(GL_COPY_READ_BUFFER, kStagingOffset, size, data); |
| glCopyBufferSubData(GL_COPY_READ_BUFFER, target, kStagingOffset, offset, size); |
| } |
| } |
| |
| void mappingGpuReadOnlyBufferGhostsBuffer(BufferUpdate update); |
| void partialBufferUpdateShouldNotBreakRenderPass(BufferUpdate update); |
| void bufferSubDataShouldNotTriggerSyncState(BufferUpdate update); |
| |
| angle::VulkanPerfCounters getPerfCounters() |
| { |
| if (mIndexMap.empty()) |
| { |
| mIndexMap = BuildCounterNameToIndexMap(); |
| } |
| |
| return GetPerfCounters(mIndexMap); |
| } |
| |
| // Support status for ANGLE features. |
| bool isFeatureEnabled(Feature feature) const |
| { |
| return getEGLWindow()->isFeatureEnabled(feature); |
| } |
| bool hasLoadOpNoneSupport() const |
| { |
| return isFeatureEnabled(Feature::SupportsRenderPassLoadStoreOpNone); |
| } |
| bool hasStoreOpNoneSupport() const |
| { |
| return isFeatureEnabled(Feature::SupportsRenderPassLoadStoreOpNone) || |
| isFeatureEnabled(Feature::SupportsRenderPassStoreOpNone); |
| } |
| bool hasMutableMipmapTextureUpload() const |
| { |
| return isFeatureEnabled(Feature::MutableMipmapTextureUpload); |
| } |
| bool hasPreferDrawOverClearAttachments() const |
| { |
| return isFeatureEnabled(Feature::PreferDrawClearOverVkCmdClearAttachments); |
| } |
| bool hasSupportsPipelineCreationFeedback() const |
| { |
| return isFeatureEnabled(Feature::SupportsPipelineCreationFeedback); |
| } |
| bool hasWarmUpPipelineCacheAtLink() const |
| { |
| return isFeatureEnabled(Feature::WarmUpPipelineCacheAtLink); |
| } |
| bool hasEffectivePipelineCacheSerialization() const |
| { |
| return isFeatureEnabled(Feature::HasEffectivePipelineCacheSerialization); |
| } |
| bool hasPreferCPUForBufferSubData() const |
| { |
| return isFeatureEnabled(Feature::PreferCPUForBufferSubData); |
| } |
| bool hasPreferSubmitAtFBOBoundary() const |
| { |
| return isFeatureEnabled(Feature::PreferSubmitAtFBOBoundary); |
| } |
| bool hasDisallowMixedDepthStencilLoadOpNoneAndLoad() const |
| { |
| return isFeatureEnabled(Feature::DisallowMixedDepthStencilLoadOpNoneAndLoad); |
| } |
| bool hasSupportsImagelessFramebuffer() const |
| { |
| return isFeatureEnabled(Feature::SupportsImagelessFramebuffer); |
| } |
| bool hasSupportsHostImageCopy() const |
| { |
| return isFeatureEnabled(Feature::SupportsHostImageCopy); |
| } |
| bool hasDepthStencilResolveThroughAttachment() const |
| { |
| return isFeatureEnabled(Feature::SupportsDepthStencilResolve) && |
| !isFeatureEnabled(Feature::DisableDepthStencilResolveThroughAttachment); |
| } |
| |
| CounterNameToIndexMap mIndexMap; |
| }; |
| |
| class VulkanPerformanceCounterTest_ES31 : public VulkanPerformanceCounterTest |
| {}; |
| |
| class VulkanPerformanceCounterTest_MSAA : public VulkanPerformanceCounterTest |
| { |
| protected: |
| VulkanPerformanceCounterTest_MSAA() : VulkanPerformanceCounterTest() |
| { |
| // Make sure the window is non-square to correctly test prerotation |
| setWindowWidth(32); |
| setWindowHeight(64); |
| setSamples(4); |
| setMultisampleEnabled(true); |
| } |
| }; |
| |
| class VulkanPerformanceCounterTest_SingleBuffer : public VulkanPerformanceCounterTest |
| { |
| protected: |
| VulkanPerformanceCounterTest_SingleBuffer() : VulkanPerformanceCounterTest() |
| { |
| setMutableRenderBuffer(true); |
| } |
| }; |
| |
| void VulkanPerformanceCounterTest::maskedFramebufferFetchDraw(const GLColor &clearColor, |
| GLBuffer &buffer) |
| { |
| // Initialize the color buffer |
| angle::Vector4 clearAsVec4 = clearColor.toNormalizedVector(); |
| glClearColor(clearAsVec4[0], clearAsVec4[1], clearAsVec4[2], clearAsVec4[3]); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_COLOR_NEAR(0, 0, clearColor, 1); |
| |
| // Create output buffer |
| constexpr GLsizei kBufferSize = kOpsTestSize * kOpsTestSize * sizeof(float[4]); |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, kBufferSize, nullptr, GL_STATIC_DRAW); |
| glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, buffer, 0, kBufferSize); |
| |
| // Zero-initialize it |
| void *bufferData = glMapBufferRange( |
| GL_SHADER_STORAGE_BUFFER, 0, kBufferSize, |
| GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_UNSYNCHRONIZED_BIT); |
| memset(bufferData, 0, kBufferSize); |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| |
| // Mask color output |
| glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); |
| |
| static constexpr char kVS[] = R"(#version 310 es |
| in highp vec4 a_position; |
| |
| void main (void) |
| { |
| gl_Position = a_position; |
| })"; |
| |
| static constexpr char kFS[] = R"(#version 310 es |
| #extension GL_EXT_shader_framebuffer_fetch_non_coherent : require |
| layout(noncoherent, location = 0) inout highp vec4 o_color; |
| |
| layout(std140, binding = 0) buffer outBlock { |
| highp vec4 data[256]; |
| }; |
| |
| uniform highp vec4 u_color; |
| void main (void) |
| { |
| uint index = uint(gl_FragCoord.y) * 16u + uint(gl_FragCoord.x); |
| data[index] = o_color; |
| o_color += u_color; |
| })"; |
| |
| // Draw |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| void VulkanPerformanceCounterTest::maskedFramebufferFetchDrawVerify(const GLColor &expectedColor, |
| GLBuffer &buffer) |
| { |
| angle::Vector4 expectedAsVec4 = expectedColor.toNormalizedVector(); |
| |
| // Read back the storage buffer and make sure framebuffer fetch worked as intended despite |
| // masked color. |
| glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); |
| |
| constexpr GLsizei kBufferSize = kOpsTestSize * kOpsTestSize * sizeof(float[4]); |
| const float *colorData = static_cast<const float *>( |
| glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kBufferSize, GL_MAP_READ_BIT)); |
| for (uint32_t y = 0; y < kOpsTestSize; ++y) |
| { |
| for (uint32_t x = 0; x < kOpsTestSize; ++x) |
| { |
| uint32_t ssboIndex = (y * kOpsTestSize + x) * 4; |
| EXPECT_NEAR(colorData[ssboIndex + 0], expectedAsVec4[0], 0.05); |
| EXPECT_NEAR(colorData[ssboIndex + 1], expectedAsVec4[1], 0.05); |
| EXPECT_NEAR(colorData[ssboIndex + 2], expectedAsVec4[2], 0.05); |
| EXPECT_NEAR(colorData[ssboIndex + 3], expectedAsVec4[3], 0.05); |
| } |
| } |
| glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); |
| } |
| |
| // Tests that texture updates to unused textures don't break the RP. |
| TEST_P(VulkanPerformanceCounterTest, NewTextureDoesNotBreakRenderPass) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| 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(); |
| |
| uint64_t expectedRenderPassCount = getPerfCounters().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(); |
| |
| uint64_t actualRenderPassCount = getPerfCounters().renderPasses; |
| EXPECT_EQ(expectedRenderPassCount, actualRenderPassCount); |
| } |
| |
| // Tests that submitting the outside command buffer due to texture upload size does not break the |
| // current render pass. |
| TEST_P(VulkanPerformanceCounterTest, SubmittingOutsideCommandBufferDoesNotBreakRenderPass) |
| { |
| constexpr size_t kMaxBufferToImageCopySize = 64 * 1024 * 1024; |
| constexpr uint64_t kNumSubmits = 2; |
| uint64_t expectedRenderPassCount = getPerfCounters().renderPasses + 1; |
| uint64_t expectedSubmitCommandsCount = getPerfCounters().vkQueueSubmitCallsTotal + kNumSubmits; |
| |
| // Step 1: Set up a simple 2D texture. |
| GLTexture texture; |
| constexpr GLsizei kTexDim = 256; |
| constexpr uint32_t kPixelSizeRGBA = 4; |
| constexpr uint32_t kTextureSize = kTexDim * kTexDim * kPixelSizeRGBA; |
| std::vector<GLColor> kInitialData(kTexDim * kTexDim, GLColor::green); |
| |
| glBindTexture(GL_TEXTURE_2D, texture); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTexDim, kTexDim, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| kInitialData.data()); |
| 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(); |
| |
| // Step 2: Load a new 2D Texture multiple times with the same Program and Framebuffer. The total |
| // size of the loaded textures must exceed the threshold to submit the outside command buffer. |
| constexpr size_t kMaxLoadCount = kMaxBufferToImageCopySize / kTextureSize * kNumSubmits + 1; |
| for (size_t loadCount = 0; loadCount < kMaxLoadCount; loadCount++) |
| { |
| GLTexture newTexture; |
| glBindTexture(GL_TEXTURE_2D, newTexture); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTexDim, kTexDim, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| kInitialData.data()); |
| 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(); |
| } |
| |
| // Verify render pass and submitted frame counts. |
| EXPECT_EQ(getPerfCounters().renderPasses, expectedRenderPassCount); |
| EXPECT_EQ(getPerfCounters().vkQueueSubmitCallsTotal, expectedSubmitCommandsCount); |
| } |
| |
| // Tests that submitting the outside command buffer due to texture upload size does not result in |
| // garbage collection of render pass resources.. |
| TEST_P(VulkanPerformanceCounterTest, SubmittingOutsideCommandBufferDoesNotCollectRenderPassGarbage) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_disjoint_timer_query")); |
| |
| // If VK_EXT_host_image_copy is used, uploads will all be done on the CPU and there would be no |
| // submissions. |
| ANGLE_SKIP_TEST_IF(hasSupportsHostImageCopy()); |
| |
| uint64_t expectedRenderPassCount = getPerfCounters().renderPasses + 1; |
| uint64_t submitCommandsCount = getPerfCounters().vkQueueSubmitCallsTotal; |
| |
| // Set up a simple 2D texture. |
| GLTexture texture; |
| constexpr GLsizei kTexDim = 256; |
| std::vector<GLColor> kInitialData(kTexDim * kTexDim, GLColor::green); |
| |
| glBindTexture(GL_TEXTURE_2D, texture); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTexDim, kTexDim, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| kInitialData.data()); |
| 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(); |
| |
| // Issue a timestamp query, just for the sake of using it as a means of knowing when a |
| // submission is finished. In the Vulkan backend, querying the status of the query results in a |
| // check of completed submissions, at which point associated garbage is also destroyed. |
| GLQuery query; |
| glQueryCounterEXT(query, GL_TIMESTAMP_EXT); |
| |
| // Issue a draw call, and delete the program |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| ASSERT_GL_NO_ERROR(); |
| program.reset(); |
| |
| ANGLE_GL_PROGRAM(program2, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D()); |
| glUseProgram(program2); |
| ASSERT_EQ(posLoc, glGetAttribLocation(program2, essl1_shaders::PositionAttrib())); |
| |
| // Issue uploads until there's an implicit submission |
| size_t textureCount = 0; |
| while (getPerfCounters().vkQueueSubmitCallsTotal == submitCommandsCount) |
| { |
| GLTexture newTexture; |
| glBindTexture(GL_TEXTURE_2D, newTexture); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTexDim, kTexDim, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| kInitialData.data()); |
| 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(); |
| textureCount++; |
| } |
| // 256x256 texture upload should not trigger a submission |
| ASSERT(textureCount > 1); |
| |
| ++submitCommandsCount; |
| EXPECT_EQ(getPerfCounters().vkQueueSubmitCallsTotal, submitCommandsCount); |
| |
| // Busy wait until the query results are available. |
| GLuint ready = GL_FALSE; |
| while (ready == GL_FALSE) |
| { |
| angle::Sleep(0); |
| glGetQueryObjectuivEXT(query, GL_QUERY_RESULT_AVAILABLE_EXT, &ready); |
| } |
| |
| // At this point, the render pass should still not be submitted, and the pipeline that is |
| // deleted should still not be garbage collected. Submit the commands and ensure there is no |
| // crash. |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| ++submitCommandsCount; |
| |
| // Verify counters. |
| EXPECT_EQ(getPerfCounters().renderPasses, expectedRenderPassCount); |
| EXPECT_EQ(getPerfCounters().vkQueueSubmitCallsTotal, submitCommandsCount); |
| } |
| |
| // Tests that submitting the outside command buffer due to texture upload size and triggers |
| // endRenderPass works correctly. |
| TEST_P(VulkanPerformanceCounterTest, SubmittingOutsideCommandBufferTriggersEndRenderPass) |
| { |
| // If VK_EXT_host_image_copy is used, uploads will all be done on the CPU and there would be no |
| // submissions. |
| ANGLE_SKIP_TEST_IF(hasSupportsHostImageCopy()); |
| |
| const int width = getWindowWidth(); |
| const int height = getWindowHeight(); |
| uint64_t expectedRenderPassCount = getPerfCounters().renderPasses + 1; |
| uint64_t submitCommandsCount = getPerfCounters().vkQueueSubmitCallsTotal; |
| |
| // Start a new renderpass with red quad on left. |
| ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| glScissor(0, 0, width / 2, height); |
| glEnable(GL_SCISSOR_TEST); |
| drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.5f); |
| |
| // Issue texture uploads and draw green quad on right until endRenderPass is triggered |
| glScissor(width / 2, 0, width / 2, height); |
| ANGLE_GL_PROGRAM(textureProgram, essl1_shaders::vs::Texture2D(), |
| essl1_shaders::fs::Texture2D()); |
| glUseProgram(textureProgram); |
| GLint textureLoc = glGetUniformLocation(textureProgram, essl1_shaders::Texture2DUniform()); |
| ASSERT_NE(-1, textureLoc); |
| glUniform1i(textureLoc, 0); |
| |
| // This test is specifically try to test outsideRPCommands submission drain the reserved |
| // queueSerials. Right now we are reserving 15 queue Serials. This is to ensure if the |
| // implementation changes, we do not end up with infinite loop here. |
| constexpr GLsizei kMaxOutsideRPCommandsSubmitCount = 17; |
| constexpr GLsizei kTexDim = 1024; |
| std::vector<GLColor> kInitialData(kTexDim * kTexDim, GLColor::green); |
| // Put a limit on the loop to avoid infinite loop in bad case. |
| while (getPerfCounters().renderPasses == expectedRenderPassCount && |
| getPerfCounters().vkQueueSubmitCallsTotal < |
| submitCommandsCount + kMaxOutsideRPCommandsSubmitCount) |
| { |
| GLTexture newTexture; |
| glBindTexture(GL_TEXTURE_2D, newTexture); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kTexDim, kTexDim); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kTexDim, kTexDim, GL_RGBA, GL_UNSIGNED_BYTE, |
| kInitialData.data()); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| drawQuad(textureProgram, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| ++expectedRenderPassCount; |
| EXPECT_EQ(getPerfCounters().renderPasses, expectedRenderPassCount); |
| |
| // Verify renderpass draw quads correctly |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| EXPECT_PIXEL_COLOR_EQ(width / 2 + 1, 0, GLColor::green); |
| } |
| |
| // Tests that mutable texture is uploaded with appropriate mip level attributes. |
| TEST_P(VulkanPerformanceCounterTest, MutableTextureCompatibleMipLevelsInit) |
| { |
| ANGLE_SKIP_TEST_IF(!hasMutableMipmapTextureUpload()); |
| |
| uint32_t expectedMutableTexturesUploaded = getPerfCounters().mutableTexturesUploaded + 1; |
| |
| std::vector<GLColor> mip0Color(4 * 4, GLColor::red); |
| std::vector<GLColor> mip1Color(2 * 2, GLColor::red); |
| |
| GLTexture texture1; |
| glBindTexture(GL_TEXTURE_2D, texture1); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, mip0Color.data()); |
| glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, mip1Color.data()); |
| EXPECT_GL_NO_ERROR(); |
| |
| GLTexture texture2; |
| glBindTexture(GL_TEXTURE_2D, texture2); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| GLColor::green.data()); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(getPerfCounters().mutableTexturesUploaded, expectedMutableTexturesUploaded); |
| } |
| |
| // Tests that a single-level mutable texture is uploaded with appropriate attributes. |
| TEST_P(VulkanPerformanceCounterTest, MutableTextureCompatibleSingleMipLevelInit) |
| { |
| ANGLE_SKIP_TEST_IF(!hasMutableMipmapTextureUpload()); |
| |
| uint32_t expectedMutableTexturesUploaded = getPerfCounters().mutableTexturesUploaded + 1; |
| |
| std::vector<GLColor> mip0Color(4 * 4, GLColor::red); |
| |
| GLTexture texture1; |
| glBindTexture(GL_TEXTURE_2D, texture1); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, mip0Color.data()); |
| EXPECT_GL_NO_ERROR(); |
| |
| GLTexture texture2; |
| glBindTexture(GL_TEXTURE_2D, texture2); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| GLColor::green.data()); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(getPerfCounters().mutableTexturesUploaded, expectedMutableTexturesUploaded); |
| } |
| |
| // Tests that mutable texture is uploaded with appropriate mip level attributes, even with unequal |
| // dimensions. |
| TEST_P(VulkanPerformanceCounterTest, MutableTextureCompatibleMipLevelsNonSquareInit) |
| { |
| ANGLE_SKIP_TEST_IF(!hasMutableMipmapTextureUpload()); |
| |
| uint32_t expectedMutableTexturesUploaded = getPerfCounters().mutableTexturesUploaded + 1; |
| |
| std::vector<GLColor> mip0Color(4 * 2, GLColor::red); |
| std::vector<GLColor> mip1Color(2 * 1, GLColor::red); |
| std::vector<GLColor> mip2Color(1 * 1, GLColor::red); |
| |
| GLTexture texture1; |
| glBindTexture(GL_TEXTURE_2D, texture1); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, mip0Color.data()); |
| glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 2, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, mip1Color.data()); |
| glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, mip2Color.data()); |
| EXPECT_GL_NO_ERROR(); |
| |
| GLTexture texture2; |
| glBindTexture(GL_TEXTURE_2D, texture2); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| GLColor::green.data()); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(getPerfCounters().mutableTexturesUploaded, expectedMutableTexturesUploaded); |
| } |
| |
| // Tests that a mutable texture is not uploaded if there are no data or updates for it. |
| TEST_P(VulkanPerformanceCounterTest, MutableTextureSingleLevelWithNoDataNoInit) |
| { |
| ANGLE_SKIP_TEST_IF(!hasMutableMipmapTextureUpload()); |
| |
| uint32_t expectedMutableTexturesUploaded = getPerfCounters().mutableTexturesUploaded; |
| |
| GLTexture texture1; |
| glBindTexture(GL_TEXTURE_2D, texture1); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| EXPECT_GL_NO_ERROR(); |
| |
| GLTexture texture2; |
| glBindTexture(GL_TEXTURE_2D, texture2); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| GLColor::green.data()); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(getPerfCounters().mutableTexturesUploaded, expectedMutableTexturesUploaded); |
| } |
| |
| // Tests that mutable texture is not uploaded with more than one updates in a single mip level. |
| TEST_P(VulkanPerformanceCounterTest, MutableTextureSingleLevelWithMultipleUpdatesNoInit) |
| { |
| ANGLE_SKIP_TEST_IF(!hasMutableMipmapTextureUpload()); |
| |
| uint32_t expectedMutableTexturesUploaded = getPerfCounters().mutableTexturesUploaded; |
| |
| std::vector<GLColor> mip0Color(4 * 4, GLColor::red); |
| |
| GLTexture texture1; |
| glBindTexture(GL_TEXTURE_2D, texture1); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 2, 2, GL_RGBA, GL_UNSIGNED_BYTE, mip0Color.data()); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 2, 0, 2, 2, GL_RGBA, GL_UNSIGNED_BYTE, mip0Color.data()); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 2, 2, 2, GL_RGBA, GL_UNSIGNED_BYTE, mip0Color.data()); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 2, 2, 2, 2, GL_RGBA, GL_UNSIGNED_BYTE, mip0Color.data()); |
| EXPECT_GL_NO_ERROR(); |
| |
| GLTexture texture2; |
| glBindTexture(GL_TEXTURE_2D, texture2); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| GLColor::green.data()); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(getPerfCounters().mutableTexturesUploaded, expectedMutableTexturesUploaded); |
| } |
| |
| // Tests that the optimization is not triggered when a mutable texture becomes immutable, e.g., |
| // after glTexStorage2D. |
| TEST_P(VulkanPerformanceCounterTest, MutableTextureChangeToImmutableNoInit) |
| { |
| ANGLE_SKIP_TEST_IF(!hasMutableMipmapTextureUpload()); |
| |
| uint32_t expectedMutableTexturesUploaded = getPerfCounters().mutableTexturesUploaded; |
| |
| std::vector<GLColor> mip0Color(4 * 4, GLColor::red); |
| std::vector<GLColor> mip1Color(2 * 2, GLColor::red); |
| |
| GLTexture texture1; |
| glBindTexture(GL_TEXTURE_2D, texture1); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, mip0Color.data()); |
| glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, mip1Color.data()); |
| glTexStorage2D(GL_TEXTURE_2D, 2, GL_RGBA8, 4, 4); |
| EXPECT_GL_NO_ERROR(); |
| |
| GLTexture texture2; |
| glBindTexture(GL_TEXTURE_2D, texture2); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| GLColor::green.data()); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(getPerfCounters().mutableTexturesUploaded, expectedMutableTexturesUploaded); |
| } |
| |
| // Tests that mutable texture is not uploaded when there is no base mip level (0). |
| TEST_P(VulkanPerformanceCounterTest, MutableTextureNoBaseLevelNoInit) |
| { |
| ANGLE_SKIP_TEST_IF(!hasMutableMipmapTextureUpload()); |
| |
| uint32_t expectedMutableTexturesUploaded = getPerfCounters().mutableTexturesUploaded; |
| |
| std::vector<GLColor> mip1Color(4 * 4, GLColor::red); |
| std::vector<GLColor> mip2Color(2 * 2, GLColor::red); |
| |
| GLTexture texture1; |
| glBindTexture(GL_TEXTURE_2D, texture1); |
| glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, mip1Color.data()); |
| glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, mip2Color.data()); |
| EXPECT_GL_NO_ERROR(); |
| |
| GLTexture texture2; |
| glBindTexture(GL_TEXTURE_2D, texture2); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| GLColor::green.data()); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(getPerfCounters().mutableTexturesUploaded, expectedMutableTexturesUploaded); |
| } |
| |
| // Tests that mutable texture is uploaded even when there is a missing mip level greater than 1 |
| // despite the defined mip levels being compatible. |
| TEST_P(VulkanPerformanceCounterTest, MutableTextureMissingMipLevelGreaterThanOneInit) |
| { |
| ANGLE_SKIP_TEST_IF(!hasMutableMipmapTextureUpload()); |
| |
| uint32_t expectedMutableTexturesUploaded = getPerfCounters().mutableTexturesUploaded + 1; |
| |
| std::vector<GLColor> mip0Color(8 * 8, GLColor::red); |
| std::vector<GLColor> mip1Color(4 * 4, GLColor::red); |
| std::vector<GLColor> mip3Color(1 * 1, GLColor::red); |
| |
| GLTexture texture1; |
| glBindTexture(GL_TEXTURE_2D, texture1); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, mip0Color.data()); |
| glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, mip1Color.data()); |
| glTexImage2D(GL_TEXTURE_2D, 3, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, mip3Color.data()); |
| EXPECT_GL_NO_ERROR(); |
| |
| GLTexture texture2; |
| glBindTexture(GL_TEXTURE_2D, texture2); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| GLColor::green.data()); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(getPerfCounters().mutableTexturesUploaded, expectedMutableTexturesUploaded); |
| } |
| |
| // Tests that mutable texture is not uploaded with incompatible mip level sizes. |
| TEST_P(VulkanPerformanceCounterTest, MutableTextureMipLevelsWithIncompatibleSizesNoInit) |
| { |
| ANGLE_SKIP_TEST_IF(!hasMutableMipmapTextureUpload()); |
| |
| uint32_t expectedMutableTexturesUploaded = getPerfCounters().mutableTexturesUploaded; |
| |
| std::vector<GLColor> mip0Color(8 * 8, GLColor::red); |
| std::vector<GLColor> mip1Color(4 * 4, GLColor::red); |
| std::vector<GLColor> mip2Color(3 * 3, GLColor::red); |
| |
| GLTexture texture1; |
| glBindTexture(GL_TEXTURE_2D, texture1); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, mip0Color.data()); |
| glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, mip1Color.data()); |
| glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA, 3, 3, 0, GL_RGBA, GL_UNSIGNED_BYTE, mip2Color.data()); |
| EXPECT_GL_NO_ERROR(); |
| |
| GLTexture texture2; |
| glBindTexture(GL_TEXTURE_2D, texture2); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| GLColor::green.data()); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(getPerfCounters().mutableTexturesUploaded, expectedMutableTexturesUploaded); |
| } |
| |
| // Tests that mutable texture is not uploaded with incompatible mip level formats. |
| TEST_P(VulkanPerformanceCounterTest, MutableTextureMipLevelsWithIncompatibleFormatsNoInit) |
| { |
| ANGLE_SKIP_TEST_IF(!hasMutableMipmapTextureUpload()); |
| |
| uint32_t expectedMutableTexturesUploaded = getPerfCounters().mutableTexturesUploaded; |
| |
| std::vector<GLColor> mip0Color(4 * 4, GLColor::red); |
| std::vector<GLColor> mip1Color(2 * 2, GLColor::red); |
| |
| GLTexture texture1; |
| glBindTexture(GL_TEXTURE_2D, texture1); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, mip0Color.data()); |
| glTexImage2D(GL_TEXTURE_2D, 1, GL_RGB, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, mip1Color.data()); |
| EXPECT_GL_NO_ERROR(); |
| |
| GLTexture texture2; |
| glBindTexture(GL_TEXTURE_2D, texture2); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| GLColor::green.data()); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(getPerfCounters().mutableTexturesUploaded, expectedMutableTexturesUploaded); |
| } |
| |
| // Tests that mutable 3D texture is uploaded with appropriate mip level attributes. |
| TEST_P(VulkanPerformanceCounterTest, MutableTexture3DCompatibleMipLevelsInit) |
| { |
| ANGLE_SKIP_TEST_IF(!hasMutableMipmapTextureUpload()); |
| |
| uint32_t expectedMutableTexturesUploaded = getPerfCounters().mutableTexturesUploaded + 1; |
| |
| std::vector<GLColor> mip0Color(4 * 4 * 4, GLColor::red); |
| std::vector<GLColor> mip1Color(2 * 2 * 2, GLColor::red); |
| |
| GLTexture texture1; |
| glBindTexture(GL_TEXTURE_3D, texture1); |
| glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 4, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| mip0Color.data()); |
| glTexImage3D(GL_TEXTURE_3D, 1, GL_RGBA, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| mip1Color.data()); |
| EXPECT_GL_NO_ERROR(); |
| |
| GLTexture texture2; |
| glBindTexture(GL_TEXTURE_3D, texture2); |
| glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| GLColor::green.data()); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(getPerfCounters().mutableTexturesUploaded, expectedMutableTexturesUploaded); |
| } |
| |
| // Tests that mutable 3D texture is uploaded with appropriate mip level attributes, even with |
| // unequal dimensions. |
| TEST_P(VulkanPerformanceCounterTest, MutableTexture3DCompatibleMipLevelsNonCubeInit) |
| { |
| ANGLE_SKIP_TEST_IF(!hasMutableMipmapTextureUpload()); |
| |
| uint32_t expectedMutableTexturesUploaded = getPerfCounters().mutableTexturesUploaded + 1; |
| |
| std::vector<GLColor> mip0Color(4 * 2 * 2, GLColor::red); |
| std::vector<GLColor> mip1Color(2 * 1 * 1, GLColor::red); |
| std::vector<GLColor> mip2Color(1 * 1 * 1, GLColor::red); |
| |
| GLTexture texture1; |
| glBindTexture(GL_TEXTURE_3D, texture1); |
| glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 4, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| mip0Color.data()); |
| glTexImage3D(GL_TEXTURE_3D, 1, GL_RGBA, 2, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| mip1Color.data()); |
| glTexImage3D(GL_TEXTURE_3D, 2, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| mip1Color.data()); |
| EXPECT_GL_NO_ERROR(); |
| |
| GLTexture texture2; |
| glBindTexture(GL_TEXTURE_3D, texture2); |
| glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| GLColor::green.data()); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(getPerfCounters().mutableTexturesUploaded, expectedMutableTexturesUploaded); |
| } |
| |
| // Tests that mutable 3D texture is not uploaded with incompatible mip level sizes. |
| TEST_P(VulkanPerformanceCounterTest, MutableTexture3DMipLevelsWithIncompatibleSizesNoInit) |
| { |
| ANGLE_SKIP_TEST_IF(!hasMutableMipmapTextureUpload()); |
| |
| uint32_t expectedMutableTexturesUploaded = getPerfCounters().mutableTexturesUploaded; |
| |
| std::vector<GLColor> mip0Color(4 * 4 * 4, GLColor::red); |
| std::vector<GLColor> mip1Color(2 * 2 * 3, GLColor::red); |
| |
| GLTexture texture1; |
| glBindTexture(GL_TEXTURE_3D, texture1); |
| glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 4, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| mip0Color.data()); |
| glTexImage3D(GL_TEXTURE_3D, 1, GL_RGBA, 2, 2, 3, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| mip1Color.data()); |
| EXPECT_GL_NO_ERROR(); |
| |
| GLTexture texture2; |
| glBindTexture(GL_TEXTURE_3D, texture2); |
| glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| GLColor::green.data()); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(getPerfCounters().mutableTexturesUploaded, expectedMutableTexturesUploaded); |
| } |
| |
| // Tests that mutable 2D array texture is not uploaded with incompatible mip level sizes. |
| TEST_P(VulkanPerformanceCounterTest, MutableTexture2DArrayMipLevelsWithIncompatibleSizesNoInit) |
| { |
| ANGLE_SKIP_TEST_IF(!hasMutableMipmapTextureUpload()); |
| |
| uint32_t expectedMutableTexturesUploaded = getPerfCounters().mutableTexturesUploaded; |
| |
| std::vector<GLColor> mip0Color(4 * 4 * 4, GLColor::red); |
| std::vector<GLColor> mip1Color(2 * 2 * 3, GLColor::red); |
| |
| GLTexture texture1; |
| glBindTexture(GL_TEXTURE_2D_ARRAY, texture1); |
| glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, 4, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| mip0Color.data()); |
| glTexImage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RGBA, 2, 2, 3, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| mip1Color.data()); |
| EXPECT_GL_NO_ERROR(); |
| |
| GLTexture texture2; |
| glBindTexture(GL_TEXTURE_3D, texture2); |
| glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| GLColor::green.data()); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(getPerfCounters().mutableTexturesUploaded, expectedMutableTexturesUploaded); |
| } |
| |
| // Tests that mutable cubemap texture is uploaded with appropriate mip level attributes. |
| TEST_P(VulkanPerformanceCounterTest, MutableTextureCubemapCompatibleMipLevelsInit) |
| { |
| ANGLE_SKIP_TEST_IF(!hasMutableMipmapTextureUpload()); |
| |
| uint32_t expectedMutableTexturesUploaded = getPerfCounters().mutableTexturesUploaded + 1; |
| |
| std::vector<GLColor> mip0Color(4 * 4, GLColor::red); |
| std::vector<GLColor> mip1Color(2 * 2, GLColor::red); |
| |
| GLTexture texture1; |
| glBindTexture(GL_TEXTURE_CUBE_MAP, texture1); |
| for (size_t i = 0; i < 6; i++) |
| { |
| glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA, 4, 4, 0, GL_RGBA, |
| GL_UNSIGNED_BYTE, mip0Color.data()); |
| glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 1, GL_RGBA, 2, 2, 0, GL_RGBA, |
| GL_UNSIGNED_BYTE, mip1Color.data()); |
| } |
| EXPECT_GL_NO_ERROR(); |
| |
| GLTexture texture2; |
| glBindTexture(GL_TEXTURE_2D, texture2); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| GLColor::green.data()); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(getPerfCounters().mutableTexturesUploaded, expectedMutableTexturesUploaded); |
| } |
| |
| // Tests that mutable cubemap texture is not uploaded with no data or updates. |
| TEST_P(VulkanPerformanceCounterTest, MutableTextureCubemapWithNoDataNoInit) |
| { |
| ANGLE_SKIP_TEST_IF(!hasMutableMipmapTextureUpload()); |
| |
| uint32_t expectedMutableTexturesUploaded = getPerfCounters().mutableTexturesUploaded; |
| |
| GLTexture texture1; |
| glBindTexture(GL_TEXTURE_CUBE_MAP, texture1); |
| for (size_t i = 0; i < 6; i++) |
| { |
| glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA, 4, 4, 0, GL_RGBA, |
| GL_UNSIGNED_BYTE, nullptr); |
| glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 1, GL_RGBA, 2, 2, 0, GL_RGBA, |
| GL_UNSIGNED_BYTE, nullptr); |
| } |
| EXPECT_GL_NO_ERROR(); |
| |
| GLTexture texture2; |
| glBindTexture(GL_TEXTURE_2D, texture2); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| GLColor::green.data()); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(getPerfCounters().mutableTexturesUploaded, expectedMutableTexturesUploaded); |
| } |
| |
| // Tests that mutable cubemap texture is not uploaded with more than one update in a cube face. |
| TEST_P(VulkanPerformanceCounterTest, MutableTextureCubemapMultipleUpdatesForOneFaceNoInit) |
| { |
| ANGLE_SKIP_TEST_IF(!hasMutableMipmapTextureUpload()); |
| |
| uint32_t expectedMutableTexturesUploaded = getPerfCounters().mutableTexturesUploaded; |
| |
| std::vector<GLColor> mip0Color(4 * 4, GLColor::red); |
| std::vector<GLColor> mip1Color(2 * 2, GLColor::red); |
| |
| GLTexture texture1; |
| glBindTexture(GL_TEXTURE_CUBE_MAP, texture1); |
| for (size_t i = 0; i < 6; i++) |
| { |
| glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA, 4, 4, 0, GL_RGBA, |
| GL_UNSIGNED_BYTE, nullptr); |
| glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 1, GL_RGBA, 2, 2, 0, GL_RGBA, |
| GL_UNSIGNED_BYTE, nullptr); |
| } |
| EXPECT_GL_NO_ERROR(); |
| for (size_t i = 0; i < 6; i++) |
| { |
| glTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, 4, 4, GL_RGBA, |
| GL_UNSIGNED_BYTE, mip0Color.data()); |
| glTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 1, 0, 0, 2, 2, GL_RGBA, |
| GL_UNSIGNED_BYTE, mip1Color.data()); |
| } |
| glTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 1, 1, 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, |
| GLColor::red.data()); |
| |
| GLTexture texture2; |
| glBindTexture(GL_TEXTURE_2D, texture2); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| GLColor::green.data()); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(getPerfCounters().mutableTexturesUploaded, expectedMutableTexturesUploaded); |
| } |
| |
| // Tests that mutable cubemap texture is not uploaded if not complete. |
| TEST_P(VulkanPerformanceCounterTest, MutableTextureCubemapIncompleteInit) |
| { |
| ANGLE_SKIP_TEST_IF(!hasMutableMipmapTextureUpload()); |
| |
| uint32_t expectedMutableTexturesUploaded = getPerfCounters().mutableTexturesUploaded; |
| |
| std::vector<GLColor> mip0Color(4 * 4, GLColor::red); |
| std::vector<GLColor> mip1Color(2 * 2, GLColor::red); |
| |
| GLTexture texture1; |
| glBindTexture(GL_TEXTURE_CUBE_MAP, texture1); |
| glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| mip0Color.data()); |
| glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 1, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| mip1Color.data()); |
| EXPECT_GL_NO_ERROR(); |
| |
| GLTexture texture2; |
| glBindTexture(GL_TEXTURE_2D, texture2); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| GLColor::green.data()); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(getPerfCounters().mutableTexturesUploaded, expectedMutableTexturesUploaded); |
| } |
| |
| // Tests that mutable cubemap array texture is uploaded with appropriate mip level attributes. |
| TEST_P(VulkanPerformanceCounterTest, MutableTextureCubemapArrayCompatibleMipLevelsInit) |
| { |
| ANGLE_SKIP_TEST_IF(!hasMutableMipmapTextureUpload()); |
| |
| uint32_t expectedMutableTexturesUploaded = getPerfCounters().mutableTexturesUploaded + 1; |
| |
| std::vector<GLColor> mip0Color(4 * 4 * 6, GLColor::red); |
| std::vector<GLColor> mip1Color(2 * 2 * 6, GLColor::red); |
| |
| GLTexture texture1; |
| glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, texture1); |
| glTexImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, 0, GL_RGBA, 4, 4, 6, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| mip0Color.data()); |
| glTexImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, 1, GL_RGBA, 2, 2, 6, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| mip1Color.data()); |
| |
| EXPECT_GL_NO_ERROR(); |
| |
| GLTexture texture2; |
| glBindTexture(GL_TEXTURE_2D, texture2); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| GLColor::green.data()); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(getPerfCounters().mutableTexturesUploaded, expectedMutableTexturesUploaded); |
| } |
| |
| // Tests that mutable cubemap array texture is not uploaded with different layer-faces. |
| TEST_P(VulkanPerformanceCounterTest, MutableTextureCubemapArrayDifferentLayerFacesNoInit) |
| { |
| ANGLE_SKIP_TEST_IF(!hasMutableMipmapTextureUpload()); |
| |
| uint32_t expectedMutableTexturesUploaded = getPerfCounters().mutableTexturesUploaded; |
| |
| std::vector<GLColor> mip0Color(4 * 4 * 6, GLColor::red); |
| std::vector<GLColor> mip1Color(2 * 2 * 12, GLColor::red); |
| |
| GLTexture texture1; |
| glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, texture1); |
| glTexImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, 0, GL_RGBA, 4, 4, 6, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| mip0Color.data()); |
| glTexImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, 1, GL_RGBA, 2, 2, 12, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| mip1Color.data()); |
| |
| EXPECT_GL_NO_ERROR(); |
| |
| GLTexture texture2; |
| glBindTexture(GL_TEXTURE_2D, texture2); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| GLColor::green.data()); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(getPerfCounters().mutableTexturesUploaded, expectedMutableTexturesUploaded); |
| } |
| |
| // Tests that RGB texture should not break renderpass. |
| TEST_P(VulkanPerformanceCounterTest, SampleFromRGBTextureDoesNotBreakRenderPass) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| 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); |
| |
| uint64_t expectedRenderPassCount = getPerfCounters().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(); |
| |
| uint64_t actualRenderPassCount = getPerfCounters().renderPasses; |
| EXPECT_EQ(expectedRenderPassCount, actualRenderPassCount); |
| } |
| |
| // Tests that RGB texture should not break renderpass. |
| TEST_P(VulkanPerformanceCounterTest, RenderToRGBTextureDoesNotBreakRenderPass) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| 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(); |
| |
| uint64_t expectedRenderPassCount = getPerfCounters().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); |
| |
| uint64_t actualRenderPassCount = getPerfCounters().renderPasses; |
| EXPECT_EQ(expectedRenderPassCount, actualRenderPassCount); |
| } |
| |
| // Tests that changing a Texture's max level hits the descriptor set cache. |
| TEST_P(VulkanPerformanceCounterTest, ChangingMaxLevelHitsDescriptorCache) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| 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(); |
| |
| uint64_t expectedWriteDescriptorSetCount = getPerfCounters().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(); |
| |
| uint64_t actualWriteDescriptorSetCount = getPerfCounters().writeDescriptorSets; |
| EXPECT_EQ(expectedWriteDescriptorSetCount, actualWriteDescriptorSetCount); |
| } |
| |
| // Tests that two glCopyBufferSubData commands can share a barrier. |
| TEST_P(VulkanPerformanceCounterTest, IndependentBufferCopiesShareSingleBarrier) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| 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); |
| // Note: We can't use GL_STATIC_COPY. Using STATIC will cause driver to allocate a host |
| // invisible memory and issue a copyToBuffer, which will trigger outsideRenderPassCommandBuffer |
| // flush when glCopyBufferSubData is called due to read after write. That will break the |
| // expectations and cause test to fail. |
| glBufferData(GL_COPY_READ_BUFFER, sizeof(srcDataA), srcDataA, GL_DYNAMIC_COPY); |
| |
| GLBuffer dstA; |
| glBindBuffer(GL_COPY_WRITE_BUFFER, dstA); |
| // Note: We can't use GL_STATIC_COPY. Using STATIC will cause driver to allocate a host |
| // invisible memory and issue a copyToBuffer, which will trigger outsideRenderPassCommandBuffer |
| // flush when glCopyBufferSubData is called due to write after write. That will break the |
| // expectations and cause test to fail. |
| glBufferData(GL_COPY_WRITE_BUFFER, sizeof(srcDataA[0]) * 2, nullptr, GL_DYNAMIC_COPY); |
| |
| GLBuffer srcB; |
| glBindBuffer(GL_COPY_READ_BUFFER, srcB); |
| glBufferData(GL_COPY_READ_BUFFER, sizeof(srcDataB), srcDataB, GL_DYNAMIC_COPY); |
| |
| GLBuffer dstB; |
| glBindBuffer(GL_COPY_WRITE_BUFFER, dstB); |
| glBufferData(GL_COPY_WRITE_BUFFER, sizeof(srcDataB[0]) * 2, nullptr, GL_DYNAMIC_COPY); |
| |
| // We expect that ANGLE generate zero additional command buffers. |
| uint64_t expectedFlushCount = getPerfCounters().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(); |
| |
| uint64_t actualFlushCount = getPerfCounters().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) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| constexpr int kSize = 16; |
| glViewport(0, 0, kSize, kSize); |
| |
| GLFramebuffer msaaFBO; |
| glBindFramebuffer(GL_FRAMEBUFFER, msaaFBO); |
| |
| GLTexture texture; |
| glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texture); |
| 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, |
| 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(); |
| |
| EXPECT_EQ(getPerfCounters().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); |
| } |
| |
| // Test resolving a multisampled texture with blit and then invalidate the msaa buffer |
| TEST_P(VulkanPerformanceCounterTest_ES31, ResolveToFBOWithInvalidate) |
| { |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, color(Clears+0, Loads+0, LoadNones+0, Stores+1, StoreNones+0) |
| setExpectedCountersForColorOps(getPerfCounters(), 1, 0, 0, 0, 1, 0, &expected); |
| expected.colorAttachmentResolves = getPerfCounters().colorAttachmentResolves + 1; |
| |
| constexpr int kWindowWidth = 4; |
| constexpr int kWindowHeight = 4; |
| GLTexture resolveTexture; |
| glBindTexture(GL_TEXTURE_2D, resolveTexture); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kWindowWidth, kWindowHeight, 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); |
| |
| GLFramebuffer resolveFBO; |
| glBindFramebuffer(GL_FRAMEBUFFER, resolveFBO); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, resolveTexture, 0); |
| |
| GLTexture msaaTexture; |
| glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msaaTexture); |
| glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, kWindowWidth, kWindowHeight, |
| GL_FALSE); |
| |
| GLFramebuffer msaaFBO; |
| glBindFramebuffer(GL_FRAMEBUFFER, msaaFBO); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, msaaFBO, |
| 0); |
| ANGLE_GL_PROGRAM(redprogram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| |
| drawQuad(redprogram, essl1_shaders::PositionAttrib(), 0.5f); |
| // Resolve into FBO |
| glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFBO); |
| glBlitFramebuffer(0, 0, kWindowWidth, kWindowHeight, 0, 0, kWindowWidth, kWindowHeight, |
| GL_COLOR_BUFFER_BIT, GL_NEAREST); |
| |
| GLenum attachment = GL_COLOR_ATTACHMENT0; |
| glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 1, &attachment); |
| glBindFramebuffer(GL_FRAMEBUFFER, resolveFBO); |
| |
| // Top-left pixels should be all red. |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| EXPECT_EQ(expected.renderPasses, getPerfCounters().renderPasses); |
| EXPECT_EQ(expected.colorAttachmentResolves, getPerfCounters().colorAttachmentResolves); |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Test resolving different attachments of an FBO to separate FBOs then invalidate |
| TEST_P(VulkanPerformanceCounterTest_ES31, MultisampleResolveBothAttachments) |
| { |
| enum class Invalidate |
| { |
| AfterEachResolve, |
| AllAtEnd, |
| }; |
| |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| |
| uniform vec4 value0; |
| uniform vec4 value2; |
| |
| layout(location = 0) out vec4 color0; |
| layout(location = 2) out vec4 color2; |
| |
| void main() |
| { |
| color0 = value0; |
| color2 = value2; |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS); |
| glUseProgram(program); |
| const GLint color0Loc = glGetUniformLocation(program, "value0"); |
| const GLint color1Loc = glGetUniformLocation(program, "value2"); |
| |
| constexpr int kWidth = 16; |
| constexpr int kHeight = 20; |
| glViewport(0, 0, kWidth, kHeight); |
| |
| GLTexture msaa0, msaa1; |
| glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msaa0); |
| glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, kWidth, kHeight, false); |
| glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msaa1); |
| glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, kWidth, kHeight, false); |
| |
| GLFramebuffer msaaFBO; |
| glBindFramebuffer(GL_FRAMEBUFFER, msaaFBO); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, msaa0, |
| 0); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D_MULTISAMPLE, msaa1, |
| 0); |
| ASSERT_GL_NO_ERROR(); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| GLenum bufs[3] = {GL_COLOR_ATTACHMENT0, GL_NONE, GL_COLOR_ATTACHMENT2}; |
| glDrawBuffers(3, bufs); |
| |
| // Create two resolve FBOs and textures. Use different texture levels and layers. |
| GLTexture resolveTexture1; |
| glBindTexture(GL_TEXTURE_2D, resolveTexture1); |
| glTexStorage2D(GL_TEXTURE_2D, 3, GL_RGBA8, kWidth * 2, kHeight * 2); |
| |
| GLFramebuffer resolveFBO1; |
| glBindFramebuffer(GL_FRAMEBUFFER, resolveFBO1); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, resolveTexture1, 1); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| GLTexture resolveTexture2; |
| glBindTexture(GL_TEXTURE_2D_ARRAY, resolveTexture2); |
| glTexStorage3D(GL_TEXTURE_2D_ARRAY, 4, GL_RGBA8, kWidth * 4, kHeight * 4, 5); |
| |
| GLFramebuffer resolveFBO2; |
| glBindFramebuffer(GL_FRAMEBUFFER, resolveFBO2); |
| glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, resolveTexture2, 2, 3); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| auto test = [&](GLColor color0, GLColor color1, Invalidate invalidate) { |
| const GLenum discards[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT2}; |
| |
| // Resolve attachments should be used and the MSAA attachments should be invalidated. |
| // Only the resolve attachments should have Store. |
| // |
| // Expect rpCount+1, color(Clears+0, Loads+0, LoadNones+0, Stores+2, StoreNones+0) |
| angle::VulkanPerfCounters expected; |
| setExpectedCountersForColorOps(getPerfCounters(), 1, 0, 0, 0, 2, 0, &expected); |
| expected.colorAttachmentResolves = getPerfCounters().colorAttachmentResolves + 2; |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, msaaFBO); |
| glUniform4fv(color0Loc, 1, color0.toNormalizedVector().data()); |
| glUniform4fv(color1Loc, 1, color1.toNormalizedVector().data()); |
| drawQuad(program, essl31_shaders::PositionAttrib(), 0.5f, 1.0f, true); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Resolve the first attachment |
| glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFBO1); |
| glReadBuffer(GL_COLOR_ATTACHMENT0); |
| glBlitFramebuffer(0, 0, kWidth, kHeight, 0, 0, kWidth, kHeight, GL_COLOR_BUFFER_BIT, |
| GL_NEAREST); |
| ASSERT_GL_NO_ERROR(); |
| |
| if (invalidate == Invalidate::AfterEachResolve) |
| { |
| glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 1, discards); |
| } |
| |
| // Resolve the second attachment |
| glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFBO2); |
| glReadBuffer(GL_COLOR_ATTACHMENT2); |
| glBlitFramebuffer(0, 0, kWidth, kHeight, 0, 0, kWidth, kHeight, GL_COLOR_BUFFER_BIT, |
| GL_NEAREST); |
| ASSERT_GL_NO_ERROR(); |
| |
| if (invalidate == Invalidate::AfterEachResolve) |
| { |
| glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 1, discards + 1); |
| } |
| else if (invalidate == Invalidate::AllAtEnd) |
| { |
| glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 2, discards); |
| } |
| |
| glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFBO1); |
| EXPECT_PIXEL_RECT_EQ(0, 0, kWidth, kHeight, color0); |
| glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFBO2); |
| EXPECT_PIXEL_RECT_EQ(0, 0, kWidth, kHeight, color1); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_EQ(expected.renderPasses, getPerfCounters().renderPasses); |
| EXPECT_EQ(expected.colorAttachmentResolves, getPerfCounters().colorAttachmentResolves); |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| }; |
| |
| test(GLColor::blue, GLColor::yellow, Invalidate::AfterEachResolve); |
| test(GLColor::cyan, GLColor::magenta, Invalidate::AllAtEnd); |
| } |
| |
| // Test resolving the depth/stencil attachment |
| TEST_P(VulkanPerformanceCounterTest_ES31, MultisampleDepthStencilResolve) |
| { |
| ANGLE_SKIP_TEST_IF(!hasDepthStencilResolveThroughAttachment()); |
| |
| constexpr int kWidth = 24; |
| constexpr int kHeight = 12; |
| glViewport(0, 0, kWidth, kHeight); |
| |
| GLFramebuffer msaaFBO; |
| glBindFramebuffer(GL_FRAMEBUFFER, msaaFBO); |
| |
| GLRenderbuffer depthStencil; |
| glBindRenderbuffer(GL_RENDERBUFFER, depthStencil); |
| glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, kWidth, kHeight); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, |
| depthStencil); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Create two resolve FBOs and textures. Use different texture levels and layers. |
| GLTexture resolveTexture; |
| glBindTexture(GL_TEXTURE_2D, resolveTexture); |
| glTexStorage2D(GL_TEXTURE_2D, 4, GL_DEPTH24_STENCIL8, kWidth * 4, kHeight * 4); |
| |
| GLFramebuffer resolveFBO; |
| glBindFramebuffer(GL_FRAMEBUFFER, resolveFBO); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, |
| resolveTexture, 2); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| ANGLE_GL_PROGRAM(red, essl1_shaders::vs::Passthrough(), essl1_shaders::fs::Red()); |
| |
| // Resolve attachment should be used and the MSAA attachment should be invalidated. |
| // Only the resolve attachment should have Store. |
| // |
| // Expect rpCount+1, depth(Clears+0, Loads+0, LoadNones+0, Stores+1, StoreNones+0), |
| // stencil(Clears+0, Loads+0, LoadNones+0, Stores+1, StoreNones+0) |
| angle::VulkanPerfCounters expected; |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 0, 0, 0, 1, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 0, 0, 1, 0, &expected); |
| expected.depthAttachmentResolves = getPerfCounters().depthAttachmentResolves + 1; |
| expected.stencilAttachmentResolves = getPerfCounters().stencilAttachmentResolves + 1; |
| |
| glEnable(GL_DEPTH_TEST); |
| glDepthFunc(GL_ALWAYS); |
| glDepthMask(GL_TRUE); |
| glEnable(GL_STENCIL_TEST); |
| glStencilFunc(GL_ALWAYS, 0x55, 0xFF); |
| glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); |
| glStencilMask(0xFF); |
| |
| // Initialize the depth/stencil image |
| glBindFramebuffer(GL_FRAMEBUFFER, msaaFBO); |
| drawQuad(red, essl1_shaders::PositionAttrib(), 0.3f); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Resolve depth and stencil, then verify the results |
| glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFBO); |
| glBlitFramebuffer(0, 0, kWidth, kHeight, 0, 0, kWidth, kHeight, |
| GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST); |
| |
| // Invalidate depth/stencil |
| const GLenum discards[] = {GL_DEPTH_STENCIL_ATTACHMENT}; |
| glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 1, discards); |
| |
| // Break the render pass |
| glFinish(); |
| |
| EXPECT_EQ(expected.renderPasses, getPerfCounters().renderPasses); |
| EXPECT_EQ(expected.depthAttachmentResolves, getPerfCounters().depthAttachmentResolves); |
| EXPECT_EQ(expected.stencilAttachmentResolves, getPerfCounters().stencilAttachmentResolves); |
| EXPECT_DEPTH_OP_COUNTERS(getPerfCounters(), expected); |
| EXPECT_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| // Ensures a read-only depth-stencil feedback loop works in a single RenderPass. |
| TEST_P(VulkanPerformanceCounterTest, ReadOnlyDepthStencilFeedbackLoopUsesSingleRenderPass) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| 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(); |
| |
| uint64_t expectedRenderPassCount = getPerfCounters().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(); |
| |
| uint64_t actualRenderPassCount = getPerfCounters().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(); |
| } |
| |
| // Ensures clear color buffer and read-only depth-stencil works in a single RenderPass (as seen in |
| // gfxbench manhattan). |
| TEST_P(VulkanPerformanceCounterTest, ClearColorBufferAndReadOnlyDepthStencilUsesSingleRenderPass) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| 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()); |
| |
| setupQuadVertexBuffer(0.5f, 1.0f); |
| glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); |
| glEnableVertexAttribArray(0); |
| |
| // Set up a depth texture and draw a quad to initialize it with known 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 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); |
| glDepthMask(GL_TRUE); |
| glUseProgram(redProgram); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Setup a color texture and FBO with color and read only depth attachment. |
| 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); |
| uint64_t expectedRenderPassCount = getPerfCounters().renderPasses + 1; |
| // First clear color buffer. This should not cause this render pass to switch to read only depth |
| // stencil mode |
| glClearColor(0, 0, 0, 0); |
| glClear(GL_COLOR_BUFFER_BIT); |
| // Now set up the read-only feedback loop. |
| glDepthMask(GL_FALSE); |
| glEnable(GL_DEPTH_TEST); |
| glBindTexture(GL_TEXTURE_2D, depthTexture); |
| glUseProgram(texProgram); |
| 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(); |
| |
| uint64_t actualRenderPassCount = getPerfCounters().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(); |
| } |
| |
| // Ensures an actual depth feedback loop (i.e, render and sample from same texture which is |
| // undefined behavior according to spec) works in a single RenderPass. This is adoptted from usage |
| // pattern from gangstar_vegas. |
| TEST_P(VulkanPerformanceCounterTest, DepthFeedbackLoopUsesSingleRenderPass) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| 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(); |
| |
| uint64_t expectedRenderPassCount = getPerfCounters().renderPasses + 1; |
| |
| // Start new RenderPass with depth write enabled and form the actual feedback loop. |
| glBindFramebuffer(GL_FRAMEBUFFER, depthAndColorFBO); |
| glDepthMask(true); |
| glUseProgram(texProgram); |
| glBindTexture(GL_TEXTURE_2D, depthTexture); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Now set up the read-only feedback loop. |
| glDepthMask(false); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| ASSERT_GL_NO_ERROR(); |
| |
| uint64_t actualRenderPassCount = getPerfCounters().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 invalidate followed by masked draws results in no load and store. |
| // |
| // - Scenario: invalidate, mask color, draw |
| TEST_P(VulkanPerformanceCounterTest, ColorInvalidateMaskDraw) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, color(Clears+0, Loads+0, LoadNones+1, Stores+0, StoreNones+1) |
| setExpectedCountersForColorOps(getPerfCounters(), 1, 0, 0, 1, 0, 1, &expected); |
| |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| setupForColorOpsTest(&framebuffer, &texture); |
| |
| // Invalidate |
| const GLenum discards[] = {GL_COLOR_ATTACHMENT0}; |
| glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, discards); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Mask color output |
| glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); |
| |
| // Draw |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_EQ(expected.renderPasses, getPerfCounters().renderPasses); |
| |
| // Use swapBuffers and then check how many loads and stores were actually done |
| swapBuffers(); |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| |
| // Start and end another render pass without color mask |
| ++expected.renderPasses; |
| ++expected.colorStoreOpStores; |
| |
| glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Test glFenceSync followed by glInvalidateFramebuffer should still allow storeOp being optimized |
| // out. |
| TEST_P(VulkanPerformanceCounterTest, FenceSyncAndColorInvalidate) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, color(Clears+0, Loads+0, LoadNones+0, Stores+0, StoreNones+0) |
| setExpectedCountersForColorOps(getPerfCounters(), 1, 0, 0, 0, 0, 0, &expected); |
| |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| setupForColorOpsTest(&framebuffer, &texture); |
| |
| // Draw |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Insert a fence which should trigger a deferred renderPass end |
| GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); |
| |
| // Invalidate FBO. This should still allow vulkan backend to optimize out the storeOp, even |
| // though we just called glFenceSync . |
| const GLenum discards[] = {GL_COLOR_ATTACHMENT0}; |
| glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, discards); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Use swapBuffers and then check how many loads and stores were actually done |
| swapBuffers(); |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED); |
| } |
| // Tests that invalidate followed by discarded draws results in no load and store. |
| // |
| // - Scenario: invalidate, rasterizer discard, draw |
| TEST_P(VulkanPerformanceCounterTest, ColorInvalidateDiscardDraw) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, color(Clears+0, Loads+0, LoadNones+1, Stores+0, StoreNones+1) |
| setExpectedCountersForColorOps(getPerfCounters(), 1, 0, 0, 1, 0, 1, &expected); |
| |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| setupForColorOpsTest(&framebuffer, &texture); |
| |
| // Invalidate |
| const GLenum discards[] = {GL_COLOR_ATTACHMENT0}; |
| glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, discards); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Mask color output |
| glEnable(GL_RASTERIZER_DISCARD); |
| |
| // Draw |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_EQ(expected.renderPasses, getPerfCounters().renderPasses); |
| |
| // Use swapBuffers and then check how many loads and stores were actually done |
| swapBuffers(); |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| |
| // Start and end another render pass without color mask |
| ++expected.renderPasses; |
| ++expected.colorStoreOpStores; |
| |
| glDisable(GL_RASTERIZER_DISCARD); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Tests that masked draws results in no load and store. |
| // |
| // - Scenario: mask color, draw |
| TEST_P(VulkanPerformanceCounterTest, ColorMaskedDraw) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, color(Clears+0, Loads+0, LoadNones+1, Stores+0, StoreNones+1) |
| setExpectedCountersForColorOps(getPerfCounters(), 1, 0, 0, 1, 0, 1, &expected); |
| |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| setupForColorOpsTest(&framebuffer, &texture); |
| |
| // Initialize the color buffer |
| glClearColor(1, 0, 0, 1); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| // Mask color output |
| glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); |
| |
| // Draw |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_EQ(expected.renderPasses, getPerfCounters().renderPasses); |
| |
| // Break the render pass and check how many loads and stores were actually done |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| |
| // Start and end another render pass without color mask |
| ++expected.renderPasses; |
| ++expected.colorLoadOpLoads; |
| ++expected.colorStoreOpStores; |
| |
| glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Tests that discarded draws results in no load and store. |
| // |
| // - Scenario: rasterizer discard, draw |
| TEST_P(VulkanPerformanceCounterTest, ColorDiscardDraw) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, color(Clears+0, Loads+0, LoadNones+1, Stores+0, StoreNones+1) |
| setExpectedCountersForColorOps(getPerfCounters(), 1, 0, 0, 1, 0, 1, &expected); |
| |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| setupForColorOpsTest(&framebuffer, &texture); |
| |
| // Initialize the color buffer |
| glClearColor(1, 0, 0, 1); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| // Mask color output |
| glEnable(GL_RASTERIZER_DISCARD); |
| |
| // Draw |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_EQ(expected.renderPasses, getPerfCounters().renderPasses); |
| |
| // Break the render pass and check how many loads and stores were actually done |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| |
| // Start and end another render pass without color mask |
| ++expected.renderPasses; |
| ++expected.colorLoadOpLoads; |
| ++expected.colorStoreOpStores; |
| |
| glDisable(GL_RASTERIZER_DISCARD); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Test that read-only color usage results in load but no store. |
| // |
| // - Scenario: mask color, framebuffer fetch draw |
| TEST_P(VulkanPerformanceCounterTest_ES31, ColorMaskedFramebufferFetchDraw) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent")); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, color(Clears+0, Loads+1, LoadNones+0, Stores+0, StoreNones+1) |
| setExpectedCountersForColorOps(getPerfCounters(), 1, 0, 1, 0, 0, 1, &expected); |
| |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| setupForColorOpsTest(&framebuffer, &texture); |
| |
| GLBuffer buffer; |
| const GLColor kClearColor(40, 70, 100, 150); |
| maskedFramebufferFetchDraw(kClearColor, buffer); |
| |
| EXPECT_EQ(expected.renderPasses, getPerfCounters().renderPasses); |
| |
| // Break the render pass and check how many loads and stores were actually done |
| EXPECT_PIXEL_COLOR_NEAR(0, 0, kClearColor, 1); |
| |
| maskedFramebufferFetchDrawVerify(kClearColor, buffer); |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Tests that clear after masked draws is optimized to use loadOp |
| // |
| // - Scenario: clear, mask color, draw, clear |
| TEST_P(VulkanPerformanceCounterTest, ColorClearMaskedDrawThenClear) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, color(Clears+1, Loads+0, LoadNones+0, Stores+1, StoreNones+0) |
| // No vkCmdClearAttachments should be issued. |
| setExpectedCountersForColorOps(getPerfCounters(), 1, 1, 0, 0, 1, 0, &expected); |
| expected.colorClearAttachments = getPerfCounters().colorClearAttachments; |
| |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| setupForColorOpsTest(&framebuffer, &texture); |
| |
| // Clear color first |
| glClearColor(1, 0, 0, 1); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| // Mask color output |
| glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); |
| |
| // Draw |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Then clear color again. This clear is moved up to loadOp, overriding the initial clear. |
| glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
| glClearColor(0, 0, 1, 1); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| EXPECT_EQ(expected.renderPasses, getPerfCounters().renderPasses); |
| |
| // Break the render pass and check how many loads and stores were actually done |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| EXPECT_CLEAR_ATTACHMENTS_COUNTER(expected.colorClearAttachments, |
| getPerfCounters().colorClearAttachments); |
| } |
| |
| // Test that clear of read-only color is not reordered with the draw. |
| // |
| // - Scenario: mask color, framebuffer fetch draw, clear |
| TEST_P(VulkanPerformanceCounterTest_ES31, ColorMaskedFramebufferFetchDrawThenClear) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent")); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, color(Clears+0, Loads+1, LoadNones+0, Stores+1, StoreNones+0) |
| // vkCmdClearAttachments should be used for the second clear. |
| setExpectedCountersForColorOps(getPerfCounters(), 1, 0, 1, 0, 1, 0, &expected); |
| expected.colorClearAttachments = getPerfCounters().colorClearAttachments + 1; |
| |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| setupForColorOpsTest(&framebuffer, &texture); |
| |
| GLBuffer buffer; |
| const GLColor kClearColor(40, 70, 100, 150); |
| maskedFramebufferFetchDraw(kClearColor, buffer); |
| |
| glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
| glClearColor(1, 0, 0, 1); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| EXPECT_EQ(expected.renderPasses, getPerfCounters().renderPasses); |
| |
| // Break the render pass and check how many loads and stores were actually done |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| maskedFramebufferFetchDrawVerify(kClearColor, buffer); |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| EXPECT_CLEAR_ATTACHMENTS_COUNTER(expected.colorClearAttachments, |
| getPerfCounters().colorClearAttachments); |
| } |
| |
| // Test that masked draw after a framebuffer fetch render pass doesn't load color. |
| // |
| // - Scenario: framebuffer fetch render pass, mask color, normal draw |
| TEST_P(VulkanPerformanceCounterTest_ES31, FramebufferFetchRenderPassThenColorMaskedDraw) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent")); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, color(Clears+0, Loads+1, LoadNones+0, Stores+0, StoreNones+1) |
| setExpectedCountersForColorOps(getPerfCounters(), 1, 0, 1, 0, 0, 1, &expected); |
| |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| setupForColorOpsTest(&framebuffer, &texture); |
| |
| GLBuffer buffer; |
| const GLColor kClearColor(40, 70, 100, 150); |
| maskedFramebufferFetchDraw(kClearColor, buffer); |
| |
| EXPECT_EQ(expected.renderPasses, getPerfCounters().renderPasses); |
| |
| // Break the render pass and check how many loads and stores were actually done |
| EXPECT_PIXEL_COLOR_NEAR(0, 0, kClearColor, 1); |
| |
| maskedFramebufferFetchDrawVerify(kClearColor, buffer); |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| |
| // Start another render pass, and don't use framebuffer fetch. Color is masked, so it should be |
| // neither loaded nor stored. |
| |
| // Expect rpCount+1, color(Clears+0, Loads+0, LoadNones+1, Stores+0, StoreNones+1) |
| setExpectedCountersForColorOps(getPerfCounters(), 1, 0, 0, 1, 0, 1, &expected); |
| |
| glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_EQ(expected.renderPasses, getPerfCounters().renderPasses); |
| |
| // Break the render pass and check how many loads and stores were actually done |
| EXPECT_PIXEL_COLOR_NEAR(0, 0, kClearColor, 1); |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Tests that clear after unused depth/stencil is optimized to use loadOp |
| // |
| // - Scenario: disable depth/stencil, draw, clear |
| TEST_P(VulkanPerformanceCounterTest, DepthStencilMaskedDrawThenClear) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| // This optimization is not implemented when this workaround is in effect. |
| ANGLE_SKIP_TEST_IF(hasPreferDrawOverClearAttachments()); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, depth(Clears+1, Loads+0, LoadNones+0, Stores+1, StoreNones+0), |
| // stencil(Clears+1, Loads+0, LoadNones+0, Stores+1, StoreNones+0) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 1, 0, 0, 1, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 1, 0, 0, 1, 0, &expected); |
| |
| // No vkCmdClearAttachments should be issued. |
| expected.depthClearAttachments = getPerfCounters().depthClearAttachments; |
| expected.stencilClearAttachments = getPerfCounters().stencilClearAttachments; |
| |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| GLRenderbuffer renderbuffer; |
| setupForDepthStencilOpsTest(&framebuffer, &texture, &renderbuffer); |
| |
| // Disable depth/stencil |
| glDisable(GL_DEPTH_TEST); |
| glDisable(GL_STENCIL_TEST); |
| |
| // Issue a draw call |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| |
| // Clear depth/stencil |
| glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); |
| |
| EXPECT_EQ(expected.renderPasses, getPerfCounters().renderPasses); |
| |
| // Break the render pass and check how many loads and stores were actually done |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| EXPECT_CLEAR_ATTACHMENTS_COUNTER(expected.depthClearAttachments, |
| getPerfCounters().depthClearAttachments); |
| EXPECT_CLEAR_ATTACHMENTS_COUNTER(expected.stencilClearAttachments, |
| getPerfCounters().depthClearAttachments); |
| } |
| |
| // Tests that depth compare function change, get correct loadop for depth buffer |
| // |
| // - Scenario: depth test enabled, depth write mask = 0, |
| // clear depth, draw red quad with compare function always, |
| // and then draw green quad with compare function less equal |
| TEST_P(VulkanPerformanceCounterTest, DepthFunctionDynamicChangeLoadOp) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| // This optimization is not implemented when this workaround is in effect. |
| ANGLE_SKIP_TEST_IF(hasPreferDrawOverClearAttachments()); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, depth(Clears+1, Loads+0, LoadNones+0, Stores+0, StoreNones+0), |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 1, 0, 0, 0, 0, &expected); |
| |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| GLRenderbuffer renderbuffer; |
| setupForColorDepthOpsTest(&framebuffer, &texture, &renderbuffer); |
| |
| // Clear color and depth. |
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
| |
| // No depth write |
| glDepthMask(GL_FALSE); |
| // Depth function always |
| glDepthFunc(GL_ALWAYS); |
| |
| // Draw read quad. |
| ANGLE_GL_PROGRAM(redprogram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| drawQuad(redprogram, essl1_shaders::PositionAttrib(), 0.5f); |
| |
| // Depth function switch to less equal |
| glDepthFunc(GL_LEQUAL); |
| |
| // Draw green quad. |
| ANGLE_GL_PROGRAM(greenprogram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); |
| drawQuad(greenprogram, essl1_shaders::PositionAttrib(), 0.7f); |
| |
| GLenum attachments = GL_DEPTH_ATTACHMENT; |
| glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, &attachments); |
| |
| EXPECT_EQ(expected.renderPasses, getPerfCounters().renderPasses); |
| |
| // Break the render pass and check how many clears were actually done |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| EXPECT_CLEAR_ATTACHMENTS_COUNTER(expected.depthLoadOpClears, |
| getPerfCounters().depthLoadOpClears); |
| } |
| |
| // Tests that common PUBG MOBILE case does not break render pass, and that counts are correct: |
| // |
| // - Scenario: invalidate, disable, draw |
| TEST_P(VulkanPerformanceCounterTest, DepthStencilInvalidateDisableDraw) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, depth(Clears+1, Loads+0, LoadNones+0, Stores+0, StoreNones+0), |
| // stencil(Clears+0, Loads+0, LoadNones+0, Stores+0, StoreNones+0) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 1, 0, 0, 0, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 0, 0, 0, 0, &expected); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| GLRenderbuffer renderbuffer; |
| setupClearAndDrawForDepthStencilOpsTest(&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, getPerfCounters().renderPasses); |
| |
| // Use swapBuffers and then check how many loads and stores were actually done |
| swapBuffers(); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| |
| // Start and end another render pass, to check that the load ops are as expected |
| setAndIncrementDepthStencilLoadCountersForOpsTest(getPerfCounters(), 0, 0, &expected); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| swapBuffers(); |
| EXPECT_DEPTH_STENCIL_LOAD_OP_COUNTERS(getPerfCounters(), 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) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, depth(Clears+1, Loads+0, LoadNones+0, Stores+0, StoreNones+0), |
| // stencil(Clears+0, Loads+0, LoadNones+0, Stores+0, StoreNones+0) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 1, 0, 0, 0, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 0, 0, 0, 0, &expected); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| GLRenderbuffer renderbuffer; |
| setupClearAndDrawForDepthStencilOpsTest(&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, getPerfCounters().renderPasses); |
| |
| // Use swapBuffers and then check how many loads and stores were actually done |
| swapBuffers(); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| |
| // Start and end another render pass, to check that the load ops are as expected |
| setAndIncrementDepthStencilLoadCountersForOpsTest(getPerfCounters(), 0, 0, &expected); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| swapBuffers(); |
| EXPECT_DEPTH_STENCIL_LOAD_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Tests that another case does not break render pass, and that counts are correct: |
| // |
| // - Scenario: disable, draw, invalidate, enable |
| TEST_P(VulkanPerformanceCounterTest, DisableDrawInvalidateEnable) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, depth(Clears+1, Loads+0, LoadNones+0, Stores+0, StoreNones+0), |
| // stencil(Clears+0, Loads+0, LoadNones+0, Stores+0, StoreNones+0) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 1, 0, 0, 0, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 0, 0, 0, 0, &expected); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| GLRenderbuffer renderbuffer; |
| setupClearAndDrawForDepthStencilOpsTest(&program, &framebuffer, &texture, &renderbuffer, false); |
| |
| // Execute the scenario that this test is for: |
| |
| // Note: setupClearAndDrawForDepthStencilOpsTest() 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, getPerfCounters().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(); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| |
| // Start and end another render pass, to check that the load ops are as expected |
| setAndIncrementDepthStencilLoadCountersForOpsTest(getPerfCounters(), 0, 0, &expected); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| swapBuffers(); |
| EXPECT_DEPTH_STENCIL_LOAD_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Tests that common TRex case does not break render pass, and that counts are correct: |
| // |
| // - Scenario: invalidate |
| TEST_P(VulkanPerformanceCounterTest, DepthStencilInvalidate) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, depth(Clears+1, Loads+0, LoadNones+0, Stores+0, StoreNones+0), |
| // stencil(Clears+0, Loads+0, LoadNones+0, Stores+0, StoreNones+0) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 1, 0, 0, 0, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 0, 0, 0, 0, &expected); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| GLRenderbuffer renderbuffer; |
| setupClearAndDrawForDepthStencilOpsTest(&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, getPerfCounters().renderPasses); |
| |
| // Use swapBuffers and then check how many loads and stores were actually done |
| swapBuffers(); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| |
| // Start and end another render pass, to check that the load ops are as expected |
| setAndIncrementDepthStencilLoadCountersForOpsTest(getPerfCounters(), 0, 0, &expected); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| swapBuffers(); |
| EXPECT_DEPTH_STENCIL_LOAD_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Similar to Invalidate, but uses glInvalidateSubFramebuffer such that the given area covers the |
| // whole framebuffer. |
| TEST_P(VulkanPerformanceCounterTest, DepthStencilInvalidateSub) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, depth(Clears+1, Loads+0, LoadNones+0, Stores+0, StoreNones+0), |
| // stencil(Clears+0, Loads+0, LoadNones+0, Stores+0, StoreNones+0) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 1, 0, 0, 0, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 0, 0, 0, 0, &expected); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| GLRenderbuffer renderbuffer; |
| setupClearAndDrawForDepthStencilOpsTest(&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, kOpsTestSize + 200, |
| kOpsTestSize + 200); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Ensure that the render pass wasn't broken |
| EXPECT_EQ(expected.renderPasses, getPerfCounters().renderPasses); |
| |
| // Use swapBuffers and then check how many loads and stores were actually done |
| swapBuffers(); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| |
| // Start and end another render pass, to check that the load ops are as expected |
| setAndIncrementDepthStencilLoadCountersForOpsTest(getPerfCounters(), 0, 0, &expected); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| swapBuffers(); |
| EXPECT_DEPTH_STENCIL_LOAD_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Similar to InvalidateSub, but uses glInvalidateSubFramebuffer such that the given area does NOT |
| // covers the whole framebuffer. |
| TEST_P(VulkanPerformanceCounterTest, DepthStencilPartialInvalidateSub) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, depth(Clears+1, Loads+0, LoadNones+0, Stores+1, StoreNones+0), |
| // stencil(Clears+1, Loads+0, LoadNones+0, Stores+1, StoreNones+0) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 1, 0, 0, 1, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 1, 0, 0, 1, 0, &expected); |
| |
| // Create the framebuffer and make sure depth/stencil have valid contents. |
| ANGLE_GL_PROGRAM(drawRed, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| GLRenderbuffer renderbuffer; |
| setupClearAndDrawForDepthStencilOpsTest(&drawRed, &framebuffer, &texture, &renderbuffer, true); |
| |
| // Break the render pass so depth/stencil values are stored. |
| EXPECT_EQ(expected.renderPasses, getPerfCounters().renderPasses); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| |
| // Start a new render pass that is scissored. Depth/stencil should be loaded. The draw call is |
| // followed by an invalidate, so store shouldn't happen. |
| |
| // Expect rpCount+1, depth(Clears+0, Loads+1, LoadNones+0, Stores+0, StoreNones+0), |
| // stencil(Clears+0, Loads+1, LoadNones+0, Stores+0, StoreNones+0) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 0, 1, 0, 0, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 1, 0, 0, 0, &expected); |
| |
| glEnable(GL_SCISSOR_TEST); |
| glScissor(kOpsTestSize / 8, kOpsTestSize / 4, kOpsTestSize / 2, kOpsTestSize / 3); |
| |
| glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); |
| glStencilFunc(GL_ALWAYS, 0x55, 0xFF); |
| glDepthFunc(GL_ALWAYS); |
| |
| ANGLE_GL_PROGRAM(drawGreen, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); |
| drawQuad(drawGreen, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| const GLenum discards[] = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT}; |
| glInvalidateSubFramebuffer(GL_FRAMEBUFFER, 2, discards, kOpsTestSize / 8, kOpsTestSize / 8, |
| 7 * kOpsTestSize / 8, 7 * kOpsTestSize / 8); |
| |
| // Break the render pass so depth/stencil values are discarded. |
| EXPECT_EQ(expected.renderPasses, getPerfCounters().renderPasses); |
| EXPECT_PIXEL_COLOR_EQ(kOpsTestSize / 2, kOpsTestSize / 2, GLColor::green); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| |
| // Start another render pass without scissor. Because parts of the framebuffer attachments were |
| // not invalidated, depth/stencil should be loaded. |
| |
| // Expect rpCount+1, depth(Clears+0, Loads+1, LoadNones+0, Stores+1, StoreNones+0), |
| // stencil(Clears+0, Loads+1, LoadNones+0, Stores+1, StoreNones+0) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 0, 1, 0, 1, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 1, 0, 1, 0, &expected); |
| |
| glDisable(GL_SCISSOR_TEST); |
| |
| ANGLE_GL_PROGRAM(drawBlue, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue()); |
| drawQuad(drawBlue, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Verify results |
| EXPECT_EQ(expected.renderPasses, getPerfCounters().renderPasses); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Tests that another case does not break render pass, and that counts are correct: |
| // |
| // - Scenario: invalidate, draw |
| TEST_P(VulkanPerformanceCounterTest, DepthStencilInvalidateDraw) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, depth(Clears+1, Loads+0, LoadNones+0, Stores+1, StoreNones+0), |
| // stencil(Clears+0, Loads+0, LoadNones+0, Stores+0, StoreNones+0) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 1, 0, 0, 1, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 0, 0, 0, 0, &expected); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| GLRenderbuffer renderbuffer; |
| setupClearAndDrawForDepthStencilOpsTest(&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, getPerfCounters().renderPasses); |
| |
| // Use swapBuffers and then check how many loads and stores were actually done |
| swapBuffers(); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| |
| // Start and end another render pass, to check that the load ops are as expected |
| setAndIncrementDepthStencilLoadCountersForOpsTest(getPerfCounters(), 1, 0, &expected); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| swapBuffers(); |
| EXPECT_DEPTH_STENCIL_LOAD_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Tests that another case does not break render pass, and that counts are correct: |
| // |
| // - Scenario: invalidate, draw, disable |
| TEST_P(VulkanPerformanceCounterTest, DepthStencilInvalidateDrawDisable) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| // http://anglebug.com/6857 |
| ANGLE_SKIP_TEST_IF(IsLinux() && IsAMD() && IsVulkan()); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, depth(Clears+1, Loads+0, LoadNones+0, Stores+1, StoreNones+0), |
| // stencil(Clears+0, Loads+0, LoadNones+1, Stores+0, StoreNones+1) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 1, 0, 0, 1, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 0, 1, 0, 1, &expected); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| GLRenderbuffer renderbuffer; |
| setupClearAndDrawForDepthStencilOpsTest(&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, getPerfCounters().renderPasses); |
| |
| // Break the render pass and then check how many loads and stores were actually done |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| |
| // Start and end another render pass, to check that the load ops are as expected |
| setAndIncrementDepthStencilLoadCountersForOpsTest(getPerfCounters(), 1, 1, &expected); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| EXPECT_DEPTH_STENCIL_LOAD_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Tests that another case does not break render pass, and that counts are correct: |
| // |
| // - Scenario: invalidate, disable, draw, enable |
| TEST_P(VulkanPerformanceCounterTest, DepthStencilInvalidateDisableDrawEnable) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, depth(Clears+1, Loads+0, LoadNones+0, Stores+0, StoreNones+0), |
| // stencil(Clears+0, Loads+0, LoadNones+0, Stores+0, StoreNones+0) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 1, 0, 0, 0, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 0, 0, 0, 0, &expected); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| GLRenderbuffer renderbuffer; |
| setupClearAndDrawForDepthStencilOpsTest(&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, getPerfCounters().renderPasses); |
| |
| // Use swapBuffers and then check how many loads and stores were actually done |
| swapBuffers(); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| |
| // Start and end another render pass, to check that the load ops are as expected |
| setAndIncrementDepthStencilLoadCountersForOpsTest(getPerfCounters(), 0, 0, &expected); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| swapBuffers(); |
| EXPECT_DEPTH_STENCIL_LOAD_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Tests that another case does not break render pass, and that counts are correct: |
| // |
| // - Scenario: invalidate, disable, draw, enable, draw |
| TEST_P(VulkanPerformanceCounterTest, DepthStencilInvalidateDisableDrawEnableDraw) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, depth(Clears+1, Loads+0, LoadNones+0, Stores+1, StoreNones+0), |
| // stencil(Clears+0, Loads+0, LoadNones+0, Stores+1, StoreNones+0) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 1, 0, 0, 1, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 0, 0, 1, 0, &expected); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| GLRenderbuffer renderbuffer; |
| setupClearAndDrawForDepthStencilOpsTest(&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); |
| glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); |
| |
| // 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, getPerfCounters().renderPasses); |
| |
| // Break the render pass and then check how many loads and stores were actually done |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| |
| // Start and end another render pass, to check that the load ops are as expected |
| setAndIncrementDepthStencilLoadCountersForOpsTest(getPerfCounters(), 1, 1, &expected); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| EXPECT_DEPTH_STENCIL_LOAD_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Tests that another case does not break render pass, and that counts are correct: |
| // |
| // - Scenario: invalidate, draw, disable, enable |
| TEST_P(VulkanPerformanceCounterTest, DepthStencilInvalidateDrawDisableEnable) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, depth(Clears+1, Loads+0, LoadNones+0, Stores+1, StoreNones+0), |
| // stencil(Clears+0, Loads+0, LoadNones+1, Stores+0, StoreNones+1) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 1, 0, 0, 1, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 0, 1, 0, 1, &expected); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| GLRenderbuffer renderbuffer; |
| setupClearAndDrawForDepthStencilOpsTest(&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, getPerfCounters().renderPasses); |
| |
| // Break the render pass and then check how many loads and stores were actually done |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| |
| // Start and end another render pass, to check that the load ops are as expected |
| setAndIncrementDepthStencilLoadCountersForOpsTest(getPerfCounters(), 1, 0, &expected); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| EXPECT_DEPTH_STENCIL_LOAD_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Tests that another case does not break render pass, and that counts are correct: |
| // |
| // - Scenario: invalidate, draw, disable, enable, invalidate |
| TEST_P(VulkanPerformanceCounterTest, DepthStencilInvalidateDrawDisableEnableInvalidate) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, depth(Clears+1, Loads+0, LoadNones+0, Stores+0, StoreNones+0), |
| // stencil(Clears+0, Loads+0, LoadNones+0, Stores+0, StoreNones+0) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 1, 0, 0, 0, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 0, 0, 0, 0, &expected); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| GLRenderbuffer renderbuffer; |
| setupClearAndDrawForDepthStencilOpsTest(&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, getPerfCounters().renderPasses); |
| |
| // Use swapBuffers and then check how many loads and stores were actually done |
| swapBuffers(); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| |
| // Start and end another render pass, to check that the load ops are as expected |
| setAndIncrementDepthStencilLoadCountersForOpsTest(getPerfCounters(), 0, 0, &expected); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| swapBuffers(); |
| EXPECT_DEPTH_STENCIL_LOAD_OP_COUNTERS(getPerfCounters(), 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, DepthStencilInvalidateDrawDisableEnableInvalidateDraw) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, depth(Clears+1, Loads+0, LoadNones+0, Stores+1, StoreNones+0), |
| // stencil(Clears+0, Loads+0, LoadNones+0, Stores+0, StoreNones+0) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 1, 0, 0, 1, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 0, 0, 0, 0, &expected); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| GLRenderbuffer renderbuffer; |
| setupClearAndDrawForDepthStencilOpsTest(&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, getPerfCounters().renderPasses); |
| |
| // Use swapBuffers and then check how many loads and stores were actually done |
| swapBuffers(); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| |
| // Start and end another render pass, to check that the load ops are as expected |
| setAndIncrementDepthStencilLoadCountersForOpsTest(getPerfCounters(), 1, 0, &expected); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| swapBuffers(); |
| EXPECT_DEPTH_STENCIL_LOAD_OP_COUNTERS(getPerfCounters(), 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, DepthStencilInvalidateDisableEnableDraw) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, depth(Clears+1, Loads+0, LoadNones+0, Stores+1, StoreNones+0), |
| // stencil(Clears+0, Loads+0, LoadNones+1, Stores+0, StoreNones+1) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 1, 0, 0, 1, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 0, 1, 0, 1, &expected); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| GLRenderbuffer renderbuffer; |
| setupClearAndDrawForDepthStencilOpsTest(&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, getPerfCounters().renderPasses); |
| |
| // Use swapBuffers and then check how many loads and stores were actually done |
| swapBuffers(); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| |
| // Start and end another render pass, to check that the load ops are as expected |
| setAndIncrementDepthStencilLoadCountersForOpsTest(getPerfCounters(), 1, 0, &expected); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| swapBuffers(); |
| EXPECT_DEPTH_STENCIL_LOAD_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Tests that an in renderpass clear after invalidate keeps content stored. |
| TEST_P(VulkanPerformanceCounterTest, DepthStencilInvalidateAndClear) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, depth(Clears+1, Loads+0, LoadNones+0, Stores+1, StoreNones+0), |
| // stencil(Clears+0, Loads+0, LoadNones+0, Stores+0, StoreNones+0) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 1, 0, 0, 1, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 0, 0, 0, 0, &expected); |
| |
| // Clear should vkCmdClearAttachments |
| expected.depthClearAttachments = getPerfCounters().depthClearAttachments + 1; |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| GLRenderbuffer renderbuffer; |
| setupClearAndDrawForDepthStencilOpsTest(&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(); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| EXPECT_CLEAR_ATTACHMENTS_COUNTER(expected.depthClearAttachments, |
| getPerfCounters().depthClearAttachments); |
| |
| // Expect rpCount+1, depth(Clears+0, Loads+1, LoadNones+0, Stores+1, StoreNones+0), |
| // stencil(Clears+0, Loads+0, LoadNones+1, Stores+0, StoreNones+1) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 0, 1, 0, 1, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 0, 1, 0, 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); |
| 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(kOpsTestSize / 2, kOpsTestSize / 2, GLColor::blue); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Tests that the draw path for clear after invalidate and disabling depth/stencil test keeps |
| // content stored. |
| TEST_P(VulkanPerformanceCounterTest, DepthStencilInvalidateAndMaskedClear) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, depth(Clears+1, Loads+0, LoadNones+0, Stores+1, StoreNones+0), |
| // stencil(Clears+1, Loads+0, LoadNones+0, Stores+1, StoreNones+0) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 1, 0, 0, 1, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 1, 0, 0, 1, 0, &expected); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| GLRenderbuffer renderbuffer; |
| setupClearAndDrawForDepthStencilOpsTest(&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(kOpsTestSize / 4, kOpsTestSize / 4, kOpsTestSize / 2, kOpsTestSize / 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(); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| |
| // Expect rpCount+1, depth(Clears+0, Loads+1, LoadNones+0, Stores+1, StoreNones+0), |
| // stencil(Clears+0, Loads+1, LoadNones+0, Stores+0, StoreNones+1) |
| // Note that depth write is enabled, while stencil is disabled. |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 0, 1, 0, 1, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 1, 0, 0, 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(kOpsTestSize / 2, kOpsTestSize / 2, GLColor::blue); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Tests that the renderpass is using depthFunc(GL_ALWAYS) and depthMask(GL_FALSE), it should not |
| // load or store depth value. |
| TEST_P(VulkanPerformanceCounterTest, DepthFuncALWAYSWithDepthMaskDisabledShouldNotLoadStore) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| GLRenderbuffer renderbuffer; |
| setupForDepthStencilOpsTest(&framebuffer, &texture, &renderbuffer); |
| |
| // Expect rpCount+1, depth(Clears+0, Loads+0, LoadNones+0, Stores+1, StoreNones+0), |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 0, 0, 0, 1, 0, &expected); |
| if (hasDisallowMixedDepthStencilLoadOpNoneAndLoad()) |
| { |
| // stencil(Clears+0, Loads+1, LoadNones+0, Stores+0, StoreNones+1) |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 1, 0, 0, 1, &expected); |
| } |
| else |
| { |
| // stencil(Clears+0, Loads+0, LoadNones+1, Stores+0, StoreNones+1) |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 0, 1, 0, 1, &expected); |
| } |
| // Initialize the buffers with known value |
| glEnable(GL_DEPTH_TEST); |
| glDepthMask(GL_TRUE); |
| glDepthFunc(GL_ALWAYS); |
| ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.95f); |
| EXPECT_PIXEL_COLOR_EQ(kOpsTestSize / 2, kOpsTestSize / 2, GLColor::red); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| |
| // Expect rpCount+1, depth(Clears+0, Loads+0, LoadNones+1, Stores+0, StoreNones+1), |
| // stencil(Clears+0, Loads+0, LoadNones+1, Stores+0, StoreNones+1) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 0, 0, 1, 0, 1, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 0, 1, 0, 1, &expected); |
| |
| glEnable(GL_DEPTH_TEST); |
| glDepthMask(GL_FALSE); |
| glDepthFunc(GL_ALWAYS); |
| drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.95f); |
| EXPECT_PIXEL_COLOR_EQ(kOpsTestSize / 2, kOpsTestSize / 2, GLColor::red); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Tests that the renderpass is using depthFunc(GL_ALWAYS) and depthMask(GL_FALSE) and draw. Then it |
| // followed by glClear, it should not load or store depth value. |
| TEST_P(VulkanPerformanceCounterTest, |
| DepthFuncALWAYSWithDepthMaskDisabledThenClearShouldNotLoadStore) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| GLRenderbuffer renderbuffer; |
| setupForDepthStencilOpsTest(&framebuffer, &texture, &renderbuffer); |
| |
| // Initialize the buffers with known value |
| glEnable(GL_DEPTH_TEST); |
| glDepthMask(GL_TRUE); |
| glDepthFunc(GL_ALWAYS); |
| ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.95f); |
| EXPECT_PIXEL_COLOR_EQ(kOpsTestSize / 2, kOpsTestSize / 2, GLColor::red); |
| |
| if (hasPreferDrawOverClearAttachments()) |
| { |
| // Expect rpCount+1, depth(Clears+0, Loads+1, LoadNones+0, Stores+1, StoreNones+0), |
| // stencil(Clears+0, Loads+0, LoadNones+0, Stores+1, StoreNones+0) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 0, 1, 0, 1, 0, &expected); |
| } |
| else |
| { |
| // Expect rpCount+1, depth(Clears+1, Loads+0, LoadNones+0, Stores+1, StoreNones+0), |
| // stencil(Clears+0, Loads+0, LoadNones+0, Stores+1, StoreNones+0) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 1, 0, 0, 1, 0, &expected); |
| } |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 0, 0, 1, 0, &expected); |
| |
| glEnable(GL_DEPTH_TEST); |
| glDepthMask(GL_FALSE); |
| glDepthFunc(GL_ALWAYS); |
| glEnable(GL_STENCIL_TEST); |
| drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.95f); |
| glDepthMask(GL_TRUE); |
| glClearDepthf(1); |
| glClearStencil(0); |
| glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); |
| glDepthFunc(GL_LEQUAL); |
| glDepthMask(GL_FALSE); |
| glEnable(GL_STENCIL_TEST); |
| glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, 0, 255); |
| glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_KEEP); |
| glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 0, 255); |
| glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_KEEP); |
| glStencilMask(255); |
| drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.9f); |
| EXPECT_PIXEL_COLOR_EQ(kOpsTestSize / 2, kOpsTestSize / 2, GLColor::red); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Tests that the renderpass is using depthFunc(GL_NEVER) and depthMask(GL_FALSE), it should not |
| // load or store depth value. |
| TEST_P(VulkanPerformanceCounterTest, DepthFuncNEVERWithDepthMaskDisabledShouldNotLoadStore) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| GLRenderbuffer renderbuffer; |
| setupForDepthStencilOpsTest(&framebuffer, &texture, &renderbuffer); |
| |
| // Initialize the buffers with known value |
| glEnable(GL_DEPTH_TEST); |
| glDepthMask(GL_TRUE); |
| glDepthFunc(GL_ALWAYS); |
| ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.95f); |
| EXPECT_PIXEL_COLOR_EQ(kOpsTestSize / 2, kOpsTestSize / 2, GLColor::red); |
| |
| // Expect rpCount+1, depth(Clears+0, Loads+0, LoadNones+1, Stores+0, StoreNones+1), |
| // stencil(Clears+0, Loads+0, LoadNones+1, Stores+0, StoreNones+1) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 0, 0, 1, 0, 1, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 0, 1, 0, 1, &expected); |
| |
| glEnable(GL_DEPTH_TEST); |
| glDepthMask(GL_FALSE); |
| glDepthFunc(GL_NEVER); |
| drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.95f); |
| EXPECT_PIXEL_COLOR_EQ(kOpsTestSize / 2, kOpsTestSize / 2, GLColor::red); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), 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, DepthStencilInvalidateDetachModifyTexAttachDrawWithBlend) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, depth(Clears+1, Loads+0, LoadNones+0, Stores+0, StoreNones+0), |
| // stencil(Clears+0, Loads+0, LoadNones+0, Stores+0, StoreNones+0) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 1, 0, 0, 0, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 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, getPerfCounters().renderPasses); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), 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, LoadNones+0, Stores+1, StoreNones+0), |
| // stencil(Clears+0, Loads+1, LoadNones+0, Stores+0, StoreNones+1) |
| // Note that depth write is enabled, while stencil is disabled. |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 0, 1, 0, 1, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 1, 0, 0, 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, getPerfCounters().renderPasses); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| |
| // Draw again, using a different depth value, so that the drawing takes place |
| // |
| // Expect rpCount+1, depth(Clears+0, Loads+1, LoadNones+0, Stores+1, StoreNones+0), |
| // stencil(Clears+0, Loads+1, LoadNones+0, Stores+0, StoreNones+1) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 0, 1, 0, 1, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 1, 0, 0, 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, getPerfCounters().renderPasses); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Tests that a GLRenderbuffer can be deleted before the render pass ends, and that everything |
| // still works. |
| // |
| // - Scenario: invalidate |
| TEST_P(VulkanPerformanceCounterTest, DepthStencilInvalidateDrawAndDeleteRenderbuffer) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, depth(Clears+1, Loads+0, LoadNones+0, Stores+1, StoreNones+0), |
| // stencil(Clears+0, Loads+0, LoadNones+0, Stores+0, StoreNones+0) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 1, 0, 0, 1, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 0, 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; |
| setupClearAndDrawForDepthStencilOpsTest(&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, getPerfCounters().renderPasses); |
| } |
| |
| // The renderbuffer should now be deleted. |
| |
| // Use swapBuffers and then check how many loads and stores were actually done |
| swapBuffers(); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| |
| // Start and end another render pass, to check that the load ops are as expected |
| setAndIncrementDepthStencilLoadCountersForOpsTest(getPerfCounters(), 0, 0, &expected); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| swapBuffers(); |
| EXPECT_DEPTH_STENCIL_LOAD_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Test that disabling color buffer after clear continues to use loadOp for it. |
| // |
| // - Scenario: clear color and depth, disable color, draw, enable color, draw |
| TEST_P(VulkanPerformanceCounterTest_ES31, ColorDisableThenDrawThenEnableThenDraw) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| GLRenderbuffer renderbuffer; |
| setupForColorDepthOpsTest(&framebuffer, &texture, &renderbuffer); |
| |
| // Expected: |
| // rpCount+1, |
| // depth(Clears+1, Loads+0, LoadNones+0, Stores+1, StoreNones+0) |
| // color(Clears+1, Loads+0, LoadNones+0, Stores+1, StoreNones+0) |
| setExpectedCountersForDepthOps(getPerfCounters(), 0, 1, 0, 0, 1, 0, &expected); |
| setExpectedCountersForColorOps(getPerfCounters(), 1, 1, 0, 0, 1, 0, &expected); |
| |
| // Clear color and depth first |
| glClearColor(1, 0, 0, 1); |
| glClearDepthf(0.123); |
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
| |
| // Disable color output |
| GLenum drawBuffers[] = {GL_NONE}; |
| glDrawBuffers(1, drawBuffers); |
| |
| // Issue a draw call, only affecting depth |
| glDepthFunc(GL_ALWAYS); |
| ANGLE_GL_PROGRAM(drawGreen, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); |
| drawQuad(drawGreen, essl1_shaders::PositionAttrib(), 0.75f); |
| |
| // Enable color output |
| drawBuffers[0] = GL_COLOR_ATTACHMENT0; |
| glDrawBuffers(1, drawBuffers); |
| |
| // Issue another draw call, verifying depth simultaneously |
| glDepthFunc(GL_LESS); |
| ANGLE_GL_PROGRAM(drawBlue, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue()); |
| drawQuad(drawBlue, essl1_shaders::PositionAttrib(), 0.74f); |
| |
| EXPECT_EQ(expected.renderPasses, getPerfCounters().renderPasses); |
| |
| // Verify results and check how many loads and stores were actually done. |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| EXPECT_DEPTH_OP_COUNTERS(getPerfCounters(), expected); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Tests that even if the app clears depth, it should be invalidated if there is no read. |
| TEST_P(VulkanPerformanceCounterTest, SwapShouldInvalidateDepthAfterClear) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| 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(); |
| |
| uint64_t expectedDepthClears = getPerfCounters().depthLoadOpClears; |
| |
| swapBuffers(); |
| |
| uint64_t actualDepthClears = getPerfCounters().depthLoadOpClears; |
| EXPECT_EQ(expectedDepthClears, actualDepthClears); |
| } |
| |
| // Tests that masked color clears don't break the RP. |
| TEST_P(VulkanPerformanceCounterTest, MaskedColorClearDoesNotBreakRenderPass) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| 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(); |
| |
| uint64_t expectedRenderPassCount = getPerfCounters().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); |
| |
| uint64_t actualRenderPassCount = getPerfCounters().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) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| 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(); |
| |
| uint64_t expectedRenderPassCount = getPerfCounters().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, getPerfCounters().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) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| uint64_t expectedRenderPassCount = getPerfCounters().renderPasses + 1; |
| uint64_t expectedDepthClears = getPerfCounters().depthLoadOpClears + 1; |
| uint64_t expectedStencilClears = getPerfCounters().stencilLoadOpClears + 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, getPerfCounters().renderPasses); |
| EXPECT_EQ(expectedDepthClears, getPerfCounters().depthLoadOpClears); |
| EXPECT_EQ(expectedStencilClears, getPerfCounters().stencilLoadOpClears); |
| |
| // 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) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| 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(); |
| |
| uint64_t expectedRenderPassCount = getPerfCounters().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, getPerfCounters().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) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| 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(); |
| |
| uint64_t expectedRenderPassCount = getPerfCounters().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); |
| |
| uint64_t actualRenderPassCount = getPerfCounters().renderPasses; |
| EXPECT_EQ(expectedRenderPassCount, actualRenderPassCount); |
| } |
| |
| // Tests the optimization that a glFlush call issued inside a renderpass will be skipped. |
| TEST_P(VulkanPerformanceCounterTest, InRenderpassFlushShouldNotBreakRenderpass) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| uint64_t expectedRenderPassCount = getPerfCounters().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(); |
| |
| uint64_t actualRenderPassCount = getPerfCounters().renderPasses; |
| EXPECT_EQ(expectedRenderPassCount, actualRenderPassCount); |
| } |
| |
| // Tests switch from query enabled draw to query disabled draw should break renderpass (so that wait |
| // for query result will be available sooner). |
| TEST_P(VulkanPerformanceCounterTest, |
| SwitchFromQueryEnabledDrawToQueryDisabledDrawShouldBreakRenderpass) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| |
| uint64_t expectedRenderPassCount = |
| getPerfCounters().renderPasses + |
| (isFeatureEnabled(Feature::PreferSubmitOnAnySamplesPassedQueryEnd) ? 2 : 1); |
| |
| GLQueryEXT query1, query2; |
| // Draw inside query |
| glBeginQueryEXT(GL_ANY_SAMPLES_PASSED_EXT, query1); |
| drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.8f, 0.5f); |
| glEndQueryEXT(GL_ANY_SAMPLES_PASSED_EXT); |
| glBeginQueryEXT(GL_ANY_SAMPLES_PASSED_EXT, query2); |
| drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.8f, 0.5f); |
| glEndQueryEXT(GL_ANY_SAMPLES_PASSED_EXT); |
| // Draw outside his query |
| drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.8f, 0.5f); |
| |
| GLuint results[2]; |
| // will block waiting for result |
| glGetQueryObjectuivEXT(query1, GL_QUERY_RESULT_EXT, &results[0]); |
| glGetQueryObjectuivEXT(query2, GL_QUERY_RESULT_EXT, &results[1]); |
| EXPECT_GL_NO_ERROR(); |
| |
| uint64_t actualRenderPassCount = getPerfCounters().renderPasses; |
| EXPECT_EQ(expectedRenderPassCount, actualRenderPassCount); |
| } |
| |
| // Tests that depth/stencil texture clear/load works correctly. |
| TEST_P(VulkanPerformanceCounterTest, DepthStencilTextureClearAndLoad) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| // TODO: http://anglebug.com/5329 Flaky test |
| ANGLE_SKIP_TEST_IF(IsWindows() && IsAMD() && IsVulkan()); |
| |
| uint64_t expectedDepthClearCount = getPerfCounters().depthLoadOpClears + 1; |
| uint64_t expectedDepthLoadCount = getPerfCounters().depthLoadOpLoads + 3; |
| uint64_t expectedStencilClearCount = getPerfCounters().stencilLoadOpClears + 1; |
| uint64_t expectedStencilLoadCount = getPerfCounters().stencilLoadOpLoads + 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(getPerfCounters().depthLoadOpClears, expectedDepthClearCount); |
| EXPECT_EQ(getPerfCounters().depthLoadOpLoads, expectedDepthLoadCount); |
| EXPECT_EQ(getPerfCounters().stencilLoadOpClears, expectedStencilClearCount); |
| EXPECT_EQ(getPerfCounters().stencilLoadOpLoads, 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) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| // http://anglebug.com/5083 |
| ANGLE_SKIP_TEST_IF(IsWindows() && IsAMD() && IsVulkan()); |
| |
| ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_multisampled_render_to_texture2")); |
| |
| uint64_t expectedDepthClearCount = getPerfCounters().depthLoadOpClears + 1; |
| uint64_t expectedDepthLoadCount = getPerfCounters().depthLoadOpLoads; |
| uint64_t expectedStencilClearCount = getPerfCounters().stencilLoadOpClears + 1; |
| uint64_t expectedStencilLoadCount = getPerfCounters().stencilLoadOpLoads; |
| |
| 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(getPerfCounters().depthLoadOpClears, expectedDepthClearCount); |
| EXPECT_EQ(getPerfCounters().depthLoadOpLoads, expectedDepthLoadCount); |
| EXPECT_EQ(getPerfCounters().stencilLoadOpClears, expectedStencilClearCount); |
| EXPECT_EQ(getPerfCounters().stencilLoadOpLoads, 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/stencil data. |
| 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")); |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters 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, LoadNones+0, Stores+3, StoreNones+0), |
| // stencil(Clears+1, Loads+3, LoadNones+0, Stores+3, StoreNones+0). Note that the Loads and |
| // Stores are from the resolve attachments. |
| setExpectedCountersForDepthOps(getPerfCounters(), 4, 1, 3, 0, 3, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 1, 3, 0, 3, 0, &expected); |
| |
| // Additionally, expect 4 resolves and 3 unresolves. |
| setExpectedCountersForUnresolveResolveTest(getPerfCounters(), 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 |
| EXPECT_DEPTH_STENCIL_LOAD_OP_COUNTERS(getPerfCounters(), expected); |
| EXPECT_COUNTERS_FOR_UNRESOLVE_RESOLVE_TEST(getPerfCounters(), 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")); |
| |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters 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, color(Clears+1, Loads+0, LoadNones+0, Stores+0, StoreNones+0) |
| setExpectedCountersForColorOps(getPerfCounters(), 4, 1, 0, 0, 0, 0, &expected); |
| // Expect rpCount+4, depth(Clears+1, Loads+0, LoadNones+0, Stores+0, StoreNones+0), |
| // stencil(Clears+1, Loads+0, LoadNones+0, Stores+0, StoreNones+0) |
| setExpectedCountersForDepthOps(getPerfCounters(), 4, 1, 0, 0, 0, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 1, 0, 0, 0, 0, &expected); |
| |
| // Additionally, expect no resolve and unresolve. |
| setExpectedCountersForUnresolveResolveTest(getPerfCounters(), 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 |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| EXPECT_COUNTERS_FOR_UNRESOLVE_RESOLVE_TEST(getPerfCounters(), 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")); |
| |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, no depth/stencil clear, load or store. |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 0, 0, 0, 0, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 0, 0, 0, 0, &expected); |
| |
| // Additionally, expect only color resolve. |
| setExpectedCountersForUnresolveResolveTest(getPerfCounters(), 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 |
| EXPECT_DEPTH_STENCIL_LOAD_OP_COUNTERS(getPerfCounters(), expected); |
| EXPECT_COUNTERS_FOR_UNRESOLVE_RESOLVE_TEST(getPerfCounters(), expected); |
| } |
| |
| // Ensures we use read-only depth layout when there is no write |
| TEST_P(VulkanPerformanceCounterTest, ReadOnlyDepthBufferLayout) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| constexpr GLsizei kSize = 64; |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Create depth only FBO and fill depth texture to leftHalf=0.0 and rightHalf=1.0. This should |
| // use writeable layout |
| expected.readOnlyDepthStencilRenderPasses = getPerfCounters().readOnlyDepthStencilRenderPasses; |
| |
| // Expect rpCount+1, depth(Clears+0, Loads+0, LoadNones+0, Stores+1, StoreNones+0), |
| // stencil(Clears+0, Loads+0, LoadNones+1, Stores+0, StoreNones+1) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 0, 0, 0, 1, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 0, 1, 0, 1, &expected); |
| |
| 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(); |
| |
| uint64_t actualReadOnlyDepthStencilCount = getPerfCounters().readOnlyDepthStencilRenderPasses; |
| EXPECT_EQ(expected.readOnlyDepthStencilRenderPasses, actualReadOnlyDepthStencilCount); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| |
| // Create a color+depth FBO and use depth as read only. This should use read only layout |
| ++expected.readOnlyDepthStencilRenderPasses; |
| // Expect rpCount+1, depth(Clears+0, Loads+1, LoadNones+0, Stores+0, StoreNones+1), |
| // stencil(Clears+0, Loads+0, LoadNones+1, Stores+0, StoreNones+1) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 0, 1, 0, 0, 1, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 0, 1, 0, 1, &expected); |
| |
| 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); |
| |
| angle::Vector4 clearColor = GLColor::blue.toNormalizedVector(); |
| 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 = getPerfCounters().readOnlyDepthStencilRenderPasses; |
| EXPECT_EQ(expected.readOnlyDepthStencilRenderPasses, actualReadOnlyDepthStencilCount); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Ensures depth/stencil is not loaded after storeOp=DONT_CARE due to optimization (as opposed to |
| // invalidate) |
| TEST_P(VulkanPerformanceCounterTest, RenderPassAfterRenderPassWithoutDepthStencilWrite) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, depth(Clears+0, Loads+0, LoadNones+1, Stores+0, StoreNones+1), |
| // stencil(Clears+0, Loads+0, LoadNones+1, Stores+0, StoreNones+1) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 0, 0, 1, 0, 1, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 0, 1, 0, 1, &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. |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| |
| // Expect rpCount+1, depth(Clears+0, Loads+0, LoadNones+1, Stores+0, StoreNones+1), |
| // stencil(Clears+0, Loads+0, LoadNones+1, Stores+0, StoreNones+1) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 0, 0, 1, 0, 1, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 0, 0, 1, 0, 1, &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. |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), 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) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| uint64_t expectedRenderPassCount = getPerfCounters().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(getPerfCounters().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) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| 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); |
| |
| uint64_t expectedRenderPassCount = getPerfCounters().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(getPerfCounters().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) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| // Set up two UBOs, one filled with "1" and the second with "2". |
| constexpr GLsizei kCount = 64; |
| std::vector<GLint> data1(kCount, 1); |
| std::vector<GLint> data2(kCount, 2); |
| |
| GLBuffer ubo1; |
| glBindBuffer(GL_UNIFORM_BUFFER, ubo1); |
| glBufferData(GL_UNIFORM_BUFFER, kCount * sizeof(data1[0]), data1.data(), GL_STATIC_DRAW); |
| |
| GLBuffer ubo2; |
| glBindBuffer(GL_UNIFORM_BUFFER, ubo2); |
| glBufferData(GL_UNIFORM_BUFFER, kCount * sizeof(data2[0]), data2.data(), GL_STATIC_DRAW); |
| |
| // Set up a program that verifies the contents of uniform blocks. |
| constexpr char kVS[] = R"(#version 300 es |
| precision mediump float; |
| in vec4 position; |
| void main() |
| { |
| gl_Position = position; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| uniform buf { |
| int data[64/4]; |
| }; |
| uniform int checkValue; |
| out vec4 outColor; |
| |
| void main() |
| { |
| for (int i = 0; i < 64/4; ++i) { |
| if (data[i] != checkValue) { |
| outColor = vec4(1, 0, 0, 1); |
| return; |
| } |
| } |
| outColor = vec4(0, 1, 0, 1); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| glUseProgram(program); |
| ASSERT_GL_NO_ERROR(); |
| |
| GLint uniLoc = glGetUniformLocation(program, "checkValue"); |
| ASSERT_NE(-1, uniLoc); |
| |
| GLuint blockIndex = glGetUniformBlockIndex(program, "buf"); |
| ASSERT_NE(blockIndex, GL_INVALID_INDEX); |
| |
| glUniformBlockBinding(program, blockIndex, 0); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Set up the rest of the GL state. |
| auto quadVerts = GetQuadVertices(); |
| GLBuffer vertexBuffer; |
| glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); |
| glBufferData(GL_ARRAY_BUFFER, quadVerts.size() * sizeof(quadVerts[0]), quadVerts.data(), |
| GL_STATIC_DRAW); |
| |
| GLint posLoc = glGetAttribLocation(program, "position"); |
| ASSERT_NE(-1, posLoc); |
| |
| glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, 0); |
| glEnableVertexAttribArray(posLoc); |
| |
| // Draw a few times with each UBO. Stream out one pixel for post-render verification. |
| constexpr int kIterations = 5; |
| constexpr GLsizei kPackBufferSize = sizeof(GLColor) * kIterations * 2; |
| |
| GLBuffer packBuffer; |
| glBindBuffer(GL_PIXEL_PACK_BUFFER, packBuffer); |
| glBufferData(GL_PIXEL_PACK_BUFFER, kPackBufferSize, nullptr, GL_STREAM_READ); |
| |
| GLsizei offset = 0; |
| |
| uint64_t expectedShaderResourcesCacheMisses = 0; |
| |
| for (int iteration = 0; iteration < kIterations; ++iteration) |
| { |
| glUniform1i(uniLoc, 1); |
| glBindBufferBase(GL_UNIFORM_BUFFER, 0, ubo1); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, |
| reinterpret_cast<GLvoid *>(static_cast<uintptr_t>(offset))); |
| offset += sizeof(GLColor); |
| glUniform1i(uniLoc, 2); |
| glBindBufferBase(GL_UNIFORM_BUFFER, 0, ubo2); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, |
| reinterpret_cast<GLvoid *>(static_cast<uintptr_t>(offset))); |
| offset += sizeof(GLColor); |
| |
| // Capture the allocations counter after the first run. |
| if (iteration == 0) |
| { |
| expectedShaderResourcesCacheMisses = |
| getPerfCounters().shaderResourcesDescriptorSetCacheMisses; |
| } |
| } |
| |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_GT(expectedShaderResourcesCacheMisses, 0u); |
| |
| // Verify correctness first. |
| std::vector<GLColor> expectedData(kIterations * 2, GLColor::green); |
| std::vector<GLColor> actualData(kIterations * 2, GLColor::black); |
| |
| void *mapPtr = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, kPackBufferSize, GL_MAP_READ_BIT); |
| ASSERT_NE(nullptr, mapPtr); |
| memcpy(actualData.data(), mapPtr, kPackBufferSize); |
| |
| glUnmapBuffer(GL_PIXEL_PACK_BUFFER); |
| |
| EXPECT_EQ(expectedData, actualData); |
| |
| // Check for unnecessary descriptor set allocations. |
| uint64_t actualShaderResourcesCacheMisses = |
| getPerfCounters().shaderResourcesDescriptorSetCacheMisses; |
| EXPECT_EQ(expectedShaderResourcesCacheMisses, actualShaderResourcesCacheMisses); |
| } |
| |
| // Test that mapping a buffer that the GPU is using as read-only ghosts the buffer, rather than |
| // waiting for the GPU access to complete before returning a pointer to the buffer. |
| void VulkanPerformanceCounterTest::mappingGpuReadOnlyBufferGhostsBuffer(BufferUpdate update) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| // 1. Create a buffer, map it, fill it with red |
| // 2. Draw with buffer (GPU read-only) |
| // 3. Map the same buffer and fill with white |
| // - This should ghost the buffer, rather than ending the render pass. |
| // 4. Draw with buffer |
| // 5. Update the buffer with glBufferSubData() or glCopyBufferSubData |
| // 6. Draw with the buffer |
| // The render pass should only be broken (counters.renderPasses == 0) due to the glReadPixels() |
| // to verify the draw at the end. |
| |
| const std::array<GLColor, 4> kInitialData = {GLColor::red, GLColor::red, GLColor::red, |
| GLColor::red}; |
| const std::array<GLColor, 4> kUpdateData1 = {GLColor::white, GLColor::white, GLColor::white, |
| GLColor::white}; |
| const std::array<GLColor, 4> kUpdateData2 = {GLColor::blue, GLColor::blue, GLColor::blue, |
| GLColor::blue}; |
| |
| GLBuffer buffer; |
| glBindBuffer(GL_UNIFORM_BUFFER, buffer); |
| glBufferData(GL_UNIFORM_BUFFER, sizeof(kInitialData), kInitialData.data(), GL_DYNAMIC_DRAW); |
| glBindBufferBase(GL_UNIFORM_BUFFER, 0, buffer); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Draw |
| constexpr char kVerifyUBO[] = R"(#version 300 es |
| precision mediump float; |
| uniform block { |
| uvec4 data; |
| } ubo; |
| uniform uint expect; |
| uniform vec4 successOutput; |
| out vec4 colorOut; |
| void main() |
| { |
| if (all(equal(ubo.data, uvec4(expect)))) |
| colorOut = successOutput; |
| else |
| colorOut = vec4(1.0, 0, 0, 1.0); |
| })"; |
| |
| ANGLE_GL_PROGRAM(verifyUbo, essl3_shaders::vs::Simple(), kVerifyUBO); |
| glUseProgram(verifyUbo); |
| |
| GLint expectLoc = glGetUniformLocation(verifyUbo, "expect"); |
| ASSERT_NE(-1, expectLoc); |
| GLint successLoc = glGetUniformLocation(verifyUbo, "successOutput"); |
| ASSERT_NE(-1, successLoc); |
| |
| glUniform1ui(expectLoc, kInitialData[0].asUint()); |
| glUniform4f(successLoc, 0, 1, 0, 1); |
| |
| drawQuad(verifyUbo, essl3_shaders::PositionAttrib(), 0.5); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Map the buffer and update it. |
| // This should ghost the buffer and avoid breaking the render pass, since the GPU is only |
| // reading it. |
| void *mappedBuffer = |
| glMapBufferRange(GL_UNIFORM_BUFFER, 0, sizeof(kInitialData), GL_MAP_WRITE_BIT); |
| // 'renderPasses == 0' here means the render pass was broken and a new one was started. |
| ASSERT_EQ(getPerfCounters().renderPasses, 1u); |
| ASSERT_EQ(getPerfCounters().buffersGhosted, 1u); |
| |
| memcpy(mappedBuffer, kUpdateData1.data(), sizeof(kInitialData)); |
| |
| glUnmapBuffer(GL_UNIFORM_BUFFER); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Verify that the buffer has the updated value. |
| glUniform1ui(expectLoc, kUpdateData1[0].asUint()); |
| glUniform4f(successLoc, 0, 0, 1, 1); |
| |
| drawQuad(verifyUbo, essl3_shaders::PositionAttrib(), 0.5); |
| ASSERT_GL_NO_ERROR(); |
| ASSERT_EQ(getPerfCounters().renderPasses, 1u); |
| |
| // Update the buffer |
| updateBuffer(update, GL_UNIFORM_BUFFER, 0, sizeof(kUpdateData2), kUpdateData2.data()); |
| ASSERT_GL_NO_ERROR(); |
| ASSERT_EQ(getPerfCounters().renderPasses, 1u); |
| |
| // Verify that the buffer has the updated value. |
| glUniform1ui(expectLoc, kUpdateData2[0].asUint()); |
| glUniform4f(successLoc, 0, 1, 1, 1); |
| |
| drawQuad(verifyUbo, essl3_shaders::PositionAttrib(), 0.5); |
| ASSERT_GL_NO_ERROR(); |
| ASSERT_EQ(getPerfCounters().renderPasses, 1u); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::cyan); |
| } |
| |
| // Test that mapping a buffer that the GPU is using as read-only ghosts the buffer, rather than |
| // waiting for the GPU access to complete before returning a pointer to the buffer. This test uses |
| // glBufferSubData to update the buffer. |
| TEST_P(VulkanPerformanceCounterTest, MappingGpuReadOnlyBufferGhostsBuffer_SubData) |
| { |
| mappingGpuReadOnlyBufferGhostsBuffer(BufferUpdate::SubData); |
| } |
| |
| // Same as MappingGpuReadOnlyBufferGhostsBuffer_SubData, but using glCopyBufferSubData to update the |
| // buffer. |
| TEST_P(VulkanPerformanceCounterTest, MappingGpuReadOnlyBufferGhostsBuffer_Copy) |
| { |
| mappingGpuReadOnlyBufferGhostsBuffer(BufferUpdate::Copy); |
| } |
| |
| void VulkanPerformanceCounterTest::partialBufferUpdateShouldNotBreakRenderPass(BufferUpdate update) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| uint64_t expectedRenderPassCount = getPerfCounters().renderPasses + 1; |
| if (!hasPreferCPUForBufferSubData()) |
| { |
| ++expectedRenderPassCount; |
| } |
| |
| const std::array<GLColor, 4> kInitialData = {GLColor::red, GLColor::green, GLColor::blue, |
| GLColor::yellow}; |
| const std::array<GLColor, 1> kUpdateData1 = {GLColor::cyan}; |
| const std::array<GLColor, 3> kUpdateData2 = {GLColor::magenta, GLColor::black, GLColor::white}; |
| |
| GLBuffer buffer; |
| glBindBuffer(GL_UNIFORM_BUFFER, buffer); |
| glBufferData(GL_UNIFORM_BUFFER, sizeof(kInitialData), kInitialData.data(), GL_DYNAMIC_DRAW); |
| glBindBufferBase(GL_UNIFORM_BUFFER, 0, buffer); |
| ASSERT_GL_NO_ERROR(); |
| |
| constexpr char kVS[] = R"(#version 300 es |
| precision highp float; |
| uniform float height; |
| void main() |
| { |
| // gl_VertexID x y |
| // 0 -1 -1 |
| // 1 1 -1 |
| // 2 -1 1 |
| // 3 1 1 |
| int bit0 = gl_VertexID & 1; |
| int bit1 = gl_VertexID >> 1; |
| gl_Position = vec4(bit0 * 2 - 1, bit1 * 2 - 1, 0, 1); |
| })"; |
| |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| out vec4 colorOut; |
| uniform block { |
| uvec4 data; |
| } ubo; |
| uniform uvec4 expect; |
| uniform vec4 successColor; |
| void main() |
| { |
| if (all(equal(ubo.data, expect))) |
| colorOut = successColor; |
| else |
| colorOut = vec4(0); |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| glUseProgram(program); |
| |
| GLint expectLoc = glGetUniformLocation(program, "expect"); |
| ASSERT_NE(-1, expectLoc); |
| GLint successLoc = glGetUniformLocation(program, "successColor"); |
| ASSERT_NE(-1, successLoc); |
| |
| glClearColor(0, 0, 0, 0); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| glEnable(GL_BLEND); |
| glBlendFunc(GL_ONE, GL_ONE); |
| |
| // Draw once, using the buffer in the render pass. |
| glUniform4ui(expectLoc, kInitialData[0].asUint(), kInitialData[1].asUint(), |
| kInitialData[2].asUint(), kInitialData[3].asUint()); |
| glUniform4f(successLoc, 1, 0, 0, 1); |
| glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |
| |
| // Upload a small part of the buffer, and draw again. |
| updateBuffer(update, GL_UNIFORM_BUFFER, 0, sizeof(kUpdateData1), kUpdateData1.data()); |
| |
| glUniform4ui(expectLoc, kUpdateData1[0].asUint(), kInitialData[1].asUint(), |
| kInitialData[2].asUint(), kInitialData[3].asUint()); |
| glUniform4f(successLoc, 0, 1, 0, 1); |
| glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |
| |
| // Upload a large part of the buffer, and draw again. |
| updateBuffer(update, GL_UNIFORM_BUFFER, sizeof(kUpdateData1), sizeof(kUpdateData2), |
| kUpdateData2.data()); |
| |
| glUniform4ui(expectLoc, kUpdateData1[0].asUint(), kUpdateData2[0].asUint(), |
| kUpdateData2[1].asUint(), kUpdateData2[2].asUint()); |
| glUniform4f(successLoc, 0, 0, 1, 1); |
| glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |
| |
| // Verify results |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Only one render pass should have been used. |
| EXPECT_EQ(getPerfCounters().renderPasses, expectedRenderPassCount); |
| } |
| |
| // Verifies that BufferSubData calls don't cause a render pass break when it only uses the buffer |
| // read-only. This test uses glBufferSubData to update the buffer. |
| TEST_P(VulkanPerformanceCounterTest, PartialBufferUpdateShouldNotBreakRenderPass_SubData) |
| { |
| partialBufferUpdateShouldNotBreakRenderPass(BufferUpdate::SubData); |
| } |
| |
| // Same as PartialBufferUpdateShouldNotBreakRenderPass_SubData, but using glCopyBufferSubData to |
| // update the buffer. |
| TEST_P(VulkanPerformanceCounterTest, PartialBufferUpdateShouldNotBreakRenderPass_Copy) |
| { |
| partialBufferUpdateShouldNotBreakRenderPass(BufferUpdate::Copy); |
| } |
| |
| void VulkanPerformanceCounterTest::bufferSubDataShouldNotTriggerSyncState(BufferUpdate update) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| ANGLE_GL_PROGRAM(testProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); |
| glUseProgram(testProgram); |
| |
| GLint posLoc = glGetAttribLocation(testProgram, essl1_shaders::PositionAttrib()); |
| ASSERT_NE(-1, posLoc); |
| |
| setupQuadVertexBuffer(0.5f, 1.0f); |
| glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr); |
| glEnableVertexAttribArray(posLoc); |
| |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| |
| EXPECT_EQ(getPerfCounters().vertexArraySyncStateCalls, 1u); |
| |
| const std::array<Vector3, 6> &quadVertices = GetQuadVertices(); |
| size_t bufferSize = sizeof(quadVertices[0]) * quadVertices.size(); |
| |
| updateBuffer(update, GL_ARRAY_BUFFER, 0, bufferSize, quadVertices.data()); |
| |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| |
| EXPECT_EQ(getPerfCounters().vertexArraySyncStateCalls, 1u); |
| |
| // Verify the BufferData with a whole buffer size is treated like the SubData call. |
| glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices[0]) * quadVertices.size(), |
| quadVertices.data(), GL_STATIC_DRAW); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| |
| EXPECT_EQ(getPerfCounters().vertexArraySyncStateCalls, 1u); |
| } |
| |
| // Verifies that BufferSubData calls don't trigger state updates for non-translated formats. This |
| // test uses glBufferSubData to update the buffer. |
| TEST_P(VulkanPerformanceCounterTest, BufferSubDataShouldNotTriggerSyncState_SubData) |
| { |
| bufferSubDataShouldNotTriggerSyncState(BufferUpdate::SubData); |
| } |
| |
| // Same as BufferSubDataShouldNotTriggerSyncState_SubData, but using glCopyBufferSubData to update |
| // the buffer. |
| TEST_P(VulkanPerformanceCounterTest, BufferSubDataShouldNotTriggerSyncState_Copy) |
| { |
| bufferSubDataShouldNotTriggerSyncState(BufferUpdate::Copy); |
| } |
| |
| // Verifies that rendering to backbuffer discards depth/stencil. |
| TEST_P(VulkanPerformanceCounterTest, SwapShouldInvalidateDepthStencil) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, depth(Clears+1, Loads+0, LoadNones+0, Stores+0, StoreNones+0), |
| // stencil(Clears+1, Loads+0, LoadNones+0, Stores+0, StoreNones+0) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 1, 0, 0, 0, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 1, 0, 0, 0, 0, &expected); |
| |
| // Clear to verify that _some_ counters did change (as opposed to for example all being reset on |
| // swap) |
| glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); |
| |
| glEnable(GL_DEPTH_TEST); |
| glDepthMask(GL_TRUE); |
| glDepthFunc(GL_ALWAYS); |
| glEnable(GL_STENCIL_TEST); |
| glStencilFunc(GL_ALWAYS, 0x00, 0xFF); |
| glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); |
| |
| ANGLE_GL_PROGRAM(drawGreen, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); |
| drawQuad(drawGreen, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Swap buffers to implicitely resolve |
| swapBuffers(); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Verifies that rendering to MSAA backbuffer discards depth/stencil. |
| TEST_P(VulkanPerformanceCounterTest_MSAA, SwapShouldInvalidateDepthStencil) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, depth(Clears+1, Loads+0, LoadNones+0, Stores+0, StoreNones+0), |
| // stencil(Clears+1, Loads+0, LoadNones+0, Stores+0, StoreNones+0) |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 1, 0, 0, 0, 0, &expected); |
| setExpectedCountersForStencilOps(getPerfCounters(), 1, 0, 0, 0, 0, &expected); |
| |
| // Clear to verify that _some_ counters did change (as opposed to for example all being reset on |
| // swap) |
| glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); |
| |
| glEnable(GL_DEPTH_TEST); |
| glDepthMask(GL_TRUE); |
| glDepthFunc(GL_ALWAYS); |
| glEnable(GL_STENCIL_TEST); |
| glStencilFunc(GL_ALWAYS, 0x00, 0xFF); |
| glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); |
| |
| ANGLE_GL_PROGRAM(drawGreen, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); |
| drawQuad(drawGreen, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Swap buffers to implicitely resolve |
| swapBuffers(); |
| EXPECT_DEPTH_STENCIL_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Verifies that multisample swapchain resolve occurs in subpass. |
| TEST_P(VulkanPerformanceCounterTest_MSAA, SwapShouldResolveWithSubpass) |
| { |
| angle::VulkanPerfCounters expected; |
| // Expect rpCount+1, color(Clears+1, Loads+0, LoadNones+0, Stores+1, StoreNones+0) |
| setExpectedCountersForColorOps(getPerfCounters(), 1, 1, 0, 0, 1, 0, &expected); |
| |
| uint64_t expectedResolvesSubpass = getPerfCounters().swapchainResolveInSubpass + 1; |
| uint64_t expectedResolvesOutside = getPerfCounters().swapchainResolveOutsideSubpass; |
| |
| // Clear color. |
| glClearColor(0.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
| |
| // 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(); |
| |
| // Swap buffers to implicitly resolve |
| swapBuffers(); |
| EXPECT_EQ(getPerfCounters().swapchainResolveInSubpass, expectedResolvesSubpass); |
| EXPECT_EQ(getPerfCounters().swapchainResolveOutsideSubpass, expectedResolvesOutside); |
| |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Verifies that in a multisample swapchain, drawing to the default FBO followed by user FBO and |
| // then swapping triggers the resolve outside the optimization subpass. |
| TEST_P(VulkanPerformanceCounterTest_MSAA, SwapAfterDrawToDifferentFBOsShouldResolveOutsideSubpass) |
| { |
| constexpr GLsizei kSize = 16; |
| angle::VulkanPerfCounters expected; |
| // Expect rpCount+1, color(Clears+1, Loads+0, LoadNones+0, Stores+2, StoreNones+0) |
| setExpectedCountersForColorOps(getPerfCounters(), 1, 1, 0, 0, 2, 0, &expected); |
| |
| uint64_t expectedResolvesSubpass = getPerfCounters().swapchainResolveInSubpass; |
| uint64_t expectedResolvesOutside = getPerfCounters().swapchainResolveOutsideSubpass + 1; |
| |
| // Create a framebuffer to clear. |
| 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(); |
| |
| // Clear color. |
| glClearColor(0.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
| |
| // 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 to default framebuffer |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f); |
| drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.95f); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Draw blue to the user framebuffer |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glUniform4f(colorUniformLocation, 0.0f, 0.0f, 1.0f, 1.0f); |
| drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.95f); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Swap buffers to resolve outside the optimization subpass |
| swapBuffers(); |
| EXPECT_EQ(getPerfCounters().swapchainResolveInSubpass, expectedResolvesSubpass); |
| EXPECT_EQ(getPerfCounters().swapchainResolveOutsideSubpass, expectedResolvesOutside); |
| |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Verifies that in a multisample swapchain, subpass resolve only happens when the render pass |
| // covers the entire area. |
| TEST_P(VulkanPerformanceCounterTest_MSAA, ResolveWhenRenderPassNotEntireArea) |
| { |
| constexpr GLsizei kSize = 16; |
| angle::VulkanPerfCounters expected; |
| // Expect rpCount+1, color(Clears+0, Loads+1, LoadNones+0, Stores+2, StoreNones+0) |
| setExpectedCountersForColorOps(getPerfCounters(), 1, 0, 1, 0, 2, 0, &expected); |
| |
| uint64_t expectedResolvesSubpass = getPerfCounters().swapchainResolveInSubpass; |
| uint64_t expectedResolvesOutside = getPerfCounters().swapchainResolveOutsideSubpass + 1; |
| |
| // Create a framebuffer to clear. |
| 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); |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Clear color. |
| glDisable(GL_SCISSOR_TEST); |
| glClearColor(0.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black); |
| |
| // 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); |
| |
| // Scissor the render area |
| glEnable(GL_SCISSOR_TEST); |
| glScissor(kSize / 4, kSize / 4, kSize / 2, kSize / 2); |
| |
| // Draw blue to the user framebuffer |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glUniform4f(colorUniformLocation, 0.0f, 0.0f, 1.0f, 1.0f); |
| drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.95f); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Draw green to default framebuffer |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f); |
| drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0.95f); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Swap buffers, which should not resolve the image in subpass |
| swapBuffers(); |
| EXPECT_EQ(getPerfCounters().swapchainResolveInSubpass, expectedResolvesSubpass); |
| EXPECT_EQ(getPerfCounters().swapchainResolveOutsideSubpass, expectedResolvesOutside); |
| |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Tests that uniform updates eventually stop updating descriptor sets. |
| TEST_P(VulkanPerformanceCounterTest, UniformUpdatesHitDescriptorSetCache) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| ANGLE_GL_PROGRAM(testProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor()); |
| glUseProgram(testProgram); |
| GLint posLoc = glGetAttribLocation(testProgram, essl1_shaders::PositionAttrib()); |
| GLint uniLoc = glGetUniformLocation(testProgram, essl1_shaders::ColorUniform()); |
| |
| std::array<Vector3, 6> quadVerts = GetQuadVertices(); |
| |
| GLBuffer vbo; |
| glBindBuffer(GL_ARRAY_BUFFER, vbo); |
| glBufferData(GL_ARRAY_BUFFER, quadVerts.size() * sizeof(quadVerts[0]), quadVerts.data(), |
| GL_STATIC_DRAW); |
| |
| glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr); |
| glEnableVertexAttribArray(posLoc); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| // Choose a number of iterations sufficiently large to ensure all uniforms are cached. |
| constexpr int kIterations = 2000; |
| |
| // First pass: cache all the uniforms. |
| RNG rng; |
| for (int iteration = 0; iteration < kIterations; ++iteration) |
| { |
| Vector3 randomVec3 = RandomVec3(rng.randomInt(), 0.0f, 1.0f); |
| |
| glUniform4f(uniLoc, randomVec3.x(), randomVec3.y(), randomVec3.z(), 1.0f); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| |
| GLColor expectedColor = GLColor(randomVec3); |
| EXPECT_PIXEL_COLOR_NEAR(0, 0, expectedColor, 5); |
| } |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| uint64_t expectedCacheMisses = getPerfCounters().uniformsAndXfbDescriptorSetCacheMisses; |
| EXPECT_GT(expectedCacheMisses, 0u); |
| |
| // Second pass: ensure all the uniforms are cached. |
| for (int iteration = 0; iteration < kIterations; ++iteration) |
| { |
| Vector3 randomVec3 = RandomVec3(rng.randomInt(), 0.0f, 1.0f); |
| |
| glUniform4f(uniLoc, randomVec3.x(), randomVec3.y(), randomVec3.z(), 1.0f); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| |
| GLColor expectedColor = GLColor(randomVec3); |
| EXPECT_PIXEL_COLOR_NEAR(0, 0, expectedColor, 5); |
| } |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| uint64_t actualCacheMisses = getPerfCounters().uniformsAndXfbDescriptorSetCacheMisses; |
| EXPECT_EQ(expectedCacheMisses, actualCacheMisses); |
| } |
| |
| // Test one texture sampled by fragment shader, then image load it by compute |
| // shader, at last fragment shader do something else. |
| TEST_P(VulkanPerformanceCounterTest_ES31, DrawDispatchImageReadDrawWithEndRP) |
| { |
| |
| constexpr char kVSSource[] = R"(#version 310 es |
| in vec4 a_position; |
| out vec2 v_texCoord; |
| |
| void main() |
| { |
| gl_Position = vec4(a_position.xy, 0.0, 1.0); |
| v_texCoord = a_position.xy * 0.5 + vec2(0.5); |
| })"; |
| |
| constexpr char kFSSource[] = R"(#version 310 es |
| precision mediump float; |
| uniform sampler2D u_tex2D; |
| in vec2 v_texCoord; |
| out vec4 out_FragColor; |
| void main() |
| { |
| out_FragColor = texture(u_tex2D, v_texCoord); |
| })"; |
| |
| constexpr char kFSSource1[] = R"(#version 310 es |
| precision mediump float; |
| out vec4 out_FragColor; |
| void main() |
| { |
| out_FragColor = vec4(1.0); |
| })"; |
| |
| constexpr char kCSSource[] = R"(#version 310 es |
| layout(local_size_x=1, local_size_y=1, local_size_z=1) in; |
| layout(rgba32f, binding=0) readonly uniform highp image2D uIn; |
| layout(std140, binding=0) buffer buf { |
| vec4 outData; |
| }; |
| |
| void main() |
| { |
| outData = imageLoad(uIn, ivec2(gl_LocalInvocationID.xy)); |
| })"; |
| |
| GLfloat initValue[4] = {1.0, 1.0, 1.0, 1.0}; |
| |
| // Step 1: Set up a simple 2D Texture rendering loop. |
| GLTexture texture; |
| glBindTexture(GL_TEXTURE_2D, texture); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA32F, 1, 1); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_FLOAT, initValue); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| |
| GLBuffer vertexBuffer; |
| GLfloat vertices[] = {-1, -1, 1, -1, -1, 1, 1, 1}; |
| glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); |
| glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(GLfloat), vertices, GL_STATIC_DRAW); |
| |
| ANGLE_GL_PROGRAM(program, kVSSource, kFSSource); |
| glUseProgram(program); |
| |
| GLint posLoc = glGetAttribLocation(program, "a_position"); |
| ASSERT_NE(-1, posLoc); |
| |
| glVertexAttribPointer(posLoc, 2, GL_FLOAT, GL_FALSE, 0, nullptr); |
| glEnableVertexAttribArray(posLoc); |
| ASSERT_GL_NO_ERROR(); |
| glBindImageTexture(0, texture, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F); |
| |
| GLBuffer ssbo; |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, 16, nullptr, GL_STREAM_DRAW); |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); |
| |
| // This is actually suboptimal, and ideally only one render pass should be necessary. |
| uint64_t expectedRenderPassCount = getPerfCounters().renderPasses + 2; |
| |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Step 2: load this image through compute |
| ANGLE_GL_COMPUTE_PROGRAM(csProgram, kCSSource); |
| glUseProgram(csProgram); |
| |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssbo); |
| |
| glDispatchCompute(1, 1, 1); |
| EXPECT_GL_NO_ERROR(); |
| |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo); |
| const GLfloat *ptr = reinterpret_cast<const GLfloat *>( |
| glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, 16, GL_MAP_READ_BIT)); |
| |
| EXPECT_GL_NO_ERROR(); |
| for (unsigned int idx = 0; idx < 4; idx++) |
| { |
| EXPECT_EQ(1.0, *(ptr + idx)); |
| } |
| |
| // Step3 |
| ANGLE_GL_PROGRAM(program2, kVSSource, kFSSource1); |
| glUseProgram(program2); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| ASSERT_GL_NO_ERROR(); |
| |
| uint64_t actualRenderPassCount = getPerfCounters().renderPasses; |
| EXPECT_EQ(expectedRenderPassCount, actualRenderPassCount); |
| } |
| |
| // Test one texture sampled by fragment shader, followed by glReadPixels, then image |
| // load it by compute shader, and at last fragment shader do something else. |
| TEST_P(VulkanPerformanceCounterTest_ES31, DrawDispatchImageReadDrawWithoutEndRP) |
| { |
| |
| constexpr char kVSSource[] = R"(#version 310 es |
| in vec4 a_position; |
| out vec2 v_texCoord; |
| |
| void main() |
| { |
| gl_Position = vec4(a_position.xy, 0.0, 1.0); |
| v_texCoord = a_position.xy * 0.5 + vec2(0.5); |
| })"; |
| |
| constexpr char kFSSource[] = R"(#version 310 es |
| precision mediump float; |
| uniform sampler2D u_tex2D; |
| in vec2 v_texCoord; |
| out vec4 out_FragColor; |
| void main() |
| { |
| out_FragColor = texture(u_tex2D, v_texCoord); |
| })"; |
| |
| constexpr char kFSSource1[] = R"(#version 310 es |
| precision mediump float; |
| out vec4 out_FragColor; |
| void main() |
| { |
| out_FragColor = vec4(1.0); |
| })"; |
| |
| constexpr char kCSSource[] = R"(#version 310 es |
| layout(local_size_x=1, local_size_y=1, local_size_z=1) in; |
| layout(rgba32f, binding=0) readonly uniform highp image2D uIn; |
| layout(std140, binding=0) buffer buf { |
| vec4 outData; |
| }; |
| |
| void main() |
| { |
| outData = imageLoad(uIn, ivec2(gl_LocalInvocationID.xy)); |
| })"; |
| |
| GLfloat initValue[4] = {1.0, 1.0, 1.0, 1.0}; |
| |
| // Step 1: Set up a simple 2D Texture rendering loop. |
| GLTexture texture; |
| glBindTexture(GL_TEXTURE_2D, texture); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA32F, 1, 1); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_FLOAT, initValue); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| |
| GLBuffer vertexBuffer; |
| GLfloat vertices[] = {-1, -1, 1, -1, -1, 1, 1, 1}; |
| glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); |
| glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(GLfloat), vertices, GL_STATIC_DRAW); |
| |
| GLBuffer ssbo; |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, 16, nullptr, GL_STREAM_DRAW); |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); |
| |
| ANGLE_GL_PROGRAM(program, kVSSource, kFSSource); |
| glUseProgram(program); |
| |
| GLint posLoc = glGetAttribLocation(program, "a_position"); |
| ASSERT_NE(-1, posLoc); |
| |
| glVertexAttribPointer(posLoc, 2, GL_FLOAT, GL_FALSE, 0, nullptr); |
| glEnableVertexAttribArray(posLoc); |
| ASSERT_GL_NO_ERROR(); |
| |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Call glReadPixels to reset the getPerfCounters().renderPasses |
| std::vector<GLColor> actualColors(1); |
| glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, actualColors.data()); |
| |
| // Ideally, the following "FS sample + CS image load + FS something", should |
| // handle in one render pass. |
| // Currently, we can ensure the first of "FS sample + CS image load" in one |
| // render pass, but will start new render pass if following the last FS operations, |
| // which need to be optimized further. |
| uint64_t expectedRenderPassCount = getPerfCounters().renderPasses + 2; |
| |
| // Now this texture owns none layout transition |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Step 2: load this image through compute |
| ANGLE_GL_COMPUTE_PROGRAM(csProgram, kCSSource); |
| glUseProgram(csProgram); |
| |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssbo); |
| glBindImageTexture(0, texture, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F); |
| |
| glDispatchCompute(1, 1, 1); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Step3 |
| ANGLE_GL_PROGRAM(program2, kVSSource, kFSSource1); |
| glUseProgram(program2); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| ASSERT_GL_NO_ERROR(); |
| |
| uint64_t actualRenderPassCount = getPerfCounters().renderPasses; |
| EXPECT_EQ(expectedRenderPassCount, actualRenderPassCount); |
| } |
| |
| // Test that one texture sampled by fragment shader, compute shader and fragment |
| // shader sequentlly. |
| TEST_P(VulkanPerformanceCounterTest_ES31, TextureSampleByDrawDispatchDraw) |
| { |
| constexpr char kVSSource[] = R"(#version 310 es |
| in vec4 a_position; |
| out vec2 v_texCoord; |
| |
| void main() |
| { |
| gl_Position = vec4(a_position.xy, 0.0, 1.0); |
| v_texCoord = a_position.xy * 0.5 + vec2(0.5); |
| })"; |
| |
| constexpr char kFSSource[] = R"(#version 310 es |
| uniform sampler2D u_tex2D; |
| precision highp float; |
| in vec2 v_texCoord; |
| out vec4 out_FragColor; |
| void main() |
| { |
| out_FragColor = texture(u_tex2D, v_texCoord); |
| })"; |
| |
| constexpr char kCSSource[] = R"(#version 310 es |
| layout(local_size_x=1, local_size_y=1, local_size_z=1) in; |
| precision highp sampler2D; |
| uniform sampler2D tex; |
| layout(std140, binding=0) buffer buf { |
| vec4 outData; |
| }; |
| void main() |
| { |
| uint x = gl_LocalInvocationID.x; |
| uint y = gl_LocalInvocationID.y; |
| outData = texture(tex, vec2(x, y)); |
| })"; |
| |
| GLfloat initValue[4] = {1.0, 1.0, 1.0, 1.0}; |
| |
| // Step 1: Set up a simple 2D Texture rendering loop. |
| GLTexture texture; |
| glBindTexture(GL_TEXTURE_2D, texture); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA32F, 1, 1); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_FLOAT, initValue); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| |
| GLfloat vertices[] = {-1, -1, 1, -1, -1, 1, 1, 1}; |
| |
| GLBuffer vertexBuffer; |
| glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); |
| glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(GLfloat), vertices, GL_STATIC_DRAW); |
| |
| ANGLE_GL_PROGRAM(program, kVSSource, kFSSource); |
| glUseProgram(program); |
| |
| GLint posLoc = glGetAttribLocation(program, "a_position"); |
| ASSERT_NE(-1, posLoc); |
| |
| glVertexAttribPointer(posLoc, 2, GL_FLOAT, GL_FALSE, 0, nullptr); |
| glEnableVertexAttribArray(posLoc); |
| ASSERT_GL_NO_ERROR(); |
| |
| glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |
| ASSERT_GL_NO_ERROR(); |
| uint64_t expectedRenderPassCount = getPerfCounters().renderPasses + 1; |
| |
| // Step 2: sample this texture through compute |
| GLBuffer ssbo; |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, 16, nullptr, GL_STREAM_DRAW); |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); |
| |
| ANGLE_GL_COMPUTE_PROGRAM(csProgram, kCSSource); |
| glUseProgram(csProgram); |
| |
| glBindTexture(GL_TEXTURE_2D, texture); |
| glUniform1i(glGetUniformLocation(csProgram, "tex"), 0); |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssbo); |
| |
| glDispatchCompute(1, 1, 1); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Step3: use the first program sample texture again |
| glUseProgram(program); |
| glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |
| ASSERT_GL_NO_ERROR(); |
| |
| uint64_t actualRenderPassCount = getPerfCounters().renderPasses; |
| EXPECT_EQ(expectedRenderPassCount, actualRenderPassCount); |
| } |
| |
| // Verify a mid-render pass clear of a newly enabled attachment uses LOAD_OP_CLEAR. |
| TEST_P(VulkanPerformanceCounterTest, DisableThenMidRenderPassClear) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| // This optimization is not implemented when this workaround is in effect. |
| ANGLE_SKIP_TEST_IF(hasPreferDrawOverClearAttachments()); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, color(Clears+1, Loads+0, LoadNones+0, Stores+2, StoreNones+0) |
| // vkCmdClearAttachments should be used for color attachment 0. |
| setExpectedCountersForColorOps(getPerfCounters(), 1, 1, 0, 0, 2, 0, &expected); |
| expected.colorClearAttachments = getPerfCounters().colorClearAttachments + 1; |
| |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| |
| GLTexture textures[2]; |
| |
| glBindTexture(GL_TEXTURE_2D, textures[0]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[0], 0); |
| |
| glBindTexture(GL_TEXTURE_2D, textures[1]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, textures[1], 0); |
| |
| // Only enable attachment 0. |
| GLenum drawBuffers[] = {GL_COLOR_ATTACHMENT0, GL_NONE}; |
| glDrawBuffers(2, drawBuffers); |
| |
| // Draw red. |
| ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Enable attachment 1. |
| drawBuffers[1] = GL_COLOR_ATTACHMENT1; |
| glDrawBuffers(2, drawBuffers); |
| |
| // Clear both attachments to green. |
| glClearColor(0.0f, 1.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| layout(location = 0) out vec4 my_FragColor0; |
| layout(location = 1) out vec4 my_FragColor1; |
| void main() |
| { |
| my_FragColor0 = vec4(0.0, 0.0, 1.0, 1.0); |
| my_FragColor1 = vec4(0.0, 0.0, 1.0, 1.0); |
| })"; |
| |
| // Draw blue to both attachments. |
| ANGLE_GL_PROGRAM(blueProgram, essl3_shaders::vs::Simple(), kFS); |
| drawQuad(blueProgram, essl3_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_EQ(expected.renderPasses, getPerfCounters().renderPasses); |
| |
| // Verify attachment 0. |
| glReadBuffer(GL_COLOR_ATTACHMENT0); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_EQ(0, 0, 0, 0, 255, 255); |
| // Verify attachment 1. |
| glReadBuffer(GL_COLOR_ATTACHMENT1); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_EQ(0, 0, 0, 0, 255, 255); |
| |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| EXPECT_CLEAR_ATTACHMENTS_COUNTER(expected.colorClearAttachments, |
| getPerfCounters().colorClearAttachments); |
| |
| // Expect rpCount+1, color(Clears+0, Loads+1, LoadNones+0, Stores+1, StoreNones+0) |
| setExpectedCountersForColorOps(getPerfCounters(), 1, 0, 1, 0, 1, 0, &expected); |
| |
| GLFramebuffer fbo2; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo2); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[1], 0); |
| |
| glEnable(GL_BLEND); |
| glBlendFunc(GL_ONE, GL_ONE); |
| |
| // Blend red |
| drawQuad(redProgram, essl3_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_EQ(expected.renderPasses, getPerfCounters().renderPasses); |
| |
| // Verify purple |
| glReadBuffer(GL_COLOR_ATTACHMENT0); |
| EXPECT_PIXEL_EQ(0, 0, 255, 0, 255, 255); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Copy of ClearTest.InceptionScissorClears. |
| // Clears many small concentric rectangles using scissor regions. Verifies vkCmdClearAttachments() |
| // is used for the scissored clears, rather than vkCmdDraw(). |
| TEST_P(VulkanPerformanceCounterTest, InceptionScissorClears) |
| { |
| // https://issuetracker.google.com/166809097 |
| ANGLE_SKIP_TEST_IF(IsQualcomm() && IsVulkan()); |
| |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, color(Clears+1, Loads+0, LoadNones+0, Stores+1, StoreNones+0) |
| setExpectedCountersForColorOps(getPerfCounters(), 1, 1, 0, 0, 1, 0, &expected); |
| |
| angle::RNG rng; |
| |
| constexpr GLuint kSize = 16; |
| |
| // Create a square user FBO so we have more control over the dimensions. |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| |
| GLRenderbuffer rbo; |
| glBindRenderbuffer(GL_RENDERBUFFER, rbo); |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kSize, kSize); |
| |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| glViewport(0, 0, kSize, kSize); |
| |
| // Clear small concentric squares using scissor. |
| std::vector<GLColor> expectedColors; |
| // TODO(syoussefi): verify this |
| [[maybe_unused]] uint64_t numScissoredClears = 0; |
| for (GLuint index = 0; index < (kSize - 1) / 2; index++) |
| { |
| // Do the first clear without the scissor. |
| if (index > 0) |
| { |
| glEnable(GL_SCISSOR_TEST); |
| glScissor(index, index, kSize - (index * 2), kSize - (index * 2)); |
| ++numScissoredClears; |
| } |
| |
| GLColor color = RandomColor(&rng); |
| expectedColors.push_back(color); |
| Vector4 floatColor = color.toNormalizedVector(); |
| glClearColor(floatColor[0], floatColor[1], floatColor[2], floatColor[3]); |
| glClear(GL_COLOR_BUFFER_BIT); |
| } |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| // Make sure everything was done in a single renderpass. |
| EXPECT_EQ(expected.renderPasses, getPerfCounters().renderPasses); |
| |
| std::vector<GLColor> actualColors(expectedColors.size()); |
| glReadPixels(0, kSize / 2, actualColors.size(), 1, GL_RGBA, GL_UNSIGNED_BYTE, |
| actualColors.data()); |
| |
| EXPECT_EQ(expectedColors, actualColors); |
| |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Copy of ClearTest.Depth16Scissored. |
| // Clears many small concentric rectangles using scissor regions. Verifies vkCmdClearAttachments() |
| // is used for the scissored clears, rather than vkCmdDraw(). |
| TEST_P(VulkanPerformanceCounterTest, Depth16Scissored) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| GLRenderbuffer renderbuffer; |
| glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); |
| constexpr int kRenderbufferSize = 64; |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, kRenderbufferSize, |
| kRenderbufferSize); |
| |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderbuffer); |
| |
| glClearDepthf(0.0f); |
| glClear(GL_DEPTH_BUFFER_BIT); |
| |
| glEnable(GL_SCISSOR_TEST); |
| constexpr int kNumSteps = 13; |
| // TODO(syoussefi): verify this |
| [[maybe_unused]] uint64_t numScissoredClears = 0; |
| for (int ndx = 1; ndx < kNumSteps; ndx++) |
| { |
| float perc = static_cast<float>(ndx) / static_cast<float>(kNumSteps); |
| glScissor(0, 0, static_cast<int>(kRenderbufferSize * perc), |
| static_cast<int>(kRenderbufferSize * perc)); |
| glClearDepthf(perc); |
| glClear(GL_DEPTH_BUFFER_BIT); |
| ++numScissoredClears; |
| } |
| |
| // Make sure everything was done in a single renderpass. |
| EXPECT_EQ(getPerfCounters().renderPasses, 1u); |
| } |
| |
| // Copy of ClearTest.InceptionScissorClears. |
| // Clears many small concentric rectangles using scissor regions. |
| TEST_P(VulkanPerformanceCounterTest, DrawThenInceptionScissorClears) |
| { |
| // https://issuetracker.google.com/166809097 |
| ANGLE_SKIP_TEST_IF(IsQualcomm() && IsVulkan()); |
| |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, color(Clears+0, Loads+0, LoadNones+0, Stores+1, StoreNones+0) |
| setExpectedCountersForColorOps(getPerfCounters(), 1, 0, 0, 0, 1, 0, &expected); |
| |
| angle::RNG rng; |
| std::vector<GLColor> expectedColors; |
| constexpr GLuint kSize = 16; |
| |
| // Create a square user FBO so we have more control over the dimensions. |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| |
| GLRenderbuffer rbo; |
| glBindRenderbuffer(GL_RENDERBUFFER, rbo); |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kSize, kSize); |
| |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| glViewport(0, 0, kSize, kSize); |
| |
| ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| expectedColors.push_back(GLColor::red); |
| |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| |
| // Expect rpCount+1, color(Clears+0, Loads+1, LoadNones+0, Stores+1, StoreNones+0) |
| // TODO: Optimize scissored clears to use loadOp = CLEAR. anglebug.com/5194 |
| setExpectedCountersForColorOps(getPerfCounters(), 1, 0, 1, 0, 1, 0, &expected); |
| |
| // Draw small concentric squares using scissor. |
| // TODO(syoussefi): verify this |
| [[maybe_unused]] uint64_t numScissoredClears = 0; |
| // All clears are to a scissored render area. |
| for (GLuint index = 1; index < (kSize - 1) / 2; index++) |
| { |
| glEnable(GL_SCISSOR_TEST); |
| glScissor(index, index, kSize - (index * 2), kSize - (index * 2)); |
| ++numScissoredClears; |
| |
| GLColor color = RandomColor(&rng); |
| expectedColors.push_back(color); |
| Vector4 floatColor = color.toNormalizedVector(); |
| glClearColor(floatColor[0], floatColor[1], floatColor[2], floatColor[3]); |
| glClear(GL_COLOR_BUFFER_BIT); |
| } |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| // Make sure everything was done in a single renderpass. |
| EXPECT_EQ(expected.renderPasses, getPerfCounters().renderPasses); |
| |
| // Close the render pass to update the performance counters. |
| std::vector<GLColor> actualColors(expectedColors.size()); |
| glReadPixels(0, kSize / 2, actualColors.size(), 1, GL_RGBA, GL_UNSIGNED_BYTE, |
| actualColors.data()); |
| EXPECT_EQ(expectedColors, actualColors); |
| |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Test that color clears are respected after invalidate |
| TEST_P(VulkanPerformanceCounterTest, ColorClearAfterInvalidate) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| GLRenderbuffer renderbuffer; |
| setupForColorDepthOpsTest(&framebuffer, &texture, &renderbuffer); |
| |
| // Execute the scenario that this test is for: |
| |
| // color+depth invalidate, color+depth clear |
| // |
| // Expected: |
| // rpCount+1, |
| // depth(Clears+1, Loads+0, LoadNones+0, Stores+0, StoreNones+0) |
| // color(Clears+1, Loads+0, LoadNones+0, Stores+1, StoreNones+0) |
| setExpectedCountersForDepthOps(getPerfCounters(), 0, 1, 0, 0, 0, 0, &expected); |
| setExpectedCountersForColorOps(getPerfCounters(), 1, 1, 0, 0, 1, 0, &expected); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); |
| |
| // Invalidate (loadOp C=DONTCARE, D=DONTCARE) |
| const GLenum discards[] = {GL_DEPTH_ATTACHMENT, GL_COLOR_ATTACHMENT0}; |
| glInvalidateFramebuffer(GL_FRAMEBUFFER, 2, discards); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Clear (loadOp C=CLEAR, D=CLEAR) |
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
| |
| // Save existing draw buffers |
| GLint maxDrawBuffers = 0; |
| glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers); |
| std::vector<GLenum> savedDrawBuffers(maxDrawBuffers); |
| for (int i = 0; i < maxDrawBuffers; i++) |
| glGetIntegerv(GL_DRAW_BUFFER0 + i, (GLint *)&savedDrawBuffers[i]); |
| |
| // Draw depth-only |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| glDrawBuffers(0, nullptr); |
| glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| glDrawBuffers(maxDrawBuffers, savedDrawBuffers.data()); |
| glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Invalidate depth only (storeOp should be C=STORE/D=CLEAR) |
| glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, discards); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| |
| EXPECT_EQ(expected.renderPasses, getPerfCounters().renderPasses); |
| |
| // Use swapBuffers and then check how many loads and stores were actually done |
| swapBuffers(); |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| EXPECT_DEPTH_OP_COUNTERS(getPerfCounters(), expected); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Test that depth clears are picked up as loadOp even if a color blit is done in between. |
| TEST_P(VulkanPerformanceCounterTest, DepthClearThenColorBlitThenDraw) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| GLRenderbuffer renderbuffer; |
| setupForColorDepthOpsTest(&framebuffer, &texture, &renderbuffer); |
| |
| // Execute the scenario that this test is for: |
| |
| // color+depth clear, blit color as source, draw |
| // |
| // Expected: |
| // rpCount+1, |
| // depth(Clears+1, Loads+0, LoadNones+0, Stores+1, StoreNones+0) |
| // color(Clears+0, Loads+1, LoadNones+0, Stores+1, StoreNones+0) |
| setExpectedCountersForDepthOps(getPerfCounters(), 0, 1, 0, 0, 1, 0, &expected); |
| setExpectedCountersForColorOps(getPerfCounters(), 1, 0, 1, 0, 1, 0, &expected); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); |
| glClearColor(0, 1, 0, 1); |
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
| |
| // Blit color into another FBO |
| GLTexture destColor; |
| glBindTexture(GL_TEXTURE_2D, destColor); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kOpsTestSize, kOpsTestSize); |
| |
| GLFramebuffer destFbo; |
| glBindFramebuffer(GL_DRAW_FRAMEBUFFER, destFbo); |
| glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, destColor, 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_DRAW_FRAMEBUFFER); |
| |
| glBlitFramebuffer(0, 0, kOpsTestSize, kOpsTestSize, 0, 0, kOpsTestSize, kOpsTestSize, |
| GL_COLOR_BUFFER_BIT, GL_NEAREST); |
| |
| // Draw back to the original framebuffer |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 1.f); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_EQ(expected.renderPasses, getPerfCounters().renderPasses); |
| |
| // Use swapBuffers and then check how many loads and stores were actually done. The clear |
| // applied to depth should be done as loadOp, not flushed during the blit. |
| swapBuffers(); |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| EXPECT_DEPTH_OP_COUNTERS(getPerfCounters(), expected); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Verify results |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| // Result of blit should be green |
| glBindFramebuffer(GL_READ_FRAMEBUFFER, destFbo); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| // Ensure that image gets marked as defined after clear + invalidate + clear, and that we use |
| // LoadOp=Load for a renderpass which draws to it after the clear has been flushed with a blit. |
| TEST_P(VulkanPerformanceCounterTest, InvalidateThenRepeatedClearThenBlitThenDraw) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| constexpr GLsizei kSize = 2; |
| |
| // tex[0] is what's being tested. The others are helpers. |
| GLTexture tex[3]; |
| for (int i = 0; i < 3; ++i) |
| { |
| glBindTexture(GL_TEXTURE_2D, tex[i]); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| nullptr); |
| } |
| |
| GLFramebuffer fbo[3]; |
| for (int i = 0; i < 3; ++i) |
| { |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo[i]); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex[i], 0); |
| } |
| |
| // Expect rpCount+1, color(Clears+0, Loads+1, LoadNones+0, Stores+1, StoreNones+0) |
| setExpectedCountersForColorOps(getPerfCounters(), 1, 0, 1, 0, 1, 0, &expected); |
| |
| // Clear the image through fbo[0], and make sure the clear is flushed outside the render pass. |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo[0]); |
| glClearColor(1, 0, 0, 1); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| // Invalidate it such that the contents are marked as undefined. Note that regardless of the |
| // marking, the image is cleared nevertheless. |
| const GLenum discards[] = {GL_COLOR_ATTACHMENT0}; |
| glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, discards); |
| |
| // Clear it again to the same color. |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| // Bind tex[0] to fbo[1] as the read fbo, and blit to fbo[2] |
| glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo[1]); |
| glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex[0], 0); |
| glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[2]); |
| |
| // Blit. This causes the second clear of tex[0] to be flushed outside the render pass, which |
| // may be optimized out. |
| glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize, GL_COLOR_BUFFER_BIT, GL_NEAREST); |
| |
| // Switch back to fbo[0] and draw with blend. If the second clear is dropped and the image |
| // continues to be marked as invalidated, loadOp=DONT_CARE would be used instead of loadOp=LOAD. |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo[0]); |
| |
| glEnable(GL_BLEND); |
| glBlendFunc(GL_ONE, GL_ONE); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue()); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0); |
| |
| EXPECT_EQ(expected.renderPasses, getPerfCounters().renderPasses); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::magenta); |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Ensure that image gets marked as defined after clear + invalidate + clear, and that we use |
| // LoadOp=Load for a renderpass which draws to it after the clear has been flushed with read pixels. |
| TEST_P(VulkanPerformanceCounterTest, InvalidateThenRepeatedClearThenReadbackThenDraw) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| constexpr GLsizei kSize = 2; |
| |
| GLTexture tex; |
| glBindTexture(GL_TEXTURE_2D, tex); |
| 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, tex, 0); |
| |
| // Expect rpCount+1, color(Clears+0, Loads+1, LoadNones+0, Stores+1, StoreNones+0) |
| setExpectedCountersForColorOps(getPerfCounters(), 1, 0, 1, 0, 1, 0, &expected); |
| |
| // Clear the image, and make sure the clear is flushed outside the render pass. |
| glClearColor(1, 0, 0, 1); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| // Invalidate it such that the contents are marked as undefined. Note that regarldess of the |
| // marking, the image is cleared nevertheless. |
| const GLenum discards[] = {GL_COLOR_ATTACHMENT0}; |
| glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, discards); |
| |
| // Clear it again to the same color, and make sure the clear is flushed outside the render pass, |
| // which may be optimized out. |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| // Draw with blend. If the second clear is dropped and the image continues to be marked as |
| // invalidated, loadOp=DONT_CARE would be used instead of loadOp=LOAD. |
| glEnable(GL_BLEND); |
| glBlendFunc(GL_ONE, GL_ONE); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue()); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::magenta); |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Test that draw after invalidate restores the contents of the color image. |
| TEST_P(VulkanPerformanceCounterTest, InvalidateThenDraw) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| constexpr GLsizei kSize = 2; |
| |
| GLTexture tex; |
| glBindTexture(GL_TEXTURE_2D, tex); |
| 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, tex, 0); |
| |
| // Expect rpCount+1, color(Clears+0, Loads+0, LoadNones+0, Stores+1, StoreNones+0) |
| setExpectedCountersForColorOps(getPerfCounters(), 1, 0, 0, 0, 1, 0, &expected); |
| |
| ANGLE_GL_PROGRAM(blue, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue()); |
| drawQuad(blue, essl1_shaders::PositionAttrib(), 0); |
| |
| // Invalidate it such that the contents are marked as undefined |
| const GLenum discards[] = {GL_COLOR_ATTACHMENT0}; |
| glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, discards); |
| |
| // Draw again. |
| ANGLE_GL_PROGRAM(green, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); |
| drawQuad(green, essl1_shaders::PositionAttrib(), 0); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Test that masked draw after invalidate does NOT restore the contents of the color image. |
| TEST_P(VulkanPerformanceCounterTest, InvalidateThenMaskedDraw) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| constexpr GLsizei kSize = 2; |
| |
| GLTexture tex; |
| glBindTexture(GL_TEXTURE_2D, tex); |
| 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, tex, 0); |
| |
| // Expect rpCount+1, color(Clears+0, Loads+0, LoadNones+0, Stores+0, StoreNones+0) |
| setExpectedCountersForColorOps(getPerfCounters(), 1, 0, 0, 0, 0, 0, &expected); |
| |
| ANGLE_GL_PROGRAM(blue, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue()); |
| drawQuad(blue, essl1_shaders::PositionAttrib(), 0); |
| |
| // Invalidate it such that the contents are marked as undefined |
| const GLenum discards[] = {GL_COLOR_ATTACHMENT0}; |
| glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, discards); |
| |
| // Draw again. |
| glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); |
| ANGLE_GL_PROGRAM(green, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); |
| drawQuad(green, essl1_shaders::PositionAttrib(), 0); |
| |
| // Break the render pass |
| glFinish(); |
| |
| EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Tests that the submission counters count the implicit submission in eglSwapBuffers(). |
| TEST_P(VulkanPerformanceCounterTest, VerifySubmitCountersForSwapBuffer) |
| { |
| uint64_t expectedVkQueueSubmitCount = getPerfCounters().vkQueueSubmitCallsTotal; |
| uint64_t expectedCommandQueueSubmitCount = getPerfCounters().commandQueueSubmitCallsTotal; |
| |
| // One submission coming from clear and read back |
| ++expectedVkQueueSubmitCount; |
| ++expectedCommandQueueSubmitCount; |
| |
| glClearColor(1, 0, 0, 1); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| EXPECT_EQ(getPerfCounters().vkQueueSubmitCallsTotal, expectedVkQueueSubmitCount); |
| EXPECT_EQ(getPerfCounters().commandQueueSubmitCallsTotal, expectedCommandQueueSubmitCount); |
| |
| // One submission coming from draw and implicit submission from eglSwapBuffers |
| ++expectedVkQueueSubmitCount; |
| ++expectedCommandQueueSubmitCount; |
| |
| ANGLE_GL_PROGRAM(drawGreen, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); |
| drawQuad(drawGreen, essl1_shaders::PositionAttrib(), 1.f); |
| swapBuffers(); |
| |
| EXPECT_EQ(getPerfCounters().vkQueueSubmitCallsTotal, expectedVkQueueSubmitCount); |
| EXPECT_EQ(getPerfCounters().commandQueueSubmitCallsTotal, expectedCommandQueueSubmitCount); |
| } |
| |
| // Tests that PreferSubmitAtFBOBoundary feature works properly. Bind to different FBO and should |
| // trigger submit of previous FBO. In this specific test, we switch to system default framebuffer |
| // which is always considered as "dirty". |
| TEST_P(VulkanPerformanceCounterTest, VerifySubmitCounterForSwitchUserFBOToSystemFramebuffer) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| uint64_t expectedCommandQueueSubmitCount = getPerfCounters().commandQueueSubmitCallsTotal; |
| uint64_t expectedCommandQueueWaitSemaphoreCount = |
| getPerfCounters().commandQueueWaitSemaphoresTotal; |
| |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| setupForColorOpsTest(&framebuffer, &texture); |
| |
| // Draw |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| // One submission coming from glBindFramebuffer and draw |
| ++expectedCommandQueueSubmitCount; |
| // This submission should not wait for any semaphore. |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_EQ(getPerfCounters().commandQueueSubmitCallsTotal, expectedCommandQueueSubmitCount); |
| EXPECT_EQ(getPerfCounters().commandQueueWaitSemaphoresTotal, |
| expectedCommandQueueWaitSemaphoreCount); |
| |
| // This submission must wait for ANI's semaphore |
| ++expectedCommandQueueWaitSemaphoreCount; |
| ++expectedCommandQueueSubmitCount; |
| swapBuffers(); |
| EXPECT_EQ(getPerfCounters().commandQueueSubmitCallsTotal, expectedCommandQueueSubmitCount); |
| EXPECT_EQ(getPerfCounters().commandQueueWaitSemaphoresTotal, |
| expectedCommandQueueWaitSemaphoreCount); |
| } |
| |
| // Tests that PreferSubmitAtFBOBoundary feature works properly. Bind to different FBO and should |
| // trigger submit of previous FBO. In this specific test, we test bind to a new user FBO which we |
| // used to had a bug. |
| TEST_P(VulkanPerformanceCounterTest, VerifySubmitCounterForSwitchUserFBOToDirtyUserFBO) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| uint64_t expectedCommandQueueSubmitCount = getPerfCounters().commandQueueSubmitCallsTotal; |
| uint64_t expectedCommandQueueWaitSemaphoreCount = |
| getPerfCounters().commandQueueWaitSemaphoresTotal; |
| |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| setupForColorOpsTest(&framebuffer, &texture); |
| |
| // Draw |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| if (hasPreferSubmitAtFBOBoundary()) |
| { |
| // One submission coming from glBindFramebuffer and draw |
| ++expectedCommandQueueSubmitCount; |
| // This submission should not wait for any semaphore. |
| } |
| |
| // Create and bind to a new FBO |
| GLFramebuffer framebuffer2; |
| GLTexture texture2; |
| setupForColorOpsTest(&framebuffer2, &texture2); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| ++expectedCommandQueueSubmitCount; |
| // This submission should not wait for ANI's semaphore |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| EXPECT_EQ(getPerfCounters().commandQueueSubmitCallsTotal, expectedCommandQueueSubmitCount); |
| EXPECT_EQ(getPerfCounters().commandQueueWaitSemaphoresTotal, |
| expectedCommandQueueWaitSemaphoreCount); |
| } |
| |
| // Ensure that glFlush doesn't lead to vkQueueSubmit if there's nothing to submit. |
| TEST_P(VulkanPerformanceCounterTest, UnnecessaryFlushDoesntCauseSubmission) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| swapBuffers(); |
| uint64_t expectedVkQueueSubmitCalls = getPerfCounters().vkQueueSubmitCallsTotal; |
| |
| glFlush(); |
| glFlush(); |
| glFlush(); |
| |
| // Nothing was recorded, so there shouldn't be anything to flush. |
| glFinish(); |
| EXPECT_EQ(getPerfCounters().vkQueueSubmitCallsTotal, expectedVkQueueSubmitCalls); |
| |
| glClearColor(1, 0, 0, 1); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| // One submission for the above readback |
| ++expectedVkQueueSubmitCalls; |
| |
| glFinish(); |
| EXPECT_EQ(getPerfCounters().vkQueueSubmitCallsTotal, expectedVkQueueSubmitCalls); |
| |
| glFlush(); |
| glFlush(); |
| glFlush(); |
| |
| // No additional submissions since last one |
| glFinish(); |
| EXPECT_EQ(getPerfCounters().vkQueueSubmitCallsTotal, expectedVkQueueSubmitCalls); |
| } |
| |
| // Ensure that glFenceSync doesn't lead to vkQueueSubmit if there's nothing to submit. |
| TEST_P(VulkanPerformanceCounterTest, SyncWihtoutCommandsDoesntCauseSubmission) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| swapBuffers(); |
| uint64_t expectedVkQueueSubmitCalls = getPerfCounters().vkQueueSubmitCallsTotal; |
| |
| glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); |
| |
| // Nothing was recorded, so there shouldn't be anything to flush. |
| glFinish(); |
| EXPECT_EQ(getPerfCounters().vkQueueSubmitCallsTotal, expectedVkQueueSubmitCalls); |
| } |
| |
| // In single-buffer mode, ensure that unnecessary eglSwapBuffers is completely ignored (i.e. doesn't |
| // lead to a command queue submission, consuming a submission serial). Used to verify an |
| // optimization that ensures CPU throttling doesn't incur GPU bubbles with unnecessary |
| // eglSwapBuffers calls. |
| TEST_P(VulkanPerformanceCounterTest_SingleBuffer, SwapBuffersAfterFlushIgnored) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| // Set mode to single buffer |
| EXPECT_EGL_TRUE(eglSurfaceAttrib(getEGLWindow()->getDisplay(), getEGLWindow()->getSurface(), |
| EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER)); |
| |
| // Swap buffers so mode switch takes effect. |
| swapBuffers(); |
| uint64_t expectedCommandQueueSubmitCalls = getPerfCounters().commandQueueSubmitCallsTotal; |
| |
| // Further swap buffers should be ineffective. |
| swapBuffers(); |
| swapBuffers(); |
| swapBuffers(); |
| swapBuffers(); |
| |
| EXPECT_EQ(getPerfCounters().commandQueueSubmitCallsTotal, expectedCommandQueueSubmitCalls); |
| |
| // Issue commands and flush them. |
| glClearColor(1, 0, 0, 1); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| // One submission for the above readback |
| ++expectedCommandQueueSubmitCalls; |
| EXPECT_EQ(getPerfCounters().commandQueueSubmitCallsTotal, expectedCommandQueueSubmitCalls); |
| |
| // Further swap buffers should again be ineffective. |
| swapBuffers(); |
| swapBuffers(); |
| swapBuffers(); |
| swapBuffers(); |
| swapBuffers(); |
| |
| EXPECT_EQ(getPerfCounters().commandQueueSubmitCallsTotal, expectedCommandQueueSubmitCalls); |
| } |
| |
| // Verifies that we share Texture descriptor sets between programs. |
| TEST_P(VulkanPerformanceCounterTest, TextureDescriptorsAreShared) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| ANGLE_GL_PROGRAM(testProgram1, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D()); |
| ANGLE_GL_PROGRAM(testProgram2, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D()); |
| |
| GLTexture texture1; |
| glBindTexture(GL_TEXTURE_2D, texture1); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &GLColor::red); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| |
| GLTexture texture2; |
| glBindTexture(GL_TEXTURE_2D, texture2); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &GLColor::red); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| |
| setupQuadVertexBuffer(0.5f, 1.0f); |
| |
| glUseProgram(testProgram1); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| glBindTexture(GL_TEXTURE_2D, texture1); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| glBindTexture(GL_TEXTURE_2D, texture2); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| GLuint expectedCacheMisses = getPerfCounters().textureDescriptorSetCacheMisses; |
| EXPECT_GT(expectedCacheMisses, 0u); |
| |
| glUseProgram(testProgram2); |
| |
| glBindTexture(GL_TEXTURE_2D, texture1); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| glBindTexture(GL_TEXTURE_2D, texture2); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| GLuint actualCacheMisses = getPerfCounters().textureDescriptorSetCacheMisses; |
| EXPECT_EQ(expectedCacheMisses, actualCacheMisses); |
| } |
| |
| // Verifies that we share Uniform Buffer descriptor sets between programs. |
| TEST_P(VulkanPerformanceCounterTest, UniformBufferDescriptorsAreShared) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| out vec4 color; |
| uniform block { |
| vec4 uniColor; |
| }; |
| |
| void main() { |
| color = uniColor; |
| })"; |
| |
| ANGLE_GL_PROGRAM(testProgram1, essl3_shaders::vs::Simple(), kFS); |
| ANGLE_GL_PROGRAM(testProgram2, essl3_shaders::vs::Simple(), kFS); |
| |
| Vector4 red(1, 0, 0, 1); |
| Vector4 green(0, 1, 0, 1); |
| |
| GLBuffer ubo1; |
| glBindBuffer(GL_UNIFORM_BUFFER, ubo1); |
| glBufferData(GL_UNIFORM_BUFFER, sizeof(Vector4), red.data(), GL_STATIC_DRAW); |
| |
| GLBuffer ubo2; |
| glBindBuffer(GL_UNIFORM_BUFFER, ubo2); |
| glBufferData(GL_UNIFORM_BUFFER, sizeof(Vector4), green.data(), GL_STATIC_DRAW); |
| |
| setupQuadVertexBuffer(0.5f, 1.0f); |
| |
| glUseProgram(testProgram1); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| glBindBufferBase(GL_UNIFORM_BUFFER, 0, ubo1); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| glBindBufferBase(GL_UNIFORM_BUFFER, 0, ubo2); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| GLuint expectedCacheMisses = getPerfCounters().shaderResourcesDescriptorSetCacheMisses; |
| EXPECT_GT(expectedCacheMisses, 0u); |
| |
| glUseProgram(testProgram2); |
| |
| glBindBufferBase(GL_UNIFORM_BUFFER, 0, ubo1); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| glBindBufferBase(GL_UNIFORM_BUFFER, 0, ubo2); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| GLuint actualCacheMisses = getPerfCounters().shaderResourcesDescriptorSetCacheMisses; |
| EXPECT_EQ(expectedCacheMisses, actualCacheMisses); |
| } |
| |
| // Test modifying texture size and render to it does not cause VkFramebuffer cache explode |
| TEST_P(VulkanPerformanceCounterTest, ResizeFBOAttachedTexture) |
| { |
| ANGLE_GL_PROGRAM(blueProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue()); |
| |
| int32_t framebufferCacheSizeBefore = getPerfCounters().framebufferCacheSize; |
| GLTexture texture; |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| for (GLint texWidth = 1; texWidth <= 10; texWidth++) |
| { |
| for (GLint texHeight = 1; texHeight <= 10; texHeight++) |
| { |
| // Allocate texture |
| glBindTexture(GL_TEXTURE_2D, texture); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, |
| GL_UNSIGNED_BYTE, nullptr); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| // Draw to FBO backed by the texture |
| glUseProgram(blueProgram); |
| drawQuad(blueProgram, std::string(essl1_shaders::PositionAttrib()), 0.0f); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); |
| } |
| } |
| int32_t framebufferCacheSizeAfter = getPerfCounters().framebufferCacheSize; |
| int32_t framebufferCacheSizeIncrease = framebufferCacheSizeAfter - framebufferCacheSizeBefore; |
| int32_t expectedFramebufferCacheSizeIncrease = (hasSupportsImagelessFramebuffer()) ? 0 : 1; |
| printf("\tframebufferCacheCountIncrease:%u\n", framebufferCacheSizeIncrease); |
| // We should not cache obsolete VkImages. Only current VkImage should be cached. |
| EXPECT_EQ(framebufferCacheSizeIncrease, expectedFramebufferCacheSizeIncrease); |
| } |
| |
| // Test calling glTexParameteri(GL_TEXTURE_SWIZZLE_*) on a texture that attached to FBO with the |
| // same value did not cause VkFramebuffer cache explode |
| TEST_P(VulkanPerformanceCounterTest, SetTextureSwizzleWithSameValueOnFBOAttachedTexture) |
| { |
| ANGLE_GL_PROGRAM(blueProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue()); |
| ANGLE_GL_PROGRAM(textureProgram, essl1_shaders::vs::Texture2D(), |
| essl1_shaders::fs::Texture2D()); |
| |
| // Allocate texture |
| GLTexture texture; |
| glBindTexture(GL_TEXTURE_2D, texture); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_GREEN); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_BLUE); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ALPHA); |
| |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| int32_t framebufferCacheSizeBefore = getPerfCounters().framebufferCacheSize; |
| for (GLint loop = 0; loop < 10; loop++) |
| { |
| // Draw to FBO |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glUseProgram(blueProgram); |
| drawQuad(blueProgram, std::string(essl1_shaders::PositionAttrib()), 0.0f); |
| |
| // Sample from texture |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| glClearColor(1, 0, 0, 1); |
| glClear(GL_COLOR_BUFFER_BIT); |
| glBindTexture(GL_TEXTURE_2D, texture); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_GREEN); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_BLUE); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ALPHA); |
| drawQuad(textureProgram, std::string(essl1_shaders::PositionAttrib()), 0.0f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); |
| } |
| // Now make fbo current and read out cache size and verify it does not grow just because of |
| // swizzle update even though there is no actual change. |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glUseProgram(blueProgram); |
| drawQuad(blueProgram, std::string(essl1_shaders::PositionAttrib()), 0.0f); |
| ASSERT_GL_NO_ERROR(); |
| int32_t framebufferCacheSizeAfter = getPerfCounters().framebufferCacheSize; |
| int32_t framebufferCacheSizeIncrease = framebufferCacheSizeAfter - framebufferCacheSizeBefore; |
| int32_t expectedFramebufferCacheSizeIncrease = (hasSupportsImagelessFramebuffer()) ? 0 : 1; |
| // This should not cause frame buffer cache increase. |
| EXPECT_EQ(framebufferCacheSizeIncrease, expectedFramebufferCacheSizeIncrease); |
| } |
| |
| // Test calling glTexParameteri(GL_TEXTURE_SWIZZLE_*) on a texture that attached to FBO with |
| // different value did not cause VkFramebuffer cache explode |
| TEST_P(VulkanPerformanceCounterTest, SetTextureSwizzleWithDifferentValueOnFBOAttachedTexture) |
| { |
| ANGLE_GL_PROGRAM(blueProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue()); |
| ANGLE_GL_PROGRAM(textureProgram, essl1_shaders::vs::Texture2D(), |
| essl1_shaders::fs::Texture2D()); |
| |
| // Allocate texture |
| GLTexture texture; |
| glBindTexture(GL_TEXTURE_2D, texture); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_GREEN); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_BLUE); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ALPHA); |
| |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| GLColor expectedColors[] = {GLColor::black, GLColor::black, GLColor::blue, GLColor::black, |
| GLColor::black, GLColor::blue, GLColor::green, GLColor::green, |
| GLColor::cyan, GLColor::black, GLColor::black, GLColor::blue, |
| GLColor::black, GLColor::black, GLColor::blue, GLColor::green, |
| GLColor::green, GLColor::cyan, GLColor::red, GLColor::red, |
| GLColor::magenta, GLColor::red, GLColor::red, GLColor::magenta, |
| GLColor::yellow, GLColor::yellow, GLColor::white}; |
| int32_t framebufferCacheSizeBefore = getPerfCounters().framebufferCacheSize; |
| int loop = 0; |
| for (GLenum swizzle_R = GL_RED; swizzle_R <= GL_BLUE; swizzle_R++) |
| { |
| for (GLenum swizzle_G = GL_RED; swizzle_G <= GL_BLUE; swizzle_G++) |
| { |
| for (GLenum swizzle_B = GL_RED; swizzle_B <= GL_BLUE; swizzle_B++) |
| { |
| // Draw to FBO |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glUseProgram(blueProgram); |
| drawQuad(blueProgram, std::string(essl1_shaders::PositionAttrib()), 0.0f); |
| |
| // Sample from texture |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| glClearColor(1, 0, 0, 1); |
| glClear(GL_COLOR_BUFFER_BIT); |
| glBindTexture(GL_TEXTURE_2D, texture); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, swizzle_R); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, swizzle_G); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, swizzle_B); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ALPHA); |
| drawQuad(textureProgram, std::string(essl1_shaders::PositionAttrib()), 0.0f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, expectedColors[loop]); |
| loop++; |
| } |
| } |
| } |
| // Now make fbo current and read out cache size and verify it does not grow just because of |
| // swizzle update even though there is no actual change. |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glUseProgram(blueProgram); |
| drawQuad(blueProgram, std::string(essl1_shaders::PositionAttrib()), 0.0f); |
| ASSERT_GL_NO_ERROR(); |
| int32_t framebufferCacheSizeAfter = getPerfCounters().framebufferCacheSize; |
| int32_t framebufferCacheSizeIncrease = framebufferCacheSizeAfter - framebufferCacheSizeBefore; |
| int32_t expectedFramebufferCacheSizeIncrease = (hasSupportsImagelessFramebuffer()) ? 0 : 1; |
| // This should not cause frame buffer cache increase. |
| EXPECT_EQ(framebufferCacheSizeIncrease, expectedFramebufferCacheSizeIncrease); |
| } |
| |
| void VulkanPerformanceCounterTest::saveAndReloadBinary(GLProgram *original, GLProgram *reloaded) |
| { |
| GLint programLength = 0; |
| GLint writtenLength = 0; |
| GLenum binaryFormat = 0; |
| |
| // Get the binary out of the program and delete it. |
| glGetProgramiv(*original, GL_PROGRAM_BINARY_LENGTH_OES, &programLength); |
| EXPECT_GL_NO_ERROR(); |
| |
| std::vector<uint8_t> binary(programLength); |
| glGetProgramBinaryOES(*original, programLength, &writtenLength, &binaryFormat, binary.data()); |
| EXPECT_GL_NO_ERROR(); |
| |
| original->reset(); |
| |
| // Reload the binary into another program |
| reloaded->makeEmpty(); |
| glProgramBinaryOES(*reloaded, binaryFormat, binary.data(), writtenLength); |
| EXPECT_GL_NO_ERROR(); |
| |
| GLint linkStatus; |
| glGetProgramiv(*reloaded, GL_LINK_STATUS, &linkStatus); |
| EXPECT_NE(linkStatus, 0); |
| } |
| |
| void VulkanPerformanceCounterTest::testPipelineCacheIsWarm(GLProgram *program, GLColor color) |
| { |
| glUseProgram(*program); |
| GLint colorUniformLocation = |
| glGetUniformLocation(*program, angle::essl1_shaders::ColorUniform()); |
| ASSERT_NE(-1, colorUniformLocation); |
| ASSERT_GL_NO_ERROR(); |
| |
| GLuint expectedCacheHits = getPerfCounters().pipelineCreationCacheHits + 1; |
| GLuint expectedCacheMisses = getPerfCounters().pipelineCreationCacheMisses; |
| |
| glUniform4fv(colorUniformLocation, 1, color.toNormalizedVector().data()); |
| drawQuad(*program, essl1_shaders::PositionAttrib(), 0.5f); |
| |
| EXPECT_EQ(getPerfCounters().pipelineCreationCacheHits, expectedCacheHits); |
| EXPECT_EQ(getPerfCounters().pipelineCreationCacheMisses, expectedCacheMisses); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, color); |
| } |
| |
| // Verifies that the pipeline cache is warmed up at link time with reasonable defaults. |
| TEST_P(VulkanPerformanceCounterTest, PipelineCacheIsWarmedUpAtLinkTime) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| // Test is only valid when pipeline creation feedback is available |
| ANGLE_SKIP_TEST_IF(!hasSupportsPipelineCreationFeedback() || !hasWarmUpPipelineCacheAtLink() || |
| !hasEffectivePipelineCacheSerialization()); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Passthrough(), essl1_shaders::fs::UniformColor()); |
| |
| testPipelineCacheIsWarm(&program, GLColor::red); |
| } |
| |
| // Verifies that the pipeline cache is reloaded correctly through glProgramBinary. |
| TEST_P(VulkanPerformanceCounterTest, PipelineCacheIsRestoredWithProgramBinary) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| // Test is only valid when pipeline creation feedback is available |
| ANGLE_SKIP_TEST_IF(!hasSupportsPipelineCreationFeedback() || !hasWarmUpPipelineCacheAtLink() || |
| !hasEffectivePipelineCacheSerialization()); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Passthrough(), essl1_shaders::fs::UniformColor()); |
| GLProgram reloadedProgram; |
| saveAndReloadBinary(&program, &reloadedProgram); |
| |
| testPipelineCacheIsWarm(&reloadedProgram, GLColor::green); |
| } |
| |
| // Verifies that the pipeline cache is reloaded correctly through glProgramBinary twice. |
| TEST_P(VulkanPerformanceCounterTest, PipelineCacheIsRestoredWithProgramBinaryTwice) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| // Test is only valid when pipeline creation feedback is available |
| ANGLE_SKIP_TEST_IF(!hasSupportsPipelineCreationFeedback() || !hasWarmUpPipelineCacheAtLink() || |
| !hasEffectivePipelineCacheSerialization()); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Passthrough(), essl1_shaders::fs::UniformColor()); |
| GLProgram reloadedProgram; |
| GLProgram twiceReloadedProgram; |
| saveAndReloadBinary(&program, &reloadedProgram); |
| saveAndReloadBinary(&reloadedProgram, &twiceReloadedProgram); |
| |
| testPipelineCacheIsWarm(&twiceReloadedProgram, GLColor::blue); |
| } |
| |
| // Test calling glEGLImageTargetTexture2DOES repeatedly with same arguments will not leak |
| // DescriptorSets. This is the same usage pattern surafceflinger is doing with notification shades |
| // except with AHB. |
| TEST_P(VulkanPerformanceCounterTest, Source2DAndRepeatedlyRespecifyTarget2DWithSameParameter) |
| { |
| EGLWindow *window = getEGLWindow(); |
| EGLDisplay dpy = window->getDisplay(); |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_EGL_image") || |
| !IsEGLDisplayExtensionEnabled(dpy, "EGL_KHR_image_base") || |
| !IsEGLDisplayExtensionEnabled(dpy, "EGL_KHR_gl_texture_2D_image")); |
| |
| ANGLE_GL_PROGRAM(textureProgram, essl1_shaders::vs::Texture2D(), |
| essl1_shaders::fs::Texture2D()); |
| |
| // Create a source 2D texture |
| GLTexture sourceTexture; |
| glBindTexture(GL_TEXTURE_2D, sourceTexture); |
| GLubyte kLinearColor[] = {132, 55, 219, 255}; |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| static_cast<void *>(&kLinearColor)); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| ASSERT_GL_NO_ERROR(); |
| // Create an eglImage from the source texture |
| constexpr EGLint kDefaultAttribs[] = {EGL_IMAGE_PRESERVED, EGL_TRUE, EGL_NONE}; |
| EGLClientBuffer clientBuffer = |
| reinterpret_cast<EGLClientBuffer>(static_cast<size_t>(sourceTexture)); |
| EGLImageKHR image = eglCreateImageKHR(window->getDisplay(), window->getContext(), |
| EGL_GL_TEXTURE_2D_KHR, clientBuffer, kDefaultAttribs); |
| ASSERT_EGL_SUCCESS(); |
| |
| // Create the target in a loop |
| GLTexture targetTexture; |
| constexpr size_t kMaxLoop = 2; |
| GLint textureDescriptorSetCacheTotalSizeBefore = |
| getPerfCounters().textureDescriptorSetCacheTotalSize; |
| for (size_t loop = 0; loop < kMaxLoop; loop++) |
| { |
| // Create a target texture from the image |
| glBindTexture(GL_TEXTURE_2D, targetTexture); |
| glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image); |
| |
| // Disable mipmapping |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| |
| // Draw a quad with the target texture |
| glBindTexture(GL_TEXTURE_2D, targetTexture); |
| drawQuad(textureProgram, std::string(essl1_shaders::PositionAttrib()), 0.0f); |
| // Expect that the rendered quad's color is the same as the reference color with a tolerance |
| // of 1 |
| EXPECT_PIXEL_NEAR(0, 0, kLinearColor[0], kLinearColor[1], kLinearColor[2], kLinearColor[3], |
| 1); |
| } |
| GLint textureDescriptorSetCacheTotalSizeIncrease = |
| getPerfCounters().textureDescriptorSetCacheTotalSize - |
| textureDescriptorSetCacheTotalSizeBefore; |
| |
| // We don't expect descriptorSet cache to keep growing |
| EXPECT_EQ(1, textureDescriptorSetCacheTotalSizeIncrease); |
| |
| // Clean up |
| eglDestroyImageKHR(window->getDisplay(), image); |
| } |
| |
| // Test create, use and then destroy a texture does not increase number of DescriptoprSets. |
| // DesriptorSet should be destroyed promptly. We are seeing this type of usage pattern in |
| // surfaceflinger, except that the texture is created from AHB (and AHB keeps changing as well). |
| TEST_P(VulkanPerformanceCounterTest, CreateDestroyTextureDoesNotIncreaseDescriptporSetCache) |
| { |
| ANGLE_GL_PROGRAM(textureProgram, essl1_shaders::vs::Texture2D(), |
| essl1_shaders::fs::Texture2D()); |
| |
| // Respecify texture in a loop |
| GLubyte kLinearColor[] = {132, 55, 219, 255}; |
| constexpr size_t kMaxLoop = 2; |
| GLint textureDescriptorSetCacheTotalSizeBefore = |
| getPerfCounters().textureDescriptorSetCacheTotalSize; |
| for (size_t loop = 0; loop < kMaxLoop; loop++) |
| { |
| // Create a 2D texture |
| GLTexture sourceTexture; |
| glBindTexture(GL_TEXTURE_2D, sourceTexture); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| static_cast<void *>(&kLinearColor)); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| ASSERT_GL_NO_ERROR(); |
| |
| drawQuad(textureProgram, std::string(essl1_shaders::PositionAttrib()), 0.0f); |
| // Expect that the rendered quad's color is the same as the reference color with a tolerance |
| // of 1 |
| EXPECT_PIXEL_NEAR(0, 0, kLinearColor[0], kLinearColor[1], kLinearColor[2], kLinearColor[3], |
| 1); |
| } |
| GLint textureDescriptorSetCacheTotalSizeIncrease = |
| getPerfCounters().textureDescriptorSetCacheTotalSize - |
| textureDescriptorSetCacheTotalSizeBefore; |
| |
| // We don't expect descriptorSet cache to keep growing |
| EXPECT_EQ(0, textureDescriptorSetCacheTotalSizeIncrease); |
| } |
| |
| // Similar to CreateDestroyTextureDoesNotIncreaseDescriptporSetCache, but for shader image. |
| TEST_P(VulkanPerformanceCounterTest_ES31, |
| CreateDestroyTextureDoesNotIncreaseComputeShaderDescriptporSetCache) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| constexpr char kCS[] = R"(#version 310 es |
| layout(local_size_x=1, local_size_y=1, local_size_z=1) in; |
| layout(r32ui, binding = 0) readonly uniform highp uimage2D uImage_1; |
| layout(r32ui, binding = 1) writeonly uniform highp uimage2D uImage_2; |
| void main() |
| { |
| uvec4 value = imageLoad(uImage_1, ivec2(gl_LocalInvocationID.xy)); |
| imageStore(uImage_2, ivec2(gl_LocalInvocationID.xy), value); |
| })"; |
| ANGLE_GL_COMPUTE_PROGRAM(program, kCS); |
| glUseProgram(program); |
| |
| constexpr int kWidth = 1, kHeight = 1; |
| constexpr GLuint kInputValues[2][1] = {{200}, {100}}; |
| constexpr size_t kMaxLoop = 20; |
| GLint shaderResourceDescriptorSetCacheTotalSizeBefore = |
| getPerfCounters().shaderResourcesDescriptorSetCacheTotalSize; |
| for (size_t loop = 0; loop < kMaxLoop; loop++) |
| { |
| // Respecify texture in a loop |
| GLTexture texture0; |
| glBindTexture(GL_TEXTURE_2D, texture0); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, kWidth, kHeight); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kWidth, kHeight, GL_RED_INTEGER, GL_UNSIGNED_INT, |
| kInputValues[0]); |
| |
| GLTexture texture1; |
| glBindTexture(GL_TEXTURE_2D, texture1); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, kWidth, kHeight); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kWidth, kHeight, GL_RED_INTEGER, GL_UNSIGNED_INT, |
| kInputValues[1]); |
| |
| glBindImageTexture(0, texture0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R32UI); |
| glBindImageTexture(1, texture1, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32UI); |
| glDispatchCompute(1, 1, 1); |
| } |
| glFinish(); |
| GLint shaderResourceDescriptorSetCacheTotalSizeIncrease = |
| getPerfCounters().shaderResourcesDescriptorSetCacheTotalSize - |
| shaderResourceDescriptorSetCacheTotalSizeBefore; |
| |
| // We don't expect descriptorSet cache to keep growing |
| EXPECT_EQ(0, shaderResourceDescriptorSetCacheTotalSizeIncrease); |
| } |
| |
| // Similar to CreateDestroyTextureDoesNotIncreaseDescriptporSetCache, but for uniform buffers. |
| TEST_P(VulkanPerformanceCounterTest, DestroyUniformBufferAlsoDestroyDescriptporSetCache) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| const char *mkFS = R"(#version 300 es |
| precision highp float; |
| uniform uni { vec4 color; }; |
| out vec4 fragColor; |
| void main() |
| { |
| fragColor = color; |
| })"; |
| ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), mkFS); |
| GLint uniformBufferIndex = glGetUniformBlockIndex(program, "uni"); |
| ASSERT_NE(uniformBufferIndex, -1); |
| |
| // Warm up. Make a draw to ensure other descriptorSets are created if needed. |
| GLBuffer intialBuffer; |
| glBindBuffer(GL_UNIFORM_BUFFER, intialBuffer); |
| std::vector<float> initialData = {0.1, 0.2, 0.3, 0.4}; |
| glBufferData(GL_UNIFORM_BUFFER, sizeof(float) * initialData.size(), initialData.data(), |
| GL_DYNAMIC_DRAW); |
| glBindBufferBase(GL_UNIFORM_BUFFER, 0, intialBuffer); |
| glUniformBlockBinding(program, uniformBufferIndex, 0); |
| glClear(GL_COLOR_BUFFER_BIT); |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_NEAR(0, 0, initialData[0] * 255, initialData[1] * 255, initialData[2] * 255, |
| initialData[3] * 255, 1); |
| |
| // Use big buffer size to force it into individual bufferBlocks |
| constexpr GLsizei kBufferSize = 4 * 1024 * 1024; |
| GLint DescriptorSetCacheTotalSizeBefore = getPerfCounters().descriptorSetCacheTotalSize; |
| |
| // Create buffer and use it and then destroy it. Because buffers are big enough they should be |
| // in a different bufferBlock. DescriptorSet created due to these temporary buffer should be |
| // destroyed promptly. |
| constexpr int kBufferCount = 16; |
| for (int i = 0; i < kBufferCount; i++) |
| { |
| GLBuffer uniformBuffer; |
| glBindBuffer(GL_UNIFORM_BUFFER, uniformBuffer); |
| glBufferData(GL_UNIFORM_BUFFER, kBufferSize, nullptr, GL_DYNAMIC_DRAW); |
| float *ptr = reinterpret_cast<float *>( |
| glMapBufferRange(GL_UNIFORM_BUFFER, 0, kBufferSize, GL_MAP_WRITE_BIT)); |
| for (int j = 0; j < 4; j++) |
| { |
| ptr[j] = (float)(i * 4 + j) / 255.0f; |
| } |
| glUnmapBuffer(GL_UNIFORM_BUFFER); |
| glBindBufferBase(GL_UNIFORM_BUFFER, 0, uniformBuffer); |
| glUniformBlockBinding(program, uniformBufferIndex, 0); |
| glClear(GL_COLOR_BUFFER_BIT); |
| drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f); |
| EXPECT_PIXEL_NEAR(0, 0, (i * 4), (i * 4 + 1), (i * 4 + 2), (i * 4 + 3), 1); |
| } |
| // Should trigger prune buffer call |
| swapBuffers(); |
| |
| GLint DescriptorSetCacheTotalSizeIncrease = |
| getPerfCounters().descriptorSetCacheTotalSize - DescriptorSetCacheTotalSizeBefore; |
| // We expect most of descriptorSet caches for temporary uniformBuffers gets destroyed. Give |
| // extra room in case a new descriptorSet is allocated due to a new driver uniform buffer gets |
| // allocated. |
| EXPECT_LT(DescriptorSetCacheTotalSizeIncrease, 2); |
| } |
| |
| // Similar to CreateDestroyTextureDoesNotIncreaseDescriptporSetCache, but for atomic acounter |
| // buffer. |
| TEST_P(VulkanPerformanceCounterTest_ES31, DestroyAtomicCounterBufferAlsoDestroyDescriptporSetCache) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| constexpr char kFS[] = |
| "#version 310 es\n" |
| "precision highp float;\n" |
| "layout(binding = 0, offset = 4) uniform atomic_uint ac;\n" |
| "out highp vec4 my_color;\n" |
| "void main()\n" |
| "{\n" |
| " uint a1 = atomicCounter(ac);\n" |
| " my_color = vec4(float(a1)/255.0, 0.0, 0.0, 1.0);\n" |
| "}\n"; |
| ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), kFS); |
| |
| // Warm up. Make a draw to ensure other descriptorSets are created if needed. |
| GLBuffer intialBuffer; |
| glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, intialBuffer); |
| uint32_t bufferData[3] = {0, 0u, 0u}; |
| glBufferData(GL_ATOMIC_COUNTER_BUFFER, sizeof(bufferData), bufferData, GL_STATIC_DRAW); |
| glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, intialBuffer); |
| drawQuad(program, essl31_shaders::PositionAttrib(), 0.0f); |
| GLColor expectedColor = GLColor::black; |
| EXPECT_PIXEL_COLOR_EQ(0, 0, expectedColor); |
| |
| GLint DescriptorSetCacheTotalSizeBefore = getPerfCounters().descriptorSetCacheTotalSize; |
| |
| // Create atomic counter buffer and use it and then destroy it. |
| constexpr int kBufferCount = 16; |
| GLBuffer paddingBuffers[kBufferCount]; |
| for (uint32_t i = 0; i < kBufferCount; i++) |
| { |
| // Allocate a padding buffer so that atomicCounterBuffer will be allocated in different |
| // offset |
| glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, paddingBuffers[i]); |
| glBufferData(GL_ATOMIC_COUNTER_BUFFER, 256, nullptr, GL_STATIC_DRAW); |
| // Allocate, use, destroy atomic counter buffer |
| GLBuffer atomicCounterBuffer; |
| glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, atomicCounterBuffer); |
| bufferData[1] = i; |
| glBufferData(GL_ATOMIC_COUNTER_BUFFER, sizeof(bufferData), bufferData, GL_STATIC_DRAW); |
| glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, atomicCounterBuffer); |
| drawQuad(program, essl31_shaders::PositionAttrib(), 0.0f); |
| expectedColor.R = bufferData[1]; |
| EXPECT_PIXEL_COLOR_EQ(0, 0, expectedColor); |
| } |
| ASSERT_GL_NO_ERROR(); |
| |
| GLint DescriptorSetCacheTotalSizeIncrease = |
| getPerfCounters().descriptorSetCacheTotalSize - DescriptorSetCacheTotalSizeBefore; |
| // We expect most of descriptorSet caches for temporary atomic counter buffers gets destroyed. |
| // Give extra room in case a new descriptorSet is allocated due to a new driver uniform buffer |
| // gets allocated. |
| EXPECT_LT(DescriptorSetCacheTotalSizeIncrease, 2); |
| } |
| |
| // Test that post-render-pass-to-swapchain glFenceSync followed by eglSwapBuffers incurs only a |
| // single submission. |
| TEST_P(VulkanPerformanceCounterTest, FenceThenSwapBuffers) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName)); |
| |
| angle::VulkanPerfCounters expected; |
| |
| // Expect rpCount+1, depth(Clears+0, Loads+0, LoadNones+0, Stores+0, StoreNones+0), |
| setExpectedCountersForDepthOps(getPerfCounters(), 1, 0, 0, 0, 0, 0, &expected); |
| expected.vkQueueSubmitCallsTotal = getPerfCounters().vkQueueSubmitCallsTotal + 1; |
| |
| // Start a render pass and render to the surface. Enable depth write so the depth/stencil image |
| // is written to. |
| glEnable(GL_DEPTH_TEST); |
| glDepthFunc(GL_ALWAYS); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Issue a fence |
| glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); |
| |
| EXPECT_EQ(getPerfCounters().renderPasses, expected.renderPasses); |
| |
| // Swap buffers. The depth/stencil attachment's storeOp should be optimized to DONT_CARE. This |
| // would not have been possible if the previous glFenceSync caused a submission. |
| swapBuffers(); |
| |
| EXPECT_EQ(getPerfCounters().vkQueueSubmitCallsTotal, expected.vkQueueSubmitCallsTotal); |
| EXPECT_DEPTH_OP_COUNTERS(getPerfCounters(), expected); |
| } |
| |
| // Verify that ending transform feedback after a render pass is closed, doesn't cause the following |
| // render pass to close when the transform feedback buffer is used. |
| TEST_P(VulkanPerformanceCounterTest, EndXfbAfterRenderPassClosed) |
| { |
| // There should be two render passes; one for the transform feedback draw, one for the other two |
| // draw calls. |
| uint64_t expectedRenderPassCount = getPerfCounters().renderPasses + 2; |
| |
| glClearColor(0.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| // Set the program's transform feedback varyings (just gl_Position) |
| std::vector<std::string> tfVaryings; |
| tfVaryings.push_back("gl_Position"); |
| ANGLE_GL_PROGRAM_TRANSFORM_FEEDBACK(drawRed, essl3_shaders::vs::SimpleForPoints(), |
| essl3_shaders::fs::Red(), tfVaryings, |
| GL_INTERLEAVED_ATTRIBS); |
| |
| glUseProgram(drawRed); |
| GLint positionLocation = glGetAttribLocation(drawRed, essl1_shaders::PositionAttrib()); |
| |
| const GLfloat vertices[] = { |
| -0.5f, 0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, |
| -0.5f, 0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, |
| }; |
| |
| glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices); |
| glEnableVertexAttribArray(positionLocation); |
| |
| // Bind the buffer for transform feedback output and start transform feedback |
| GLBuffer xfbBuffer; |
| glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, xfbBuffer); |
| glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 100, nullptr, GL_STATIC_DRAW); |
| glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, xfbBuffer); |
| glBeginTransformFeedback(GL_POINTS); |
| |
| glDrawArrays(GL_POINTS, 0, 6); |
| |
| // Break the render pass |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black); |
| |
| // End transform feedback after the render pass is closed |
| glEndTransformFeedback(); |
| |
| glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Issue an unrelated draw call. |
| ANGLE_GL_PROGRAM(drawGreen, essl3_shaders::vs::Simple(), essl3_shaders::fs::Green()); |
| drawQuad(drawGreen, essl3_shaders::PositionAttrib(), 0.0f); |
| |
| // Issue a draw call using the transform feedback buffer. |
| glBindBuffer(GL_ARRAY_BUFFER, xfbBuffer); |
| glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, 0); |
| glEnableVertexAttribArray(positionLocation); |
| |
| glClearColor(0.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| glUseProgram(drawRed); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| |
| const int w = getWindowWidth(); |
| const int h = getWindowHeight(); |
| |
| EXPECT_PIXEL_RECT_EQ(0, 0, w, h / 4, GLColor::black); |
| EXPECT_PIXEL_RECT_EQ(0, 3 * h / 4, w, h / 4, GLColor::black); |
| EXPECT_PIXEL_RECT_EQ(0, h / 4, w / 4, h / 2, GLColor::black); |
| EXPECT_PIXEL_RECT_EQ(3 * w / 4, h / 4, w / 4, h / 2, GLColor::black); |
| |
| EXPECT_PIXEL_RECT_EQ(w / 4, h / 4, w / 2, h / 2, GLColor::red); |
| EXPECT_GL_NO_ERROR(); |
| |
| EXPECT_EQ(getPerfCounters().renderPasses, expectedRenderPassCount); |
| } |
| |
| // Verify that monolithic pipeline handles correctly replace the linked pipelines, if |
| // VK_EXT_graphics_pipeline_library is supported. |
| TEST_P(VulkanPerformanceCounterTest, AsyncMonolithicPipelineCreation) |
| { |
| const bool hasAsyncMonolithicPipelineCreation = |
| isFeatureEnabled(Feature::SupportsGraphicsPipelineLibrary) && |
| isFeatureEnabled(Feature::PreferMonolithicPipelinesOverLibraries); |
| ANGLE_SKIP_TEST_IF(!hasAsyncMonolithicPipelineCreation); |
| |
| uint64_t expectedMonolithicPipelineCreationCount = |
| getPerfCounters().monolithicPipelineCreation + 2; |
| |
| // Create two programs: |
| ANGLE_GL_PROGRAM(drawRed, essl3_shaders::vs::Simple(), essl3_shaders::fs::Red()); |
| ANGLE_GL_PROGRAM(drawGreen, essl3_shaders::vs::Simple(), essl3_shaders::fs::Green()); |
| |
| // Ping pong between the programs, letting async monolithic pipeline creation happen. |
| uint32_t drawCount = 0; |
| constexpr uint32_t kDrawCountLimit = 200; |
| |
| while (getPerfCounters().monolithicPipelineCreation < expectedMonolithicPipelineCreationCount) |
| { |
| drawQuad(drawGreen, essl3_shaders::PositionAttrib(), 0.0f); |
| drawQuad(drawRed, essl3_shaders::PositionAttrib(), 0.0f); |
| |
| ++drawCount; |
| if (drawCount > kDrawCountLimit) |
| { |
| drawCount = 0; |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| } |
| } |
| |
| // Make sure the monolithic pipelines are replaced correctly |
| drawQuad(drawGreen, essl3_shaders::PositionAttrib(), 0.0f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| |
| drawQuad(drawRed, essl3_shaders::PositionAttrib(), 0.0f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| } |
| |
| // Verify that changing framebuffer and back doesn't break the render pass. |
| TEST_P(VulkanPerformanceCounterTest, FBOChangeAndBackDoesNotBreakRenderPass) |
| { |
| uint64_t expectedRenderPassCount = getPerfCounters().renderPasses + 1; |
| |
| ANGLE_GL_PROGRAM(drawRed, essl3_shaders::vs::Simple(), essl3_shaders::fs::Red()); |
| drawQuad(drawRed, essl1_shaders::PositionAttrib(), 0); |
| |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| |
| drawQuad(drawRed, essl1_shaders::PositionAttrib(), 0); |
| |
| // Verify render pass count. |
| EXPECT_EQ(getPerfCounters().renderPasses, expectedRenderPassCount); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| } |
| |
| // Verify that changing framebuffer and issue a (nop or deferred) clear and change framebuffer back |
| // doesn't break the render pass. |
| TEST_P(VulkanPerformanceCounterTest, FBOChangeAndClearAndBackDoesNotBreakRenderPass) |
| { |
| // switch to FBO and issue clear on fbo and then switch back to default framebuffer |
| GLFramebuffer framebuffer; |
| GLTexture texture; |
| setupForColorOpsTest(&framebuffer, &texture); |
| glClearColor(0.0f, 1.0f, 0.0f, 1.0f); // clear to green |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| |
| uint64_t expectedRenderPassCount = getPerfCounters().renderPasses + 1; |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| ANGLE_GL_PROGRAM(drawRed, essl3_shaders::vs::Simple(), essl3_shaders::fs::Red()); |
| drawQuad(drawRed, essl1_shaders::PositionAttrib(), 0); |
| |
| // switch to FBO and issue a deferred clear on fbo and then switch back to default framebuffer |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| drawQuad(drawRed, essl1_shaders::PositionAttrib(), 0); |
| |
| // Verify render pass count. |
| EXPECT_EQ(getPerfCounters().renderPasses, expectedRenderPassCount); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| } |
| |
| // This is test for optimization in vulkan backend. efootball_pes_2021 usage shows this usage |
| // pattern and we expect implementation to reuse the storage for performance. |
| TEST_P(VulkanPerformanceCounterTest, |
| bufferDataWithSizeFollowedByZeroAndThenSizeAgainShouldReuseStorage) |
| { |
| GLBuffer buffer; |
| glBindBuffer(GL_ARRAY_BUFFER, buffer); |
| constexpr size_t count = 288; |
| std::array<uint32_t, count> data; |
| constexpr size_t bufferSize = data.size() * sizeof(uint32_t); |
| data.fill(0x1234567); |
| |
| glBufferData(GL_ARRAY_BUFFER, bufferSize, data.data(), GL_DYNAMIC_DRAW); |
| glBufferData(GL_ARRAY_BUFFER, 0, nullptr, GL_DYNAMIC_DRAW); |
| // This should get back the original storage with proper BufferVk optimization |
| data.fill(0x89abcdef); |
| uint64_t expectedSuballocationCalls = getPerfCounters().bufferSuballocationCalls; |
| glBufferData(GL_ARRAY_BUFFER, bufferSize, data.data(), GL_DYNAMIC_DRAW); |
| EXPECT_EQ(getPerfCounters().bufferSuballocationCalls, expectedSuballocationCalls); |
| |
| uint32_t *mapPtr = reinterpret_cast<uint32_t *>( |
| glMapBufferRange(GL_ARRAY_BUFFER, 0, bufferSize, GL_MAP_READ_BIT)); |
| ASSERT_NE(nullptr, mapPtr); |
| EXPECT_EQ(0x89abcdef, mapPtr[0]); |
| EXPECT_EQ(0x89abcdef, mapPtr[count - 1]); |
| glUnmapBuffer(GL_ARRAY_BUFFER); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| class VulkanPerformanceCounterTest_AsyncCQ : public VulkanPerformanceCounterTest |
| {}; |
| |
| // Tests that submitting the outside command buffer during flushing staged updates and |
| // "asyncCommandQueue" enabled, properly updates old command buffer with the new one. |
| TEST_P(VulkanPerformanceCounterTest_AsyncCQ, SubmittingOutsideCommandBufferAssertIsOpen) |
| { |
| // If VK_EXT_host_image_copy is used, uploads will all be done on the CPU and there would be no |
| // submissions. |
| ANGLE_SKIP_TEST_IF(hasSupportsHostImageCopy()); |
| |
| uint64_t submitCommandsCount = getPerfCounters().vkQueueSubmitCallsTotal; |
| |
| ANGLE_GL_PROGRAM(textureProgram, essl1_shaders::vs::Texture2D(), |
| essl1_shaders::fs::Texture2D()); |
| glUseProgram(textureProgram); |
| GLint textureLoc = glGetUniformLocation(textureProgram, essl1_shaders::Texture2DUniform()); |
| ASSERT_NE(-1, textureLoc); |
| glUniform1i(textureLoc, 0); |
| |
| // This loop shouls update texture with multiple staged updates. When kMaxBufferToImageCopySize |
| // threshold reached, outside command buffer will be submitted in the middle of staged updates |
| // flushing. If "asyncCommandQueue" enabled and bug present, old command buffer will not be |
| // replaced by a new one, casing "ASSERT(mIsOpen)" or UB in release. |
| constexpr GLsizei kMaxOutsideRPCommandsSubmitCount = 10; |
| constexpr GLsizei kTexDim = 1024; |
| constexpr GLint kMaxSubOffset = 10; |
| std::vector<GLColor> kInitialData(kTexDim * kTexDim, GLColor::green); |
| while (getPerfCounters().vkQueueSubmitCallsTotal < |
| submitCommandsCount + kMaxOutsideRPCommandsSubmitCount) |
| { |
| GLTexture newTexture; |
| glBindTexture(GL_TEXTURE_2D, newTexture); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| // Provide data for the texture without previously defining a storage. |
| // This should prevent immediate staged update flushing. |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTexDim, kTexDim, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| kInitialData.data()); |
| // Append staged updates... |
| for (GLsizei offset = 1; offset <= kMaxSubOffset; ++offset) |
| { |
| glTexSubImage2D(GL_TEXTURE_2D, 0, offset, offset, kTexDim - offset, kTexDim - offset, |
| GL_RGBA, GL_UNSIGNED_BYTE, kInitialData.data()); |
| } |
| // This will flush multiple staged updates |
| drawQuad(textureProgram, essl1_shaders::PositionAttrib(), 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| } |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VulkanPerformanceCounterTest); |
| ANGLE_INSTANTIATE_TEST( |
| VulkanPerformanceCounterTest, |
| ES3_VULKAN(), |
| ES3_VULKAN_SWIFTSHADER().disable(Feature::PreferMonolithicPipelinesOverLibraries), |
| ES3_VULKAN_SWIFTSHADER().enable(Feature::PreferMonolithicPipelinesOverLibraries), |
| ES3_VULKAN_SWIFTSHADER() |
| .enable(Feature::PreferMonolithicPipelinesOverLibraries) |
| .enable(Feature::SlowDownMonolithicPipelineCreationForTesting), |
| ES3_VULKAN_SWIFTSHADER() |
| .enable(Feature::PreferMonolithicPipelinesOverLibraries) |
| .disable(Feature::MergeProgramPipelineCachesToGlobalCache)); |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VulkanPerformanceCounterTest_ES31); |
| ANGLE_INSTANTIATE_TEST(VulkanPerformanceCounterTest_ES31, ES31_VULKAN(), ES31_VULKAN_SWIFTSHADER()); |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VulkanPerformanceCounterTest_MSAA); |
| ANGLE_INSTANTIATE_TEST(VulkanPerformanceCounterTest_MSAA, |
| ES3_VULKAN(), |
| ES3_VULKAN_SWIFTSHADER(), |
| ES3_VULKAN().enable(Feature::EmulatedPrerotation90), |
| ES3_VULKAN().enable(Feature::EmulatedPrerotation180), |
| ES3_VULKAN().enable(Feature::EmulatedPrerotation270)); |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VulkanPerformanceCounterTest_SingleBuffer); |
| ANGLE_INSTANTIATE_TEST(VulkanPerformanceCounterTest_SingleBuffer, ES3_VULKAN()); |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VulkanPerformanceCounterTest_AsyncCQ); |
| ANGLE_INSTANTIATE_TEST(VulkanPerformanceCounterTest_AsyncCQ, |
| ES3_VULKAN(), |
| ES3_VULKAN().enable(Feature::AsyncCommandQueue)); |
| |
| } // anonymous namespace |