1 //
2 // Copyright 2014 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 // BufferSubDataBenchmark:
7 //   Performance test for ANGLE buffer updates.
8 //
9 
10 #include <sstream>
11 
12 #include "ANGLEPerfTest.h"
13 #include "test_utils/draw_call_perf_utils.h"
14 
15 using namespace angle;
16 
17 namespace
18 {
19 constexpr unsigned int kIterationsPerStep = 4;
20 
21 struct BufferSubDataParams final : public RenderTestParams
22 {
BufferSubDataParams__anon3c8dddbd0111::BufferSubDataParams23     BufferSubDataParams()
24     {
25         // Common default values
26         majorVersion      = 2;
27         minorVersion      = 0;
28         windowWidth       = 512;
29         windowHeight      = 512;
30         updateSize        = 32000;
31         bufferSize        = 40000;
32         iterationsPerStep = kIterationsPerStep;
33         updateRate        = 1;
34     }
35 
36     std::string story() const override;
37 
38     GLboolean vertexNormalized;
39     GLenum vertexType;
40     GLint vertexComponentCount;
41     unsigned int updateRate;
42 
43     // static parameters
44     GLsizeiptr updateSize;
45     GLsizeiptr bufferSize;
46 };
47 
operator <<(std::ostream & os,const BufferSubDataParams & params)48 std::ostream &operator<<(std::ostream &os, const BufferSubDataParams &params)
49 {
50     os << params.backendAndStory().substr(1);
51     return os;
52 }
53 
54 class BufferSubDataBenchmark : public ANGLERenderTest,
55                                public ::testing::WithParamInterface<BufferSubDataParams>
56 {
57   public:
58     BufferSubDataBenchmark();
59 
60     void initializeBenchmark() override;
61     void destroyBenchmark() override;
62     void drawBenchmark() override;
63 
64   private:
65     GLuint mProgram;
66     GLuint mBuffer;
67     uint8_t *mUpdateData;
68     int mNumTris;
69 };
70 
GetFloatData(GLint componentCount)71 GLfloat *GetFloatData(GLint componentCount)
72 {
73     static GLfloat vertices2[] = {
74         1, 2, 0, 0, 2, 0,
75     };
76 
77     static GLfloat vertices3[] = {
78         1, 2, 1, 0, 0, 1, 2, 0, 1,
79     };
80 
81     static GLfloat vertices4[] = {
82         1, 2, 1, 3, 0, 0, 1, 3, 2, 0, 1, 3,
83     };
84 
85     switch (componentCount)
86     {
87         case 2:
88             return vertices2;
89         case 3:
90             return vertices3;
91         case 4:
92             return vertices4;
93         default:
94             return nullptr;
95     }
96 }
97 
98 template <class T>
GetNormalizedData(GLsizeiptr numElements,GLfloat * floatData,std::vector<uint8_t> * data)99 GLsizeiptr GetNormalizedData(GLsizeiptr numElements, GLfloat *floatData, std::vector<uint8_t> *data)
100 {
101     GLsizeiptr triDataSize = sizeof(T) * numElements;
102     data->resize(triDataSize);
103 
104     T *destPtr = reinterpret_cast<T *>(data->data());
105 
106     for (GLsizeiptr dataIndex = 0; dataIndex < numElements; dataIndex++)
107     {
108         GLfloat scaled = floatData[dataIndex] * 0.25f;
109         destPtr[dataIndex] =
110             static_cast<T>(scaled * static_cast<GLfloat>(std::numeric_limits<T>::max()));
111     }
112 
113     return triDataSize;
114 }
115 
116 template <class T>
GetIntData(GLsizeiptr numElements,GLfloat * floatData,std::vector<uint8_t> * data)117 GLsizeiptr GetIntData(GLsizeiptr numElements, GLfloat *floatData, std::vector<uint8_t> *data)
118 {
119     GLsizeiptr triDataSize = sizeof(T) * numElements;
120     data->resize(triDataSize);
121 
122     T *destPtr = reinterpret_cast<T *>(data->data());
123 
124     for (GLsizeiptr dataIndex = 0; dataIndex < numElements; dataIndex++)
125     {
126         destPtr[dataIndex] = static_cast<T>(floatData[dataIndex]);
127     }
128 
129     return triDataSize;
130 }
131 
GetVertexData(GLenum type,GLint componentCount,GLboolean normalized,std::vector<uint8_t> * data)132 GLsizeiptr GetVertexData(GLenum type,
133                          GLint componentCount,
134                          GLboolean normalized,
135                          std::vector<uint8_t> *data)
136 {
137     GLsizeiptr triDataSize = 0;
138     GLfloat *floatData     = GetFloatData(componentCount);
139 
140     if (type == GL_FLOAT)
141     {
142         triDataSize = sizeof(GLfloat) * componentCount * 3;
143         data->resize(triDataSize);
144         memcpy(data->data(), floatData, triDataSize);
145     }
146     else if (normalized == GL_TRUE)
147     {
148         GLsizeiptr numElements = componentCount * 3;
149 
150         switch (type)
151         {
152             case GL_BYTE:
153                 triDataSize = GetNormalizedData<GLbyte>(numElements, floatData, data);
154                 break;
155             case GL_SHORT:
156                 triDataSize = GetNormalizedData<GLshort>(numElements, floatData, data);
157                 break;
158             case GL_INT:
159                 triDataSize = GetNormalizedData<GLint>(numElements, floatData, data);
160                 break;
161             case GL_UNSIGNED_BYTE:
162                 triDataSize = GetNormalizedData<GLubyte>(numElements, floatData, data);
163                 break;
164             case GL_UNSIGNED_SHORT:
165                 triDataSize = GetNormalizedData<GLushort>(numElements, floatData, data);
166                 break;
167             case GL_UNSIGNED_INT:
168                 triDataSize = GetNormalizedData<GLuint>(numElements, floatData, data);
169                 break;
170             default:
171                 assert(0);
172         }
173     }
174     else
175     {
176         GLsizeiptr numElements = componentCount * 3;
177 
178         switch (type)
179         {
180             case GL_BYTE:
181                 triDataSize = GetIntData<GLbyte>(numElements, floatData, data);
182                 break;
183             case GL_SHORT:
184                 triDataSize = GetIntData<GLshort>(numElements, floatData, data);
185                 break;
186             case GL_INT:
187                 triDataSize = GetIntData<GLint>(numElements, floatData, data);
188                 break;
189             case GL_UNSIGNED_BYTE:
190                 triDataSize = GetIntData<GLubyte>(numElements, floatData, data);
191                 break;
192             case GL_UNSIGNED_SHORT:
193                 triDataSize = GetIntData<GLushort>(numElements, floatData, data);
194                 break;
195             case GL_UNSIGNED_INT:
196                 triDataSize = GetIntData<GLuint>(numElements, floatData, data);
197                 break;
198             default:
199                 assert(0);
200         }
201     }
202 
203     return triDataSize;
204 }
205 
story() const206 std::string BufferSubDataParams::story() const
207 {
208     std::stringstream strstr;
209 
210     strstr << RenderTestParams::story();
211 
212     if (vertexNormalized)
213     {
214         strstr << "_norm";
215     }
216 
217     switch (vertexType)
218     {
219         case GL_FLOAT:
220             strstr << "_float";
221             break;
222         case GL_INT:
223             strstr << "_int";
224             break;
225         case GL_BYTE:
226             strstr << "_byte";
227             break;
228         case GL_SHORT:
229             strstr << "_short";
230             break;
231         case GL_UNSIGNED_INT:
232             strstr << "_uint";
233             break;
234         case GL_UNSIGNED_BYTE:
235             strstr << "_ubyte";
236             break;
237         case GL_UNSIGNED_SHORT:
238             strstr << "_ushort";
239             break;
240         default:
241             strstr << "_vunk_" << vertexType << "_";
242             break;
243     }
244 
245     strstr << vertexComponentCount;
246     strstr << "_every" << updateRate;
247 
248     return strstr.str();
249 }
250 
BufferSubDataBenchmark()251 BufferSubDataBenchmark::BufferSubDataBenchmark()
252     : ANGLERenderTest("BufferSubData", GetParam()),
253       mProgram(0),
254       mBuffer(0),
255       mUpdateData(nullptr),
256       mNumTris(0)
257 {}
258 
initializeBenchmark()259 void BufferSubDataBenchmark::initializeBenchmark()
260 {
261     const auto &params = GetParam();
262 
263     ASSERT_LT(1, params.vertexComponentCount);
264 
265     mProgram = SetupSimpleScaleAndOffsetProgram();
266     ASSERT_NE(0u, mProgram);
267 
268     if (params.vertexNormalized == GL_TRUE)
269     {
270         GLfloat scale  = 2.0f;
271         GLfloat offset = -0.5f;
272         glUniform1f(glGetUniformLocation(mProgram, "uScale"), scale);
273         glUniform1f(glGetUniformLocation(mProgram, "uOffset"), offset);
274     }
275 
276     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
277 
278     std::vector<uint8_t> zeroData(params.bufferSize);
279     memset(&zeroData[0], 0, zeroData.size());
280 
281     glGenBuffers(1, &mBuffer);
282     glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
283     glBufferData(GL_ARRAY_BUFFER, params.bufferSize, &zeroData[0], GL_DYNAMIC_DRAW);
284 
285     glVertexAttribPointer(0, params.vertexComponentCount, params.vertexType,
286                           params.vertexNormalized, 0, 0);
287     glEnableVertexAttribArray(0);
288 
289     if (params.updateSize > 0)
290     {
291         mUpdateData = new uint8_t[params.updateSize];
292     }
293 
294     std::vector<uint8_t> data;
295     GLsizei triDataSize = static_cast<GLsizei>(GetVertexData(
296         params.vertexType, params.vertexComponentCount, params.vertexNormalized, &data));
297 
298     mNumTris = static_cast<int>(params.updateSize / triDataSize);
299     for (int i = 0, offset = 0; i < mNumTris; ++i)
300     {
301         memcpy(mUpdateData + offset, &data[0], triDataSize);
302         offset += triDataSize;
303     }
304 
305     if (params.updateSize == 0)
306     {
307         mNumTris = 1;
308         glBufferSubData(GL_ARRAY_BUFFER, 0, data.size(), &data[0]);
309     }
310 
311     // Set the viewport
312     glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight());
313 
314     ASSERT_GL_NO_ERROR();
315 }
316 
destroyBenchmark()317 void BufferSubDataBenchmark::destroyBenchmark()
318 {
319     glDeleteProgram(mProgram);
320     glDeleteBuffers(1, &mBuffer);
321     SafeDeleteArray(mUpdateData);
322 }
323 
drawBenchmark()324 void BufferSubDataBenchmark::drawBenchmark()
325 {
326     glClear(GL_COLOR_BUFFER_BIT);
327 
328     const auto &params = GetParam();
329 
330     for (unsigned int it = 0; it < params.iterationsPerStep; it++)
331     {
332         if (params.updateSize > 0 && ((getNumStepsPerformed() % params.updateRate) == 0))
333         {
334             glBufferSubData(GL_ARRAY_BUFFER, 0, params.updateSize, mUpdateData);
335         }
336 
337         glDrawArrays(GL_TRIANGLES, 0, 3 * mNumTris);
338     }
339 
340     ASSERT_GL_NO_ERROR();
341 }
342 
BufferUpdateD3D11Params()343 BufferSubDataParams BufferUpdateD3D11Params()
344 {
345     BufferSubDataParams params;
346     params.eglParameters        = egl_platform::D3D11();
347     params.vertexType           = GL_FLOAT;
348     params.vertexComponentCount = 4;
349     params.vertexNormalized     = GL_FALSE;
350     return params;
351 }
352 
BufferUpdateOpenGLOrGLESParams()353 BufferSubDataParams BufferUpdateOpenGLOrGLESParams()
354 {
355     BufferSubDataParams params;
356     params.eglParameters        = egl_platform::OPENGL_OR_GLES();
357     params.vertexType           = GL_FLOAT;
358     params.vertexComponentCount = 4;
359     params.vertexNormalized     = GL_FALSE;
360     return params;
361 }
362 
BufferUpdateVulkanParams()363 BufferSubDataParams BufferUpdateVulkanParams()
364 {
365     BufferSubDataParams params;
366     params.eglParameters        = egl_platform::VULKAN();
367     params.vertexType           = GL_FLOAT;
368     params.vertexComponentCount = 4;
369     params.vertexNormalized     = GL_FALSE;
370     return params;
371 }
372 
TEST_P(BufferSubDataBenchmark,Run)373 TEST_P(BufferSubDataBenchmark, Run)
374 {
375     run();
376 }
377 
378 ANGLE_INSTANTIATE_TEST(BufferSubDataBenchmark,
379                        BufferUpdateD3D11Params(),
380                        BufferUpdateOpenGLOrGLESParams(),
381                        BufferUpdateVulkanParams());
382 
383 }  // namespace
384