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