Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/angle
Path: blob/main_old/src/tests/gl_tests/AttributeLayoutTest.cpp
1693 views
1
//
2
// Copyright 2018 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
// AttributeLayoutTest:
7
// Test various layouts of vertex attribute data:
8
// - in memory, in buffer object, or combination of both
9
// - sequential or interleaved
10
// - various combinations of data types
11
12
#include <vector>
13
14
#include "test_utils/ANGLETest.h"
15
#include "test_utils/gl_raii.h"
16
17
using namespace angle;
18
19
namespace
20
{
21
22
// Test will draw these four triangles.
23
// clang-format off
24
constexpr double kTriangleData[] = {
25
// xy rgb
26
0,0, 1,1,0,
27
-1,+1, 1,1,0,
28
+1,+1, 1,1,0,
29
30
0,0, 0,1,0,
31
+1,+1, 0,1,0,
32
+1,-1, 0,1,0,
33
34
0,0, 0,1,1,
35
+1,-1, 0,1,1,
36
-1,-1, 0,1,1,
37
38
0,0, 1,0,1,
39
-1,-1, 1,0,1,
40
-1,+1, 1,0,1,
41
};
42
// clang-format on
43
44
constexpr size_t kNumVertices = ArraySize(kTriangleData) / 5;
45
46
// Vertex data source description.
47
class VertexData
48
{
49
public:
50
VertexData(int dimension, const double *data, unsigned offset, unsigned stride)
51
: mDimension(dimension), mData(data), mOffset(offset), mStride(stride)
52
{}
53
int getDimension() const { return mDimension; }
54
double getValue(unsigned vertexNumber, int component) const
55
{
56
return mData[mOffset + mStride * vertexNumber + component];
57
}
58
59
private:
60
int mDimension;
61
const double *mData;
62
// offset and stride in doubles
63
unsigned mOffset;
64
unsigned mStride;
65
};
66
67
// A container for one or more vertex attributes.
68
class Container
69
{
70
public:
71
static constexpr size_t kSize = 1024;
72
73
void open(void) { memset(mMemory, 0xff, kSize); }
74
void *getDestination(size_t offset) { return mMemory + offset; }
75
virtual void close(void) {}
76
virtual ~Container() {}
77
virtual const char *getAddress() = 0;
78
virtual GLuint getBuffer() = 0;
79
80
protected:
81
char mMemory[kSize];
82
};
83
84
// Vertex attribute data in client memory.
85
class Memory : public Container
86
{
87
public:
88
const char *getAddress() override { return mMemory; }
89
GLuint getBuffer() override { return 0; }
90
};
91
92
// Vertex attribute data in buffer object.
93
class Buffer : public Container
94
{
95
public:
96
void close(void) override
97
{
98
glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
99
glBufferData(GL_ARRAY_BUFFER, sizeof(mMemory), mMemory, GL_STATIC_DRAW);
100
}
101
102
const char *getAddress() override { return nullptr; }
103
GLuint getBuffer() override { return mBuffer; }
104
105
protected:
106
GLBuffer mBuffer;
107
};
108
109
// Encapsulate the storage, layout, format and data of a vertex attribute.
110
struct Attrib
111
{
112
void openContainer(void) const { mContainer->open(); }
113
114
void fillContainer(void) const
115
{
116
for (unsigned i = 0; i < kNumVertices; ++i)
117
{
118
for (int j = 0; j < mData.getDimension(); ++j)
119
{
120
size_t destOffset = mOffset + mStride * i + mCTypeSize * j;
121
if (destOffset + mCTypeSize > Container::kSize)
122
FAIL() << "test case does not fit container";
123
124
double value = mData.getValue(i, j);
125
if (mGLType == GL_FIXED)
126
value *= 1 << 16;
127
else if (mNormalized)
128
{
129
if (value < mMinIn || value > mMaxIn)
130
FAIL() << "test data does not fit format";
131
value = (value - mMinIn) * mScale + mMinOut;
132
}
133
134
mStore(value, mContainer->getDestination(destOffset));
135
}
136
}
137
}
138
139
void closeContainer(void) const { mContainer->close(); }
140
141
void enable(unsigned index) const
142
{
143
glBindBuffer(GL_ARRAY_BUFFER, mContainer->getBuffer());
144
glVertexAttribPointer(index, mData.getDimension(), mGLType, mNormalized, mStride,
145
mContainer->getAddress() + mOffset);
146
EXPECT_GL_NO_ERROR();
147
glEnableVertexAttribArray(index);
148
}
149
150
bool inClientMemory(void) const { return mContainer->getAddress() != nullptr; }
151
152
std::shared_ptr<Container> mContainer;
153
unsigned mOffset;
154
unsigned mStride;
155
const VertexData &mData;
156
void (*mStore)(double value, void *dest);
157
GLenum mGLType;
158
GLboolean mNormalized;
159
size_t mCTypeSize;
160
double mMinIn;
161
double mMaxIn;
162
double mMinOut;
163
double mScale;
164
};
165
166
// Change type and store.
167
template <class T>
168
void Store(double value, void *dest)
169
{
170
T v = static_cast<T>(value);
171
memcpy(dest, &v, sizeof(v));
172
}
173
174
// Function object that makes Attrib structs according to a vertex format.
175
template <class CType, GLenum GLType, bool Normalized>
176
class Format
177
{
178
static_assert(!(Normalized && GLType == GL_FLOAT), "Normalized float does not make sense.");
179
180
public:
181
Format(bool es3) : mES3(es3) {}
182
183
Attrib operator()(std::shared_ptr<Container> container,
184
unsigned offset,
185
unsigned stride,
186
const VertexData &data) const
187
{
188
double minIn = 0;
189
double maxIn = 1;
190
double minOut = std::numeric_limits<CType>::min();
191
double rangeOut = std::numeric_limits<CType>::max() - minOut;
192
193
if (std::is_signed<CType>::value)
194
{
195
minIn = -1;
196
maxIn = +1;
197
if (mES3)
198
{
199
minOut += 1;
200
rangeOut -= 1;
201
}
202
}
203
204
return {
205
container, offset, stride, data, Store<CType>, GLType,
206
Normalized, sizeof(CType), minIn, maxIn, minOut, rangeOut / (maxIn - minIn),
207
};
208
}
209
210
protected:
211
const bool mES3;
212
};
213
214
typedef std::vector<Attrib> TestCase;
215
216
void PrepareTestCase(const TestCase &tc)
217
{
218
for (const Attrib &a : tc)
219
a.openContainer();
220
for (const Attrib &a : tc)
221
a.fillContainer();
222
for (const Attrib &a : tc)
223
a.closeContainer();
224
unsigned i = 0;
225
for (const Attrib &a : tc)
226
a.enable(i++);
227
}
228
229
class AttributeLayoutTest : public ANGLETest
230
{
231
protected:
232
AttributeLayoutTest()
233
: mProgram(0), mCoord(2, kTriangleData, 0, 5), mColor(3, kTriangleData, 2, 5)
234
{
235
setWindowWidth(128);
236
setWindowHeight(128);
237
setConfigRedBits(8);
238
setConfigGreenBits(8);
239
setConfigBlueBits(8);
240
setConfigAlphaBits(8);
241
}
242
243
void GetTestCases(void);
244
245
void testSetUp() override
246
{
247
glClearColor(.2f, .2f, .2f, .0f);
248
glClear(GL_COLOR_BUFFER_BIT);
249
250
glDisable(GL_DEPTH_TEST);
251
252
constexpr char kVS[] =
253
"attribute mediump vec2 coord;\n"
254
"attribute mediump vec3 color;\n"
255
"varying mediump vec3 vcolor;\n"
256
"void main(void)\n"
257
"{\n"
258
" gl_Position = vec4(coord, 0, 1);\n"
259
" vcolor = color;\n"
260
"}\n";
261
262
constexpr char kFS[] =
263
"varying mediump vec3 vcolor;\n"
264
"void main(void)\n"
265
"{\n"
266
" gl_FragColor = vec4(vcolor, 0);\n"
267
"}\n";
268
269
mProgram = CompileProgram(kVS, kFS);
270
ASSERT_NE(0u, mProgram);
271
glUseProgram(mProgram);
272
273
glGenBuffers(1, &mIndexBuffer);
274
275
GetTestCases();
276
}
277
278
void testTearDown() override
279
{
280
mTestCases.clear();
281
glDeleteProgram(mProgram);
282
glDeleteBuffers(1, &mIndexBuffer);
283
}
284
285
virtual bool Skip(const TestCase &) { return false; }
286
virtual void Draw(int firstVertex, unsigned vertexCount, const GLushort *indices) = 0;
287
288
void Run(bool drawFirstTriangle)
289
{
290
glViewport(0, 0, getWindowWidth(), getWindowHeight());
291
glUseProgram(mProgram);
292
293
for (unsigned i = 0; i < mTestCases.size(); ++i)
294
{
295
if (mTestCases[i].size() == 0 || Skip(mTestCases[i]))
296
continue;
297
298
PrepareTestCase(mTestCases[i]);
299
300
glClear(GL_COLOR_BUFFER_BIT);
301
302
std::string testCase;
303
if (drawFirstTriangle)
304
{
305
Draw(0, kNumVertices, mIndices);
306
testCase = "draw";
307
}
308
else
309
{
310
Draw(3, kNumVertices - 3, mIndices + 3);
311
testCase = "skip";
312
}
313
314
testCase += " first triangle case ";
315
int w = getWindowWidth() / 4;
316
int h = getWindowHeight() / 4;
317
if (drawFirstTriangle)
318
{
319
EXPECT_PIXEL_EQ(w * 2, h * 3, 255, 255, 0, 0) << testCase << i;
320
}
321
else
322
{
323
EXPECT_PIXEL_EQ(w * 2, h * 3, 51, 51, 51, 0) << testCase << i;
324
}
325
EXPECT_PIXEL_EQ(w * 3, h * 2, 0, 255, 0, 0) << testCase << i;
326
EXPECT_PIXEL_EQ(w * 2, h * 1, 0, 255, 255, 0) << testCase << i;
327
EXPECT_PIXEL_EQ(w * 1, h * 2, 255, 0, 255, 0) << testCase << i;
328
}
329
}
330
331
static const GLushort mIndices[kNumVertices];
332
333
GLuint mProgram;
334
GLuint mIndexBuffer;
335
336
std::vector<TestCase> mTestCases;
337
338
VertexData mCoord;
339
VertexData mColor;
340
};
341
const GLushort AttributeLayoutTest::mIndices[kNumVertices] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
342
343
void AttributeLayoutTest::GetTestCases(void)
344
{
345
const bool es3 = getClientMajorVersion() >= 3;
346
347
Format<GLfloat, GL_FLOAT, false> Float(es3);
348
Format<GLint, GL_FIXED, false> Fixed(es3);
349
350
Format<GLbyte, GL_BYTE, false> SByte(es3);
351
Format<GLubyte, GL_UNSIGNED_BYTE, false> UByte(es3);
352
Format<GLshort, GL_SHORT, false> SShort(es3);
353
Format<GLushort, GL_UNSIGNED_SHORT, false> UShort(es3);
354
Format<GLint, GL_INT, false> SInt(es3);
355
Format<GLuint, GL_UNSIGNED_INT, false> UInt(es3);
356
357
Format<GLbyte, GL_BYTE, true> NormSByte(es3);
358
Format<GLubyte, GL_UNSIGNED_BYTE, true> NormUByte(es3);
359
Format<GLshort, GL_SHORT, true> NormSShort(es3);
360
Format<GLushort, GL_UNSIGNED_SHORT, true> NormUShort(es3);
361
Format<GLint, GL_INT, true> NormSInt(es3);
362
Format<GLuint, GL_UNSIGNED_INT, true> NormUInt(es3);
363
364
std::shared_ptr<Container> M0 = std::make_shared<Memory>();
365
std::shared_ptr<Container> M1 = std::make_shared<Memory>();
366
std::shared_ptr<Container> B0 = std::make_shared<Buffer>();
367
std::shared_ptr<Container> B1 = std::make_shared<Buffer>();
368
369
// 0. two buffers
370
mTestCases.push_back({Float(B0, 0, 8, mCoord), Float(B1, 0, 12, mColor)});
371
372
// 1. two memory
373
mTestCases.push_back({Float(M0, 0, 8, mCoord), Float(M1, 0, 12, mColor)});
374
375
// 2. one memory, sequential
376
mTestCases.push_back({Float(M0, 0, 8, mCoord), Float(M0, 96, 12, mColor)});
377
378
// 3. one memory, interleaved
379
mTestCases.push_back({Float(M0, 0, 20, mCoord), Float(M0, 8, 20, mColor)});
380
381
// 4. buffer and memory
382
mTestCases.push_back({Float(B0, 0, 8, mCoord), Float(M0, 0, 12, mColor)});
383
384
// 5. stride != size
385
mTestCases.push_back({Float(B0, 0, 16, mCoord), Float(B1, 0, 12, mColor)});
386
387
// 6-7. same stride and format, switching data between memory and buffer
388
mTestCases.push_back({Float(M0, 0, 16, mCoord), Float(M1, 0, 12, mColor)});
389
mTestCases.push_back({Float(B0, 0, 16, mCoord), Float(B1, 0, 12, mColor)});
390
391
// 8-9. same stride and format, offset change
392
mTestCases.push_back({Float(B0, 0, 8, mCoord), Float(B1, 0, 12, mColor)});
393
mTestCases.push_back({Float(B0, 3, 8, mCoord), Float(B1, 4, 12, mColor)});
394
395
// 10-11. unaligned buffer data
396
mTestCases.push_back({Float(M0, 0, 8, mCoord), Float(B0, 1, 13, mColor)});
397
mTestCases.push_back({Float(M0, 0, 8, mCoord), Float(B1, 1, 13, mColor)});
398
399
// 12-15. byte/short
400
mTestCases.push_back({SByte(M0, 0, 20, mCoord), UByte(M0, 10, 20, mColor)});
401
mTestCases.push_back({SShort(M0, 0, 20, mCoord), UShort(M0, 8, 20, mColor)});
402
mTestCases.push_back({NormSByte(M0, 0, 8, mCoord), NormUByte(M0, 4, 8, mColor)});
403
mTestCases.push_back({NormSShort(M0, 0, 20, mCoord), NormUShort(M0, 8, 20, mColor)});
404
405
// 16. one buffer, sequential
406
mTestCases.push_back({Fixed(B0, 0, 8, mCoord), Float(B0, 96, 12, mColor)});
407
408
// 17. one buffer, interleaved
409
mTestCases.push_back({Fixed(B0, 0, 20, mCoord), Float(B0, 8, 20, mColor)});
410
411
// 18. memory and buffer, float and integer
412
mTestCases.push_back({Float(M0, 0, 8, mCoord), SByte(B0, 0, 12, mColor)});
413
414
// 19. buffer and memory, unusual offset and stride
415
mTestCases.push_back({Float(B0, 11, 13, mCoord), Float(M0, 23, 17, mColor)});
416
417
// 20-21. remaining ES3 formats
418
if (es3)
419
{
420
mTestCases.push_back({SInt(M0, 0, 40, mCoord), UInt(M0, 16, 40, mColor)});
421
// Fails on Nexus devices (anglebug.com/2641)
422
if (!IsNexus5X())
423
mTestCases.push_back({NormSInt(M0, 0, 40, mCoord), NormUInt(M0, 16, 40, mColor)});
424
}
425
}
426
427
class AttributeLayoutNonIndexed : public AttributeLayoutTest
428
{
429
void Draw(int firstVertex, unsigned vertexCount, const GLushort *indices) override
430
{
431
glDrawArrays(GL_TRIANGLES, firstVertex, vertexCount);
432
}
433
};
434
435
class AttributeLayoutMemoryIndexed : public AttributeLayoutTest
436
{
437
void Draw(int firstVertex, unsigned vertexCount, const GLushort *indices) override
438
{
439
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
440
glDrawElements(GL_TRIANGLES, vertexCount, GL_UNSIGNED_SHORT, indices);
441
}
442
};
443
444
class AttributeLayoutBufferIndexed : public AttributeLayoutTest
445
{
446
void Draw(int firstVertex, unsigned vertexCount, const GLushort *indices) override
447
{
448
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
449
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(*mIndices) * vertexCount, indices,
450
GL_STATIC_DRAW);
451
glDrawElements(GL_TRIANGLES, vertexCount, GL_UNSIGNED_SHORT, nullptr);
452
}
453
};
454
455
TEST_P(AttributeLayoutNonIndexed, Test)
456
{
457
Run(true);
458
ANGLE_SKIP_TEST_IF(IsWindows() && IsAMD() && IsOpenGL());
459
Run(false);
460
}
461
462
TEST_P(AttributeLayoutMemoryIndexed, Test)
463
{
464
Run(true);
465
ANGLE_SKIP_TEST_IF(IsWindows() && IsAMD() && (IsOpenGL() || IsD3D11_FL93()));
466
Run(false);
467
}
468
469
TEST_P(AttributeLayoutBufferIndexed, Test)
470
{
471
Run(true);
472
ANGLE_SKIP_TEST_IF(IsWindows() && IsAMD() && (IsOpenGL() || IsD3D11_FL93()));
473
Run(false);
474
}
475
476
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(AttributeLayoutNonIndexed);
477
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(AttributeLayoutMemoryIndexed);
478
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(AttributeLayoutBufferIndexed);
479
480
} // anonymous namespace
481
482