• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 namespace angle
11 {
12 
13 // These two colors are equivelent in different colorspaces
14 constexpr GLColor kLinearColor(64, 127, 191, 255);
15 constexpr GLColor kNonlinearColor(13, 54, 133, 255);
16 
17 class SRGBTextureTest : public ANGLETest
18 {
19   protected:
SRGBTextureTest()20     SRGBTextureTest()
21     {
22         setWindowWidth(128);
23         setWindowHeight(128);
24         setConfigRedBits(8);
25         setConfigGreenBits(8);
26         setConfigBlueBits(8);
27         setConfigAlphaBits(8);
28     }
29 
testSetUp()30     void testSetUp() override
31     {
32         constexpr char kVS[] =
33             "precision highp float;\n"
34             "attribute vec4 position;\n"
35             "varying vec2 texcoord;\n"
36             "\n"
37             "void main()\n"
38             "{\n"
39             "   gl_Position = vec4(position.xy, 0.0, 1.0);\n"
40             "   texcoord = (position.xy * 0.5) + 0.5;\n"
41             "}\n";
42 
43         constexpr char kFS[] =
44             "precision highp float;\n"
45             "uniform sampler2D tex;\n"
46             "varying vec2 texcoord;\n"
47             "\n"
48             "void main()\n"
49             "{\n"
50             "   gl_FragColor = texture2D(tex, texcoord);\n"
51             "}\n";
52 
53         mProgram = CompileProgram(kVS, kFS);
54         ASSERT_NE(0u, mProgram);
55 
56         mTextureLocation = glGetUniformLocation(mProgram, "tex");
57         ASSERT_NE(-1, mTextureLocation);
58     }
59 
testTearDown()60     void testTearDown() override { glDeleteProgram(mProgram); }
61 
getSRGBA8TextureInternalFormat() const62     GLenum getSRGBA8TextureInternalFormat() const
63     {
64         return getClientMajorVersion() >= 3 ? GL_SRGB8_ALPHA8 : GL_SRGB_ALPHA_EXT;
65     }
66 
getSRGBA8TextureFormat() const67     GLenum getSRGBA8TextureFormat() const
68     {
69         return getClientMajorVersion() >= 3 ? GL_RGBA : GL_SRGB_ALPHA_EXT;
70     }
71 
getSRGB8TextureInternalFormat() const72     GLenum getSRGB8TextureInternalFormat() const
73     {
74         return getClientMajorVersion() >= 3 ? GL_SRGB8 : GL_SRGB_EXT;
75     }
76 
getSRGB8TextureFormat() const77     GLenum getSRGB8TextureFormat() const
78     {
79         return getClientMajorVersion() >= 3 ? GL_RGB : GL_SRGB_EXT;
80     }
81 
82     GLuint mProgram        = 0;
83     GLint mTextureLocation = -1;
84 };
85 
86 // GenerateMipmaps should generate INVALID_OPERATION in ES 2.0 / WebGL 1.0 with EXT_sRGB.
87 // https://bugs.chromium.org/p/chromium/issues/detail?id=769989
TEST_P(SRGBTextureTest,SRGBValidation)88 TEST_P(SRGBTextureTest, SRGBValidation)
89 {
90     // TODO(fjhenigman): Figure out why this fails on Ozone Intel.
91     ANGLE_SKIP_TEST_IF(IsOzone() && IsIntel() && IsOpenGLES());
92 
93     bool supported = IsGLExtensionEnabled("GL_EXT_sRGB") || getClientMajorVersion() == 3;
94 
95     GLuint tex = 0;
96     glGenTextures(1, &tex);
97     glBindTexture(GL_TEXTURE_2D, tex);
98 
99     GLubyte pixel[3] = {0};
100     glTexImage2D(GL_TEXTURE_2D, 0, getSRGB8TextureInternalFormat(), 1, 1, 0,
101                  getSRGB8TextureFormat(), GL_UNSIGNED_BYTE, pixel);
102     if (supported)
103     {
104         EXPECT_GL_NO_ERROR();
105 
106         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, getSRGB8TextureFormat(), GL_UNSIGNED_BYTE,
107                         pixel);
108         EXPECT_GL_NO_ERROR();
109 
110         // Mipmap generation always generates errors for SRGB unsized in ES2 or SRGB8 sized in ES3.
111         glGenerateMipmap(GL_TEXTURE_2D);
112         EXPECT_GL_ERROR(GL_INVALID_OPERATION);
113     }
114     else
115     {
116         EXPECT_GL_ERROR(GL_INVALID_ENUM);
117     }
118 
119     glDeleteTextures(1, &tex);
120 }
121 
TEST_P(SRGBTextureTest,SRGBAValidation)122 TEST_P(SRGBTextureTest, SRGBAValidation)
123 {
124     // TODO(fjhenigman): Figure out why this fails on Ozone Intel.
125     ANGLE_SKIP_TEST_IF(IsOzone() && IsIntel() && IsOpenGLES());
126 
127     bool supported = IsGLExtensionEnabled("GL_EXT_sRGB") || getClientMajorVersion() == 3;
128 
129     GLuint tex = 0;
130     glGenTextures(1, &tex);
131     glBindTexture(GL_TEXTURE_2D, tex);
132 
133     GLubyte pixel[4] = {0};
134     glTexImage2D(GL_TEXTURE_2D, 0, getSRGBA8TextureInternalFormat(), 1, 1, 0,
135                  getSRGBA8TextureFormat(), GL_UNSIGNED_BYTE, pixel);
136     if (supported)
137     {
138         EXPECT_GL_NO_ERROR();
139 
140         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, getSRGBA8TextureFormat(), GL_UNSIGNED_BYTE,
141                         pixel);
142         EXPECT_GL_NO_ERROR();
143 
144         glGenerateMipmap(GL_TEXTURE_2D);
145         if (getClientMajorVersion() < 3)
146         {
147             EXPECT_GL_ERROR(GL_INVALID_OPERATION);
148         }
149         else
150         {
151             EXPECT_GL_NO_ERROR();
152         }
153     }
154     else
155     {
156         EXPECT_GL_ERROR(GL_INVALID_ENUM);
157     }
158 
159     glDeleteTextures(1, &tex);
160 }
161 
162 // Test that sized SRGBA formats allow generating mipmaps
TEST_P(SRGBTextureTest,SRGBASizedValidation)163 TEST_P(SRGBTextureTest, SRGBASizedValidation)
164 {
165     // TODO(fjhenigman): Figure out why this fails on Ozone Intel.
166     ANGLE_SKIP_TEST_IF(IsOzone() && IsIntel() && IsOpenGLES());
167 
168     // ES3 required for sized SRGB textures
169     ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3);
170 
171     GLTexture tex;
172     glBindTexture(GL_TEXTURE_2D, tex);
173 
174     GLubyte pixel[4] = {0};
175     glTexImage2D(GL_TEXTURE_2D, 0, getSRGBA8TextureInternalFormat(), 1, 1, 0,
176                  getSRGBA8TextureFormat(), GL_UNSIGNED_BYTE, pixel);
177 
178     EXPECT_GL_NO_ERROR();
179 
180     glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
181     EXPECT_GL_NO_ERROR();
182 
183     glGenerateMipmap(GL_TEXTURE_2D);
184     EXPECT_GL_NO_ERROR();
185 }
186 
TEST_P(SRGBTextureTest,SRGBARenderbuffer)187 TEST_P(SRGBTextureTest, SRGBARenderbuffer)
188 {
189     bool supported = IsGLExtensionEnabled("GL_EXT_sRGB") || getClientMajorVersion() == 3;
190 
191     GLuint rbo = 0;
192     glGenRenderbuffers(1, &rbo);
193     glBindRenderbuffer(GL_RENDERBUFFER, rbo);
194 
195     glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8_ALPHA8_EXT, 1, 1);
196     if (supported)
197     {
198         EXPECT_GL_NO_ERROR();
199     }
200     else
201     {
202         EXPECT_GL_ERROR(GL_INVALID_ENUM);
203 
204         // Make sure the rbo has a size for future tests
205         glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_OES, 1, 1);
206         EXPECT_GL_NO_ERROR();
207     }
208 
209     GLuint fbo = 0;
210     glGenFramebuffers(1, &fbo);
211     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
212     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo);
213     EXPECT_GL_NO_ERROR();
214 
215     GLint colorEncoding = 0;
216     glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
217                                           GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT,
218                                           &colorEncoding);
219     if (supported)
220     {
221         EXPECT_GL_NO_ERROR();
222         EXPECT_EQ(GL_SRGB_EXT, colorEncoding);
223     }
224     else
225     {
226         EXPECT_GL_ERROR(GL_INVALID_ENUM);
227     }
228 
229     glDeleteFramebuffers(1, &fbo);
230     glDeleteRenderbuffers(1, &rbo);
231 }
232 
233 // Verify that if the srgb decode extension is available, srgb textures are too
TEST_P(SRGBTextureTest,SRGBDecodeExtensionAvailability)234 TEST_P(SRGBTextureTest, SRGBDecodeExtensionAvailability)
235 {
236     bool hasSRGBDecode = IsGLExtensionEnabled("GL_EXT_texture_sRGB_decode");
237     if (hasSRGBDecode)
238     {
239         bool hasSRGBTextures = IsGLExtensionEnabled("GL_EXT_sRGB") || getClientMajorVersion() >= 3;
240         EXPECT_TRUE(hasSRGBTextures);
241     }
242 }
243 
244 // Test basic functionality of SRGB decode using the texture parameter
TEST_P(SRGBTextureTest,SRGBDecodeTextureParameter)245 TEST_P(SRGBTextureTest, SRGBDecodeTextureParameter)
246 {
247     // TODO(fjhenigman): Figure out why this fails on Ozone Intel.
248     ANGLE_SKIP_TEST_IF(IsOzone() && IsIntel() && IsOpenGLES());
249 
250     ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_sRGB_decode"));
251 
252     GLColor linearColor = kLinearColor;
253     GLColor srgbColor   = kNonlinearColor;
254 
255     GLTexture tex;
256     glBindTexture(GL_TEXTURE_2D, tex.get());
257     glTexImage2D(GL_TEXTURE_2D, 0, getSRGBA8TextureInternalFormat(), 1, 1, 0,
258                  getSRGBA8TextureFormat(), GL_UNSIGNED_BYTE, &linearColor);
259     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SRGB_DECODE_EXT, GL_DECODE_EXT);
260     ASSERT_GL_NO_ERROR();
261 
262     glUseProgram(mProgram);
263     glUniform1i(mTextureLocation, 0);
264 
265     glDisable(GL_DEPTH_TEST);
266     drawQuad(mProgram, "position", 0.5f);
267 
268     EXPECT_PIXEL_COLOR_NEAR(0, 0, srgbColor, 1.0);
269 
270     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
271     drawQuad(mProgram, "position", 0.5f);
272 
273     EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
274 }
275 
276 // Test basic functionality of SRGB override using the texture parameter
TEST_P(SRGBTextureTest,SRGBOverrideTextureParameter)277 TEST_P(SRGBTextureTest, SRGBOverrideTextureParameter)
278 {
279     ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_sRGB_override"));
280 
281     GLColor linearColor = kLinearColor;
282     GLColor srgbColor   = kNonlinearColor;
283 
284     GLenum internalFormat = getClientMajorVersion() >= 3 ? GL_RGBA8 : GL_RGBA;
285 
286     GLTexture tex;
287     glBindTexture(GL_TEXTURE_2D, tex.get());
288     glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
289                  &linearColor);
290     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_FORMAT_SRGB_OVERRIDE_EXT, GL_NONE);
291     ASSERT_GL_NO_ERROR();
292 
293     glUseProgram(mProgram);
294     glUniform1i(mTextureLocation, 0);
295 
296     glDisable(GL_DEPTH_TEST);
297     drawQuad(mProgram, "position", 0.5f);
298 
299     EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
300 
301     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_FORMAT_SRGB_OVERRIDE_EXT, GL_SRGB);
302     drawQuad(mProgram, "position", 0.5f);
303 
304     EXPECT_PIXEL_COLOR_NEAR(0, 0, srgbColor, 1.0);
305 }
306 
307 // Test that SRGB override is a noop when used on a nonlinear texture format
308 // EXT_texture_format_sRGB_override spec says:
309 // "If the internal format is not one of the above formats, then
310 // the value of TEXTURE_FORMAT_SRGB_OVERRIDE_EXT is ignored."
TEST_P(SRGBTextureTest,SRGBOverrideTextureParameterNoop)311 TEST_P(SRGBTextureTest, SRGBOverrideTextureParameterNoop)
312 {
313     ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_sRGB_override"));
314     ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_sRGB") || getClientMajorVersion() < 3);
315 
316     GLColor linearColor = kLinearColor;
317     GLColor srgbColor   = kNonlinearColor;
318 
319     GLTexture tex;
320     glBindTexture(GL_TEXTURE_2D, tex.get());
321     glTexImage2D(GL_TEXTURE_2D, 0, getSRGBA8TextureInternalFormat(), 1, 1, 0,
322                  getSRGBA8TextureFormat(), GL_UNSIGNED_BYTE, &linearColor);
323     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_FORMAT_SRGB_OVERRIDE_EXT, GL_NONE);
324     ASSERT_GL_NO_ERROR();
325 
326     glUseProgram(mProgram);
327     glUniform1i(mTextureLocation, 0);
328 
329     glDisable(GL_DEPTH_TEST);
330     drawQuad(mProgram, "position", 0.5f);
331 
332     EXPECT_PIXEL_COLOR_NEAR(0, 0, srgbColor, 1.0);
333 
334     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_FORMAT_SRGB_OVERRIDE_EXT, GL_SRGB);
335     drawQuad(mProgram, "position", 0.5f);
336 
337     EXPECT_PIXEL_COLOR_NEAR(0, 0, srgbColor, 1.0);
338 }
339 
340 // Test basic functionality of SRGB decode using the sampler parameter
TEST_P(SRGBTextureTest,SRGBDecodeSamplerParameter)341 TEST_P(SRGBTextureTest, SRGBDecodeSamplerParameter)
342 {
343     ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_sRGB_decode") ||
344                        getClientMajorVersion() < 3);
345 
346     GLColor linearColor = kLinearColor;
347     GLColor srgbColor   = kNonlinearColor;
348 
349     GLTexture tex;
350     glBindTexture(GL_TEXTURE_2D, tex.get());
351     glTexImage2D(GL_TEXTURE_2D, 0, getSRGBA8TextureInternalFormat(), 1, 1, 0,
352                  getSRGBA8TextureFormat(), GL_UNSIGNED_BYTE, &linearColor);
353     ASSERT_GL_NO_ERROR();
354 
355     GLSampler sampler;
356     glBindSampler(0, sampler.get());
357     glSamplerParameteri(sampler.get(), GL_TEXTURE_SRGB_DECODE_EXT, GL_DECODE_EXT);
358 
359     glUseProgram(mProgram);
360     glUniform1i(mTextureLocation, 0);
361 
362     glDisable(GL_DEPTH_TEST);
363     drawQuad(mProgram, "position", 0.5f);
364 
365     EXPECT_PIXEL_COLOR_NEAR(0, 0, srgbColor, 1.0);
366 
367     glSamplerParameteri(sampler.get(), GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
368     drawQuad(mProgram, "position", 0.5f);
369 
370     EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
371 }
372 
373 // Test that mipmaps are generated correctly for sRGB textures
TEST_P(SRGBTextureTest,GenerateMipmaps)374 TEST_P(SRGBTextureTest, GenerateMipmaps)
375 {
376     ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3);
377 
378     ANGLE_SKIP_TEST_IF(IsOpenGL() && ((IsIntel() && IsOSX()) || IsAMD()));
379 
380     auto createAndReadBackTexture = [this](GLenum internalFormat, const GLColor &color) {
381         constexpr GLsizei width  = 128;
382         constexpr GLsizei height = 128;
383 
384         std::array<GLColor, width * height> buf;
385         std::fill(buf.begin(), buf.end(), color);
386 
387         // Set up-left region of the texture as red color.
388         // In order to make sure bi-linear interpolation operates on different colors, red region
389         // is 1 pixel smaller than a quarter of the full texture on each side.
390         constexpr GLsizei redWidth  = width / 2 - 1;
391         constexpr GLsizei redHeight = height / 2 - 1;
392         std::array<GLColor, redWidth * redHeight> redBuf;
393         std::fill(redBuf.begin(), redBuf.end(), GLColor::red);
394 
395         GLTexture tex;
396         glBindTexture(GL_TEXTURE_2D, tex.get());
397         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
398         glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE,
399                      buf.data());
400         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, redWidth, redHeight, GL_RGBA, GL_UNSIGNED_BYTE,
401                         redBuf.data());
402         glGenerateMipmap(GL_TEXTURE_2D);
403 
404         constexpr GLsizei drawWidth  = 32;
405         constexpr GLsizei drawHeight = 32;
406         glViewport(0, 0, drawWidth, drawHeight);
407 
408         drawQuad(mProgram, "position", 0.5f);
409 
410         std::array<GLColor, drawWidth * drawHeight> result;
411         glReadPixels(0, 0, drawWidth, drawHeight, GL_RGBA, GL_UNSIGNED_BYTE, result.data());
412 
413         EXPECT_GL_NO_ERROR();
414 
415         return result;
416     };
417 
418     GLColor srgbaColor(0, 63, 127, 255);
419     auto srgbaReadback = createAndReadBackTexture(GL_SRGB8_ALPHA8, srgbaColor);
420 
421     GLColor linearColor(0, 13, 54, 255);
422     auto rgbaReadback = createAndReadBackTexture(GL_RGBA8, linearColor);
423 
424     ASSERT_EQ(srgbaReadback.size(), rgbaReadback.size());
425     for (size_t i = 0; i < srgbaReadback.size(); i++)
426     {
427         constexpr double tolerence = 7.0;
428         EXPECT_COLOR_NEAR(srgbaReadback[i], rgbaReadback[i], tolerence);
429     }
430 }
431 
432 // Use this to select which configurations (e.g. which renderer, which GLES major version) these
433 // tests should be run against.
434 ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(SRGBTextureTest);
435 
436 }  // namespace angle
437