| // Copyright (C) 2018 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "GLSnapshotTestStateUtils.h" |
| #include "GLSnapshotTesting.h" |
| #include "apigen-codec-common/glUtils.h" |
| |
| #include <gtest/gtest.h> |
| |
| namespace gfxstream { |
| namespace gl { |
| namespace { |
| |
| struct GlTextureUnitState { |
| GLuint binding2D; |
| GLuint bindingCubeMap; |
| }; |
| |
| struct GlTextureImageState { |
| GLenum format; |
| GLenum type; |
| GLsizei width; |
| GLsizei height; |
| |
| GLboolean isCompressed; |
| GLsizei compressedSize; |
| |
| std::vector<GLubyte> bytes; |
| }; |
| |
| using GlMipmapArray = std::vector<GlTextureImageState>; |
| |
| struct GlTextureObjectState { |
| GLenum minFilter; |
| GLenum magFilter; |
| GLenum wrapS; |
| GLenum wrapT; |
| |
| GLenum target; |
| GlMipmapArray images2D; |
| std::vector<GlMipmapArray> imagesCubeMap; |
| }; |
| |
| static const GLenum kGLES2TextureCubeMapSides[] = { |
| GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, |
| GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, |
| GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, |
| }; |
| |
| static const GlMipmapArray kGLES2TestTexture2D = {{GL_RGBA, |
| GL_UNSIGNED_BYTE, |
| 4, |
| 4, |
| false, |
| 0, |
| { |
| 0x11, |
| 0x22, |
| 0x33, |
| 0x44, |
| 0x55, |
| 0x66, |
| 0x77, |
| 0x88, |
| 0x99, |
| 0xaa, |
| 0xbb, |
| 0xcc, |
| 0xdd, |
| 0xee, |
| 0xff, |
| 0x00, |
| }}, |
| {GL_RGBA, |
| GL_UNSIGNED_SHORT_4_4_4_4, |
| 2, |
| 2, |
| false, |
| 0, |
| { |
| 0x51, |
| 0x52, |
| 0x53, |
| 0x54, |
| }}, |
| {GL_RGBA, |
| GL_UNSIGNED_SHORT_5_5_5_1, |
| 1, |
| 1, |
| false, |
| 0, |
| { |
| 0xab, |
| }}}; |
| |
| static const std::vector<GlMipmapArray> kGLES2TestTextureCubeMap = { |
| {{GL_RGBA, |
| GL_UNSIGNED_BYTE, |
| 2, |
| 2, |
| false, |
| 0, |
| { |
| 0x11, |
| 0x12, |
| 0x13, |
| 0x14, |
| }}}, |
| {{GL_RGBA, |
| GL_UNSIGNED_BYTE, |
| 2, |
| 2, |
| false, |
| 0, |
| { |
| 0x21, |
| 0x22, |
| 0x23, |
| 0x24, |
| }}}, |
| {{GL_RGBA, |
| GL_UNSIGNED_BYTE, |
| 2, |
| 2, |
| false, |
| 0, |
| { |
| 0x31, |
| 0x32, |
| 0x33, |
| 0x34, |
| }}}, |
| {{GL_RGBA, |
| GL_UNSIGNED_BYTE, |
| 2, |
| 2, |
| false, |
| 0, |
| { |
| 0x41, |
| 0x42, |
| 0x43, |
| 0x44, |
| }}}, |
| {{GL_RGBA, |
| GL_UNSIGNED_BYTE, |
| 2, |
| 2, |
| false, |
| 0, |
| { |
| 0x51, |
| 0x52, |
| 0x53, |
| 0x54, |
| }}}, |
| {{GL_RGBA, |
| GL_UNSIGNED_BYTE, |
| 2, |
| 2, |
| false, |
| 0, |
| { |
| 0x61, |
| 0x62, |
| 0x63, |
| 0x64, |
| }}}, |
| }; |
| |
| class SnapshotGlTextureUnitActiveTest : public SnapshotPreserveTest { |
| public: |
| void defaultStateCheck() override { |
| EXPECT_TRUE(compareGlobalGlInt(gl, GL_ACTIVE_TEXTURE, GL_TEXTURE0)); |
| } |
| |
| void changedStateCheck() override { |
| EXPECT_TRUE(compareGlobalGlInt(gl, GL_ACTIVE_TEXTURE, |
| GL_TEXTURE0 + m_active_texture_unit)); |
| } |
| |
| void stateChange() override { |
| gl->glActiveTexture(GL_TEXTURE0 + m_active_texture_unit); |
| } |
| |
| void useTextureUnit(GLuint unit) { |
| GLint maxTextureUnits; |
| gl->glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, |
| &maxTextureUnits); |
| EXPECT_EQ(GL_NO_ERROR, gl->glGetError()); |
| |
| if (unit < maxTextureUnits) { |
| m_active_texture_unit = unit; |
| } else { |
| fprintf(stderr, |
| "Tried to use texture unit %d when max unit was %d." |
| " Defaulting to unit 0.\n", |
| unit, maxTextureUnits); |
| m_active_texture_unit = 0; |
| } |
| } |
| |
| protected: |
| GLuint m_active_texture_unit; |
| }; |
| |
| TEST_F(SnapshotGlTextureUnitActiveTest, ActiveTextureUnit) { |
| useTextureUnit(1); |
| doCheckedSnapshot(); |
| } |
| |
| class SnapshotGlTextureUnitBindingsTest : public SnapshotPreserveTest { |
| public: |
| void defaultStateCheck() override { |
| GLint maxTextureUnits; |
| gl->glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, |
| &maxTextureUnits); |
| for (int i = 0; i < maxTextureUnits; i++) { |
| gl->glActiveTexture(GL_TEXTURE0 + i); |
| EXPECT_TRUE(compareGlobalGlInt(gl, GL_TEXTURE_BINDING_2D, 0)); |
| EXPECT_TRUE(compareGlobalGlInt(gl, GL_TEXTURE_BINDING_CUBE_MAP, 0)); |
| } |
| } |
| |
| void changedStateCheck() override { |
| GLint maxTextureUnits; |
| gl->glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, |
| &maxTextureUnits); |
| EXPECT_EQ(m_unit_states.size(), maxTextureUnits); |
| |
| for (int i = 0; i < maxTextureUnits; i++) { |
| gl->glActiveTexture(GL_TEXTURE0 + i); |
| EXPECT_TRUE(compareGlobalGlInt(gl, GL_TEXTURE_BINDING_2D, |
| m_unit_states[i].binding2D)); |
| EXPECT_TRUE(compareGlobalGlInt(gl, GL_TEXTURE_BINDING_CUBE_MAP, |
| m_unit_states[i].bindingCubeMap)); |
| } |
| } |
| |
| void stateChange() override { |
| GLint maxTextureUnits; |
| gl->glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, |
| &maxTextureUnits); |
| m_unit_states.resize(maxTextureUnits); |
| |
| m_state_changer(); |
| } |
| |
| void setStateChanger(std::function<void()> changer) { |
| m_state_changer = changer; |
| } |
| |
| protected: |
| // Create a texture object, bind to texture unit |unit| at binding point |
| // |bindPoint|, and record that we've done so. |
| GLuint createAndBindTexture(GLuint unit, GLenum bindPoint) { |
| GLint maxTextureUnits; |
| gl->glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, |
| &maxTextureUnits); |
| if (unit >= maxTextureUnits) { |
| fprintf(stderr, |
| "Cannot bind to unit %d: max units is %d. Binding to %d " |
| "instead.\n", |
| unit, maxTextureUnits, maxTextureUnits - 1); |
| unit = maxTextureUnits - 1; |
| } |
| |
| GLuint testTexture; |
| gl->glGenTextures(1, &testTexture); |
| gl->glActiveTexture(GL_TEXTURE0 + unit); |
| gl->glBindTexture(bindPoint, testTexture); |
| EXPECT_EQ(GL_NO_ERROR, gl->glGetError()); |
| switch (bindPoint) { |
| case GL_TEXTURE_2D: |
| m_unit_states[unit].binding2D = testTexture; |
| break; |
| case GL_TEXTURE_CUBE_MAP: |
| m_unit_states[unit].bindingCubeMap = testTexture; |
| break; |
| default: |
| ADD_FAILURE() << "Unsupported texture unit bind point " + |
| describeGlEnum(bindPoint); |
| } |
| return testTexture; |
| } |
| |
| std::vector<GlTextureUnitState> m_unit_states; |
| std::function<void()> m_state_changer = [] {}; |
| }; |
| |
| TEST_F(SnapshotGlTextureUnitBindingsTest, BindTextures) { |
| setStateChanger([this] { |
| createAndBindTexture(1, GL_TEXTURE_2D); |
| createAndBindTexture(8, GL_TEXTURE_CUBE_MAP); |
| createAndBindTexture(16, GL_TEXTURE_2D); |
| createAndBindTexture(32, GL_TEXTURE_CUBE_MAP); |
| }); |
| doCheckedSnapshot(); |
| } |
| |
| class SnapshotGlTextureObjectTest : public SnapshotPreserveTest { |
| public: |
| void defaultStateCheck() override { |
| EXPECT_EQ(GL_FALSE, gl->glIsTexture(m_object_name)); |
| } |
| |
| void changedStateCheck() override { |
| SCOPED_TRACE("Texture object " + std::to_string(m_object_name) + |
| ", target " + describeGlEnum(m_state.target)); |
| EXPECT_EQ(GL_TRUE, gl->glIsTexture(m_object_name)); |
| |
| EXPECT_TRUE(compareGlobalGlInt(gl, GL_ACTIVE_TEXTURE, GL_TEXTURE0)); |
| EXPECT_TRUE(compareGlobalGlInt(gl, getTargetBindingName(m_state.target), |
| m_object_name)); |
| |
| EXPECT_TRUE(compareParameter(GL_TEXTURE_MIN_FILTER, m_state.minFilter)); |
| EXPECT_TRUE(compareParameter(GL_TEXTURE_MAG_FILTER, m_state.magFilter)); |
| EXPECT_TRUE(compareParameter(GL_TEXTURE_WRAP_S, m_state.wrapS)); |
| EXPECT_TRUE(compareParameter(GL_TEXTURE_WRAP_T, m_state.wrapT)); |
| |
| auto compareImageFunc = [this](GLenum imageTarget, |
| GlMipmapArray& levels) { |
| for (int i = 0; i < levels.size(); i++) { |
| EXPECT_TRUE(compareVector<GLubyte>( |
| levels[i].bytes, |
| getTextureImageData(gl, m_object_name, imageTarget, i, |
| levels[i].width, levels[i].height, |
| levels[i].format, levels[i].type), |
| "mipmap level " + std::to_string(i))); |
| EXPECT_EQ(GL_NO_ERROR, gl->glGetError()); |
| } |
| }; |
| |
| switch (m_state.target) { |
| case GL_TEXTURE_2D: { |
| compareImageFunc(m_state.target, m_state.images2D); |
| } break; |
| case GL_TEXTURE_CUBE_MAP: { |
| if (m_state.imagesCubeMap.size() > 6) { |
| ADD_FAILURE() << "Test texture cube map had " |
| << m_state.imagesCubeMap.size() |
| << " 'sides' of data."; |
| break; |
| } |
| for (int j = 0; j < m_state.imagesCubeMap.size(); j++) { |
| compareImageFunc(kGLES2TextureCubeMapSides[j], |
| m_state.imagesCubeMap[j]); |
| } |
| } break; |
| default: |
| ADD_FAILURE() |
| << "Unsupported texture target " << m_state.target; |
| break; |
| } |
| } |
| |
| void stateChange() override { |
| gl->glGenTextures(1, &m_object_name); |
| |
| // Bind to texture unit TEXTURE0 for test simplicity |
| gl->glActiveTexture(GL_TEXTURE0); |
| gl->glBindTexture(m_state.target, m_object_name); |
| |
| // Set texture sample parameters |
| gl->glTexParameteri(m_state.target, GL_TEXTURE_MIN_FILTER, |
| m_state.minFilter); |
| gl->glTexParameteri(m_state.target, GL_TEXTURE_MAG_FILTER, |
| m_state.magFilter); |
| gl->glTexParameteri(m_state.target, GL_TEXTURE_WRAP_S, m_state.wrapS); |
| gl->glTexParameteri(m_state.target, GL_TEXTURE_WRAP_T, m_state.wrapT); |
| |
| auto initImageFunc = [this](GLenum imageTarget, GlMipmapArray& levels) { |
| for (int i = 0; i < levels.size(); i++) { |
| levels[i].bytes.resize( |
| levels[i].width * levels[i].height * |
| glUtilsPixelBitSize( |
| levels[i].format, |
| GL_UNSIGNED_BYTE /* levels[i].type */) / 8); |
| gl->glTexImage2D(imageTarget, i, levels[i].format, |
| levels[i].width, levels[i].height, 0, |
| levels[i].format, |
| GL_UNSIGNED_BYTE /* levels[i].type */, |
| levels[i].bytes.data()); |
| } |
| }; |
| |
| switch (m_state.target) { |
| case GL_TEXTURE_2D: { |
| initImageFunc(m_state.target, m_state.images2D); |
| } break; |
| case GL_TEXTURE_CUBE_MAP: { |
| if (m_state.imagesCubeMap.size() > 6) { |
| ADD_FAILURE() << "Test texture cube map had " |
| << m_state.imagesCubeMap.size() |
| << " 'sides' of data."; |
| break; |
| } |
| for (int j = 0; j < m_state.imagesCubeMap.size(); j++) { |
| GLenum side = kGLES2TextureCubeMapSides[j]; |
| initImageFunc(side, m_state.imagesCubeMap[j]); |
| } |
| } break; |
| default: |
| ADD_FAILURE() |
| << "Unsupported texture target " << m_state.target; |
| break; |
| } |
| } |
| |
| protected: |
| // Compares a symbolic constant value |expected| against the parameter named |
| // |paramName| of the texture object which is bound in unit TEXTURE0. |
| testing::AssertionResult compareParameter(GLenum paramName, |
| GLenum expected) { |
| GLint actual; |
| gl->glGetTexParameteriv(m_state.target, paramName, &actual); |
| return compareValue<GLint>( |
| expected, actual, |
| "GL texture object " + std::to_string(m_object_name) + |
| " mismatch for param " + describeGlEnum(paramName) + |
| " on target " + describeGlEnum(m_state.target)); |
| } |
| |
| GLenum getTargetBindingName(GLenum target) { |
| switch (target) { |
| case GL_TEXTURE_2D: |
| return GL_TEXTURE_BINDING_2D; |
| case GL_TEXTURE_CUBE_MAP: |
| return GL_TEXTURE_BINDING_CUBE_MAP; |
| default: |
| ADD_FAILURE() << "Unsupported texture target " << target; |
| return 0; |
| } |
| } |
| |
| GLuint m_object_name; |
| GlTextureObjectState m_state = {}; |
| }; |
| |
| TEST_F(SnapshotGlTextureObjectTest, SetObjectParameters) { |
| m_state = { |
| .minFilter = GL_LINEAR, |
| .magFilter = GL_NEAREST, |
| .wrapS = GL_MIRRORED_REPEAT, |
| .wrapT = GL_CLAMP_TO_EDGE, |
| .target = GL_TEXTURE_2D, |
| }; |
| doCheckedSnapshot(); |
| } |
| |
| TEST_F(SnapshotGlTextureObjectTest, Create2DMipmap) { |
| m_state = {.minFilter = GL_LINEAR, |
| .magFilter = GL_NEAREST, |
| .wrapS = GL_MIRRORED_REPEAT, |
| .wrapT = GL_CLAMP_TO_EDGE, |
| .target = GL_TEXTURE_2D, |
| .images2D = kGLES2TestTexture2D}; |
| doCheckedSnapshot(); |
| } |
| |
| TEST_F(SnapshotGlTextureObjectTest, CreateCubeMap) { |
| m_state = {.minFilter = GL_LINEAR, |
| .magFilter = GL_NEAREST, |
| .wrapS = GL_MIRRORED_REPEAT, |
| .wrapT = GL_CLAMP_TO_EDGE, |
| .target = GL_TEXTURE_CUBE_MAP, |
| .images2D = {}, // mingw compiler cannot deal with gaps |
| .imagesCubeMap = kGLES2TestTextureCubeMap}; |
| doCheckedSnapshot(); |
| } |
| |
| } // namespace |
| } // namespace gl |
| } // namespace gfxstream |