1 //
2 // Copyright 2015 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 #include "test_utils/ANGLETest.h"
8 #include "test_utils/gl_raii.h"
9
10 using namespace angle;
11
12 class CubeMapTextureTest : public ANGLETest<>
13 {
14 protected:
CubeMapTextureTest()15 CubeMapTextureTest()
16 {
17 setWindowWidth(256);
18 setWindowHeight(256);
19 setConfigRedBits(8);
20 setConfigGreenBits(8);
21 setConfigBlueBits(8);
22 setConfigAlphaBits(8);
23 }
24
testSetUp()25 void testSetUp() override
26 {
27 mProgram = CompileProgram(essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
28 if (mProgram == 0)
29 {
30 FAIL() << "shader compilation failed.";
31 }
32
33 mColorLocation = glGetUniformLocation(mProgram, essl1_shaders::ColorUniform());
34
35 glUseProgram(mProgram);
36
37 glClearColor(0, 0, 0, 0);
38 glClearDepthf(0.0);
39 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
40
41 glEnable(GL_BLEND);
42 glDisable(GL_DEPTH_TEST);
43
44 ASSERT_GL_NO_ERROR();
45 }
46
testTearDown()47 void testTearDown() override { glDeleteProgram(mProgram); }
48
49 void runSampleCoordinateTransformTest(const char *shader, const bool useES3);
50
51 GLuint mProgram;
52 GLint mColorLocation;
53 };
54
55 // Verify that rendering to the faces of a cube map consecutively will correctly render to each
56 // face.
TEST_P(CubeMapTextureTest,RenderToFacesConsecutively)57 TEST_P(CubeMapTextureTest, RenderToFacesConsecutively)
58 {
59 // TODO: Diagnose and fix. http://anglebug.com/42261648
60 ANGLE_SKIP_TEST_IF(IsVulkan() && IsIntel() && IsWindows());
61
62 // http://anglebug.com/42261821
63 ANGLE_SKIP_TEST_IF(IsVulkan() && IsIntel() && IsFuchsia());
64
65 const GLfloat faceColors[] = {
66 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
67 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f,
68 };
69
70 GLuint tex = 0;
71 glGenTextures(1, &tex);
72 glBindTexture(GL_TEXTURE_CUBE_MAP, tex);
73 for (GLenum face = 0; face < 6; face++)
74 {
75 glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, GL_RGBA, 1, 1, 0, GL_RGBA,
76 GL_UNSIGNED_BYTE, nullptr);
77 }
78 EXPECT_GL_NO_ERROR();
79
80 GLuint fbo = 0;
81 glGenFramebuffers(1, &fbo);
82 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
83 EXPECT_GL_NO_ERROR();
84
85 for (GLenum face = 0; face < 6; face++)
86 {
87 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
88 GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, tex, 0);
89 EXPECT_GL_NO_ERROR();
90
91 glUseProgram(mProgram);
92
93 const GLfloat *faceColor = faceColors + (face * 4);
94 glUniform4f(mColorLocation, faceColor[0], faceColor[1], faceColor[2], faceColor[3]);
95
96 drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f);
97 EXPECT_GL_NO_ERROR();
98 }
99
100 for (GLenum face = 0; face < 6; face++)
101 {
102 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
103 GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, tex, 0);
104 EXPECT_GL_NO_ERROR();
105
106 const GLfloat *faceColor = faceColors + (face * 4);
107 EXPECT_PIXEL_EQ(0, 0, faceColor[0] * 255, faceColor[1] * 255, faceColor[2] * 255,
108 faceColor[3] * 255);
109 EXPECT_GL_NO_ERROR();
110 }
111
112 glDeleteFramebuffers(1, &fbo);
113 glDeleteTextures(1, &tex);
114
115 EXPECT_GL_NO_ERROR();
116 }
117
runSampleCoordinateTransformTest(const char * shader,const bool useES3)118 void CubeMapTextureTest::runSampleCoordinateTransformTest(const char *shader, const bool useES3)
119 {
120 // Fails to compile the shader. anglebug.com/42262420
121 ANGLE_SKIP_TEST_IF(IsOpenGL() && IsIntel() && IsWindows());
122
123 constexpr GLsizei kCubeFaceCount = 6;
124 constexpr GLsizei kCubeFaceSectionCount = 4;
125 constexpr GLsizei kCubeFaceSectionCountSqrt = 2;
126
127 constexpr GLColor faceColors[kCubeFaceCount][kCubeFaceSectionCount] = {
128 {GLColor(255, 0, 0, 255), GLColor(191, 0, 0, 255), GLColor(127, 0, 0, 255),
129 GLColor(63, 0, 0, 255)},
130 {GLColor(0, 255, 0, 255), GLColor(0, 191, 0, 255), GLColor(0, 127, 0, 255),
131 GLColor(0, 63, 0, 255)},
132 {GLColor(0, 0, 255, 255), GLColor(0, 0, 191, 255), GLColor(0, 0, 127, 255),
133 GLColor(0, 0, 63, 255)},
134 {GLColor(255, 63, 0, 255), GLColor(191, 127, 0, 255), GLColor(127, 191, 0, 255),
135 GLColor(63, 255, 0, 255)},
136 {GLColor(0, 255, 63, 255), GLColor(0, 191, 127, 255), GLColor(0, 127, 191, 255),
137 GLColor(0, 63, 255, 255)},
138 {GLColor(63, 0, 255, 255), GLColor(127, 0, 191, 255), GLColor(191, 0, 127, 255),
139 GLColor(255, 0, 63, 255)},
140 };
141
142 constexpr GLsizei kTextureSize = 32;
143
144 GLTexture tex;
145 glBindTexture(GL_TEXTURE_CUBE_MAP, tex);
146 for (GLenum face = 0; face < kCubeFaceCount; face++)
147 {
148 std::vector<GLColor> faceData(kTextureSize * kTextureSize);
149
150 // Create the face with four sections, each with a solid color from |faceColors|.
151 for (size_t row = 0; row < kTextureSize / kCubeFaceSectionCountSqrt; ++row)
152 {
153 for (size_t col = 0; col < kTextureSize / kCubeFaceSectionCountSqrt; ++col)
154 {
155 for (size_t srow = 0; srow < kCubeFaceSectionCountSqrt; ++srow)
156 {
157 for (size_t scol = 0; scol < kCubeFaceSectionCountSqrt; ++scol)
158 {
159 size_t r = row + srow * kTextureSize / kCubeFaceSectionCountSqrt;
160 size_t c = col + scol * kTextureSize / kCubeFaceSectionCountSqrt;
161 size_t s = srow * kCubeFaceSectionCountSqrt + scol;
162 faceData[r * kTextureSize + c] = faceColors[face][s];
163 }
164 }
165 }
166 }
167
168 glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, GL_RGBA, kTextureSize, kTextureSize,
169 0, GL_RGBA, GL_UNSIGNED_BYTE, faceData.data());
170 }
171 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
172 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
173 EXPECT_GL_NO_ERROR();
174
175 GLTexture fboTex;
176 glBindTexture(GL_TEXTURE_2D, fboTex);
177 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kCubeFaceCount, kCubeFaceSectionCount, 0, GL_RGBA,
178 GL_UNSIGNED_BYTE, nullptr);
179
180 GLFramebuffer fbo;
181 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
182 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboTex, 0);
183 EXPECT_GL_NO_ERROR();
184
185 ANGLE_GL_PROGRAM(program, useES3 ? essl3_shaders::vs::Simple() : essl1_shaders::vs::Simple(),
186 shader);
187 glUseProgram(program);
188
189 GLint texCubeLocation = glGetUniformLocation(program, "texCube");
190 ASSERT_NE(-1, texCubeLocation);
191 glUniform1i(texCubeLocation, 0);
192
193 drawQuad(program, useES3 ? essl3_shaders::PositionAttrib() : essl1_shaders::PositionAttrib(),
194 0.5f);
195 EXPECT_GL_NO_ERROR();
196
197 for (GLenum face = 0; face < kCubeFaceCount; face++)
198 {
199 // The following table defines the translation from textureCube coordinates to coordinates
200 // in each face. The framebuffer has width 6 and height 4. Every column corresponding to
201 // an x value represents one cube face. The values in rows are samples from the four
202 // sections of the face.
203 //
204 // Major Axis Direction Target sc tc ma
205 // +rx TEXTURE_CUBE_MAP_POSITIVE_X −rz −ry rx
206 // −rx TEXTURE_CUBE_MAP_NEGATIVE_X rz −ry rx
207 // +ry TEXTURE_CUBE_MAP_POSITIVE_Y rx rz ry
208 // −ry TEXTURE_CUBE_MAP_NEGATIVE_Y rx −rz ry
209 // +rz TEXTURE_CUBE_MAP_POSITIVE_Z rx −ry rz
210 // −rz TEXTURE_CUBE_MAP_NEGATIVE_Z −rx −ry rz
211 //
212 // This table is used only to determine the direction of growth for s and t. The shader
213 // always generates (row,col) coordinates (0, 0), (0, 1), (1, 0), (1, 1) which is the order
214 // the data is uploaded to the faces, but based on the table above, the sample order would
215 // be different.
216 constexpr size_t faceSampledSections[kCubeFaceCount][kCubeFaceSectionCount] = {
217 {3, 2, 1, 0}, {2, 3, 0, 1}, {0, 1, 2, 3}, {2, 3, 0, 1}, {2, 3, 0, 1}, {3, 2, 1, 0},
218 };
219
220 for (size_t section = 0; section < kCubeFaceSectionCount; ++section)
221 {
222 const GLColor sectionColor = faceColors[face][faceSampledSections[face][section]];
223
224 EXPECT_PIXEL_COLOR_EQ(face, section, sectionColor)
225 << "face " << face << ", section " << section;
226 }
227 }
228 EXPECT_GL_NO_ERROR();
229 }
230
231 // Verify that cube map sampling follows the rules that map cubemap coordinates to coordinates
232 // within each face. See section 3.7.5 of GLES2.0 (Cube Map Texture Selection).
TEST_P(CubeMapTextureTest,SampleCoordinateTransform)233 TEST_P(CubeMapTextureTest, SampleCoordinateTransform)
234 {
235 // http://anglebug.com/40096654
236 ANGLE_SKIP_TEST_IF(IsWindows() && IsD3D9());
237 // Create a program that samples from 6x4 directions of the cubemap, draw and verify that the
238 // colors match the right color from |faceColors|.
239 constexpr char kFS[] = R"(precision mediump float;
240
241 uniform samplerCube texCube;
242
243 const mat4 coordInSection = mat4(
244 vec4(-0.5, -0.5, 0, 0),
245 vec4( 0.5, -0.5, 0, 0),
246 vec4(-0.5, 0.5, 0, 0),
247 vec4( 0.5, 0.5, 0, 0)
248 );
249
250 void main()
251 {
252 vec3 coord;
253 if (gl_FragCoord.x < 2.0)
254 {
255 coord.x = gl_FragCoord.x < 1.0 ? 1.0 : -1.0;
256 coord.zy = coordInSection[int(gl_FragCoord.y)].xy;
257 }
258 else if (gl_FragCoord.x < 4.0)
259 {
260 coord.y = gl_FragCoord.x < 3.0 ? 1.0 : -1.0;
261 coord.xz = coordInSection[int(gl_FragCoord.y)].xy;
262 }
263 else
264 {
265 coord.z = gl_FragCoord.x < 5.0 ? 1.0 : -1.0;
266 coord.xy = coordInSection[int(gl_FragCoord.y)].xy;
267 }
268
269 gl_FragColor = textureCube(texCube, coord);
270 })";
271
272 runSampleCoordinateTransformTest(kFS, false);
273 }
274
275 // On Android Vulkan, unequal x and y derivatives cause this test to fail.
TEST_P(CubeMapTextureTest,SampleCoordinateTransformGrad)276 TEST_P(CubeMapTextureTest, SampleCoordinateTransformGrad)
277 {
278 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_texture_lod"));
279
280 constexpr char kFS[] = R"(#extension GL_EXT_shader_texture_lod : require
281 precision mediump float;
282
283 uniform samplerCube texCube;
284
285 const mat4 coordInSection = mat4(
286 vec4(-0.5, -0.5, 0, 0),
287 vec4( 0.5, -0.5, 0, 0),
288 vec4(-0.5, 0.5, 0, 0),
289 vec4( 0.5, 0.5, 0, 0)
290 );
291
292 void main()
293 {
294 vec3 coord;
295 if (gl_FragCoord.x < 2.0)
296 {
297 coord.x = gl_FragCoord.x < 1.0 ? 1.0 : -1.0;
298 coord.zy = coordInSection[int(gl_FragCoord.y)].xy;
299 }
300 else if (gl_FragCoord.x < 4.0)
301 {
302 coord.y = gl_FragCoord.x < 3.0 ? 1.0 : -1.0;
303 coord.xz = coordInSection[int(gl_FragCoord.y)].xy;
304 }
305 else
306 {
307 coord.z = gl_FragCoord.x < 5.0 ? 1.0 : -1.0;
308 coord.xy = coordInSection[int(gl_FragCoord.y)].xy;
309 }
310
311 gl_FragColor = textureCubeGradEXT(texCube, coord,
312 vec3(10.0, 10.0, 0.0), vec3(0.0, 10.0, 10.0));
313 })";
314
315 runSampleCoordinateTransformTest(kFS, false);
316 }
317
318 // Same as the previous but uses the ES 3.0 explicit gradient function.
TEST_P(CubeMapTextureTest,SampleCoordinateTransformGrad_ES3)319 TEST_P(CubeMapTextureTest, SampleCoordinateTransformGrad_ES3)
320 {
321 ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3);
322
323 constexpr char kFS[] = R"(#version 300 es
324 precision mediump float;
325
326 uniform samplerCube texCube;
327 out vec4 my_FragColor;
328
329 const mat4 coordInSection = mat4(
330 vec4(-0.5, -0.5, 0, 0),
331 vec4( 0.5, -0.5, 0, 0),
332 vec4(-0.5, 0.5, 0, 0),
333 vec4( 0.5, 0.5, 0, 0)
334 );
335
336 void main()
337 {
338 vec3 coord;
339 if (gl_FragCoord.x < 2.0)
340 {
341 coord.x = gl_FragCoord.x < 1.0 ? 1.0 : -1.0;
342 coord.zy = coordInSection[int(gl_FragCoord.y)].xy;
343 }
344 else if (gl_FragCoord.x < 4.0)
345 {
346 coord.y = gl_FragCoord.x < 3.0 ? 1.0 : -1.0;
347 coord.xz = coordInSection[int(gl_FragCoord.y)].xy;
348 }
349 else
350 {
351 coord.z = gl_FragCoord.x < 5.0 ? 1.0 : -1.0;
352 coord.xy = coordInSection[int(gl_FragCoord.y)].xy;
353 }
354
355 my_FragColor = textureGrad(texCube, coord,
356 vec3(10.0, 10.0, 0.0), vec3(0.0, 10.0, 10.0));
357 })";
358
359 runSampleCoordinateTransformTest(kFS, true);
360 }
361
362 // Use this to select which configurations (e.g. which renderer, which GLES major version) these
363 // tests should be run against.
364 ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(CubeMapTextureTest);
365