1 //
2 // Copyright 2016 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 // SRGBFramebufferTest.cpp: Tests of sRGB framebuffer functionality.
8
9 #include "test_utils/ANGLETest.h"
10 #include "test_utils/gl_raii.h"
11
12 namespace
13 {
14 constexpr angle::GLColor linearColor(64, 127, 191, 255);
15 constexpr angle::GLColor srgbColor(13, 54, 133, 255);
16 } // namespace
17
18 namespace angle
19 {
20
21 class SRGBFramebufferTest : public ANGLETest<>
22 {
23 protected:
SRGBFramebufferTest()24 SRGBFramebufferTest()
25 {
26 setWindowWidth(128);
27 setWindowHeight(128);
28 setConfigRedBits(8);
29 setConfigGreenBits(8);
30 setConfigBlueBits(8);
31 setConfigAlphaBits(8);
32 }
33
testSetUp()34 void testSetUp() override
35 {
36 mProgram = CompileProgram(essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
37 ASSERT_NE(0u, mProgram);
38
39 mColorLocation = glGetUniformLocation(mProgram, essl1_shaders::ColorUniform());
40 ASSERT_NE(-1, mColorLocation);
41 }
42
testTearDown()43 void testTearDown() override { glDeleteProgram(mProgram); }
44
45 GLuint mProgram = 0;
46 GLint mColorLocation = -1;
47 };
48
49 class SRGBFramebufferTestES3 : public SRGBFramebufferTest
50 {};
51
52 // Test basic validation of GL_EXT_sRGB_write_control
TEST_P(SRGBFramebufferTest,Validation)53 TEST_P(SRGBFramebufferTest, Validation)
54 {
55 GLenum expectedError =
56 IsGLExtensionEnabled("GL_EXT_sRGB_write_control") ? GL_NO_ERROR : GL_INVALID_ENUM;
57
58 GLboolean value = GL_FALSE;
59 glEnable(GL_FRAMEBUFFER_SRGB_EXT);
60 EXPECT_GL_ERROR(expectedError);
61
62 glGetBooleanv(GL_FRAMEBUFFER_SRGB_EXT, &value);
63 EXPECT_GL_ERROR(expectedError);
64 if (expectedError == GL_NO_ERROR)
65 {
66 EXPECT_GL_TRUE(value);
67 }
68
69 glDisable(GL_FRAMEBUFFER_SRGB_EXT);
70 EXPECT_GL_ERROR(expectedError);
71
72 glGetBooleanv(GL_FRAMEBUFFER_SRGB_EXT, &value);
73 EXPECT_GL_ERROR(expectedError);
74 if (expectedError == GL_NO_ERROR)
75 {
76 EXPECT_GL_FALSE(value);
77 }
78 }
79
80 // Test basic functionality of GL_EXT_sRGB_write_control
TEST_P(SRGBFramebufferTest,BasicUsage)81 TEST_P(SRGBFramebufferTest, BasicUsage)
82 {
83 if (!IsGLExtensionEnabled("GL_EXT_sRGB_write_control") ||
84 (!IsGLExtensionEnabled("GL_EXT_sRGB") && getClientMajorVersion() < 3))
85 {
86 std::cout
87 << "Test skipped because GL_EXT_sRGB_write_control and GL_EXT_sRGB are not available."
88 << std::endl;
89 return;
90 }
91
92 GLTexture texture;
93 glBindTexture(GL_TEXTURE_2D, texture);
94 glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB_ALPHA_EXT, 1, 1, 0, GL_SRGB_ALPHA_EXT, GL_UNSIGNED_BYTE,
95 nullptr);
96
97 GLFramebuffer framebuffer;
98 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
99 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
100
101 glUseProgram(mProgram);
102 glUniform4fv(mColorLocation, 1, srgbColor.toNormalizedVector().data());
103
104 glEnable(GL_FRAMEBUFFER_SRGB_EXT);
105 drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f);
106 EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
107
108 glDisable(GL_FRAMEBUFFER_SRGB_EXT);
109 drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f);
110 EXPECT_PIXEL_COLOR_NEAR(0, 0, srgbColor, 1.0);
111 }
112
113 // Test that GL_EXT_sRGB_write_control state applies to all framebuffers if multiple are used
114 // 1. disable srgb
115 // 2. draw to both framebuffers
116 // 3. enable srgb
117 // 4. draw to both framebuffers
TEST_P(SRGBFramebufferTest,MultipleFramebuffers)118 TEST_P(SRGBFramebufferTest, MultipleFramebuffers)
119 {
120 if (!IsGLExtensionEnabled("GL_EXT_sRGB_write_control") ||
121 (!IsGLExtensionEnabled("GL_EXT_sRGB") && getClientMajorVersion() < 3))
122 {
123 std::cout
124 << "Test skipped because GL_EXT_sRGB_write_control and GL_EXT_sRGB are not available."
125 << std::endl;
126 return;
127 }
128
129 // NVIDIA failures on older drivers
130 // http://anglebug.com/42264177
131 ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsOpenGLES());
132
133 GLTexture texture;
134 glBindTexture(GL_TEXTURE_2D, texture);
135 glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB_ALPHA_EXT, 1, 1, 0, GL_SRGB_ALPHA_EXT, GL_UNSIGNED_BYTE,
136 nullptr);
137
138 GLFramebuffer framebuffer1;
139 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer1);
140 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
141
142 glUseProgram(mProgram);
143 glUniform4fv(mColorLocation, 1, srgbColor.toNormalizedVector().data());
144
145 glDisable(GL_FRAMEBUFFER_SRGB_EXT);
146 drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f);
147 EXPECT_PIXEL_COLOR_NEAR(0, 0, srgbColor, 1.0);
148
149 GLFramebuffer framebuffer2;
150 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer2);
151 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
152 drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f);
153 EXPECT_PIXEL_COLOR_NEAR(0, 0, srgbColor, 1.0);
154
155 glEnable(GL_FRAMEBUFFER_SRGB_EXT);
156 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer1);
157 drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f);
158 EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
159
160 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer2);
161 drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f);
162 EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
163 }
164
165 // Test that we behave correctly when we toggle FRAMEBUFFER_SRGB_EXT on a framebuffer that has an
166 // attachment in linear colorspace
TEST_P(SRGBFramebufferTest,NegativeAlreadyLinear)167 TEST_P(SRGBFramebufferTest, NegativeAlreadyLinear)
168 {
169 if (!IsGLExtensionEnabled("GL_EXT_sRGB_write_control") ||
170 (!IsGLExtensionEnabled("GL_EXT_sRGB") && getClientMajorVersion() < 3))
171 {
172 std::cout
173 << "Test skipped because GL_EXT_sRGB_write_control and GL_EXT_sRGB are not available."
174 << std::endl;
175 return;
176 }
177
178 GLTexture texture;
179 glBindTexture(GL_TEXTURE_2D, texture);
180 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
181
182 GLFramebuffer framebuffer;
183 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
184 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
185
186 glUseProgram(mProgram);
187 glUniform4fv(mColorLocation, 1, linearColor.toNormalizedVector().data());
188
189 glEnable(GL_FRAMEBUFFER_SRGB_EXT);
190 drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f);
191 EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
192
193 glDisable(GL_FRAMEBUFFER_SRGB_EXT);
194 drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f);
195 EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
196 }
197
198 // Test that lifetimes of internal resources are tracked correctly by deleting a texture and then
199 // attempting to use it. This is expected to produce a non-fatal error.
TEST_P(SRGBFramebufferTest,NegativeLifetimeTracking)200 TEST_P(SRGBFramebufferTest, NegativeLifetimeTracking)
201 {
202 if (!IsGLExtensionEnabled("GL_EXT_sRGB_write_control") ||
203 (!IsGLExtensionEnabled("GL_EXT_sRGB") && getClientMajorVersion() < 3))
204 {
205 std::cout
206 << "Test skipped because GL_EXT_sRGB_write_control and GL_EXT_sRGB are not available."
207 << std::endl;
208 return;
209 }
210
211 // NVIDIA failures
212 // http://anglebug.com/42264177
213 ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsOpenGLES());
214
215 GLTexture texture;
216 glBindTexture(GL_TEXTURE_2D, texture);
217 glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB_ALPHA_EXT, 1, 1, 0, GL_SRGB_ALPHA_EXT, GL_UNSIGNED_BYTE,
218 nullptr);
219
220 GLFramebuffer framebuffer;
221 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
222 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
223
224 glUseProgram(mProgram);
225 glUniform4fv(mColorLocation, 1, srgbColor.toNormalizedVector().data());
226
227 glDisable(GL_FRAMEBUFFER_SRGB_EXT);
228 drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f);
229 EXPECT_PIXEL_COLOR_NEAR(0, 0, srgbColor, 1.0);
230
231 // Delete the texture
232 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
233 texture.reset();
234
235 drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f);
236 EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION);
237
238 GLColor throwaway_color;
239 glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &throwaway_color);
240 EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION);
241 }
242
243 // Test that glBlitFramebuffer correctly converts colorspaces
TEST_P(SRGBFramebufferTestES3,BlitFramebuffer)244 TEST_P(SRGBFramebufferTestES3, BlitFramebuffer)
245 {
246 // http://anglebug.com/42264326
247 ANGLE_SKIP_TEST_IF(!IsVulkan());
248
249 if (!IsGLExtensionEnabled("GL_EXT_sRGB_write_control") ||
250 (!IsGLExtensionEnabled("GL_EXT_sRGB") && getClientMajorVersion() < 3))
251 {
252 std::cout
253 << "Test skipped because GL_EXT_sRGB_write_control and GL_EXT_sRGB are not available."
254 << std::endl;
255 return;
256 }
257
258 GLTexture dstTexture;
259 glBindTexture(GL_TEXTURE_2D, dstTexture);
260 glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB_ALPHA_EXT, 1, 1, 0, GL_SRGB_ALPHA_EXT, GL_UNSIGNED_BYTE,
261 nullptr);
262 GLFramebuffer dstFramebuffer;
263 glBindFramebuffer(GL_FRAMEBUFFER, dstFramebuffer);
264 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dstTexture, 0);
265
266 GLTexture srcTexture;
267 glBindTexture(GL_TEXTURE_2D, srcTexture);
268 glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB_ALPHA_EXT, 1, 1, 0, GL_SRGB_ALPHA_EXT, GL_UNSIGNED_BYTE,
269 nullptr);
270
271 GLFramebuffer srcFramebuffer;
272 glBindFramebuffer(GL_FRAMEBUFFER, srcFramebuffer);
273 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, srcTexture, 0);
274
275 glUseProgram(mProgram);
276 glUniform4fv(mColorLocation, 1, srgbColor.toNormalizedVector().data());
277
278 // Draw onto the framebuffer normally
279 glEnable(GL_FRAMEBUFFER_SRGB_EXT);
280 drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f);
281 EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
282
283 // Blit the framebuffer normally
284 glEnable(GL_FRAMEBUFFER_SRGB_EXT);
285 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFramebuffer);
286 glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFramebuffer);
287 glBlitFramebuffer(0, 0, 1, 1, 0, 0, 1, 1, GL_COLOR_BUFFER_BIT, GL_NEAREST);
288
289 glBindFramebuffer(GL_FRAMEBUFFER, dstFramebuffer);
290 EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
291
292 // Blit the framebuffer with forced linear colorspace
293 glDisable(GL_FRAMEBUFFER_SRGB_EXT);
294 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFramebuffer);
295 glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFramebuffer);
296 glBlitFramebuffer(0, 0, 1, 1, 0, 0, 1, 1, GL_COLOR_BUFFER_BIT, GL_NEAREST);
297
298 glBindFramebuffer(GL_FRAMEBUFFER, dstFramebuffer);
299 EXPECT_PIXEL_COLOR_NEAR(0, 0, srgbColor, 1.0);
300 }
301
302 // This test reproduces an issue in the Vulkan backend found in the Chromium CI that
303 // was caused by enabling the VK_KHR_image_format_list extension on SwiftShader
304 // which exposed GL_EXT_sRGB_write_control.
TEST_P(SRGBFramebufferTest,DrawToSmallFBOClearLargeFBO)305 TEST_P(SRGBFramebufferTest, DrawToSmallFBOClearLargeFBO)
306 {
307 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_sRGB_write_control") ||
308 (!IsGLExtensionEnabled("GL_EXT_sRGB") && getClientMajorVersion() < 3));
309
310 // Disabling GL_FRAMEBUFFER_SRGB_EXT caused the issue
311 glDisable(GL_FRAMEBUFFER_SRGB_EXT);
312
313 // The issue involved framebuffers of two different sizes.
314 // The smaller needed to be drawn to, while the larger one could be just cleared
315 // to reproduce the issue. These are the smallest tested sizes that generated
316 // the validation error.
317 constexpr GLsizei kDimensionsSmall[] = {1, 1};
318 constexpr GLsizei kDimensionsLarge[] = {2, 2};
319 {
320 GLTexture texture;
321 glBindTexture(GL_TEXTURE_2D, texture);
322 glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, kDimensionsSmall[0], kDimensionsSmall[1]);
323 glBindTexture(GL_TEXTURE_2D, 0);
324
325 GLFramebuffer framebuffer;
326 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
327 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
328
329 unsigned char vertexData[] = {0};
330 GLBuffer vertexBuffer;
331 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
332 glBufferData(GL_ARRAY_BUFFER, sizeof(char), vertexData, GL_STATIC_DRAW);
333
334 unsigned int indexData[] = {0};
335 GLBuffer indexBuffer;
336 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
337 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int), indexData, GL_STATIC_DRAW);
338
339 glUseProgram(mProgram);
340
341 glDrawElements(GL_POINTS, 1, GL_UNSIGNED_INT, nullptr);
342
343 EXPECT_GL_NO_ERROR();
344 }
345 {
346 GLTexture texture;
347 glBindTexture(GL_TEXTURE_2D, texture);
348 glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, kDimensionsLarge[0], kDimensionsLarge[1]);
349 glBindTexture(GL_TEXTURE_2D, 0);
350
351 GLFramebuffer framebuffer;
352 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
353 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
354
355 // Vulkan validation happened to fail here with:
356 // "Cannot execute a render pass with renderArea not within the bound of the framebuffer"
357 glClear(GL_COLOR_BUFFER_BIT);
358
359 EXPECT_GL_NO_ERROR();
360 }
361 }
362
363 // Use this to select which configurations (e.g. which renderer, which GLES major version) these
364 // tests should be run against.
365 ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(SRGBFramebufferTest);
366 ANGLE_INSTANTIATE_TEST_ES3(SRGBFramebufferTestES3);
367
368 } // namespace angle
369