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::__anonff492a010111::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 // Flaky on OpenGL. http://anglebug.com/6264
115 if (GetParam().eglParameters.renderer == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE)
116 {
117 mSkipTest = true;
118 }
119 }
120
initializeBenchmark()121 void BindingsBenchmark::initializeBenchmark()
122 {
123 const BindingsParams ¶ms = GetParam();
124
125 mBuffers.resize(params.numObjects, 0);
126 if (params.allocationStyle == AT_INITIALIZATION)
127 {
128 glGenBuffers(static_cast<GLsizei>(mBuffers.size()), mBuffers.data());
129 for (size_t bufferIdx = 0; bufferIdx < mBuffers.size(); bufferIdx++)
130 {
131 glBindBuffer(GL_ARRAY_BUFFER, mBuffers[bufferIdx]);
132 }
133 glBindBuffer(GL_ARRAY_BUFFER, 0);
134 }
135
136 if (params.testMode == TestMode::MultipleBindings)
137 {
138 mBindingPoints.push_back(GL_ARRAY_BUFFER);
139 mBindingPoints.push_back(GL_ELEMENT_ARRAY_BUFFER);
140 if (params.majorVersion >= 3)
141 {
142 mBindingPoints.push_back(GL_PIXEL_PACK_BUFFER);
143 mBindingPoints.push_back(GL_PIXEL_UNPACK_BUFFER);
144 mBindingPoints.push_back(GL_COPY_READ_BUFFER);
145 mBindingPoints.push_back(GL_COPY_WRITE_BUFFER);
146 mBindingPoints.push_back(GL_TRANSFORM_FEEDBACK_BUFFER);
147 mBindingPoints.push_back(GL_UNIFORM_BUFFER);
148 }
149 if (params.majorVersion > 3 || (params.majorVersion == 3 && params.minorVersion >= 1))
150 {
151 mBindingPoints.push_back(GL_ATOMIC_COUNTER_BUFFER);
152 mBindingPoints.push_back(GL_SHADER_STORAGE_BUFFER);
153 mBindingPoints.push_back(GL_DRAW_INDIRECT_BUFFER);
154 mBindingPoints.push_back(GL_DISPATCH_INDIRECT_BUFFER);
155 }
156 }
157 else
158 {
159 mBindingPoints.resize(mBuffers.size(), GL_ARRAY_BUFFER);
160 }
161
162 glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, reinterpret_cast<GLint *>(&mMaxVertexAttribs));
163 }
164
destroyBenchmark()165 void BindingsBenchmark::destroyBenchmark()
166 {
167 const BindingsParams ¶ms = GetParam();
168 if (params.allocationStyle == AT_INITIALIZATION)
169 {
170 glDeleteBuffers(static_cast<GLsizei>(mBuffers.size()), mBuffers.data());
171 }
172 }
173
drawBenchmark()174 void BindingsBenchmark::drawBenchmark()
175 {
176 const BindingsParams ¶ms = GetParam();
177
178 for (unsigned int it = 0; it < params.iterationsPerStep; ++it)
179 {
180 // Generate a buffer (if needed) and bind it to a "random" binding point
181 if (params.allocationStyle == EVERY_ITERATION)
182 {
183 glGenBuffers(static_cast<GLsizei>(mBuffers.size()), mBuffers.data());
184 }
185
186 // Fetch a few variables from the underlying data structure to keep them in registers.
187 // Otherwise each loop iteration they'll be fetched again because the compiler cannot
188 // guarantee that those are unchanged when calling glBindBuffer.
189 const GLuint *buffers = mBuffers.data();
190 const GLenum *bindingPoints = mBindingPoints.data();
191 size_t bindingPointsSize = mBindingPoints.size();
192 size_t buffersSize = mBuffers.size();
193 size_t bindingIndex = it % bindingPointsSize;
194 for (GLuint bufferIdx = 0; bufferIdx < buffersSize; bufferIdx++)
195 {
196 GLenum binding = bindingPoints[bindingIndex];
197 glBindBuffer(binding, buffers[bufferIdx]);
198
199 // Instead of doing a costly division to get an index in the range [0,bindingPointsSize)
200 // do a bounds-check and reset the index.
201 ++bindingIndex;
202 bindingIndex = (bindingIndex >= bindingPointsSize) ? 0 : bindingIndex;
203
204 if (params.testMode == TestMode::VertexArray)
205 {
206 GLuint vertexAttribIndex = bufferIdx % mMaxVertexAttribs;
207 glVertexAttribPointer(vertexAttribIndex, 1, GL_FLOAT, GL_FALSE, 0, 0);
208 }
209 }
210
211 // Delete all the buffers
212 if (params.allocationStyle == EVERY_ITERATION)
213 {
214 glDeleteBuffers(static_cast<GLsizei>(mBuffers.size()), mBuffers.data());
215 }
216 }
217
218 ASSERT_GL_NO_ERROR();
219 }
220
D3D11Params(AllocationStyle allocationStyle)221 BindingsParams D3D11Params(AllocationStyle allocationStyle)
222 {
223 BindingsParams params;
224 params.eglParameters = egl_platform::D3D11_NULL();
225 params.allocationStyle = allocationStyle;
226 return params;
227 }
228
OpenGLOrGLESParams(AllocationStyle allocationStyle)229 BindingsParams OpenGLOrGLESParams(AllocationStyle allocationStyle)
230 {
231 BindingsParams params;
232 params.eglParameters = egl_platform::OPENGL_OR_GLES_NULL();
233 params.allocationStyle = allocationStyle;
234 return params;
235 }
236
VulkanParams(AllocationStyle allocationStyle,TestMode testMode)237 BindingsParams VulkanParams(AllocationStyle allocationStyle, TestMode testMode)
238 {
239 BindingsParams params;
240 params.eglParameters = egl_platform::VULKAN_NULL();
241 params.allocationStyle = allocationStyle;
242 params.testMode = testMode;
243 return params;
244 }
245
TEST_P(BindingsBenchmark,Run)246 TEST_P(BindingsBenchmark, Run)
247 {
248 run();
249 }
250 } // namespace
251
252 ANGLE_INSTANTIATE_TEST(BindingsBenchmark,
253 D3D11Params(EVERY_ITERATION),
254 D3D11Params(AT_INITIALIZATION),
255 OpenGLOrGLESParams(EVERY_ITERATION),
256 OpenGLOrGLESParams(AT_INITIALIZATION),
257 VulkanParams(EVERY_ITERATION, TestMode::MultipleBindings),
258 VulkanParams(AT_INITIALIZATION, TestMode::MultipleBindings),
259 VulkanParams(AT_INITIALIZATION, TestMode::VertexArray));
260
261 } // namespace angle
262