Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/angle
Path: blob/main_old/src/tests/perf_tests/DrawCallPerf.cpp
1693 views
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
// DrawCallPerf:
7
// Performance tests for ANGLE draw call overhead.
8
//
9
10
#include "ANGLEPerfTest.h"
11
#include "DrawCallPerfParams.h"
12
#include "common/PackedEnums.h"
13
#include "test_utils/draw_call_perf_utils.h"
14
#include "util/shader_utils.h"
15
16
namespace
17
{
18
enum class StateChange
19
{
20
NoChange,
21
VertexAttrib,
22
VertexBuffer,
23
ManyVertexBuffers,
24
Texture,
25
Program,
26
VertexBufferCycle,
27
Scissor,
28
InvalidEnum,
29
EnumCount = InvalidEnum,
30
};
31
32
constexpr size_t kCycleVBOPoolSize = 200;
33
34
struct DrawArraysPerfParams : public DrawCallPerfParams
35
{
36
DrawArraysPerfParams() = default;
37
DrawArraysPerfParams(const DrawCallPerfParams &base) : DrawCallPerfParams(base) {}
38
39
std::string story() const override;
40
41
StateChange stateChange = StateChange::NoChange;
42
};
43
44
std::string DrawArraysPerfParams::story() const
45
{
46
std::stringstream strstr;
47
48
strstr << DrawCallPerfParams::story();
49
50
switch (stateChange)
51
{
52
case StateChange::VertexAttrib:
53
strstr << "_attrib_change";
54
break;
55
case StateChange::VertexBuffer:
56
strstr << "_vbo_change";
57
break;
58
case StateChange::ManyVertexBuffers:
59
strstr << "_manyvbos_change";
60
break;
61
case StateChange::Texture:
62
strstr << "_tex_change";
63
break;
64
case StateChange::Program:
65
strstr << "_prog_change";
66
break;
67
case StateChange::VertexBufferCycle:
68
strstr << "_vbo_cycle";
69
break;
70
case StateChange::Scissor:
71
strstr << "_scissor_change";
72
break;
73
default:
74
break;
75
}
76
77
return strstr.str();
78
}
79
80
std::ostream &operator<<(std::ostream &os, const DrawArraysPerfParams &params)
81
{
82
os << params.backendAndStory().substr(1);
83
return os;
84
}
85
86
GLuint CreateSimpleTexture2D()
87
{
88
// Use tightly packed data
89
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
90
91
// Generate a texture object
92
GLuint texture;
93
glGenTextures(1, &texture);
94
95
// Bind the texture object
96
glBindTexture(GL_TEXTURE_2D, texture);
97
98
// Load the texture: 2x2 Image, 3 bytes per pixel (R, G, B)
99
constexpr size_t width = 2;
100
constexpr size_t height = 2;
101
GLubyte pixels[width * height * 3] = {
102
255, 0, 0, // Red
103
0, 255, 0, // Green
104
0, 0, 255, // Blue
105
255, 255, 0, // Yellow
106
};
107
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
108
109
// Set the filtering mode
110
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
111
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
112
113
return texture;
114
}
115
116
class DrawCallPerfBenchmark : public ANGLERenderTest,
117
public ::testing::WithParamInterface<DrawArraysPerfParams>
118
{
119
public:
120
DrawCallPerfBenchmark();
121
122
void initializeBenchmark() override;
123
void destroyBenchmark() override;
124
void drawBenchmark() override;
125
126
private:
127
GLuint mProgram1 = 0;
128
GLuint mProgram2 = 0;
129
GLuint mBuffer1 = 0;
130
GLuint mBuffer2 = 0;
131
GLuint mFBO = 0;
132
GLuint mFBOTexture = 0;
133
GLuint mTexture1 = 0;
134
GLuint mTexture2 = 0;
135
int mNumTris = GetParam().numTris;
136
std::vector<GLuint> mVBOPool;
137
size_t mCurrentVBO = 0;
138
};
139
140
DrawCallPerfBenchmark::DrawCallPerfBenchmark() : ANGLERenderTest("DrawCallPerf", GetParam()) {}
141
142
void DrawCallPerfBenchmark::initializeBenchmark()
143
{
144
const auto &params = GetParam();
145
146
if (params.stateChange == StateChange::Texture)
147
{
148
mProgram1 = SetupSimpleTextureProgram();
149
}
150
if (params.stateChange == StateChange::Program)
151
{
152
mProgram1 = SetupSimpleTextureProgram();
153
mProgram2 = SetupDoubleTextureProgram();
154
155
ASSERT_NE(0u, mProgram2);
156
}
157
else if (params.stateChange == StateChange::ManyVertexBuffers)
158
{
159
constexpr char kVS[] = R"(attribute vec2 vPosition;
160
attribute vec2 v0;
161
attribute vec2 v1;
162
attribute vec2 v2;
163
attribute vec2 v3;
164
const float scale = 0.5;
165
const float offset = -0.5;
166
167
varying vec2 v;
168
169
void main()
170
{
171
gl_Position = vec4(vPosition * vec2(scale) + vec2(offset), 0, 1);
172
v = (v0 + v1 + v2 + v3) * 0.25;
173
})";
174
175
constexpr char kFS[] = R"(precision mediump float;
176
varying vec2 v;
177
void main()
178
{
179
gl_FragColor = vec4(v, 0, 1);
180
})";
181
182
mProgram1 = CompileProgram(kVS, kFS);
183
glBindAttribLocation(mProgram1, 1, "v0");
184
glBindAttribLocation(mProgram1, 2, "v1");
185
glBindAttribLocation(mProgram1, 3, "v2");
186
glBindAttribLocation(mProgram1, 4, "v3");
187
glEnableVertexAttribArray(1);
188
glEnableVertexAttribArray(2);
189
glEnableVertexAttribArray(3);
190
glEnableVertexAttribArray(4);
191
}
192
else if (params.stateChange == StateChange::VertexBufferCycle)
193
{
194
mProgram1 = SetupSimpleDrawProgram();
195
196
for (size_t bufferIndex = 0; bufferIndex < kCycleVBOPoolSize; ++bufferIndex)
197
{
198
GLuint buffer = Create2DTriangleBuffer(mNumTris, GL_STATIC_DRAW);
199
mVBOPool.push_back(buffer);
200
}
201
}
202
else
203
{
204
mProgram1 = SetupSimpleDrawProgram();
205
}
206
207
ASSERT_NE(0u, mProgram1);
208
209
// Re-link program to ensure the attrib bindings are used.
210
glBindAttribLocation(mProgram1, 0, "vPosition");
211
glLinkProgram(mProgram1);
212
glUseProgram(mProgram1);
213
214
if (mProgram2)
215
{
216
glBindAttribLocation(mProgram2, 0, "vPosition");
217
glLinkProgram(mProgram2);
218
}
219
220
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
221
222
mBuffer1 = Create2DTriangleBuffer(mNumTris, GL_STATIC_DRAW);
223
mBuffer2 = Create2DTriangleBuffer(mNumTris, GL_STATIC_DRAW);
224
225
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
226
glEnableVertexAttribArray(0);
227
228
// Set the viewport
229
glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight());
230
231
if (params.surfaceType == SurfaceType::Offscreen)
232
{
233
CreateColorFBO(getWindow()->getWidth(), getWindow()->getHeight(), &mFBOTexture, &mFBO);
234
}
235
236
mTexture1 = CreateSimpleTexture2D();
237
mTexture2 = CreateSimpleTexture2D();
238
239
if (params.stateChange == StateChange::Program)
240
{
241
// Bind the textures as appropriate, they are not modified during the test.
242
GLint program1Tex1Loc = glGetUniformLocation(mProgram1, "tex");
243
GLint program2Tex1Loc = glGetUniformLocation(mProgram2, "tex1");
244
GLint program2Tex2Loc = glGetUniformLocation(mProgram2, "tex2");
245
246
glUseProgram(mProgram1);
247
glUniform1i(program1Tex1Loc, 0);
248
249
glUseProgram(mProgram2);
250
glUniform1i(program2Tex1Loc, 0);
251
glUniform1i(program2Tex2Loc, 1);
252
253
glActiveTexture(GL_TEXTURE0);
254
glBindTexture(GL_TEXTURE_2D, mTexture1);
255
256
glActiveTexture(GL_TEXTURE1);
257
glBindTexture(GL_TEXTURE_2D, mTexture2);
258
}
259
260
ASSERT_GL_NO_ERROR();
261
}
262
263
void DrawCallPerfBenchmark::destroyBenchmark()
264
{
265
glDeleteProgram(mProgram1);
266
glDeleteProgram(mProgram2);
267
glDeleteBuffers(1, &mBuffer1);
268
glDeleteBuffers(1, &mBuffer2);
269
glDeleteTextures(1, &mFBOTexture);
270
glDeleteTextures(1, &mTexture1);
271
glDeleteTextures(1, &mTexture2);
272
glDeleteFramebuffers(1, &mFBO);
273
274
if (!mVBOPool.empty())
275
{
276
glDeleteBuffers(mVBOPool.size(), mVBOPool.data());
277
}
278
}
279
280
void ClearThenDraw(unsigned int iterations, GLsizei numElements)
281
{
282
glClear(GL_COLOR_BUFFER_BIT);
283
284
for (unsigned int it = 0; it < iterations; it++)
285
{
286
glDrawArrays(GL_TRIANGLES, 0, numElements);
287
}
288
}
289
290
void JustDraw(unsigned int iterations, GLsizei numElements)
291
{
292
for (unsigned int it = 0; it < iterations; it++)
293
{
294
glDrawArrays(GL_TRIANGLES, 0, numElements);
295
}
296
}
297
298
template <int kArrayBufferCount>
299
void ChangeVertexAttribThenDraw(unsigned int iterations, GLsizei numElements, GLuint buffer)
300
{
301
glBindBuffer(GL_ARRAY_BUFFER, buffer);
302
for (unsigned int it = 0; it < iterations; it++)
303
{
304
for (int arrayIndex = 0; arrayIndex < kArrayBufferCount; ++arrayIndex)
305
{
306
glVertexAttribPointer(arrayIndex, 2, GL_FLOAT, GL_FALSE, 0, 0);
307
}
308
glDrawArrays(GL_TRIANGLES, 0, numElements);
309
310
for (int arrayIndex = 0; arrayIndex < kArrayBufferCount; ++arrayIndex)
311
{
312
glVertexAttribPointer(arrayIndex, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0);
313
}
314
glDrawArrays(GL_TRIANGLES, 0, numElements);
315
}
316
}
317
template <int kArrayBufferCount>
318
void ChangeArrayBuffersThenDraw(unsigned int iterations,
319
GLsizei numElements,
320
GLuint buffer1,
321
GLuint buffer2)
322
{
323
for (unsigned int it = 0; it < iterations; it++)
324
{
325
glBindBuffer(GL_ARRAY_BUFFER, buffer1);
326
for (int arrayIndex = 0; arrayIndex < kArrayBufferCount; ++arrayIndex)
327
{
328
glVertexAttribPointer(arrayIndex, 2, GL_FLOAT, GL_FALSE, 0, 0);
329
}
330
glDrawArrays(GL_TRIANGLES, 0, numElements);
331
332
glBindBuffer(GL_ARRAY_BUFFER, buffer2);
333
for (int arrayIndex = 0; arrayIndex < kArrayBufferCount; ++arrayIndex)
334
{
335
glVertexAttribPointer(arrayIndex, 2, GL_FLOAT, GL_FALSE, 0, 0);
336
}
337
glDrawArrays(GL_TRIANGLES, 0, numElements);
338
}
339
}
340
341
void ChangeTextureThenDraw(unsigned int iterations,
342
GLsizei numElements,
343
GLuint texture1,
344
GLuint texture2)
345
{
346
for (unsigned int it = 0; it < iterations; it++)
347
{
348
glBindTexture(GL_TEXTURE_2D, texture1);
349
glDrawArrays(GL_TRIANGLES, 0, numElements);
350
351
glBindTexture(GL_TEXTURE_2D, texture2);
352
glDrawArrays(GL_TRIANGLES, 0, numElements);
353
}
354
}
355
356
void ChangeProgramThenDraw(unsigned int iterations,
357
GLsizei numElements,
358
GLuint program1,
359
GLuint program2)
360
{
361
for (unsigned int it = 0; it < iterations; it++)
362
{
363
glUseProgram(program1);
364
glDrawArrays(GL_TRIANGLES, 0, numElements);
365
366
glUseProgram(program2);
367
glDrawArrays(GL_TRIANGLES, 0, numElements);
368
}
369
}
370
371
void CycleVertexBufferThenDraw(unsigned int iterations,
372
GLsizei numElements,
373
const std::vector<GLuint> &vbos,
374
size_t *currentVBO)
375
{
376
for (unsigned int it = 0; it < iterations; it++)
377
{
378
GLuint vbo = vbos[*currentVBO];
379
glBindBuffer(GL_ARRAY_BUFFER, vbo);
380
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
381
glDrawArrays(GL_TRIANGLES, 0, numElements);
382
*currentVBO = (*currentVBO + 1) % vbos.size();
383
}
384
}
385
386
void ChangeScissorThenDraw(unsigned int iterations,
387
GLsizei numElements,
388
unsigned int windowWidth,
389
unsigned int windowHeight)
390
{
391
// Change scissor as such:
392
//
393
// - Start with a narrow vertical bar:
394
//
395
// Scissor
396
// |
397
// V
398
// +-----+-+-----+
399
// | | | | <-- Window
400
// | | | |
401
// | | | |
402
// | | | |
403
// | | | |
404
// | | | |
405
// +-----+-+-----+
406
//
407
// - Gradually reduce height and increase width, to end up with a narrow horizontal bar:
408
//
409
// +-------------+
410
// | |
411
// | |
412
// +-------------+ <-- Scissor
413
// +-------------+
414
// | |
415
// | |
416
// +-------------+
417
//
418
// - If more iterations left, restart, but shift the initial bar left to cover more area:
419
//
420
// +---+-+-------+ +-------------+
421
// | | | | | |
422
// | | | | +-------------+
423
// | | | | ---> | |
424
// | | | | | |
425
// | | | | +-------------+
426
// | | | | | |
427
// +---+-+-------+ +-------------+
428
//
429
// +-+-+---------+ +-------------+
430
// | | | | +-------------+
431
// | | | | | |
432
// | | | | ---> | |
433
// | | | | | |
434
// | | | | | |
435
// | | | | +-------------+
436
// +-+-+---------+ +-------------+
437
438
glEnable(GL_SCISSOR_TEST);
439
440
constexpr unsigned int kScissorStep = 2;
441
unsigned int scissorX = windowWidth / 2 - 1;
442
unsigned int scissorY = 0;
443
unsigned int scissorWidth = 2;
444
unsigned int scissorHeight = windowHeight;
445
unsigned int scissorPatternIteration = 0;
446
447
for (unsigned int it = 0; it < iterations; it++)
448
{
449
glScissor(scissorX, scissorY, scissorWidth, scissorHeight);
450
glDrawArrays(GL_TRIANGLES, 0, numElements);
451
452
if (scissorX < kScissorStep || scissorHeight < kScissorStep * 2)
453
{
454
++scissorPatternIteration;
455
scissorX = windowWidth / 2 - 1 - scissorPatternIteration * 2;
456
scissorY = 0;
457
scissorWidth = 2;
458
scissorHeight = windowHeight;
459
}
460
else
461
{
462
scissorX -= kScissorStep;
463
scissorY += kScissorStep;
464
scissorWidth += kScissorStep * 2;
465
scissorHeight -= kScissorStep * 2;
466
}
467
}
468
}
469
470
void DrawCallPerfBenchmark::drawBenchmark()
471
{
472
// This workaround fixes a huge queue of graphics commands accumulating on the GL
473
// back-end. The GL back-end doesn't have a proper NULL device at the moment.
474
// TODO(jmadill): Remove this when/if we ever get a proper OpenGL NULL device.
475
const auto &eglParams = GetParam().eglParameters;
476
const auto &params = GetParam();
477
GLsizei numElements = static_cast<GLsizei>(3 * mNumTris);
478
479
switch (params.stateChange)
480
{
481
case StateChange::VertexAttrib:
482
ChangeVertexAttribThenDraw<1>(params.iterationsPerStep, numElements, mBuffer1);
483
break;
484
case StateChange::VertexBuffer:
485
ChangeArrayBuffersThenDraw<1>(params.iterationsPerStep, numElements, mBuffer1,
486
mBuffer2);
487
break;
488
case StateChange::ManyVertexBuffers:
489
ChangeArrayBuffersThenDraw<5>(params.iterationsPerStep, numElements, mBuffer1,
490
mBuffer2);
491
break;
492
case StateChange::Texture:
493
ChangeTextureThenDraw(params.iterationsPerStep, numElements, mTexture1, mTexture2);
494
break;
495
case StateChange::Program:
496
ChangeProgramThenDraw(params.iterationsPerStep, numElements, mProgram1, mProgram2);
497
break;
498
case StateChange::NoChange:
499
if (eglParams.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE ||
500
(eglParams.renderer != EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE &&
501
eglParams.renderer != EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE))
502
{
503
ClearThenDraw(params.iterationsPerStep, numElements);
504
}
505
else
506
{
507
JustDraw(params.iterationsPerStep, numElements);
508
}
509
break;
510
case StateChange::VertexBufferCycle:
511
CycleVertexBufferThenDraw(params.iterationsPerStep, numElements, mVBOPool,
512
&mCurrentVBO);
513
break;
514
case StateChange::Scissor:
515
ChangeScissorThenDraw(params.iterationsPerStep, numElements, getWindow()->getWidth(),
516
getWindow()->getHeight());
517
break;
518
case StateChange::InvalidEnum:
519
ADD_FAILURE() << "Invalid state change.";
520
break;
521
}
522
523
ASSERT_GL_NO_ERROR();
524
}
525
526
TEST_P(DrawCallPerfBenchmark, Run)
527
{
528
run();
529
}
530
531
using namespace params;
532
533
DrawArraysPerfParams CombineStateChange(const DrawArraysPerfParams &in, StateChange stateChange)
534
{
535
DrawArraysPerfParams out = in;
536
out.stateChange = stateChange;
537
538
// Crank up iteration count to ensure we cycle through all VBs before a swap.
539
if (stateChange == StateChange::VertexBufferCycle)
540
{
541
out.iterationsPerStep = kCycleVBOPoolSize * 2;
542
}
543
544
return out;
545
}
546
547
using P = DrawArraysPerfParams;
548
549
std::vector<P> gTestsWithStateChange =
550
CombineWithValues({P()}, angle::AllEnums<StateChange>(), CombineStateChange);
551
std::vector<P> gTestsWithRenderer =
552
CombineWithFuncs(gTestsWithStateChange, {D3D11<P>, GL<P>, Vulkan<P>, WGL<P>});
553
std::vector<P> gTestsWithDevice =
554
CombineWithFuncs(gTestsWithRenderer, {Passthrough<P>, Offscreen<P>, NullDevice<P>});
555
556
ANGLE_INSTANTIATE_TEST_ARRAY(DrawCallPerfBenchmark, gTestsWithDevice);
557
558
} // anonymous namespace
559
560