1 //
2 // Copyright 2021 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 // MapBufferRangeBenchmark::
7 // Performance test for ANGLE GLES mapped buffers.
8 //
9
10 #include "ANGLEPerfTest.h"
11
12 #include <sstream>
13 #include <vector>
14
15 #include "common/debug.h"
16 #include "test_utils/draw_call_perf_utils.h"
17
18 using namespace angle;
19
20 namespace
21 {
22 constexpr unsigned int kIterationsPerStep = 10;
23
24 struct MapBufferRangeParams final : public RenderTestParams
25 {
MapBufferRangeParams__anon7a62b7090111::MapBufferRangeParams26 MapBufferRangeParams()
27 {
28 // Common default values
29 majorVersion = 3;
30 minorVersion = 0;
31 windowWidth = 512;
32 windowHeight = 512;
33 // Test intentionally small update versus buffer size to begin with.
34 updateSize = 32768;
35 updateOffset = 0;
36 bufferSize = 1048576;
37 iterationsPerStep = kIterationsPerStep;
38 access = GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT;
39 }
40
41 std::string story() const override;
42
43 GLboolean vertexNormalized;
44 GLenum vertexType;
45 GLint vertexComponentCount;
46
47 // static parameters
48 GLsizeiptr updateSize;
49 GLsizeiptr updateOffset;
50 GLsizeiptr bufferSize;
51 GLbitfield access;
52 };
53
operator <<(std::ostream & os,const MapBufferRangeParams & params)54 std::ostream &operator<<(std::ostream &os, const MapBufferRangeParams ¶ms)
55 {
56 os << params.backendAndStory().substr(1);
57 return os;
58 }
59
60 class MapBufferRangeBenchmark : public ANGLERenderTest,
61 public ::testing::WithParamInterface<MapBufferRangeParams>
62 {
63 public:
64 MapBufferRangeBenchmark();
65
66 void initializeBenchmark() override;
67 void destroyBenchmark() override;
68 void drawBenchmark() override;
69
70 private:
71 GLuint mProgram;
72 GLuint mBuffer;
73 std::vector<uint8_t> mVertexData;
74 int mTriSize;
75 int mNumUpdateTris;
76 };
77
GetFloatData(GLint componentCount)78 const GLfloat *GetFloatData(GLint componentCount)
79 {
80 static GLfloat vertices2[] = {
81 1, 2, 0, 0, 2, 0,
82 };
83
84 static GLfloat vertices3[] = {
85 1, 2, 1, 0, 0, 1, 2, 0, 1,
86 };
87
88 static GLfloat vertices4[] = {
89 1, 2, 1, 3, 0, 0, 1, 3, 2, 0, 1, 3,
90 };
91
92 switch (componentCount)
93 {
94 case 2:
95 return vertices2;
96 case 3:
97 return vertices3;
98 case 4:
99 return vertices4;
100 default:
101 UNREACHABLE();
102 }
103
104 return 0;
105 }
106
107 template <class T>
GetNormalizedData(GLsizeiptr numElements,const GLfloat * floatData,std::vector<uint8_t> * data)108 GLsizei GetNormalizedData(GLsizeiptr numElements,
109 const GLfloat *floatData,
110 std::vector<uint8_t> *data)
111 {
112 GLsizei triDataSize = sizeof(T) * numElements;
113 data->resize(triDataSize);
114
115 T *destPtr = reinterpret_cast<T *>(data->data());
116
117 for (GLsizeiptr dataIndex = 0; dataIndex < numElements; dataIndex++)
118 {
119 GLfloat scaled = floatData[dataIndex] * 0.25f;
120 destPtr[dataIndex] =
121 static_cast<T>(scaled * static_cast<GLfloat>(std::numeric_limits<T>::max()));
122 }
123
124 return triDataSize;
125 }
126
127 template <class T>
GetIntData(GLsizeiptr numElements,const GLfloat * floatData,std::vector<uint8_t> * data)128 GLsizei GetIntData(GLsizeiptr numElements, const GLfloat *floatData, std::vector<uint8_t> *data)
129 {
130 GLsizei triDataSize = sizeof(T) * numElements;
131 data->resize(triDataSize);
132
133 T *destPtr = reinterpret_cast<T *>(data->data());
134
135 for (GLsizeiptr dataIndex = 0; dataIndex < numElements; dataIndex++)
136 {
137 destPtr[dataIndex] = static_cast<T>(floatData[dataIndex]);
138 }
139
140 return triDataSize;
141 }
142
GetVertexData(GLenum type,GLint componentCount,GLboolean normalized,std::vector<uint8_t> * data)143 GLsizei GetVertexData(GLenum type,
144 GLint componentCount,
145 GLboolean normalized,
146 std::vector<uint8_t> *data)
147 {
148 GLsizei triDataSize = 0;
149 const GLfloat *floatData = GetFloatData(componentCount);
150
151 if (type == GL_FLOAT)
152 {
153 triDataSize = sizeof(GLfloat) * componentCount * 3;
154 data->resize(triDataSize);
155 memcpy(data->data(), floatData, triDataSize);
156 }
157 else if (normalized == GL_TRUE)
158 {
159 GLsizeiptr numElements = componentCount * 3;
160
161 switch (type)
162 {
163 case GL_BYTE:
164 triDataSize = GetNormalizedData<GLbyte>(numElements, floatData, data);
165 break;
166 case GL_SHORT:
167 triDataSize = GetNormalizedData<GLshort>(numElements, floatData, data);
168 break;
169 case GL_INT:
170 triDataSize = GetNormalizedData<GLint>(numElements, floatData, data);
171 break;
172 case GL_UNSIGNED_BYTE:
173 triDataSize = GetNormalizedData<GLubyte>(numElements, floatData, data);
174 break;
175 case GL_UNSIGNED_SHORT:
176 triDataSize = GetNormalizedData<GLushort>(numElements, floatData, data);
177 break;
178 case GL_UNSIGNED_INT:
179 triDataSize = GetNormalizedData<GLuint>(numElements, floatData, data);
180 break;
181 default:
182 UNREACHABLE();
183 }
184 }
185 else
186 {
187 GLsizeiptr numElements = componentCount * 3;
188
189 switch (type)
190 {
191 case GL_BYTE:
192 triDataSize = GetIntData<GLbyte>(numElements, floatData, data);
193 break;
194 case GL_SHORT:
195 triDataSize = GetIntData<GLshort>(numElements, floatData, data);
196 break;
197 case GL_INT:
198 triDataSize = GetIntData<GLint>(numElements, floatData, data);
199 break;
200 case GL_UNSIGNED_BYTE:
201 triDataSize = GetIntData<GLubyte>(numElements, floatData, data);
202 break;
203 case GL_UNSIGNED_SHORT:
204 triDataSize = GetIntData<GLushort>(numElements, floatData, data);
205 break;
206 case GL_UNSIGNED_INT:
207 triDataSize = GetIntData<GLuint>(numElements, floatData, data);
208 break;
209 default:
210 assert(0);
211 }
212 }
213
214 return triDataSize;
215 }
216
story() const217 std::string MapBufferRangeParams::story() const
218 {
219 std::stringstream strstr;
220
221 strstr << RenderTestParams::story();
222
223 if (vertexNormalized)
224 {
225 strstr << "_norm";
226 }
227
228 switch (vertexType)
229 {
230 case GL_FLOAT:
231 strstr << "_float";
232 break;
233 case GL_INT:
234 strstr << "_int";
235 break;
236 case GL_BYTE:
237 strstr << "_byte";
238 break;
239 case GL_SHORT:
240 strstr << "_short";
241 break;
242 case GL_UNSIGNED_INT:
243 strstr << "_uint";
244 break;
245 case GL_UNSIGNED_BYTE:
246 strstr << "_ubyte";
247 break;
248 case GL_UNSIGNED_SHORT:
249 strstr << "_ushort";
250 break;
251 default:
252 UNREACHABLE();
253 }
254
255 strstr << vertexComponentCount;
256 strstr << "_updateOffset" << updateOffset;
257 strstr << "_updateSize" << updateSize;
258 strstr << "_bufferSize" << bufferSize;
259 strstr << "_access0x" << std::hex << access;
260
261 return strstr.str();
262 }
263
MapBufferRangeBenchmark()264 MapBufferRangeBenchmark::MapBufferRangeBenchmark()
265 : ANGLERenderTest("MapBufferRange", GetParam()),
266 mProgram(0),
267 mBuffer(0),
268 mTriSize(0),
269 mNumUpdateTris(0)
270 {}
271
initializeBenchmark()272 void MapBufferRangeBenchmark::initializeBenchmark()
273 {
274 const auto ¶ms = GetParam();
275
276 ASSERT_LT(1, params.vertexComponentCount);
277 ASSERT_LE(params.updateSize, params.bufferSize);
278 ASSERT_LT(params.updateOffset, params.bufferSize);
279 ASSERT_LE(params.updateOffset + params.updateSize, params.bufferSize);
280
281 mProgram = SetupSimpleScaleAndOffsetProgram();
282 ASSERT_NE(0u, mProgram);
283
284 if (params.vertexNormalized == GL_TRUE)
285 {
286 GLfloat scale = 2.0f;
287 GLfloat offset = -0.5f;
288 glUniform1f(glGetUniformLocation(mProgram, "uScale"), scale);
289 glUniform1f(glGetUniformLocation(mProgram, "uOffset"), offset);
290 }
291
292 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
293
294 glGenBuffers(1, &mBuffer);
295 glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
296 glBufferData(GL_ARRAY_BUFFER, params.bufferSize, nullptr, GL_DYNAMIC_DRAW);
297
298 glVertexAttribPointer(0, params.vertexComponentCount, params.vertexType,
299 params.vertexNormalized, 0, 0);
300 glEnableVertexAttribArray(0);
301
302 mTriSize = GetVertexData(params.vertexType, params.vertexComponentCount,
303 params.vertexNormalized, &mVertexData);
304
305 mNumUpdateTris = static_cast<int>(params.updateSize / mTriSize);
306 int totalTris = static_cast<int>(params.updateSize / mTriSize);
307
308 mVertexData.resize(params.bufferSize);
309
310 for (int i = 1; i < totalTris; ++i)
311 {
312 memcpy(mVertexData.data() + i * mTriSize, mVertexData.data(), mTriSize);
313 }
314
315 if (params.updateSize == 0)
316 {
317 mNumUpdateTris = 1;
318 glBufferSubData(GL_ARRAY_BUFFER, 0, mVertexData.size(), mVertexData.data());
319 }
320
321 // Set the viewport
322 glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight());
323
324 ASSERT_GL_NO_ERROR();
325 }
326
destroyBenchmark()327 void MapBufferRangeBenchmark::destroyBenchmark()
328 {
329 glDeleteProgram(mProgram);
330 glDeleteBuffers(1, &mBuffer);
331 }
332
drawBenchmark()333 void MapBufferRangeBenchmark::drawBenchmark()
334 {
335 glClear(GL_COLOR_BUFFER_BIT);
336
337 const auto ¶ms = GetParam();
338
339 for (unsigned int it = 0; it < params.iterationsPerStep; it++)
340 {
341 if (params.updateSize > 0)
342 {
343 void *mapPtr = glMapBufferRange(GL_ARRAY_BUFFER, params.updateOffset, params.updateSize,
344 params.access);
345 memcpy(mapPtr, mVertexData.data() + params.updateOffset, params.updateSize);
346 glUnmapBuffer(GL_ARRAY_BUFFER);
347 }
348
349 glDrawArrays(GL_TRIANGLES, params.updateOffset / mTriSize, 3 * mNumUpdateTris);
350 }
351
352 ASSERT_GL_NO_ERROR();
353 }
354
BufferUpdateD3D11Params()355 MapBufferRangeParams BufferUpdateD3D11Params()
356 {
357 MapBufferRangeParams params;
358 params.eglParameters = egl_platform::D3D11();
359 params.vertexType = GL_FLOAT;
360 params.vertexComponentCount = 4;
361 params.vertexNormalized = GL_FALSE;
362 return params;
363 }
364
BufferUpdateOpenGLOrGLESParams()365 MapBufferRangeParams BufferUpdateOpenGLOrGLESParams()
366 {
367 MapBufferRangeParams params;
368 params.eglParameters = egl_platform::OPENGL_OR_GLES();
369 params.vertexType = GL_FLOAT;
370 params.vertexComponentCount = 4;
371 params.vertexNormalized = GL_FALSE;
372 return params;
373 }
374
BufferUpdateVulkanParams()375 MapBufferRangeParams BufferUpdateVulkanParams()
376 {
377 MapBufferRangeParams params;
378 params.eglParameters = egl_platform::VULKAN();
379 params.vertexType = GL_FLOAT;
380 params.vertexComponentCount = 4;
381 params.vertexNormalized = GL_FALSE;
382 return params;
383 }
384
BufferUpdateVulkanParamsMidBuffer()385 MapBufferRangeParams BufferUpdateVulkanParamsMidBuffer()
386 {
387 MapBufferRangeParams params;
388 params.eglParameters = egl_platform::VULKAN();
389 params.vertexType = GL_FLOAT;
390 params.vertexComponentCount = 4;
391 params.vertexNormalized = GL_FALSE;
392 params.updateOffset = 524288;
393 return params;
394 }
395
BufferUpdateVulkanParamsLargeUpdate()396 MapBufferRangeParams BufferUpdateVulkanParamsLargeUpdate()
397 {
398 MapBufferRangeParams params;
399 params.eglParameters = egl_platform::VULKAN();
400 params.vertexType = GL_FLOAT;
401 params.vertexComponentCount = 4;
402 params.vertexNormalized = GL_FALSE;
403 params.updateSize = 524288;
404 return params;
405 }
406
BufferUpdateVulkanParamsFullBuffer()407 MapBufferRangeParams BufferUpdateVulkanParamsFullBuffer()
408 {
409 MapBufferRangeParams params;
410 params.eglParameters = egl_platform::VULKAN();
411 params.vertexType = GL_FLOAT;
412 params.vertexComponentCount = 4;
413 params.vertexNormalized = GL_FALSE;
414 params.updateSize = 1048576;
415 return params;
416 }
417
BufferUpdateVulkanParamsTinyUpdate()418 MapBufferRangeParams BufferUpdateVulkanParamsTinyUpdate()
419 {
420 MapBufferRangeParams params;
421 params.eglParameters = egl_platform::VULKAN();
422 params.vertexType = GL_FLOAT;
423 params.vertexComponentCount = 4;
424 params.vertexNormalized = GL_FALSE;
425 params.updateSize = 128;
426 return params;
427 }
428
BufferUpdateVulkanParamsNonPowerOf2()429 MapBufferRangeParams BufferUpdateVulkanParamsNonPowerOf2()
430 {
431 MapBufferRangeParams params;
432 params.eglParameters = egl_platform::VULKAN();
433 params.vertexType = GL_FLOAT;
434 params.vertexComponentCount = 4;
435 params.vertexNormalized = GL_FALSE;
436 params.updateSize = 32000;
437 params.bufferSize = 800000;
438 return params;
439 }
440
BufferUpdateVulkanParamsUnsynchronized()441 MapBufferRangeParams BufferUpdateVulkanParamsUnsynchronized()
442 {
443 MapBufferRangeParams params;
444 params.eglParameters = egl_platform::VULKAN();
445 params.vertexType = GL_FLOAT;
446 params.vertexComponentCount = 4;
447 params.vertexNormalized = GL_FALSE;
448 params.access = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT;
449 return params;
450 }
451
BufferUpdateVulkanParamsLargeUpdateUnsynchronized()452 MapBufferRangeParams BufferUpdateVulkanParamsLargeUpdateUnsynchronized()
453 {
454 MapBufferRangeParams params;
455 params.eglParameters = egl_platform::VULKAN();
456 params.vertexType = GL_FLOAT;
457 params.vertexComponentCount = 4;
458 params.vertexNormalized = GL_FALSE;
459 params.updateSize = 524288;
460 params.access = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT;
461 return params;
462 }
463
TEST_P(MapBufferRangeBenchmark,Run)464 TEST_P(MapBufferRangeBenchmark, Run)
465 {
466 run();
467 }
468
469 ANGLE_INSTANTIATE_TEST(MapBufferRangeBenchmark,
470 BufferUpdateD3D11Params(),
471 BufferUpdateOpenGLOrGLESParams(),
472 BufferUpdateVulkanParams(),
473 BufferUpdateVulkanParamsMidBuffer(),
474 BufferUpdateVulkanParamsLargeUpdate(),
475 BufferUpdateVulkanParamsFullBuffer(),
476 BufferUpdateVulkanParamsTinyUpdate(),
477 BufferUpdateVulkanParamsNonPowerOf2(),
478 BufferUpdateVulkanParamsUnsynchronized(),
479 BufferUpdateVulkanParamsLargeUpdateUnsynchronized());
480
481 } // namespace
482