1 //
2 // Copyright 2016 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 // BindingPerf:
7 // Performance test for binding objects
8 //
9
10 #include "ANGLEPerfTest.h"
11
12 #include <iostream>
13 #include <random>
14 #include <sstream>
15
16 #include "test_utils/angle_test_instantiate.h"
17 #include "util/shader_utils.h"
18
19 namespace angle
20 {
21 namespace
22 {
23 constexpr unsigned int kIterationsPerStep = 128;
24
25 enum TestMode
26 {
27 VertexArray,
28 MultipleBindings,
29 };
30
31 enum AllocationStyle
32 {
33 EVERY_ITERATION,
34 AT_INITIALIZATION
35 };
36
37 struct BindingsParams final : public RenderTestParams
38 {
BindingsParamsangle::__anon94a4391d0111::BindingsParams39 BindingsParams()
40 {
41 // Common default params
42 majorVersion = 2;
43 minorVersion = 0;
44 windowWidth = 720;
45 windowHeight = 720;
46
47 numObjects = 100;
48 allocationStyle = EVERY_ITERATION;
49 iterationsPerStep = kIterationsPerStep;
50 }
51
52 std::string story() const override;
53 TestMode testMode = TestMode::MultipleBindings;
54 size_t numObjects;
55 AllocationStyle allocationStyle;
56 };
57
operator <<(std::ostream & os,const BindingsParams & params)58 std::ostream &operator<<(std::ostream &os, const BindingsParams ¶ms)
59 {
60 os << params.backendAndStory().substr(1);
61 return os;
62 }
63
story() const64 std::string BindingsParams::story() const
65 {
66 std::stringstream strstr;
67
68 strstr << RenderTestParams::story();
69
70 if (testMode == TestMode::VertexArray)
71 {
72 strstr << "_vertexarray";
73 }
74 else
75 {
76 strstr << "_" << numObjects << "_objects";
77
78 switch (allocationStyle)
79 {
80 case EVERY_ITERATION:
81 strstr << "_allocated_every_iteration";
82 break;
83 case AT_INITIALIZATION:
84 strstr << "_allocated_at_initialization";
85 break;
86 default:
87 strstr << "_err";
88 break;
89 }
90 }
91
92 return strstr.str();
93 }
94
95 class BindingsBenchmark : public ANGLERenderTest,
96 public ::testing::WithParamInterface<BindingsParams>
97 {
98 public:
99 BindingsBenchmark();
100
101 void initializeBenchmark() override;
102 void destroyBenchmark() override;
103 void drawBenchmark() override;
104
105 private:
106 // TODO: Test binding perf of more than just buffers
107 std::vector<GLuint> mBuffers;
108 std::vector<GLenum> mBindingPoints;
109 GLuint mMaxVertexAttribs = 0;
110 };
111
BindingsBenchmark()112 BindingsBenchmark::BindingsBenchmark() : ANGLERenderTest("Bindings", GetParam())
113 {
114 if (GetParam().eglParameters.renderer == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE)
115 {
116 skipTest("http://anglebug.com/42264783 Flaky on OpenGL");
117 }
118 }
119
initializeBenchmark()120 void BindingsBenchmark::initializeBenchmark()
121 {
122 const BindingsParams ¶ms = GetParam();
123
124 mBuffers.resize(params.numObjects, 0);
125 if (params.allocationStyle == AT_INITIALIZATION)
126 {
127 glGenBuffers(static_cast<GLsizei>(mBuffers.size()), mBuffers.data());
128 for (size_t bufferIdx = 0; bufferIdx < mBuffers.size(); bufferIdx++)
129 {
130 glBindBuffer(GL_ARRAY_BUFFER, mBuffers[bufferIdx]);
131 }
132 glBindBuffer(GL_ARRAY_BUFFER, 0);
133 }
134
135 if (params.testMode == TestMode::MultipleBindings)
136 {
137 mBindingPoints.push_back(GL_ARRAY_BUFFER);
138 mBindingPoints.push_back(GL_ELEMENT_ARRAY_BUFFER);
139 if (params.majorVersion >= 3)
140 {
141 mBindingPoints.push_back(GL_PIXEL_PACK_BUFFER);
142 mBindingPoints.push_back(GL_PIXEL_UNPACK_BUFFER);
143 mBindingPoints.push_back(GL_COPY_READ_BUFFER);
144 mBindingPoints.push_back(GL_COPY_WRITE_BUFFER);
145 mBindingPoints.push_back(GL_TRANSFORM_FEEDBACK_BUFFER);
146 mBindingPoints.push_back(GL_UNIFORM_BUFFER);
147 }
148 if (params.majorVersion > 3 || (params.majorVersion == 3 && params.minorVersion >= 1))
149 {
150 mBindingPoints.push_back(GL_ATOMIC_COUNTER_BUFFER);
151 mBindingPoints.push_back(GL_SHADER_STORAGE_BUFFER);
152 mBindingPoints.push_back(GL_DRAW_INDIRECT_BUFFER);
153 mBindingPoints.push_back(GL_DISPATCH_INDIRECT_BUFFER);
154 }
155 }
156 else
157 {
158 mBindingPoints.resize(mBuffers.size(), GL_ARRAY_BUFFER);
159 }
160
161 glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, reinterpret_cast<GLint *>(&mMaxVertexAttribs));
162 }
163
destroyBenchmark()164 void BindingsBenchmark::destroyBenchmark()
165 {
166 const BindingsParams ¶ms = GetParam();
167 if (params.allocationStyle == AT_INITIALIZATION)
168 {
169 glDeleteBuffers(static_cast<GLsizei>(mBuffers.size()), mBuffers.data());
170 }
171 }
172
drawBenchmark()173 void BindingsBenchmark::drawBenchmark()
174 {
175 const BindingsParams ¶ms = GetParam();
176
177 for (unsigned int it = 0; it < params.iterationsPerStep; ++it)
178 {
179 // Generate a buffer (if needed) and bind it to a "random" binding point
180 if (params.allocationStyle == EVERY_ITERATION)
181 {
182 glGenBuffers(static_cast<GLsizei>(mBuffers.size()), mBuffers.data());
183 }
184
185 // Fetch a few variables from the underlying data structure to keep them in registers.
186 // Otherwise each loop iteration they'll be fetched again because the compiler cannot
187 // guarantee that those are unchanged when calling glBindBuffer.
188 const GLuint *buffers = mBuffers.data();
189 const GLenum *bindingPoints = mBindingPoints.data();
190 size_t bindingPointsSize = mBindingPoints.size();
191 size_t buffersSize = mBuffers.size();
192 size_t bindingIndex = it % bindingPointsSize;
193 for (GLuint bufferIdx = 0; bufferIdx < buffersSize; bufferIdx++)
194 {
195 GLenum binding = bindingPoints[bindingIndex];
196 glBindBuffer(binding, buffers[bufferIdx]);
197
198 // Instead of doing a costly division to get an index in the range [0,bindingPointsSize)
199 // do a bounds-check and reset the index.
200 ++bindingIndex;
201 bindingIndex = (bindingIndex >= bindingPointsSize) ? 0 : bindingIndex;
202
203 if (params.testMode == TestMode::VertexArray)
204 {
205 GLuint vertexAttribIndex = bufferIdx % mMaxVertexAttribs;
206 glVertexAttribPointer(vertexAttribIndex, 1, GL_FLOAT, GL_FALSE, 0, 0);
207 }
208 }
209
210 // Delete all the buffers
211 if (params.allocationStyle == EVERY_ITERATION)
212 {
213 glDeleteBuffers(static_cast<GLsizei>(mBuffers.size()), mBuffers.data());
214 }
215 }
216
217 ASSERT_GL_NO_ERROR();
218 }
219
D3D11Params(AllocationStyle allocationStyle)220 BindingsParams D3D11Params(AllocationStyle allocationStyle)
221 {
222 BindingsParams params;
223 params.eglParameters = egl_platform::D3D11_NULL();
224 params.allocationStyle = allocationStyle;
225 return params;
226 }
227
OpenGLOrGLESParams(AllocationStyle allocationStyle)228 BindingsParams OpenGLOrGLESParams(AllocationStyle allocationStyle)
229 {
230 BindingsParams params;
231 params.eglParameters = egl_platform::OPENGL_OR_GLES_NULL();
232 params.allocationStyle = allocationStyle;
233 return params;
234 }
235
VulkanParams(AllocationStyle allocationStyle,TestMode testMode)236 BindingsParams VulkanParams(AllocationStyle allocationStyle, TestMode testMode)
237 {
238 BindingsParams params;
239 params.eglParameters = egl_platform::VULKAN_NULL();
240 params.allocationStyle = allocationStyle;
241 params.testMode = testMode;
242 return params;
243 }
244
TEST_P(BindingsBenchmark,Run)245 TEST_P(BindingsBenchmark, Run)
246 {
247 run();
248 }
249 } // namespace
250
251 ANGLE_INSTANTIATE_TEST(BindingsBenchmark,
252 D3D11Params(EVERY_ITERATION),
253 D3D11Params(AT_INITIALIZATION),
254 OpenGLOrGLESParams(EVERY_ITERATION),
255 OpenGLOrGLESParams(AT_INITIALIZATION),
256 VulkanParams(EVERY_ITERATION, TestMode::MultipleBindings),
257 VulkanParams(AT_INITIALIZATION, TestMode::MultipleBindings),
258 VulkanParams(AT_INITIALIZATION, TestMode::VertexArray));
259
260 } // namespace angle
261