| // |
| // 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); |