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