1 //
2 // Copyright 2024 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // MultisampleResolvePerf:
7 // Performance tests for glBlitFramebuffer and glInvalidateFramebuffer where the framebuffer is
8 // multisampled.
9
10 #include "ANGLEPerfTest.h"
11
12 #include "util/shader_utils.h"
13
14 namespace
15 {
16 constexpr unsigned int kIterationsPerStep = 1;
17
18 enum class Multisample
19 {
20 Yes,
21 No,
22 };
23
24 enum class WithDepthStencil
25 {
26 Yes,
27 No,
28 };
29
30 struct MultisampleResolveParams final : public RenderTestParams
31 {
MultisampleResolveParams__anonb4b2056f0111::MultisampleResolveParams32 MultisampleResolveParams(Multisample multisample, WithDepthStencil depthStencil)
33 {
34 iterationsPerStep = kIterationsPerStep;
35 majorVersion = 3;
36 minorVersion = 0;
37 windowWidth = 256;
38 windowHeight = 256;
39
40 if (multisample == Multisample::No)
41 {
42 samples = 0;
43 }
44
45 withDepthStencil = depthStencil == WithDepthStencil::Yes;
46 }
47
story__anonb4b2056f0111::MultisampleResolveParams48 std::string story() const override
49 {
50 std::stringstream storyStr;
51 storyStr << RenderTestParams::story();
52 if (withDepthStencil)
53 {
54 storyStr << "_ds";
55 }
56 if (samples == 0)
57 {
58 storyStr << "_singlesampled_reference";
59 }
60 return storyStr.str();
61 }
62
63 unsigned int framebufferSize = 1024;
64 unsigned int samples = 4;
65 bool withDepthStencil = false;
66 };
67
operator <<(std::ostream & os,const MultisampleResolveParams & params)68 std::ostream &operator<<(std::ostream &os, const MultisampleResolveParams ¶ms)
69 {
70 os << params.backendAndStory().substr(1);
71 return os;
72 }
73
74 class MultisampleResolvePerf : public ANGLERenderTest,
75 public ::testing::WithParamInterface<MultisampleResolveParams>
76 {
77 public:
MultisampleResolvePerf()78 MultisampleResolvePerf() : ANGLERenderTest("MultisampleResolvePerf", GetParam()) {}
79
80 void initializeBenchmark() override;
81 void destroyBenchmark() override;
82 void drawBenchmark() override;
83
84 private:
85 GLuint mMSAAFramebuffer = 0;
86 GLuint mMSAAColor[2] = {};
87 GLuint mMSAADepthStencil = 0;
88 GLuint mResolveFramebuffer[2] = {};
89 GLuint mResolveColor[2] = {};
90 GLuint mResolveDepthStencil = 0;
91 GLuint mReferenceFramebuffer = 0;
92 GLuint mProgram = 0;
93 };
94
initializeBenchmark()95 void MultisampleResolvePerf::initializeBenchmark()
96 {
97 const MultisampleResolveParams ¶m = GetParam();
98
99 glGenFramebuffers(1, &mMSAAFramebuffer);
100 glGenFramebuffers(2, mResolveFramebuffer);
101 glGenFramebuffers(1, &mReferenceFramebuffer);
102
103 // Create source and destination Renderbuffers.
104 glGenRenderbuffers(2, mMSAAColor);
105 glGenRenderbuffers(1, &mMSAADepthStencil);
106 glGenRenderbuffers(2, mResolveColor);
107 glGenRenderbuffers(1, &mResolveDepthStencil);
108
109 ASSERT_GL_NO_ERROR();
110
111 const GLuint size = param.framebufferSize;
112
113 for (uint32_t i = 0; i < 2; ++i)
114 {
115 glBindRenderbuffer(GL_RENDERBUFFER, mResolveColor[i]);
116 glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, size, size);
117
118 glBindFramebuffer(GL_FRAMEBUFFER, mResolveFramebuffer[i]);
119 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
120 mResolveColor[i]);
121 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
122 }
123
124 if (param.withDepthStencil)
125 {
126 glBindRenderbuffer(GL_RENDERBUFFER, mResolveDepthStencil);
127 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, size, size);
128 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
129 mResolveDepthStencil);
130 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
131 }
132
133 if (param.samples > 0)
134 {
135 glBindRenderbuffer(GL_RENDERBUFFER, mMSAAColor[0]);
136 glRenderbufferStorageMultisample(GL_RENDERBUFFER, param.samples, GL_RGBA8, size, size);
137 glBindRenderbuffer(GL_RENDERBUFFER, mMSAAColor[1]);
138 glRenderbufferStorageMultisample(GL_RENDERBUFFER, param.samples, GL_RGBA8, size, size);
139
140 glBindFramebuffer(GL_FRAMEBUFFER, mMSAAFramebuffer);
141 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
142 mMSAAColor[0]);
143 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_RENDERBUFFER,
144 mMSAAColor[1]);
145
146 if (param.withDepthStencil)
147 {
148 glBindRenderbuffer(GL_RENDERBUFFER, mMSAADepthStencil);
149 glRenderbufferStorageMultisample(GL_RENDERBUFFER, param.samples, GL_DEPTH24_STENCIL8,
150 size, size);
151 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
152 mMSAADepthStencil);
153 }
154
155 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
156 }
157 else
158 {
159 glBindFramebuffer(GL_FRAMEBUFFER, mReferenceFramebuffer);
160 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
161 mResolveColor[0]);
162 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_RENDERBUFFER,
163 mResolveColor[1]);
164
165 if (param.withDepthStencil)
166 {
167 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
168 mResolveDepthStencil);
169 }
170
171 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
172 }
173
174 GLenum bufs[3] = {GL_COLOR_ATTACHMENT0, GL_NONE, GL_COLOR_ATTACHMENT2};
175 glDrawBuffers(3, bufs);
176
177 ASSERT_GL_NO_ERROR();
178
179 constexpr char kVS[] = R"(#version 300 es
180 precision highp float;
181 void main()
182 {
183 // gl_VertexID x y
184 // 0 -1 -1
185 // 1 1 -1
186 // 2 -1 1
187 // 3 1 1
188 int bit0 = gl_VertexID & 1;
189 int bit1 = gl_VertexID >> 1;
190 gl_Position = vec4(bit0 * 2 - 1, bit1 * 2 - 1, gl_VertexID % 2 == 0 ? -1 : 1, 1);
191 })";
192
193 constexpr char kFS[] = R"(#version 300 es
194 precision highp float;
195
196 uniform vec4 value0;
197 uniform vec4 value2;
198
199 layout(location = 0) out vec4 color0;
200 layout(location = 2) out vec4 color2;
201
202 void main()
203 {
204 color0 = value0;
205 color2 = value2;
206 })";
207
208 mProgram = CompileProgram(kVS, kFS);
209 ASSERT_NE(0u, mProgram);
210 glUseProgram(mProgram);
211
212 const GLint color0Loc = glGetUniformLocation(mProgram, "value0");
213 const GLint color1Loc = glGetUniformLocation(mProgram, "value2");
214
215 glUniform4f(color0Loc, 1, 0, 0, 1);
216 glUniform4f(color1Loc, 0, 1, 0, 1);
217 }
218
destroyBenchmark()219 void MultisampleResolvePerf::destroyBenchmark()
220 {
221 glDeleteFramebuffers(1, &mMSAAFramebuffer);
222 glDeleteFramebuffers(2, mResolveFramebuffer);
223 glDeleteFramebuffers(1, &mReferenceFramebuffer);
224
225 glDeleteRenderbuffers(2, mMSAAColor);
226 glDeleteRenderbuffers(1, &mMSAADepthStencil);
227 glDeleteRenderbuffers(2, mResolveColor);
228 glDeleteRenderbuffers(1, &mResolveDepthStencil);
229
230 glDeleteProgram(mProgram);
231 }
232
drawBenchmark()233 void MultisampleResolvePerf::drawBenchmark()
234 {
235 const MultisampleResolveParams ¶m = GetParam();
236 const int size = param.framebufferSize;
237 const bool singleSampled = param.samples == 0;
238
239 glViewport(0, 0, size, size);
240 glEnable(GL_DEPTH_TEST);
241 glDepthFunc(GL_ALWAYS);
242 glEnable(GL_STENCIL_TEST);
243 glStencilFunc(GL_ALWAYS, 0x55, 0xFF);
244 glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
245 glStencilMask(0xFF);
246
247 for (unsigned int iteration = 0; iteration < param.iterationsPerStep; ++iteration)
248 {
249 const GLenum discards[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT2,
250 GL_DEPTH_STENCIL_ATTACHMENT};
251
252 glBindFramebuffer(GL_FRAMEBUFFER, singleSampled ? mReferenceFramebuffer : mMSAAFramebuffer);
253 glInvalidateFramebuffer(GL_FRAMEBUFFER, param.withDepthStencil ? 3 : 2, discards);
254
255 // Start a render pass, then resolve each attachment + invalidate them. Every render pass
256 // should thus start with LOAD_OP_DONT_CARE and end in STORE_OP_DONT_CARE (for the
257 // attachments) and STORE_OP_STORE (for the resolve attachments).
258 //
259 // In single-sampled mode, just draw. This is used to compare the performance of
260 // multisampled with single sampled rendering.
261 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
262
263 if (!singleSampled)
264 {
265 for (uint32_t i = 0; i < 2; ++i)
266 {
267 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mResolveFramebuffer[i]);
268 glReadBuffer(discards[i]);
269 glBlitFramebuffer(0, 0, size, size, 0, 0, size, size, GL_COLOR_BUFFER_BIT,
270 GL_NEAREST);
271 ASSERT_GL_NO_ERROR();
272
273 glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 1, &discards[i]);
274 }
275
276 if (param.withDepthStencil)
277 {
278 glBlitFramebuffer(0, 0, size, size, 0, 0, size, size,
279 GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST);
280
281 glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 1, &discards[2]);
282 }
283
284 ASSERT_GL_NO_ERROR();
285 }
286 }
287
288 ASSERT_GL_NO_ERROR();
289 }
290
291 // Test that multisample resolve + invalidate is as efficient as single sampling on tilers.
TEST_P(MultisampleResolvePerf,Run)292 TEST_P(MultisampleResolvePerf, Run)
293 {
294 run();
295 }
296
Vulkan(Multisample multisample,WithDepthStencil depthStencil)297 MultisampleResolveParams Vulkan(Multisample multisample, WithDepthStencil depthStencil)
298 {
299 MultisampleResolveParams params(multisample, depthStencil);
300 params.eglParameters = angle::egl_platform::VULKAN();
301 return params;
302 }
303 } // anonymous namespace
304
305 ANGLE_INSTANTIATE_TEST(MultisampleResolvePerf,
306 Vulkan(Multisample::No, WithDepthStencil::No),
307 Vulkan(Multisample::Yes, WithDepthStencil::No),
308 Vulkan(Multisample::No, WithDepthStencil::Yes),
309 Vulkan(Multisample::Yes, WithDepthStencil::Yes));
310
311 // This test suite is not instantiated on some OSes.
312 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MultisampleResolvePerf);
313