1 // Copyright (C) 2018 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "GLSnapshotTestStateUtils.h"
16 #include "GLSnapshotTesting.h"
17 #include "apigen-codec-common/glUtils.h"
18
19 #include <gtest/gtest.h>
20
21 namespace gfxstream {
22 namespace gl {
23 namespace {
24
25 struct GlTextureUnitState {
26 GLuint binding2D;
27 GLuint bindingCubeMap;
28 };
29
30 struct GlTextureImageState {
31 GLenum format;
32 GLenum type;
33 GLsizei width;
34 GLsizei height;
35
36 GLboolean isCompressed;
37 GLsizei compressedSize;
38
39 std::vector<GLubyte> bytes;
40 };
41
42 using GlMipmapArray = std::vector<GlTextureImageState>;
43
44 struct GlTextureObjectState {
45 GLenum minFilter;
46 GLenum magFilter;
47 GLenum wrapS;
48 GLenum wrapT;
49
50 GLenum target;
51 GlMipmapArray images2D;
52 std::vector<GlMipmapArray> imagesCubeMap;
53 };
54
55 static const GLenum kGLES2TextureCubeMapSides[] = {
56 GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
57 GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
58 GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
59 };
60
61 static const GlMipmapArray kGLES2TestTexture2D = {{GL_RGBA,
62 GL_UNSIGNED_BYTE,
63 4,
64 4,
65 false,
66 0,
67 {
68 0x11,
69 0x22,
70 0x33,
71 0x44,
72 0x55,
73 0x66,
74 0x77,
75 0x88,
76 0x99,
77 0xaa,
78 0xbb,
79 0xcc,
80 0xdd,
81 0xee,
82 0xff,
83 0x00,
84 }},
85 {GL_RGBA,
86 GL_UNSIGNED_SHORT_4_4_4_4,
87 2,
88 2,
89 false,
90 0,
91 {
92 0x51,
93 0x52,
94 0x53,
95 0x54,
96 }},
97 {GL_RGBA,
98 GL_UNSIGNED_SHORT_5_5_5_1,
99 1,
100 1,
101 false,
102 0,
103 {
104 0xab,
105 }}};
106
107 static const std::vector<GlMipmapArray> kGLES2TestTextureCubeMap = {
108 {{GL_RGBA,
109 GL_UNSIGNED_BYTE,
110 2,
111 2,
112 false,
113 0,
114 {
115 0x11,
116 0x12,
117 0x13,
118 0x14,
119 }}},
120 {{GL_RGBA,
121 GL_UNSIGNED_BYTE,
122 2,
123 2,
124 false,
125 0,
126 {
127 0x21,
128 0x22,
129 0x23,
130 0x24,
131 }}},
132 {{GL_RGBA,
133 GL_UNSIGNED_BYTE,
134 2,
135 2,
136 false,
137 0,
138 {
139 0x31,
140 0x32,
141 0x33,
142 0x34,
143 }}},
144 {{GL_RGBA,
145 GL_UNSIGNED_BYTE,
146 2,
147 2,
148 false,
149 0,
150 {
151 0x41,
152 0x42,
153 0x43,
154 0x44,
155 }}},
156 {{GL_RGBA,
157 GL_UNSIGNED_BYTE,
158 2,
159 2,
160 false,
161 0,
162 {
163 0x51,
164 0x52,
165 0x53,
166 0x54,
167 }}},
168 {{GL_RGBA,
169 GL_UNSIGNED_BYTE,
170 2,
171 2,
172 false,
173 0,
174 {
175 0x61,
176 0x62,
177 0x63,
178 0x64,
179 }}},
180 };
181
182 class SnapshotGlTextureUnitActiveTest : public SnapshotPreserveTest {
183 public:
defaultStateCheck()184 void defaultStateCheck() override {
185 EXPECT_TRUE(compareGlobalGlInt(gl, GL_ACTIVE_TEXTURE, GL_TEXTURE0));
186 }
187
changedStateCheck()188 void changedStateCheck() override {
189 EXPECT_TRUE(compareGlobalGlInt(gl, GL_ACTIVE_TEXTURE,
190 GL_TEXTURE0 + m_active_texture_unit));
191 }
192
stateChange()193 void stateChange() override {
194 gl->glActiveTexture(GL_TEXTURE0 + m_active_texture_unit);
195 }
196
useTextureUnit(GLuint unit)197 void useTextureUnit(GLuint unit) {
198 GLint maxTextureUnits;
199 gl->glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS,
200 &maxTextureUnits);
201 EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
202
203 if (unit < maxTextureUnits) {
204 m_active_texture_unit = unit;
205 } else {
206 fprintf(stderr,
207 "Tried to use texture unit %d when max unit was %d."
208 " Defaulting to unit 0.\n",
209 unit, maxTextureUnits);
210 m_active_texture_unit = 0;
211 }
212 }
213
214 protected:
215 GLuint m_active_texture_unit;
216 };
217
TEST_F(SnapshotGlTextureUnitActiveTest,ActiveTextureUnit)218 TEST_F(SnapshotGlTextureUnitActiveTest, ActiveTextureUnit) {
219 useTextureUnit(1);
220 doCheckedSnapshot();
221 }
222
223 class SnapshotGlTextureUnitBindingsTest : public SnapshotPreserveTest {
224 public:
defaultStateCheck()225 void defaultStateCheck() override {
226 GLint maxTextureUnits;
227 gl->glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS,
228 &maxTextureUnits);
229 for (int i = 0; i < maxTextureUnits; i++) {
230 gl->glActiveTexture(GL_TEXTURE0 + i);
231 EXPECT_TRUE(compareGlobalGlInt(gl, GL_TEXTURE_BINDING_2D, 0));
232 EXPECT_TRUE(compareGlobalGlInt(gl, GL_TEXTURE_BINDING_CUBE_MAP, 0));
233 }
234 }
235
changedStateCheck()236 void changedStateCheck() override {
237 GLint maxTextureUnits;
238 gl->glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS,
239 &maxTextureUnits);
240 EXPECT_EQ(m_unit_states.size(), maxTextureUnits);
241
242 for (int i = 0; i < maxTextureUnits; i++) {
243 gl->glActiveTexture(GL_TEXTURE0 + i);
244 EXPECT_TRUE(compareGlobalGlInt(gl, GL_TEXTURE_BINDING_2D,
245 m_unit_states[i].binding2D));
246 EXPECT_TRUE(compareGlobalGlInt(gl, GL_TEXTURE_BINDING_CUBE_MAP,
247 m_unit_states[i].bindingCubeMap));
248 }
249 }
250
stateChange()251 void stateChange() override {
252 GLint maxTextureUnits;
253 gl->glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS,
254 &maxTextureUnits);
255 m_unit_states.resize(maxTextureUnits);
256
257 m_state_changer();
258 }
259
setStateChanger(std::function<void ()> changer)260 void setStateChanger(std::function<void()> changer) {
261 m_state_changer = changer;
262 }
263
264 protected:
265 // Create a texture object, bind to texture unit |unit| at binding point
266 // |bindPoint|, and record that we've done so.
createAndBindTexture(GLuint unit,GLenum bindPoint)267 GLuint createAndBindTexture(GLuint unit, GLenum bindPoint) {
268 GLint maxTextureUnits;
269 gl->glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS,
270 &maxTextureUnits);
271 if (unit >= maxTextureUnits) {
272 fprintf(stderr,
273 "Cannot bind to unit %d: max units is %d. Binding to %d "
274 "instead.\n",
275 unit, maxTextureUnits, maxTextureUnits - 1);
276 unit = maxTextureUnits - 1;
277 }
278
279 GLuint testTexture;
280 gl->glGenTextures(1, &testTexture);
281 gl->glActiveTexture(GL_TEXTURE0 + unit);
282 gl->glBindTexture(bindPoint, testTexture);
283 EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
284 switch (bindPoint) {
285 case GL_TEXTURE_2D:
286 m_unit_states[unit].binding2D = testTexture;
287 break;
288 case GL_TEXTURE_CUBE_MAP:
289 m_unit_states[unit].bindingCubeMap = testTexture;
290 break;
291 default:
292 ADD_FAILURE() << "Unsupported texture unit bind point " +
293 describeGlEnum(bindPoint);
294 }
295 return testTexture;
296 }
297
298 std::vector<GlTextureUnitState> m_unit_states;
__anon0400c4820202null299 std::function<void()> m_state_changer = [] {};
300 };
301
TEST_F(SnapshotGlTextureUnitBindingsTest,BindTextures)302 TEST_F(SnapshotGlTextureUnitBindingsTest, BindTextures) {
303 setStateChanger([this] {
304 createAndBindTexture(1, GL_TEXTURE_2D);
305 createAndBindTexture(8, GL_TEXTURE_CUBE_MAP);
306 createAndBindTexture(16, GL_TEXTURE_2D);
307 createAndBindTexture(32, GL_TEXTURE_CUBE_MAP);
308 });
309 doCheckedSnapshot();
310 }
311
312 class SnapshotGlTextureObjectTest : public SnapshotPreserveTest {
313 public:
defaultStateCheck()314 void defaultStateCheck() override {
315 EXPECT_EQ(GL_FALSE, gl->glIsTexture(m_object_name));
316 }
317
changedStateCheck()318 void changedStateCheck() override {
319 SCOPED_TRACE("Texture object " + std::to_string(m_object_name) +
320 ", target " + describeGlEnum(m_state.target));
321 EXPECT_EQ(GL_TRUE, gl->glIsTexture(m_object_name));
322
323 EXPECT_TRUE(compareGlobalGlInt(gl, GL_ACTIVE_TEXTURE, GL_TEXTURE0));
324 EXPECT_TRUE(compareGlobalGlInt(gl, getTargetBindingName(m_state.target),
325 m_object_name));
326
327 EXPECT_TRUE(compareParameter(GL_TEXTURE_MIN_FILTER, m_state.minFilter));
328 EXPECT_TRUE(compareParameter(GL_TEXTURE_MAG_FILTER, m_state.magFilter));
329 EXPECT_TRUE(compareParameter(GL_TEXTURE_WRAP_S, m_state.wrapS));
330 EXPECT_TRUE(compareParameter(GL_TEXTURE_WRAP_T, m_state.wrapT));
331
332 auto compareImageFunc = [this](GLenum imageTarget,
333 GlMipmapArray& levels) {
334 for (int i = 0; i < levels.size(); i++) {
335 EXPECT_TRUE(compareVector<GLubyte>(
336 levels[i].bytes,
337 getTextureImageData(gl, m_object_name, imageTarget, i,
338 levels[i].width, levels[i].height,
339 levels[i].format, levels[i].type),
340 "mipmap level " + std::to_string(i)));
341 EXPECT_EQ(GL_NO_ERROR, gl->glGetError());
342 }
343 };
344
345 switch (m_state.target) {
346 case GL_TEXTURE_2D: {
347 compareImageFunc(m_state.target, m_state.images2D);
348 } break;
349 case GL_TEXTURE_CUBE_MAP: {
350 if (m_state.imagesCubeMap.size() > 6) {
351 ADD_FAILURE() << "Test texture cube map had "
352 << m_state.imagesCubeMap.size()
353 << " 'sides' of data.";
354 break;
355 }
356 for (int j = 0; j < m_state.imagesCubeMap.size(); j++) {
357 compareImageFunc(kGLES2TextureCubeMapSides[j],
358 m_state.imagesCubeMap[j]);
359 }
360 } break;
361 default:
362 ADD_FAILURE()
363 << "Unsupported texture target " << m_state.target;
364 break;
365 }
366 }
367
stateChange()368 void stateChange() override {
369 gl->glGenTextures(1, &m_object_name);
370
371 // Bind to texture unit TEXTURE0 for test simplicity
372 gl->glActiveTexture(GL_TEXTURE0);
373 gl->glBindTexture(m_state.target, m_object_name);
374
375 // Set texture sample parameters
376 gl->glTexParameteri(m_state.target, GL_TEXTURE_MIN_FILTER,
377 m_state.minFilter);
378 gl->glTexParameteri(m_state.target, GL_TEXTURE_MAG_FILTER,
379 m_state.magFilter);
380 gl->glTexParameteri(m_state.target, GL_TEXTURE_WRAP_S, m_state.wrapS);
381 gl->glTexParameteri(m_state.target, GL_TEXTURE_WRAP_T, m_state.wrapT);
382
383 auto initImageFunc = [this](GLenum imageTarget, GlMipmapArray& levels) {
384 for (int i = 0; i < levels.size(); i++) {
385 levels[i].bytes.resize(
386 levels[i].width * levels[i].height *
387 glUtilsPixelBitSize(
388 levels[i].format,
389 GL_UNSIGNED_BYTE /* levels[i].type */) / 8);
390 gl->glTexImage2D(imageTarget, i, levels[i].format,
391 levels[i].width, levels[i].height, 0,
392 levels[i].format,
393 GL_UNSIGNED_BYTE /* levels[i].type */,
394 levels[i].bytes.data());
395 }
396 };
397
398 switch (m_state.target) {
399 case GL_TEXTURE_2D: {
400 initImageFunc(m_state.target, m_state.images2D);
401 } break;
402 case GL_TEXTURE_CUBE_MAP: {
403 if (m_state.imagesCubeMap.size() > 6) {
404 ADD_FAILURE() << "Test texture cube map had "
405 << m_state.imagesCubeMap.size()
406 << " 'sides' of data.";
407 break;
408 }
409 for (int j = 0; j < m_state.imagesCubeMap.size(); j++) {
410 GLenum side = kGLES2TextureCubeMapSides[j];
411 initImageFunc(side, m_state.imagesCubeMap[j]);
412 }
413 } break;
414 default:
415 ADD_FAILURE()
416 << "Unsupported texture target " << m_state.target;
417 break;
418 }
419 }
420
421 protected:
422 // Compares a symbolic constant value |expected| against the parameter named
423 // |paramName| of the texture object which is bound in unit TEXTURE0.
compareParameter(GLenum paramName,GLenum expected)424 testing::AssertionResult compareParameter(GLenum paramName,
425 GLenum expected) {
426 GLint actual;
427 gl->glGetTexParameteriv(m_state.target, paramName, &actual);
428 return compareValue<GLint>(
429 expected, actual,
430 "GL texture object " + std::to_string(m_object_name) +
431 " mismatch for param " + describeGlEnum(paramName) +
432 " on target " + describeGlEnum(m_state.target));
433 }
434
getTargetBindingName(GLenum target)435 GLenum getTargetBindingName(GLenum target) {
436 switch (target) {
437 case GL_TEXTURE_2D:
438 return GL_TEXTURE_BINDING_2D;
439 case GL_TEXTURE_CUBE_MAP:
440 return GL_TEXTURE_BINDING_CUBE_MAP;
441 default:
442 ADD_FAILURE() << "Unsupported texture target " << target;
443 return 0;
444 }
445 }
446
447 GLuint m_object_name;
448 GlTextureObjectState m_state = {};
449 };
450
TEST_F(SnapshotGlTextureObjectTest,SetObjectParameters)451 TEST_F(SnapshotGlTextureObjectTest, SetObjectParameters) {
452 m_state = {
453 .minFilter = GL_LINEAR,
454 .magFilter = GL_NEAREST,
455 .wrapS = GL_MIRRORED_REPEAT,
456 .wrapT = GL_CLAMP_TO_EDGE,
457 .target = GL_TEXTURE_2D,
458 };
459 doCheckedSnapshot();
460 }
461
TEST_F(SnapshotGlTextureObjectTest,Create2DMipmap)462 TEST_F(SnapshotGlTextureObjectTest, Create2DMipmap) {
463 m_state = {.minFilter = GL_LINEAR,
464 .magFilter = GL_NEAREST,
465 .wrapS = GL_MIRRORED_REPEAT,
466 .wrapT = GL_CLAMP_TO_EDGE,
467 .target = GL_TEXTURE_2D,
468 .images2D = kGLES2TestTexture2D};
469 doCheckedSnapshot();
470 }
471
TEST_F(SnapshotGlTextureObjectTest,CreateCubeMap)472 TEST_F(SnapshotGlTextureObjectTest, CreateCubeMap) {
473 m_state = {.minFilter = GL_LINEAR,
474 .magFilter = GL_NEAREST,
475 .wrapS = GL_MIRRORED_REPEAT,
476 .wrapT = GL_CLAMP_TO_EDGE,
477 .target = GL_TEXTURE_CUBE_MAP,
478 .images2D = {}, // mingw compiler cannot deal with gaps
479 .imagesCubeMap = kGLES2TestTextureCubeMap};
480 doCheckedSnapshot();
481 }
482
483 } // namespace
484 } // namespace gl
485 } // namespace gfxstream
486