1 //
2 // Copyright 2015 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 // InstancingPerf:
7 // Performance tests for ANGLE instanced draw calls.
8 //
9
10 #include "ANGLEPerfTest.h"
11
12 #include <cmath>
13 #include <sstream>
14
15 #include "util/Matrix.h"
16 #include "util/random_utils.h"
17 #include "util/shader_utils.h"
18
19 using namespace angle;
20 using namespace egl_platform;
21
22 namespace
23 {
24
AnimationSignal(float t)25 float AnimationSignal(float t)
26 {
27 float l = t / 2.0f;
28 float f = l - std::floor(l);
29 return (f > 0.5f ? 1.0f - f : f) * 4.0f - 1.0f;
30 }
31
32 template <typename T>
VectorSizeBytes(const std::vector<T> & vec)33 size_t VectorSizeBytes(const std::vector<T> &vec)
34 {
35 return sizeof(T) * vec.size();
36 }
37
RandomVector3(RNG * rng)38 Vector3 RandomVector3(RNG *rng)
39 {
40 return Vector3(rng->randomNegativeOneToOne(), rng->randomNegativeOneToOne(),
41 rng->randomNegativeOneToOne());
42 }
43
44 struct InstancingPerfParams final : public RenderTestParams
45 {
46 // Common default options
InstancingPerfParams__anon5ac92ae50111::InstancingPerfParams47 InstancingPerfParams()
48 {
49 majorVersion = 2;
50 minorVersion = 0;
51 windowWidth = 256;
52 windowHeight = 256;
53 iterationsPerStep = 1;
54 runTimeSeconds = 10.0;
55 animationEnabled = false;
56 instancingEnabled = true;
57 }
58
story__anon5ac92ae50111::InstancingPerfParams59 std::string story() const override
60 {
61 std::stringstream strstr;
62
63 strstr << RenderTestParams::story();
64
65 if (!instancingEnabled)
66 {
67 strstr << "_billboards";
68 }
69
70 return strstr.str();
71 }
72
73 double runTimeSeconds;
74 bool animationEnabled;
75 bool instancingEnabled;
76 };
77
operator <<(std::ostream & os,const InstancingPerfParams & params)78 std::ostream &operator<<(std::ostream &os, const InstancingPerfParams ¶ms)
79 {
80 os << params.backendAndStory().substr(1);
81 return os;
82 }
83
84 class InstancingPerfBenchmark : public ANGLERenderTest,
85 public ::testing::WithParamInterface<InstancingPerfParams>
86 {
87 public:
88 InstancingPerfBenchmark();
89
90 void initializeBenchmark() override;
91 void destroyBenchmark() override;
92 void drawBenchmark() override;
93
94 private:
95 GLuint mProgram;
96 std::vector<GLuint> mBuffers;
97 GLuint mNumPoints;
98 std::vector<Vector3> mTranslateData;
99 std::vector<float> mSizeData;
100 std::vector<Vector3> mColorData;
101 angle::RNG mRNG;
102 };
103
InstancingPerfBenchmark()104 InstancingPerfBenchmark::InstancingPerfBenchmark()
105 : ANGLERenderTest("InstancingPerf", GetParam()), mProgram(0), mNumPoints(75000)
106 {}
107
initializeBenchmark()108 void InstancingPerfBenchmark::initializeBenchmark()
109 {
110 const auto ¶ms = GetParam();
111
112 const char kVS[] =
113 "attribute vec2 aPosition;\n"
114 "attribute vec3 aTranslate;\n"
115 "attribute float aScale;\n"
116 "attribute vec3 aColor;\n"
117 "uniform mat4 uWorldMatrix;\n"
118 "uniform mat4 uProjectionMatrix;\n"
119 "varying vec3 vColor;\n"
120 "void main()\n"
121 "{\n"
122 " vec4 position = uWorldMatrix * vec4(aTranslate, 1.0);\n"
123 " position.xy += aPosition * aScale;\n"
124 " gl_Position = uProjectionMatrix * position;\n"
125 " vColor = aColor;\n"
126 "}\n";
127
128 constexpr char kFS[] =
129 "precision mediump float;\n"
130 "varying vec3 vColor;\n"
131 "void main()\n"
132 "{\n"
133 " gl_FragColor = vec4(vColor, 1.0);\n"
134 "}\n";
135
136 mProgram = CompileProgram(kVS, kFS);
137 ASSERT_NE(0u, mProgram);
138
139 glUseProgram(mProgram);
140
141 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
142
143 GLuint baseIndexData[6] = {0, 1, 2, 1, 3, 2};
144 Vector2 basePositionData[4] = {Vector2(-1.0f, 1.0f), Vector2(1.0f, 1.0f), Vector2(-1.0f, -1.0f),
145 Vector2(1.0f, -1.0f)};
146
147 std::vector<GLuint> indexData;
148 std::vector<Vector2> positionData;
149
150 if (!params.instancingEnabled)
151 {
152 GLuint pointVertexStride = 4;
153 for (GLuint pointIndex = 0; pointIndex < mNumPoints; ++pointIndex)
154 {
155 for (GLuint indexIndex = 0; indexIndex < 6; ++indexIndex)
156 {
157 indexData.push_back(baseIndexData[indexIndex] + pointIndex * pointVertexStride);
158 }
159
160 Vector3 randVec = RandomVector3(&mRNG);
161 for (GLuint vertexIndex = 0; vertexIndex < 4; ++vertexIndex)
162 {
163 positionData.push_back(basePositionData[vertexIndex]);
164 mTranslateData.push_back(randVec);
165 }
166 }
167
168 mSizeData.resize(mNumPoints * 4, 0.012f);
169 mColorData.resize(mNumPoints * 4, Vector3(1.0f, 0.0f, 0.0f));
170 }
171 else
172 {
173 for (GLuint index : baseIndexData)
174 {
175 indexData.push_back(index);
176 }
177
178 for (const Vector2 &position : basePositionData)
179 {
180 positionData.push_back(position);
181 }
182
183 for (GLuint pointIndex = 0; pointIndex < mNumPoints; ++pointIndex)
184 {
185 Vector3 randVec = RandomVector3(&mRNG);
186 mTranslateData.push_back(randVec);
187 }
188
189 mSizeData.resize(mNumPoints, 0.012f);
190 mColorData.resize(mNumPoints, Vector3(1.0f, 0.0f, 0.0f));
191 }
192
193 mBuffers.resize(5, 0);
194 glGenBuffers(static_cast<GLsizei>(mBuffers.size()), &mBuffers[0]);
195
196 // Index Data
197 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mBuffers[0]);
198 glBufferData(GL_ELEMENT_ARRAY_BUFFER, VectorSizeBytes(indexData), &indexData[0],
199 GL_STATIC_DRAW);
200
201 // Position Data
202 glBindBuffer(GL_ARRAY_BUFFER, mBuffers[1]);
203 glBufferData(GL_ARRAY_BUFFER, VectorSizeBytes(positionData), &positionData[0], GL_STATIC_DRAW);
204 GLint positionLocation = glGetAttribLocation(mProgram, "aPosition");
205 ASSERT_NE(-1, positionLocation);
206 glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 8, nullptr);
207 glEnableVertexAttribArray(positionLocation);
208
209 // Translate Data
210 glBindBuffer(GL_ARRAY_BUFFER, mBuffers[2]);
211 glBufferData(GL_ARRAY_BUFFER, VectorSizeBytes(mTranslateData), &mTranslateData[0],
212 GL_STATIC_DRAW);
213 GLint translateLocation = glGetAttribLocation(mProgram, "aTranslate");
214 ASSERT_NE(-1, translateLocation);
215 glVertexAttribPointer(translateLocation, 3, GL_FLOAT, GL_FALSE, 12, nullptr);
216 glEnableVertexAttribArray(translateLocation);
217 glVertexAttribDivisorANGLE(translateLocation, 1);
218
219 // Scale Data
220 glBindBuffer(GL_ARRAY_BUFFER, mBuffers[3]);
221 glBufferData(GL_ARRAY_BUFFER, VectorSizeBytes(mSizeData), nullptr, GL_DYNAMIC_DRAW);
222 GLint scaleLocation = glGetAttribLocation(mProgram, "aScale");
223 ASSERT_NE(-1, scaleLocation);
224 glVertexAttribPointer(scaleLocation, 1, GL_FLOAT, GL_FALSE, 4, nullptr);
225 glEnableVertexAttribArray(scaleLocation);
226 glVertexAttribDivisorANGLE(scaleLocation, 1);
227
228 // Color Data
229 glBindBuffer(GL_ARRAY_BUFFER, mBuffers[4]);
230 glBufferData(GL_ARRAY_BUFFER, VectorSizeBytes(mColorData), nullptr, GL_DYNAMIC_DRAW);
231 GLint colorLocation = glGetAttribLocation(mProgram, "aColor");
232 ASSERT_NE(-1, colorLocation);
233 glVertexAttribPointer(colorLocation, 3, GL_FLOAT, GL_FALSE, 12, nullptr);
234 glEnableVertexAttribArray(colorLocation);
235 glVertexAttribDivisorANGLE(colorLocation, 1);
236
237 // Set the viewport
238 glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight());
239
240 // Init matrices
241 GLint worldMatrixLocation = glGetUniformLocation(mProgram, "uWorldMatrix");
242 ASSERT_NE(-1, worldMatrixLocation);
243 Matrix4 worldMatrix = Matrix4::translate(Vector3(0, 0, -3.0f));
244 worldMatrix *= Matrix4::rotate(25.0f, Vector3(0.6f, 1.0f, 0.0f));
245 glUniformMatrix4fv(worldMatrixLocation, 1, GL_FALSE, &worldMatrix.data[0]);
246
247 GLint projectionMatrixLocation = glGetUniformLocation(mProgram, "uProjectionMatrix");
248 ASSERT_NE(-1, projectionMatrixLocation);
249 float fov =
250 static_cast<float>(getWindow()->getWidth()) / static_cast<float>(getWindow()->getHeight());
251 Matrix4 projectionMatrix = Matrix4::perspective(60.0f, fov, 1.0f, 300.0f);
252 glUniformMatrix4fv(projectionMatrixLocation, 1, GL_FALSE, &projectionMatrix.data[0]);
253
254 getWindow()->setVisible(true);
255
256 ASSERT_GL_NO_ERROR();
257 }
258
destroyBenchmark()259 void InstancingPerfBenchmark::destroyBenchmark()
260 {
261 glDeleteProgram(mProgram);
262
263 if (!mBuffers.empty())
264 {
265 glDeleteBuffers(static_cast<GLsizei>(mBuffers.size()), &mBuffers[0]);
266 mBuffers.clear();
267 }
268 }
269
drawBenchmark()270 void InstancingPerfBenchmark::drawBenchmark()
271 {
272 glClear(GL_COLOR_BUFFER_BIT);
273
274 const auto ¶ms = GetParam();
275
276 // Animatino makes the test more interesting visually, but also eats up many CPU cycles.
277 if (params.animationEnabled)
278 {
279 float time = static_cast<float>(mTimer.getElapsedTime());
280
281 for (size_t pointIndex = 0; pointIndex < mTranslateData.size(); ++pointIndex)
282 {
283 const Vector3 &translate = mTranslateData[pointIndex];
284
285 float tx = translate.x() + time;
286 float ty = translate.y() + time;
287 float tz = translate.z() + time;
288
289 float scale = AnimationSignal(tx) * 0.01f + 0.01f;
290 mSizeData[pointIndex] = scale;
291
292 Vector3 color =
293 Vector3(AnimationSignal(tx), AnimationSignal(ty), AnimationSignal(tz)) * 0.5f +
294 Vector3(0.5f);
295
296 mColorData[pointIndex] = color;
297 }
298 }
299
300 // Update scales and colors.
301 glBindBuffer(GL_ARRAY_BUFFER, mBuffers[3]);
302 glBufferSubData(GL_ARRAY_BUFFER, 0, VectorSizeBytes(mSizeData), &mSizeData[0]);
303
304 glBindBuffer(GL_ARRAY_BUFFER, mBuffers[4]);
305 glBufferSubData(GL_ARRAY_BUFFER, 0, VectorSizeBytes(mColorData), &mColorData[0]);
306
307 // Render the instances/billboards.
308 if (params.instancingEnabled)
309 {
310 for (unsigned int it = 0; it < params.iterationsPerStep; it++)
311 {
312 glDrawElementsInstancedANGLE(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr, mNumPoints);
313 }
314 }
315 else
316 {
317 for (unsigned int it = 0; it < params.iterationsPerStep; it++)
318 {
319 glDrawElements(GL_TRIANGLES, 6 * mNumPoints, GL_UNSIGNED_INT, nullptr);
320 }
321 }
322
323 ASSERT_GL_NO_ERROR();
324 }
325
InstancingPerfD3D11Params()326 InstancingPerfParams InstancingPerfD3D11Params()
327 {
328 InstancingPerfParams params;
329 params.eglParameters = D3D11();
330 return params;
331 }
332
InstancingPerfD3D9Params()333 InstancingPerfParams InstancingPerfD3D9Params()
334 {
335 InstancingPerfParams params;
336 params.eglParameters = D3D9();
337 return params;
338 }
339
InstancingPerfOpenGLOrGLESParams()340 InstancingPerfParams InstancingPerfOpenGLOrGLESParams()
341 {
342 InstancingPerfParams params;
343 params.eglParameters = OPENGL_OR_GLES();
344 return params;
345 }
346
TEST_P(InstancingPerfBenchmark,Run)347 TEST_P(InstancingPerfBenchmark, Run)
348 {
349 run();
350 }
351
352 ANGLE_INSTANTIATE_TEST(InstancingPerfBenchmark,
353 InstancingPerfD3D11Params(),
354 InstancingPerfD3D9Params(),
355 InstancingPerfOpenGLOrGLESParams());
356
357 } // anonymous namespace
358