1 /*
2 * Copyright © 2012 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 /** \file blit.c
25 *
26 * Test the sRGB behaviour of blits.
27 *
28 * The various GL 4.x specifications contain a lot of conflicting rules
29 * about how blits should be handled when the source or destination buffer
30 * is sRGB.
31 *
32 * Here are the latest rules from GL 4.4 (October 18th, 2013)
33 * section 18.3.1 Blitting Pixel Rectangles:
34 *
35 * (1) When values are taken from the read buffer, if [[FRAMEBUFFER_SRGB
36 * is enabled and]] the value of FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING
37 * for the framebuffer attachment corresponding to the read buffer is
38 * SRGB (see section 9.2.3), the red, green, and blue components are
39 * converted from the non-linear sRGB color space according to
40 * equation 8.14.
41 *
42 * (2) When values are written to the draw buffers, blit operations
43 * bypass most of the fragment pipeline. The only fragment
44 * operations which affect a blit are the pixel ownership test,
45 * the scissor test, and sRGB conversion (see section
46 * 17.3.9). Color, depth, and stencil masks (see section 17.4.2)
47 * are ignored.
48 *
49 * And from section 17.3.9 sRGB Conversion:
50 *
51 * (3) If FRAMEBUFFER_SRGB is enabled and the value of
52 * FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING for the framebuffer
53 * attachment corresponding to the destination buffer is SRGB1
54 * (see section 9.2.3), the R, G, and B values after blending are
55 * converted into the non-linear sRGB color space by computing
56 * ... [formula follows] ... If FRAMEBUFFER_SRGB is disabled or
57 * the value of FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING is not SRGB,
58 * then ... [no conversion is applied].
59 *
60 * Rules differ in other specifications:
61 *
62 * -------------------------------------------------------------------
63 *
64 * ES 3.0 contains identical rules, however, ES has no FRAMEBUFFER_SRGB
65 * setting. References to that are deleted, making encode and decode
66 * happen regardless.
67 *
68 * -------------------------------------------------------------------
69 *
70 * The GL 4.3 revision from February 14th, 2013 deletes the bracketed
71 * text in paragraph (1), which appears to indicate that sRGB decode
72 * should happen regardless of the GL_FRAMEBUFFER_SRGB setting.
73 *
74 * This forces decode, but allows encode or no encode. This makes it
75 * impossible to do blits in a linear colorspace, which is not ideal.
76 *
77 * I believe this was an oversight: it looks like Khronos imported
78 * paragraph (1) from ES 3.x but neglected to add a FRAMEBUFFER_SRGB
79 * interaction on decode.
80 *
81 * -------------------------------------------------------------------
82 *
83 * The older GL 4.3 revision from August 6th, 2012 contains that
84 * same decode-always version of paragraph (1), but also contains
85 * another paragraph immediately after:
86 *
87 * (4) When values are taken from the read buffer, no linearization is
88 * performed even if the format of the buffer is SRGB.
89 *
90 * These are irreconcilable: the first says that linearization should
91 * happen when reading from SRGB buffers, while the second says that
92 * it shouldn't. These rules are not implementable, which is probably
93 * why they changed in a point revision.
94 *
95 * -------------------------------------------------------------------
96 *
97 * GL 4.2 omits paragraph (1) entirely but contains (4), suggesting that
98 * decode should never happen, but encode might.
99 *
100 * -------------------------------------------------------------------
101 *
102 * GL 4.1 and earlier specifications omits both paragraphs (1) and (4),
103 * and contain an alternate version of paragraph (2):
104 *
105 * (2b) Blit operations bypass the fragment pipeline. The only fragment
106 * operations which affect a blit are the pixel ownership test and
107 * the scissor test.
108 *
109 * Notably missing is sRGB conversion.
110 *
111 * This suggests that neither encode nor decode should happen, regardless
112 * of the FRAMEBUFFER_SRGB setting. These are the traditional GL rules.
113 *
114 * -------------------------------------------------------------------
115 *
116 * To summarize the rule differences:
117 *
118 * Specification Decoding Encoding
119 * ES 3.x Yes Yes
120 * GL 4.1 No No
121 * GL 4.2 No Optional
122 * GL 4.3 2012 Yes & No Optional
123 * GL 4.3 2013 Yes Optional
124 * GL 4.4 Optional Optional
125 *
126 * -------------------------------------------------------------------
127 *
128 * When this test was written in 2012, the author surveyed the nVidia
129 * and AMD drivers of the time. They appeared to follow the simpler rule
130 * that blits preserved the underlying binary representation of the pixels,
131 * regardless of whether the format was sRGB and regardless of the setting
132 * of FRAMEBUFFER_SRGB. Left 4 Dead 2 appeared to rely on this behavior
133 * at the time, but no longer does as of 2016.
134 *
135 * Unlike OpenGL, the ES 3.x rules have always been clear: always decode
136 * and encode. Both dEQP and WebGL conformance tests require this.
137 *
138 * The new GL 4.4 rules are flexible: if GL_FRAMEBUFFER_SRGB is disabled
139 * (the default setting), BlitFramebuffer will neither decode nor encode
140 * (the traditional GL rules). If it's enabled, then it follows the ES 3
141 * rules (both decode and encode). This isn't entirely compatible, but it
142 * seems like the best solution possible, and the one we should implement.
143 *
144 * This test verifies that blitting is permitted, and preserves the
145 * underlying binary representation of the pixels, under any specified
146 * combination of the following circumstances:
147 *
148 * - Using framebuffers backed by textures vs renderbuffers.
149 * - Blitting from sRGB vs linear, and to sRGB vs linear.
150 * - Doing a 1:1 blit from a single-sampled vs MSAA buffer, and to a
151 * single-sampled vs MSAA buffer, or doing a scaled blit between
152 * two single-sampled buffers.
153 * - With FRAMEBUFFER_SRGB enabled vs disabled.
154 *
155 * The combination to test is selected using command-line parameters.
156 *
157 * The test operates by rendering an image to a source framebuffer
158 * where each pixel's 8-bit color value is equal to its X coordinate.
159 * Then it blits this image to a destination framebuffer, and checks
160 * (using glReadPixels) that each pixel's 8-bit color value is still
161 * equal to its X coordinate.
162 *
163 * Since glReadPixels cannot be used directly on MSAA buffers, an
164 * additional resolve blit is added when necessary, to convert the
165 * image to single-sampled before reading the pixel values.
166 *
167 * Since the pixels in the test image depend only on the X coordinate,
168 * it is easy to test proper sRGB performance of scaled blits: we
169 * simply make the source rectangle one pixel high, so that the blit
170 * requires scaling. Note that the purpose of this test is to verify
171 * that blits exhibit correct sRGB behaviour, not to verify that
172 * scaling is performed correctly, so it is not necessary for us to
173 * exhaustively test a wide variety of scaling behaviours.
174 */
175
176 #include "piglit-util-gl.h"
177
178 const int PATTERN_WIDTH = 256;
179 const int PATTERN_HEIGHT = 64;
180 const float src_clear_col = 128.0 / 255.0;
181
182 PIGLIT_GL_TEST_CONFIG_BEGIN
183
184 config.supports_gl_compat_version = 10;
185
186 config.window_visual = PIGLIT_GL_VISUAL_DOUBLE | PIGLIT_GL_VISUAL_RGBA;
187 config.khr_no_error_support = PIGLIT_NO_ERRORS;
188
189 PIGLIT_GL_TEST_CONFIG_END
190
191 /* Test parameters */
192 static bool use_textures;
193 static GLenum src_format;
194 static GLenum dst_format;
195 static GLsizei src_samples;
196 static GLsizei dst_samples;
197 static bool scaled_blit;
198 static bool enable_srgb_framebuffer;
199 static bool src_fill_mode_clear;
200
201 /* GL objects */
202 static GLuint src_fbo;
203 static GLuint dst_fbo;
204 static GLuint resolve_fbo;
205 static GLint prog;
206
207 static char *vs_text =
208 "#version 120\n"
209 "void main()\n"
210 "{\n"
211 " gl_Position = gl_Vertex;\n"
212 "}\n";
213
214 static char *fs_text =
215 "#version 120\n"
216 "void main()\n"
217 "{\n"
218 " float x = gl_FragCoord.x;\n"
219 " gl_FragColor = vec4((x - 0.5) / 255.0);\n"
220 "}\n";
221
222 static GLuint
setup_fbo(GLenum internalformat,GLsizei num_samples)223 setup_fbo(GLenum internalformat, GLsizei num_samples)
224 {
225 GLuint fbo;
226 glGenFramebuffers(1, &fbo);
227 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
228 if (use_textures && num_samples == 0) {
229 GLuint tex;
230 const GLint level = 0;
231 const GLint border = 0;
232 glGenTextures(1, &tex);
233 glBindTexture(GL_TEXTURE_2D, tex);
234 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
235 GL_NEAREST);
236 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
237 GL_NEAREST);
238 glTexImage2D(GL_TEXTURE_2D, level,
239 internalformat, PATTERN_WIDTH, PATTERN_HEIGHT,
240 border, GL_RGBA, GL_BYTE, NULL);
241 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER,
242 GL_COLOR_ATTACHMENT0,
243 GL_TEXTURE_2D, tex, level);
244 } else {
245 GLuint rb;
246 glGenRenderbuffers(1, &rb);
247 glBindRenderbuffer(GL_RENDERBUFFER, rb);
248 glRenderbufferStorageMultisample(GL_RENDERBUFFER, num_samples,
249 internalformat, PATTERN_WIDTH,
250 PATTERN_HEIGHT);
251 glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER,
252 GL_COLOR_ATTACHMENT0,
253 GL_RENDERBUFFER, rb);
254 }
255 return fbo;
256 }
257
258 static void
print_usage_and_exit(char * prog_name)259 print_usage_and_exit(char *prog_name)
260 {
261 printf("Usage: %s <backing_type> <sRGB_types> <blit_type>\n"
262 " <framebuffer_srgb_setting>\n"
263 " <src_fill_mode>\n"
264 " where <backing_type> is one of:\n"
265 " texture (ignored for multisampled framebuffers)\n"
266 " renderbuffer\n"
267 " where <sRGB_types> is one of:\n"
268 " linear (both buffers linear)\n"
269 " srgb (both buffers sRGB)\n"
270 " linear_to_srgb\n"
271 " srgb_to_linear\n"
272 " where <blit_type> is one of:\n"
273 " single_sampled\n"
274 " upsample\n"
275 " downsample\n"
276 " msaa\n"
277 " scaled\n"
278 " where framebuffer_srgb_setting is one of:\n"
279 " enabled\n"
280 " disabled\n"
281 " where src_fill_mode is one of:\n"
282 " clear\n"
283 " render\n",
284 prog_name);
285 piglit_report_result(PIGLIT_FAIL);
286 }
287
288 void
piglit_init(int argc,char ** argv)289 piglit_init(int argc, char **argv)
290 {
291 GLint max_samples;
292
293 if (argc != 6) {
294 print_usage_and_exit(argv[0]);
295 }
296
297 if (strcmp(argv[1], "texture") == 0) {
298 use_textures = true;
299 } else if (strcmp(argv[1], "renderbuffer") == 0) {
300 use_textures = false;
301 } else {
302 print_usage_and_exit(argv[0]);
303 }
304
305 if (strcmp(argv[2], "linear") == 0) {
306 src_format = GL_RGBA;
307 dst_format = GL_RGBA;
308 } else if (strcmp(argv[2], "srgb") == 0) {
309 src_format = GL_SRGB8_ALPHA8;
310 dst_format = GL_SRGB8_ALPHA8;
311 } else if (strcmp(argv[2], "linear_to_srgb") == 0) {
312 src_format = GL_RGBA;
313 dst_format = GL_SRGB8_ALPHA8;
314 } else if (strcmp(argv[2], "srgb_to_linear") == 0) {
315 src_format = GL_SRGB8_ALPHA8;
316 dst_format = GL_RGBA;
317 } else {
318 print_usage_and_exit(argv[0]);
319 }
320
321 if (strcmp(argv[3], "single_sampled") == 0) {
322 src_samples = 0;
323 dst_samples = 0;
324 scaled_blit = false;
325 } else if (strcmp(argv[3], "upsample") == 0) {
326 src_samples = 0;
327 dst_samples = 1; /* selects minimum available sample count */
328 scaled_blit = false;
329 } else if (strcmp(argv[3], "downsample") == 0) {
330 src_samples = 1;
331 dst_samples = 0;
332 scaled_blit = false;
333 } else if (strcmp(argv[3], "msaa") == 0) {
334 src_samples = 1;
335 dst_samples = 1;
336 scaled_blit = false;
337 } else if (strcmp(argv[3], "scaled") == 0) {
338 src_samples = 0;
339 dst_samples = 0;
340 scaled_blit = true;
341 } else {
342 print_usage_and_exit(argv[0]);
343 }
344
345 if (strcmp(argv[4], "enabled") == 0) {
346 enable_srgb_framebuffer = true;
347 } else if (strcmp(argv[4], "disabled") == 0) {
348 enable_srgb_framebuffer = false;
349 } else {
350 print_usage_and_exit(argv[0]);
351 }
352
353 if (strcmp(argv[5], "clear") == 0) {
354 src_fill_mode_clear = true;
355 } else if (strcmp(argv[5], "render") == 0) {
356 src_fill_mode_clear = false;
357 } else {
358 print_usage_and_exit(argv[0]);
359 }
360
361 piglit_require_gl_version(21);
362 piglit_require_extension("GL_ARB_framebuffer_object");
363 piglit_require_extension("GL_ARB_framebuffer_sRGB");
364
365 /* skip the test if we don't support multisampling */
366 glGetIntegerv(GL_MAX_SAMPLES, &max_samples);
367 if (src_samples > max_samples ||
368 dst_samples > max_samples) {
369 piglit_report_result(PIGLIT_SKIP);
370 }
371
372 prog = piglit_build_simple_program(vs_text, fs_text);
373
374 src_fbo = setup_fbo(src_format, src_samples);
375 dst_fbo = setup_fbo(dst_format, dst_samples);
376 if (dst_samples != 0)
377 resolve_fbo = setup_fbo(dst_format, 0);
378 else
379 resolve_fbo = 0;
380 }
381
382 /**
383 * Implements GL 4.4 equation 8.14.
384 */
385 static float
srgb_to_linear(float c_s)386 srgb_to_linear(float c_s)
387 {
388 return c_s <= 0.04045 ? c_s / 12.92f
389 : powf((c_s + 0.055f) / 1.055f, 2.4f);
390 }
391
392 /**
393 * Implements GL 4.4 equation 17.1.
394 */
395 static float
linear_to_srgb(float c_l)396 linear_to_srgb(float c_l)
397 {
398 if (c_l <= 0.0f)
399 return 0.0f;
400 else if (c_l < 0.0031308f)
401 return 12.92f * c_l;
402 else if (c_l < 1.0f)
403 return 1.055f * powf(c_l, 0.41666f) - 0.055f;
404 return 1.0f;
405 }
406
407 static bool
analyze_image(GLuint fbo)408 analyze_image(GLuint fbo)
409 {
410 GLfloat *expected_data = malloc(PATTERN_WIDTH * PATTERN_HEIGHT * 4 *
411 sizeof(GLfloat));
412 unsigned x, y, component;
413 bool pass;
414
415 for (y = 0; y < PATTERN_HEIGHT; ++y) {
416 for (x = 0; x < PATTERN_WIDTH; ++x) {
417 for (component = 0; component < 4; ++component) {
418 float val = src_fill_mode_clear ?
419 src_clear_col : x / 255.0;
420 if (component < 3 && enable_srgb_framebuffer) {
421 if (src_format == GL_SRGB8_ALPHA8)
422 val = srgb_to_linear(val);
423 if (dst_format == GL_SRGB8_ALPHA8)
424 val = linear_to_srgb(val);
425 }
426
427 expected_data[(y * PATTERN_WIDTH + x)
428 * 4 + component] = val;
429 }
430 }
431 }
432
433 glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
434 pass = piglit_probe_image_rgba(0, 0, PATTERN_WIDTH, PATTERN_HEIGHT,
435 expected_data);
436 free(expected_data);
437 return pass;
438 }
439
440 enum piglit_result
piglit_display()441 piglit_display()
442 {
443 bool pass;
444
445 glUseProgram(prog);
446 glDisable(GL_FRAMEBUFFER_SRGB);
447
448 /* Clear buffers */
449 if (resolve_fbo != 0) {
450 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolve_fbo);
451 glClear(GL_COLOR_BUFFER_BIT);
452 }
453 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst_fbo);
454 glClear(GL_COLOR_BUFFER_BIT);
455
456 /* Draw the source image */
457 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, src_fbo);
458 if (src_fill_mode_clear) {
459 /* This case is of particular interest to Intel GPUs. */
460 glClearColor(src_clear_col, src_clear_col,
461 src_clear_col, src_clear_col);
462 glClear(GL_COLOR_BUFFER_BIT);
463 } else {
464 glViewport(0, 0, PATTERN_WIDTH, PATTERN_HEIGHT);
465 piglit_draw_rect(-1, -1, 2, 2);
466 }
467
468 /* Do the blit */
469 glBindFramebuffer(GL_READ_FRAMEBUFFER, src_fbo);
470 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst_fbo);
471 if (enable_srgb_framebuffer)
472 glEnable(GL_FRAMEBUFFER_SRGB);
473 glBlitFramebuffer(0, 0, PATTERN_WIDTH,
474 scaled_blit ? 1 : PATTERN_HEIGHT,
475 0, 0, PATTERN_WIDTH, PATTERN_HEIGHT,
476 GL_COLOR_BUFFER_BIT, GL_NEAREST);
477 glDisable(GL_FRAMEBUFFER_SRGB);
478
479 /* If necessary, do a resolve blit */
480 if (resolve_fbo != 0) {
481 glBindFramebuffer(GL_READ_FRAMEBUFFER, dst_fbo);
482 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolve_fbo);
483 glBlitFramebuffer(0, 0, PATTERN_WIDTH, PATTERN_HEIGHT,
484 0, 0, PATTERN_WIDTH, PATTERN_HEIGHT,
485 GL_COLOR_BUFFER_BIT, GL_NEAREST);
486 pass = analyze_image(resolve_fbo);
487 } else {
488 pass = analyze_image(dst_fbo);
489 }
490
491 return pass ? PIGLIT_PASS : PIGLIT_FAIL;
492 }
493