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 &params)
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 &params = 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 &params = 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