| // | |
| // Copyright 2015 The ANGLE Project Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // | |
| #include "test_utils/ANGLETest.h" | |
| #include "test_utils/gl_raii.h" | |
| using namespace angle; | |
| class CubeMapTextureTest : public ANGLETest | |
| { | |
| protected: | |
| CubeMapTextureTest() | |
| { | |
| setWindowWidth(256); | |
| setWindowHeight(256); | |
| setConfigRedBits(8); | |
| setConfigGreenBits(8); | |
| setConfigBlueBits(8); | |
| setConfigAlphaBits(8); | |
| } | |
| void testSetUp() override | |
| { | |
| mProgram = CompileProgram(essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor()); | |
| if (mProgram == 0) | |
| { | |
| FAIL() << "shader compilation failed."; | |
| } | |
| mColorLocation = glGetUniformLocation(mProgram, essl1_shaders::ColorUniform()); | |
| glUseProgram(mProgram); | |
| glClearColor(0, 0, 0, 0); | |
| glClearDepthf(0.0); | |
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
| glEnable(GL_BLEND); | |
| glDisable(GL_DEPTH_TEST); | |
| ASSERT_GL_NO_ERROR(); | |
| } | |
| void testTearDown() override { glDeleteProgram(mProgram); } | |
| void runSampleCoordinateTransformTest(const char *shader); | |
| GLuint mProgram; | |
| GLint mColorLocation; | |
| }; | |
| // Verify that rendering to the faces of a cube map consecutively will correctly render to each | |
| // face. | |
| TEST_P(CubeMapTextureTest, RenderToFacesConsecutively) | |
| { | |
| // TODO: Diagnose and fix. http://anglebug.com/2954 | |
| ANGLE_SKIP_TEST_IF(IsVulkan() && IsIntel() && IsWindows()); | |
| // http://anglebug.com/3145 | |
| ANGLE_SKIP_TEST_IF(IsVulkan() && IsIntel() && IsFuchsia()); | |
| // TODO(hqle): Find what wrong with NVIDIA GPU. http://anglebug.com/4138 | |
| ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsMetal()); | |
| const GLfloat faceColors[] = { | |
| 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, | |
| 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, | |
| }; | |
| GLuint tex = 0; | |
| glGenTextures(1, &tex); | |
| glBindTexture(GL_TEXTURE_CUBE_MAP, tex); | |
| for (GLenum face = 0; face < 6; face++) | |
| { | |
| glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, GL_RGBA, 1, 1, 0, GL_RGBA, | |
| GL_UNSIGNED_BYTE, nullptr); | |
| } | |
| EXPECT_GL_NO_ERROR(); | |
| GLuint fbo = 0; | |
| glGenFramebuffers(1, &fbo); | |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); | |
| EXPECT_GL_NO_ERROR(); | |
| for (GLenum face = 0; face < 6; face++) | |
| { | |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, | |
| GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, tex, 0); | |
| EXPECT_GL_NO_ERROR(); | |
| glUseProgram(mProgram); | |
| const GLfloat *faceColor = faceColors + (face * 4); | |
| glUniform4f(mColorLocation, faceColor[0], faceColor[1], faceColor[2], faceColor[3]); | |
| drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f); | |
| EXPECT_GL_NO_ERROR(); | |
| } | |
| for (GLenum face = 0; face < 6; face++) | |
| { | |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, | |
| GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, tex, 0); | |
| EXPECT_GL_NO_ERROR(); | |
| const GLfloat *faceColor = faceColors + (face * 4); | |
| EXPECT_PIXEL_EQ(0, 0, faceColor[0] * 255, faceColor[1] * 255, faceColor[2] * 255, | |
| faceColor[3] * 255); | |
| EXPECT_GL_NO_ERROR(); | |
| } | |
| glDeleteFramebuffers(1, &fbo); | |
| glDeleteTextures(1, &tex); | |
| EXPECT_GL_NO_ERROR(); | |
| } | |
| void CubeMapTextureTest::runSampleCoordinateTransformTest(const char *shader) | |
| { | |
| // Fails to compile the shader. anglebug.com/3776 | |
| ANGLE_SKIP_TEST_IF(IsOpenGL() && IsIntel() && IsWindows()); | |
| constexpr GLsizei kCubeFaceCount = 6; | |
| constexpr GLsizei kCubeFaceSectionCount = 4; | |
| constexpr GLsizei kCubeFaceSectionCountSqrt = 2; | |
| constexpr GLColor faceColors[kCubeFaceCount][kCubeFaceSectionCount] = { | |
| {GLColor(255, 0, 0, 255), GLColor(191, 0, 0, 255), GLColor(127, 0, 0, 255), | |
| GLColor(63, 0, 0, 255)}, | |
| {GLColor(0, 255, 0, 255), GLColor(0, 191, 0, 255), GLColor(0, 127, 0, 255), | |
| GLColor(0, 63, 0, 255)}, | |
| {GLColor(0, 0, 255, 255), GLColor(0, 0, 191, 255), GLColor(0, 0, 127, 255), | |
| GLColor(0, 0, 63, 255)}, | |
| {GLColor(255, 63, 0, 255), GLColor(191, 127, 0, 255), GLColor(127, 191, 0, 255), | |
| GLColor(63, 255, 0, 255)}, | |
| {GLColor(0, 255, 63, 255), GLColor(0, 191, 127, 255), GLColor(0, 127, 191, 255), | |
| GLColor(0, 63, 255, 255)}, | |
| {GLColor(63, 0, 255, 255), GLColor(127, 0, 191, 255), GLColor(191, 0, 127, 255), | |
| GLColor(255, 0, 63, 255)}, | |
| }; | |
| constexpr GLsizei kTextureSize = 32; | |
| GLTexture tex; | |
| glBindTexture(GL_TEXTURE_CUBE_MAP, tex); | |
| for (GLenum face = 0; face < kCubeFaceCount; face++) | |
| { | |
| std::vector<GLColor> faceData(kTextureSize * kTextureSize); | |
| // Create the face with four sections, each with a solid color from |faceColors|. | |
| for (size_t row = 0; row < kTextureSize / kCubeFaceSectionCountSqrt; ++row) | |
| { | |
| for (size_t col = 0; col < kTextureSize / kCubeFaceSectionCountSqrt; ++col) | |
| { | |
| for (size_t srow = 0; srow < kCubeFaceSectionCountSqrt; ++srow) | |
| { | |
| for (size_t scol = 0; scol < kCubeFaceSectionCountSqrt; ++scol) | |
| { | |
| size_t r = row + srow * kTextureSize / kCubeFaceSectionCountSqrt; | |
| size_t c = col + scol * kTextureSize / kCubeFaceSectionCountSqrt; | |
| size_t s = srow * kCubeFaceSectionCountSqrt + scol; | |
| faceData[r * kTextureSize + c] = faceColors[face][s]; | |
| } | |
| } | |
| } | |
| } | |
| glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, GL_RGBA, kTextureSize, kTextureSize, | |
| 0, GL_RGBA, GL_UNSIGNED_BYTE, faceData.data()); | |
| } | |
| glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
| glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
| EXPECT_GL_NO_ERROR(); | |
| GLTexture fboTex; | |
| glBindTexture(GL_TEXTURE_2D, fboTex); | |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kCubeFaceCount, kCubeFaceSectionCount, 0, GL_RGBA, | |
| GL_UNSIGNED_BYTE, nullptr); | |
| GLFramebuffer fbo; | |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); | |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboTex, 0); | |
| EXPECT_GL_NO_ERROR(); | |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), shader); | |
| glUseProgram(program); | |
| GLint texCubeLocation = glGetUniformLocation(program, "texCube"); | |
| ASSERT_NE(-1, texCubeLocation); | |
| glUniform1i(texCubeLocation, 0); | |
| drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f); | |
| EXPECT_GL_NO_ERROR(); | |
| for (GLenum face = 0; face < kCubeFaceCount; face++) | |
| { | |
| // The following table defines the translation from textureCube coordinates to coordinates | |
| // in each face. The framebuffer has width 6 and height 4. Every column corresponding to | |
| // an x value represents one cube face. The values in rows are samples from the four | |
| // sections of the face. | |
| // | |
| // Major Axis Direction Target sc tc ma | |
| // +rx TEXTURE_CUBE_MAP_POSITIVE_X −rz −ry rx | |
| // −rx TEXTURE_CUBE_MAP_NEGATIVE_X rz −ry rx | |
| // +ry TEXTURE_CUBE_MAP_POSITIVE_Y rx rz ry | |
| // −ry TEXTURE_CUBE_MAP_NEGATIVE_Y rx −rz ry | |
| // +rz TEXTURE_CUBE_MAP_POSITIVE_Z rx −ry rz | |
| // −rz TEXTURE_CUBE_MAP_NEGATIVE_Z −rx −ry rz | |
| // | |
| // This table is used only to determine the direction of growth for s and t. The shader | |
| // always generates (row,col) coordinates (0, 0), (0, 1), (1, 0), (1, 1) which is the order | |
| // the data is uploaded to the faces, but based on the table above, the sample order would | |
| // be different. | |
| constexpr size_t faceSampledSections[kCubeFaceCount][kCubeFaceSectionCount] = { | |
| {3, 2, 1, 0}, {2, 3, 0, 1}, {0, 1, 2, 3}, {2, 3, 0, 1}, {2, 3, 0, 1}, {3, 2, 1, 0}, | |
| }; | |
| for (size_t section = 0; section < kCubeFaceSectionCount; ++section) | |
| { | |
| const GLColor sectionColor = faceColors[face][faceSampledSections[face][section]]; | |
| EXPECT_PIXEL_COLOR_EQ(face, section, sectionColor) | |
| << "face " << face << ", section " << section; | |
| } | |
| } | |
| EXPECT_GL_NO_ERROR(); | |
| } | |
| // Verify that cube map sampling follows the rules that map cubemap coordinates to coordinates | |
| // within each face. See section 3.7.5 of GLES2.0 (Cube Map Texture Selection). | |
| TEST_P(CubeMapTextureTest, SampleCoordinateTransform) | |
| { | |
| // http://anglebug.com/4092 | |
| ANGLE_SKIP_TEST_IF(IsWindows() && IsD3D9()); | |
| // Create a program that samples from 6x4 directions of the cubemap, draw and verify that the | |
| // colors match the right color from |faceColors|. | |
| constexpr char kFS[] = R"(precision mediump float; | |
| uniform samplerCube texCube; | |
| const mat4 coordInSection = mat4( | |
| vec4(-0.5, -0.5, 0, 0), | |
| vec4( 0.5, -0.5, 0, 0), | |
| vec4(-0.5, 0.5, 0, 0), | |
| vec4( 0.5, 0.5, 0, 0) | |
| ); | |
| void main() | |
| { | |
| vec3 coord; | |
| if (gl_FragCoord.x < 2.0) | |
| { | |
| coord.x = gl_FragCoord.x < 1.0 ? 1.0 : -1.0; | |
| coord.zy = coordInSection[int(gl_FragCoord.y)].xy; | |
| } | |
| else if (gl_FragCoord.x < 4.0) | |
| { | |
| coord.y = gl_FragCoord.x < 3.0 ? 1.0 : -1.0; | |
| coord.xz = coordInSection[int(gl_FragCoord.y)].xy; | |
| } | |
| else | |
| { | |
| coord.z = gl_FragCoord.x < 5.0 ? 1.0 : -1.0; | |
| coord.xy = coordInSection[int(gl_FragCoord.y)].xy; | |
| } | |
| gl_FragColor = textureCube(texCube, coord); | |
| })"; | |
| runSampleCoordinateTransformTest(kFS); | |
| } | |
| // On Android Vulkan, unequal x and y derivatives cause this test to fail. | |
| TEST_P(CubeMapTextureTest, SampleCoordinateTransformGrad) | |
| { | |
| ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan()); // anglebug.com/3814 | |
| ANGLE_SKIP_TEST_IF(IsD3D11()); // anglebug.com/3856 | |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_texture_lod")); | |
| // http://anglebug.com/4092 | |
| ANGLE_SKIP_TEST_IF(IsWindows() && IsD3D9()); | |
| constexpr char kFS[] = R"(#extension GL_EXT_shader_texture_lod : require | |
| precision mediump float; | |
| uniform samplerCube texCube; | |
| const mat4 coordInSection = mat4( | |
| vec4(-0.5, -0.5, 0, 0), | |
| vec4( 0.5, -0.5, 0, 0), | |
| vec4(-0.5, 0.5, 0, 0), | |
| vec4( 0.5, 0.5, 0, 0) | |
| ); | |
| void main() | |
| { | |
| vec3 coord; | |
| if (gl_FragCoord.x < 2.0) | |
| { | |
| coord.x = gl_FragCoord.x < 1.0 ? 1.0 : -1.0; | |
| coord.zy = coordInSection[int(gl_FragCoord.y)].xy; | |
| } | |
| else if (gl_FragCoord.x < 4.0) | |
| { | |
| coord.y = gl_FragCoord.x < 3.0 ? 1.0 : -1.0; | |
| coord.xz = coordInSection[int(gl_FragCoord.y)].xy; | |
| } | |
| else | |
| { | |
| coord.z = gl_FragCoord.x < 5.0 ? 1.0 : -1.0; | |
| coord.xy = coordInSection[int(gl_FragCoord.y)].xy; | |
| } | |
| gl_FragColor = textureCubeGradEXT(texCube, coord, | |
| vec3(10.0, 10.0, 0.0), vec3(0.0, 10.0, 10.0)); | |
| })"; | |
| runSampleCoordinateTransformTest(kFS); | |
| } | |
| // Use this to select which configurations (e.g. which renderer, which GLES major version) these | |
| // tests should be run against. | |
| ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(CubeMapTextureTest); |