1 //
2 // Copyright 2017 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 // DrawElementsPerf:
7 // Performance tests for ANGLE DrawElements call overhead.
8 //
9
10 #include <sstream>
11
12 #include "ANGLEPerfTest.h"
13 #include "DrawCallPerfParams.h"
14 #include "test_utils/draw_call_perf_utils.h"
15
16 namespace
17 {
18
CreateElementArrayBuffer(size_t count,GLenum type,GLenum usage)19 GLuint CreateElementArrayBuffer(size_t count, GLenum type, GLenum usage)
20 {
21 GLuint buffer = 0u;
22 glGenBuffers(1, &buffer);
23 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
24 glBufferData(GL_ELEMENT_ARRAY_BUFFER, count * sizeof(type), nullptr, usage);
25
26 return buffer;
27 }
28
29 struct DrawElementsPerfParams final : public DrawCallPerfParams
30 {
31 // Common default options
DrawElementsPerfParams__anonc3ed57d70111::DrawElementsPerfParams32 DrawElementsPerfParams()
33 {
34 runTimeSeconds = 5.0;
35 numTris = 2;
36 }
37
story__anonc3ed57d70111::DrawElementsPerfParams38 std::string story() const override
39 {
40 std::stringstream strstr;
41
42 strstr << DrawCallPerfParams::story();
43
44 if (indexBufferChanged)
45 {
46 strstr << "_index_buffer_changed";
47 }
48
49 if (type == GL_UNSIGNED_SHORT)
50 {
51 strstr << "_ushort";
52 }
53
54 return strstr.str();
55 }
56
57 GLenum type = GL_UNSIGNED_INT;
58 bool indexBufferChanged = false;
59 };
60
operator <<(std::ostream & os,const DrawElementsPerfParams & params)61 std::ostream &operator<<(std::ostream &os, const DrawElementsPerfParams ¶ms)
62 {
63 os << params.backendAndStory().substr(1);
64 return os;
65 }
66
67 class DrawElementsPerfBenchmark : public ANGLERenderTest,
68 public ::testing::WithParamInterface<DrawElementsPerfParams>
69 {
70 public:
71 DrawElementsPerfBenchmark();
72
73 void initializeBenchmark() override;
74 void destroyBenchmark() override;
75 void drawBenchmark() override;
76
77 private:
78 GLuint mProgram = 0;
79 GLuint mBuffer = 0;
80 GLuint mIndexBuffer = 0;
81 GLuint mFBO = 0;
82 GLuint mTexture = 0;
83 GLsizei mBufferSize = 0;
84 int mCount = 3 * GetParam().numTris;
85 std::vector<GLuint> mIntIndexData;
86 std::vector<GLushort> mShortIndexData;
87 };
88
DrawElementsPerfBenchmark()89 DrawElementsPerfBenchmark::DrawElementsPerfBenchmark()
90 : ANGLERenderTest("DrawElementsPerf", GetParam())
91 {
92 if (GetParam().type == GL_UNSIGNED_INT)
93 {
94 addExtensionPrerequisite("GL_OES_element_index_uint");
95 }
96 }
97
ElementTypeSize(GLenum elementType)98 GLsizei ElementTypeSize(GLenum elementType)
99 {
100 switch (elementType)
101 {
102 case GL_UNSIGNED_BYTE:
103 return sizeof(GLubyte);
104 case GL_UNSIGNED_SHORT:
105 return sizeof(GLushort);
106 case GL_UNSIGNED_INT:
107 return sizeof(GLuint);
108 default:
109 return 0;
110 }
111 }
112
initializeBenchmark()113 void DrawElementsPerfBenchmark::initializeBenchmark()
114 {
115 const auto ¶ms = GetParam();
116
117 mProgram = SetupSimpleDrawProgram();
118 ASSERT_NE(0u, mProgram);
119
120 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
121
122 mBuffer = Create2DTriangleBuffer(params.numTris, GL_STATIC_DRAW);
123 mIndexBuffer = CreateElementArrayBuffer(mCount, params.type, GL_STATIC_DRAW);
124
125 for (int i = 0; i < mCount; i++)
126 {
127 ASSERT_GE(std::numeric_limits<GLushort>::max(), mCount);
128 mShortIndexData.push_back(static_cast<GLushort>(rand() % mCount));
129 mIntIndexData.push_back(rand() % mCount);
130 }
131
132 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
133
134 mBufferSize = ElementTypeSize(params.type) * mCount;
135
136 if (params.type == GL_UNSIGNED_INT)
137 {
138 glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, mBufferSize, mIntIndexData.data());
139 }
140 else
141 {
142 glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, mBufferSize, mShortIndexData.data());
143 }
144
145 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
146 glEnableVertexAttribArray(0);
147
148 // Set the viewport
149 glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight());
150
151 if (params.surfaceType == SurfaceType::Offscreen)
152 {
153 CreateColorFBO(getWindow()->getWidth(), getWindow()->getHeight(), &mTexture, &mFBO);
154 }
155
156 ASSERT_GL_NO_ERROR();
157 }
158
destroyBenchmark()159 void DrawElementsPerfBenchmark::destroyBenchmark()
160 {
161 glDeleteProgram(mProgram);
162 glDeleteBuffers(1, &mBuffer);
163 glDeleteBuffers(1, &mIndexBuffer);
164 glDeleteTextures(1, &mTexture);
165 glDeleteFramebuffers(1, &mFBO);
166 }
167
drawBenchmark()168 void DrawElementsPerfBenchmark::drawBenchmark()
169 {
170 // This workaround fixes a huge queue of graphics commands accumulating on the GL
171 // back-end. The GL back-end doesn't have a proper NULL device at the moment.
172 // TODO(jmadill): Remove this when/if we ever get a proper OpenGL NULL device.
173 const auto &eglParams = GetParam().eglParameters;
174 if (eglParams.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE ||
175 (eglParams.renderer != EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE &&
176 eglParams.renderer != EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE))
177 {
178 glClear(GL_COLOR_BUFFER_BIT);
179 }
180
181 const DrawElementsPerfParams ¶ms = GetParam();
182
183 if (params.indexBufferChanged)
184 {
185 const void *bufferData = (params.type == GL_UNSIGNED_INT)
186 ? static_cast<GLvoid *>(mIntIndexData.data())
187 : static_cast<GLvoid *>(mShortIndexData.data());
188 for (unsigned int it = 0; it < params.iterationsPerStep; it++)
189 {
190 glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, mBufferSize, bufferData);
191 glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(mCount), params.type, 0);
192 }
193 }
194 else
195 {
196 for (unsigned int it = 0; it < params.iterationsPerStep; it++)
197 {
198 glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(mCount), params.type, 0);
199 }
200 }
201
202 ASSERT_GL_NO_ERROR();
203 }
204
TEST_P(DrawElementsPerfBenchmark,Run)205 TEST_P(DrawElementsPerfBenchmark, Run)
206 {
207 run();
208 }
209
210 using namespace angle;
211 using namespace params;
212 using P = DrawElementsPerfParams;
213
CombineIndexType(const P & in,GLenum indexType)214 P CombineIndexType(const P &in, GLenum indexType)
215 {
216 P out = in;
217 out.type = indexType;
218 return out;
219 }
220
CombineIndexBufferChanged(const P & in,bool indexBufferChanged)221 P CombineIndexBufferChanged(const P &in, bool indexBufferChanged)
222 {
223 P out = in;
224 out.indexBufferChanged = indexBufferChanged;
225
226 // Scale down iterations for slower tests.
227 if (indexBufferChanged)
228 out.iterationsPerStep /= 100;
229
230 return out;
231 }
232
233 std::vector<GLenum> gIndexTypes = {GL_UNSIGNED_INT, GL_UNSIGNED_SHORT};
234 std::vector<P> gWithIndexType = CombineWithValues({P()}, gIndexTypes, CombineIndexType);
235 std::vector<P> gWithRenderer =
236 CombineWithFuncs(gWithIndexType, {D3D11<P>, GL<P>, Vulkan<P>, WGL<P>});
237 std::vector<P> gWithChange =
238 CombineWithValues(gWithRenderer, {false, true}, CombineIndexBufferChanged);
239 std::vector<P> gWithDevice = CombineWithFuncs(gWithChange, {Passthrough<P>, NullDevice<P>});
240
241 ANGLE_INSTANTIATE_TEST_ARRAY(DrawElementsPerfBenchmark, gWithDevice);
242
243 } // anonymous namespace
244