| // | |
| // Copyright 2024 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. | |
| // | |
| // MultisampleResolvePerf: | |
| // Performance tests for glBlitFramebuffer and glInvalidateFramebuffer where the framebuffer is | |
| // multisampled. | |
| #include <array> | |
| #include "ANGLEPerfTest.h" | |
| #include "common/unsafe_buffers.h" | |
| #include "test_utils/ANGLETest.h" | |
| #include "util/shader_utils.h" | |
| namespace | |
| { | |
| constexpr unsigned int kIterationsPerStep = 1; | |
| enum class Multisample | |
| { | |
| Yes, | |
| No, | |
| }; | |
| enum class WithDepthStencil | |
| { | |
| Yes, | |
| No, | |
| }; | |
| struct MultisampleResolveParams final : public RenderTestParams | |
| { | |
| MultisampleResolveParams(Multisample multisample, WithDepthStencil depthStencil) | |
| { | |
| iterationsPerStep = kIterationsPerStep; | |
| majorVersion = 3; | |
| minorVersion = 0; | |
| windowWidth = 256; | |
| windowHeight = 256; | |
| if (multisample == Multisample::No) | |
| { | |
| samples = 0; | |
| } | |
| withDepthStencil = depthStencil == WithDepthStencil::Yes; | |
| } | |
| std::string story() const override | |
| { | |
| std::stringstream storyStr; | |
| storyStr << RenderTestParams::story(); | |
| if (withDepthStencil) | |
| { | |
| storyStr << "_ds"; | |
| } | |
| if (samples == 0) | |
| { | |
| storyStr << "_singlesampled_reference"; | |
| } | |
| return storyStr.str(); | |
| } | |
| unsigned int framebufferSize = 1024; | |
| unsigned int samples = 4; | |
| bool withDepthStencil = false; | |
| }; | |
| std::ostream &operator<<(std::ostream &os, const MultisampleResolveParams ¶ms) | |
| { | |
| os << params.backendAndStory().substr(1); | |
| return os; | |
| } | |
| class MultisampleResolvePerf : public ANGLERenderTest, | |
| public ::testing::WithParamInterface<MultisampleResolveParams> | |
| { | |
| public: | |
| MultisampleResolvePerf() : ANGLERenderTest("MultisampleResolvePerf", GetParam()) {} | |
| void initializeBenchmark() override; | |
| void destroyBenchmark() override; | |
| void drawBenchmark() override; | |
| private: | |
| GLuint mMSAAFramebuffer = 0; | |
| GLuint mMSAAColor[2] = {}; | |
| GLuint mMSAADepthStencil = 0; | |
| std::array<GLuint, 2> mResolveFramebuffer = {}; | |
| std::array<GLuint, 2> mResolveColor = {}; | |
| GLuint mResolveDepthStencil = 0; | |
| GLuint mReferenceFramebuffer = 0; | |
| GLuint mProgram = 0; | |
| }; | |
| void MultisampleResolvePerf::initializeBenchmark() | |
| { | |
| const MultisampleResolveParams ¶m = GetParam(); | |
| glGenFramebuffers(1, &mMSAAFramebuffer); | |
| glGenFramebuffers(2, mResolveFramebuffer.data()); | |
| glGenFramebuffers(1, &mReferenceFramebuffer); | |
| // Create source and destination Renderbuffers. | |
| glGenRenderbuffers(2, mMSAAColor); | |
| glGenRenderbuffers(1, &mMSAADepthStencil); | |
| glGenRenderbuffers(2, mResolveColor.data()); | |
| glGenRenderbuffers(1, &mResolveDepthStencil); | |
| ASSERT_GL_NO_ERROR(); | |
| const GLuint size = param.framebufferSize; | |
| for (uint32_t i = 0; i < 2; ++i) | |
| { | |
| glBindRenderbuffer(GL_RENDERBUFFER, mResolveColor[i]); | |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, size, size); | |
| glBindFramebuffer(GL_FRAMEBUFFER, mResolveFramebuffer[i]); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, | |
| mResolveColor[i]); | |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); | |
| } | |
| if (param.withDepthStencil) | |
| { | |
| glBindRenderbuffer(GL_RENDERBUFFER, mResolveDepthStencil); | |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, size, size); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, | |
| mResolveDepthStencil); | |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); | |
| } | |
| if (param.samples > 0) | |
| { | |
| glBindRenderbuffer(GL_RENDERBUFFER, mMSAAColor[0]); | |
| glRenderbufferStorageMultisample(GL_RENDERBUFFER, param.samples, GL_RGBA8, size, size); | |
| glBindRenderbuffer(GL_RENDERBUFFER, mMSAAColor[1]); | |
| glRenderbufferStorageMultisample(GL_RENDERBUFFER, param.samples, GL_RGBA8, size, size); | |
| glBindFramebuffer(GL_FRAMEBUFFER, mMSAAFramebuffer); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, | |
| mMSAAColor[0]); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_RENDERBUFFER, | |
| mMSAAColor[1]); | |
| if (param.withDepthStencil) | |
| { | |
| glBindRenderbuffer(GL_RENDERBUFFER, mMSAADepthStencil); | |
| glRenderbufferStorageMultisample(GL_RENDERBUFFER, param.samples, GL_DEPTH24_STENCIL8, | |
| size, size); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, | |
| mMSAADepthStencil); | |
| } | |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); | |
| } | |
| else | |
| { | |
| glBindFramebuffer(GL_FRAMEBUFFER, mReferenceFramebuffer); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, | |
| mResolveColor[0]); | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_RENDERBUFFER, | |
| mResolveColor[1]); | |
| if (param.withDepthStencil) | |
| { | |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, | |
| mResolveDepthStencil); | |
| } | |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); | |
| } | |
| GLenum bufs[3] = {GL_COLOR_ATTACHMENT0, GL_NONE, GL_COLOR_ATTACHMENT2}; | |
| glDrawBuffers(3, bufs); | |
| ASSERT_GL_NO_ERROR(); | |
| constexpr char kVS[] = R"(#version 300 es | |
| precision highp float; | |
| 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, gl_VertexID % 2 == 0 ? -1 : 1, 1); | |
| })"; | |
| 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; | |
| })"; | |
| mProgram = CompileProgram(kVS, kFS); | |
| ASSERT_NE(0u, mProgram); | |
| glUseProgram(mProgram); | |
| const GLint color0Loc = glGetUniformLocation(mProgram, "value0"); | |
| const GLint color1Loc = glGetUniformLocation(mProgram, "value2"); | |
| glUniform4f(color0Loc, 1, 0, 0, 1); | |
| glUniform4f(color1Loc, 0, 1, 0, 1); | |
| } | |
| void MultisampleResolvePerf::destroyBenchmark() | |
| { | |
| glDeleteFramebuffers(1, &mMSAAFramebuffer); | |
| glDeleteFramebuffers(2, mResolveFramebuffer.data()); | |
| glDeleteFramebuffers(1, &mReferenceFramebuffer); | |
| glDeleteRenderbuffers(2, mMSAAColor); | |
| glDeleteRenderbuffers(1, &mMSAADepthStencil); | |
| glDeleteRenderbuffers(2, mResolveColor.data()); | |
| glDeleteRenderbuffers(1, &mResolveDepthStencil); | |
| glDeleteProgram(mProgram); | |
| } | |
| void MultisampleResolvePerf::drawBenchmark() | |
| { | |
| const MultisampleResolveParams ¶m = GetParam(); | |
| const int size = param.framebufferSize; | |
| const bool singleSampled = param.samples == 0; | |
| glViewport(0, 0, size, size); | |
| 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); | |
| for (unsigned int iteration = 0; iteration < param.iterationsPerStep; ++iteration) | |
| { | |
| static constexpr std::array<GLenum, 3> discards = { | |
| GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT2, GL_DEPTH_STENCIL_ATTACHMENT}; | |
| glBindFramebuffer(GL_FRAMEBUFFER, singleSampled ? mReferenceFramebuffer : mMSAAFramebuffer); | |
| glInvalidateFramebuffer(GL_FRAMEBUFFER, param.withDepthStencil ? 3 : 2, discards.data()); | |
| // Start a render pass, then resolve each attachment + invalidate them. Every render pass | |
| // should thus start with LOAD_OP_DONT_CARE and end in STORE_OP_DONT_CARE (for the | |
| // attachments) and STORE_OP_STORE (for the resolve attachments). | |
| // | |
| // In single-sampled mode, just draw. This is used to compare the performance of | |
| // multisampled with single sampled rendering. | |
| glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | |
| if (!singleSampled) | |
| { | |
| for (uint32_t i = 0; i < 2; ++i) | |
| { | |
| glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mResolveFramebuffer[i]); | |
| glReadBuffer(discards[i]); | |
| glBlitFramebuffer(0, 0, size, size, 0, 0, size, size, GL_COLOR_BUFFER_BIT, | |
| GL_NEAREST); | |
| ASSERT_GL_NO_ERROR(); | |
| glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 1, &discards[i]); | |
| } | |
| if (param.withDepthStencil) | |
| { | |
| glBlitFramebuffer(0, 0, size, size, 0, 0, size, size, | |
| GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST); | |
| glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 1, &discards[2]); | |
| } | |
| ASSERT_GL_NO_ERROR(); | |
| } | |
| } | |
| ASSERT_GL_NO_ERROR(); | |
| } | |
| // Test that multisample resolve + invalidate is as efficient as single sampling on tilers. | |
| TEST_P(MultisampleResolvePerf, Run) | |
| { | |
| run(); | |
| } | |
| MultisampleResolveParams Vulkan(Multisample multisample, WithDepthStencil depthStencil) | |
| { | |
| MultisampleResolveParams params(multisample, depthStencil); | |
| params.eglParameters = angle::egl_platform::VULKAN(); | |
| return params; | |
| } | |
| } // anonymous namespace | |
| ANGLE_INSTANTIATE_TEST(MultisampleResolvePerf, | |
| Vulkan(Multisample::No, WithDepthStencil::No), | |
| Vulkan(Multisample::Yes, WithDepthStencil::No), | |
| Vulkan(Multisample::No, WithDepthStencil::Yes), | |
| Vulkan(Multisample::Yes, WithDepthStencil::Yes)); | |
| // This test suite is not instantiated on some OSes. | |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MultisampleResolvePerf); |