1 /*
2  * Copyright (c) The Piglit project 2007
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  * on the rights to use, copy, modify, merge, publish, distribute, sub
8  * license, and/or sell copies of the Software, and to permit persons to whom
9  * the 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 NON-INFRINGEMENT.  IN NO EVENT SHALL
18  * VA LINUX SYSTEM, IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21  * USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 
24 #include <errno.h>
25 
26 #include "piglit-util-gl.h"
27 #include "piglit-subprocess.h"
28 
piglit_get_glsl_version(bool * es,int * major,int * minor)29 void piglit_get_glsl_version(bool *es, int* major, int* minor)
30 {
31 	bool es_local;
32 	int major_local;
33 	int minor_local;
34 
35 	const char *version_string;
36 	int c; /* scanf count */
37 
38 	(void)c;
39 	version_string = (const char*) glGetString(GL_SHADING_LANGUAGE_VERSION);
40 	es_local = strncmp("OpenGL ES", version_string, 9) == 0;
41 	if (es_local) {
42 		c = sscanf(version_string,
43 		           "OpenGL ES GLSL ES %i.%i",
44 		           &major_local,
45 		           &minor_local);
46 	} else {
47 		c = sscanf(version_string,
48 		           "%i.%i",
49 		           &major_local,
50 		           &minor_local);
51 	}
52 	assert(c == 2);
53 
54 	/* Write outputs. */
55 	if (es != NULL)
56 		*es = es_local;
57 	if (major != NULL)
58 		*major = major_local;
59 	if (minor != NULL)
60 		*minor = minor_local;
61 }
62 
63 /**
64  * Convenience function to compile a GLSL shader from a file.
65  */
66 GLuint
piglit_compile_shader(GLenum target,const char * filename)67 piglit_compile_shader(GLenum target, const char *filename)
68 {
69 	GLuint prog;
70 	GLchar *prog_string;
71 	const char *source_dir;
72 	char filename_with_path[FILENAME_MAX];
73 
74 	source_dir = getenv("PIGLIT_SOURCE_DIR");
75 	if (source_dir == NULL) {
76 		source_dir = SOURCE_DIR;
77 	}
78 
79 	snprintf(filename_with_path, FILENAME_MAX - 1,
80 		 "%s/tests/%s", source_dir, filename);
81 	filename_with_path[FILENAME_MAX - 1] = 0;
82 
83 	prog_string = piglit_load_text_file(filename_with_path, NULL);
84 	if (!prog_string) {
85 		fprintf(stderr, "Couldn't read shader %s: %s\n",
86 			filename_with_path, strerror(errno));
87 		fprintf(stderr, "You can override the source dir by setting the"
88 			" PIGLIT_SOURCE_DIR environment variable.\n");
89 		exit(1);
90 	}
91 
92 	prog = piglit_compile_shader_text(target, prog_string);
93 
94 	free(prog_string);
95 
96 	return prog;
97 }
98 
99 /** Return a string name for a shader target enum */
100 static const char *
shader_name(GLenum target)101 shader_name(GLenum target)
102 {
103    switch (target) {
104    case GL_VERTEX_SHADER:
105       return "vertex";
106    case GL_TESS_CONTROL_SHADER:
107       return "tessellation control";
108    case GL_TESS_EVALUATION_SHADER:
109       return "tessellation evaluation";
110    case GL_GEOMETRY_SHADER:
111       return "geometry";
112    case GL_FRAGMENT_SHADER:
113       return "fragment";
114    case GL_COMPUTE_SHADER:
115       return "compute";
116    default:
117       assert(!"Unexpected shader target in shader_name()");
118    }
119 
120    return "error";
121 }
122 
123 /**
124  * Convenience function to compile a GLSL shader.
125  */
126 GLuint
piglit_compile_shader_text_nothrow(GLenum target,const char * text,bool err_to_stderr)127 piglit_compile_shader_text_nothrow(GLenum target, const char *text, bool err_to_stderr)
128 {
129 	GLuint prog;
130 	GLint ok;
131 
132 	piglit_require_GLSL();
133 
134 	prog = glCreateShader(target);
135 	glShaderSource(prog, 1, (const GLchar **) &text, NULL);
136 	glCompileShader(prog);
137 
138 	glGetShaderiv(prog, GL_COMPILE_STATUS, &ok);
139 
140 	{
141 		GLchar *info;
142 		GLint size;
143 
144 		glGetShaderiv(prog, GL_INFO_LOG_LENGTH, &size);
145 		info = malloc(size);
146 
147 		glGetShaderInfoLog(prog, size, NULL, info);
148 		if (!ok) {
149 			if (err_to_stderr) {
150 				fprintf(stderr, "Failed to compile %s shader: %s\n",
151 					shader_name(target),
152 					info);
153 
154 				fprintf(stderr, "source:\n%s", text);
155 			}
156 			glDeleteShader(prog);
157 			prog = 0;
158 		}
159 		else if (0) {
160 			/* Enable this to get extra compilation info.
161 			 * Even if there's no compilation errors, the info
162 			 * log may have some remarks.
163 			 */
164 			fprintf(stderr, "Shader compiler warning: %s\n", info);
165 		}
166 		free(info);
167 	}
168 
169 	return prog;
170 }
171 
172 /**
173  * Convenience function to compile a GLSL shader.  Throws PIGLIT_FAIL
174  * on error terminating the program.
175  */
176 GLuint
piglit_compile_shader_text(GLenum target,const char * text)177 piglit_compile_shader_text(GLenum target, const char *text)
178 {
179         GLuint shader = piglit_compile_shader_text_nothrow(target, text, true);
180 
181         if (!shader)
182                 piglit_report_result(PIGLIT_FAIL);
183 
184         return shader;
185 }
186 
187 /**
188  * Convenience function to compile a GLSL shader with printf formatting.
189  * Throws PIGLIT_FAIL on error, terminating the program.
190  */
191 GLuint
piglit_compile_shader_formatted(GLenum target,const char * fmt,...)192 piglit_compile_shader_formatted(GLenum target, const char *fmt, ...)
193 {
194 	va_list ap;
195 	va_start(ap, fmt);
196 	char *source;
197 	int ret = vasprintf(&source, fmt, ap);
198 	va_end(ap);
199 
200 	if (ret == -1) {
201 		fprintf(stderr, "vaspritnf failed\n");
202 		piglit_report_result(PIGLIT_FAIL);
203 	}
204 
205 	GLuint shader = piglit_compile_shader_text(target, source);
206 	free(source);
207 	return shader;
208 }
209 
210 static GLboolean
link_check_status(GLint prog,FILE * output)211 link_check_status(GLint prog, FILE *output)
212 {
213 	GLchar *info = NULL;
214 	GLint size;
215 	GLint ok;
216 
217 	piglit_require_GLSL();
218 
219 	glGetProgramiv(prog, GL_LINK_STATUS, &ok);
220 
221 	/* Some drivers return a size of 1 for an empty log.  This is the size
222 	 * of a log that contains only a terminating NUL character.
223 	 */
224 	glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &size);
225 	if (size > 1) {
226 		info = malloc(size);
227 		glGetProgramInfoLog(prog, size, NULL, info);
228 	}
229 
230 	if (!ok) {
231 		fprintf(output, "Failed to link: %s\n",
232 			(info != NULL) ? info : "<empty log>");
233 	}
234 	else if (0 && info != NULL) {
235 		/* Enable this to get extra linking info.
236 		 * Even if there's no link errors, the info log may
237 		 * have some remarks.
238 		 */
239 		printf("Linker warning: %s\n", info);
240 	}
241 
242 	free(info);
243 
244 	return ok;
245 }
246 
247 GLboolean
piglit_link_check_status(GLint prog)248 piglit_link_check_status(GLint prog)
249 {
250 	return link_check_status(prog, stderr);
251 }
252 
253 /**
254  * Check link status
255  *
256  * Similar to piglit_link_check_status except it logs error messages
257  * to standard output instead of standard error.  This is useful for
258  * tests that want to produce negative link results.
259  *
260  * \sa piglit_link_check_status
261  */
262 GLboolean
piglit_link_check_status_quiet(GLint prog)263 piglit_link_check_status_quiet(GLint prog)
264 {
265 	return link_check_status(prog, stdout);
266 }
267 
268 
piglit_link_simple_program(GLint vs,GLint fs)269 GLint piglit_link_simple_program(GLint vs, GLint fs)
270 {
271 	GLint prog;
272 
273 	piglit_require_GLSL();
274 
275 	prog = glCreateProgram();
276 	if (vs)
277 		glAttachShader(prog, vs);
278 	if (fs)
279 		glAttachShader(prog, fs);
280 
281 	/* If the shaders reference piglit_vertex or piglit_tex, bind
282 	 * them to some fixed attribute locations so they can be used
283 	 * with piglit_draw_rect_tex() in GLES.
284 	 */
285 	glBindAttribLocation(prog, PIGLIT_ATTRIB_POS, "piglit_vertex");
286 	glBindAttribLocation(prog, PIGLIT_ATTRIB_TEX, "piglit_texcoord");
287 
288 	glLinkProgram(prog);
289 
290 	if (!piglit_link_check_status(prog)) {
291 		glDeleteProgram(prog);
292 		prog = 0;
293 	}
294 
295 	return prog;
296 }
297 
298 
299 /**
300  * Builds a program from optional VS and FS sources, but does not link
301  * it.  If there is a compile failure, the test is terminated.
302  */
303 GLuint
piglit_build_simple_program_unlinked(const char * vs_source,const char * fs_source)304 piglit_build_simple_program_unlinked(const char *vs_source,
305 				     const char *fs_source)
306 {
307 	GLuint prog;
308 
309 	piglit_require_GLSL();
310 	prog = glCreateProgram();
311 	if (vs_source) {
312 		GLuint vs = piglit_compile_shader_text(GL_VERTEX_SHADER,
313 						       vs_source);
314 		glAttachShader(prog, vs);
315 		glDeleteShader(vs);
316 	}
317 	if (fs_source) {
318 		GLuint fs = piglit_compile_shader_text(GL_FRAGMENT_SHADER,
319 						       fs_source);
320 		glAttachShader(prog, fs);
321 		glDeleteShader(fs);
322 	}
323 	return prog;
324 }
325 
326 
327 /**
328  * Builds and links a program from optional VS and FS sources,
329  * throwing PIGLIT_FAIL on error.
330  */
331 GLint
piglit_build_simple_program(const char * vs_source,const char * fs_source)332 piglit_build_simple_program(const char *vs_source, const char *fs_source)
333 {
334 	GLuint vs = 0, fs = 0, prog;
335 
336 	if (vs_source) {
337 		vs = piglit_compile_shader_text(GL_VERTEX_SHADER, vs_source);
338 	}
339 
340 	if (fs_source) {
341 		fs = piglit_compile_shader_text(GL_FRAGMENT_SHADER, fs_source);
342 	}
343 
344 	prog = piglit_link_simple_program(vs, fs);
345 	if (!prog)
346 		piglit_report_result(PIGLIT_FAIL);
347 
348 	if (fs)
349 		glDeleteShader(fs);
350 	if (vs)
351 		glDeleteShader(vs);
352 
353 	return prog;
354 }
355 
piglit_link_simple_program_multiple_shaders(GLint shader1,...)356 GLint piglit_link_simple_program_multiple_shaders(GLint shader1, ...)
357 {
358 	va_list ap;
359 	GLint prog, sh;
360 
361 	piglit_require_GLSL();
362 
363 	prog = glCreateProgram();
364 
365 	va_start(ap, shader1);
366 	sh = shader1;
367 
368 	while (sh != 0) {
369 		glAttachShader(prog, sh);
370 		sh = va_arg(ap, GLint);
371 	}
372 
373 	va_end(ap);
374 
375 	/* If the shaders reference piglit_vertex or piglit_tex, bind
376 	 * them to some fixed attribute locations so they can be used
377 	 * with piglit_draw_rect_tex() in GLES.
378 	 */
379 	glBindAttribLocation(prog, PIGLIT_ATTRIB_POS, "piglit_vertex");
380 	glBindAttribLocation(prog, PIGLIT_ATTRIB_TEX, "piglit_texcoord");
381 
382 	glLinkProgram(prog);
383 
384 	if (!piglit_link_check_status(prog)) {
385 		glDeleteProgram(prog);
386 		prog = 0;
387 	}
388 
389 	return prog;
390 }
391 
392 GLint
piglit_build_simple_program_unlinked_multiple_shaders_v(GLenum target1,const char * source1,va_list ap)393 piglit_build_simple_program_unlinked_multiple_shaders_v(GLenum target1,
394 						        const char *source1,
395 						        va_list ap)
396 {
397 	GLuint prog;
398 	GLenum target;
399 	const char *source;
400 
401 	piglit_require_GLSL();
402 	prog = glCreateProgram();
403 
404 	source = source1;
405 	target = target1;
406 
407 	while (target != 0) {
408 		/* do not compile/attach a NULL shader */
409 		if (source) {
410 			GLuint shader = piglit_compile_shader_text(target,
411 								   source);
412 
413 			glAttachShader(prog, shader);
414 			glDeleteShader(shader);
415 		}
416 
417 		target  = va_arg(ap, GLenum);
418 		if (target != 0)
419 			source = va_arg(ap, char*);
420 	}
421 
422 	return prog;
423 }
424 
425 /**
426  * Builds and links a program from optional sources,  but does not link
427  * it. The last target must be 0. If there is a compile failure,
428  * the test is terminated.
429  *
430  * example:
431  * piglit_build_simple_program_unlinked_multiple_shaders(
432  *				GL_VERTEX_SHADER,   vs,
433  *				GL_GEOMETRY_SHADER, gs,
434  *				GL_FRAGMENT_SHADER, fs,
435  *				0);
436  */
437 GLint
piglit_build_simple_program_unlinked_multiple_shaders(GLenum target1,const char * source1,...)438 piglit_build_simple_program_unlinked_multiple_shaders(GLenum target1,
439 						      const char *source1,
440 						      ...)
441 {
442 	GLuint prog;
443 	va_list ap;
444 
445 	va_start(ap, source1);
446 
447 	prog = piglit_build_simple_program_unlinked_multiple_shaders_v(target1,
448 								       source1,
449 								       ap);
450 	va_end(ap);
451 
452 	return prog;
453 }
454 
455 /**
456  * Builds and links a program from optional sources, throwing
457  * PIGLIT_FAIL on error. The last target must be 0.
458  */
459 GLint
piglit_build_simple_program_multiple_shaders(GLenum target1,const char * source1,...)460 piglit_build_simple_program_multiple_shaders(GLenum target1,
461 					    const char *source1,
462 					    ...)
463 {
464 	va_list ap;
465 	GLuint prog;
466 
467 	va_start(ap, source1);
468 
469 	prog = piglit_build_simple_program_unlinked_multiple_shaders_v(target1,
470 								       source1,
471 								       ap);
472 
473 	va_end(ap);
474 
475 	/* If the shaders reference piglit_vertex or piglit_tex, bind
476 	 * them to some fixed attribute locations so they can be used
477 	 * with piglit_draw_rect_tex() in GLES.
478 	 */
479 	glBindAttribLocation(prog, PIGLIT_ATTRIB_POS, "piglit_vertex");
480 	glBindAttribLocation(prog, PIGLIT_ATTRIB_TEX, "piglit_texcoord");
481 
482 	glLinkProgram(prog);
483 
484 	if (!piglit_link_check_status(prog)) {
485 		glDeleteProgram(prog);
486 		prog = 0;
487 		piglit_report_result(PIGLIT_FAIL);
488 	}
489 
490 	return prog;
491 }
492 
493 GLuint
piglit_assemble_spirv(GLenum target,size_t source_length,const char * source)494 piglit_assemble_spirv(GLenum target,
495 		      size_t source_length,
496 		      const char *source)
497 {
498 	char *arguments[] = {
499 		getenv("PIGLIT_SPIRV_AS_BINARY"),
500 		"--target-env", "opengl4.5",
501 		"-o", "-",
502 		NULL
503 	};
504 
505 	if (arguments[0] == NULL)
506 		arguments[0] = "spirv-as";
507 
508 	uint8_t *binary_source;
509 	size_t binary_source_length;
510 	bool res = piglit_subprocess(arguments,
511 				     source_length,
512 				     (const uint8_t *) source,
513 				     &binary_source_length,
514 				     &binary_source);
515 
516 	if (!res) {
517 		fprintf(stderr, "spirv-as failed\n");
518 		piglit_report_result(PIGLIT_FAIL);
519 	}
520 
521 	if (getenv("PIGLIT_SPIRV_VALIDATE")) {
522 		char *arguments[] = {
523 			getenv("PIGLIT_SPIRV_VAL_BINARY"),
524 			"--target-env", "opengl4.5",
525 			NULL,
526 		};
527 
528 		if (arguments[0] == NULL)
529 			arguments[0] = "spirv-val";
530 
531 		uint8_t *validate_result;
532 		size_t validate_result_length;
533 		bool res = piglit_subprocess(arguments,
534 					     binary_source_length,
535 					     (const uint8_t *) binary_source,
536 					     &validate_result_length,
537 					     &validate_result);
538 
539 		if (!res) {
540 			fprintf(stderr, "spirv-val failed\n");
541 			piglit_report_result(PIGLIT_FAIL);
542 		}
543 	}
544 
545 	GLuint shader = glCreateShader(target);
546 	glShaderBinary(1, &shader, GL_SHADER_BINARY_FORMAT_SPIR_V_ARB,
547 		       binary_source, binary_source_length);
548 
549 	free(binary_source);
550 
551 	return shader;
552 }
553 
554 void
piglit_require_GLSL(void)555 piglit_require_GLSL(void)
556 {
557 	if (piglit_get_gl_version() < 20
558 	    && !(piglit_is_extension_supported("GL_ARB_shader_objects")
559 	         && piglit_is_extension_supported("GL_ARB_shading_language_100"))) {
560 		printf("GLSL not supported.\n");
561 		piglit_report_result(PIGLIT_SKIP);
562 	}
563 }
564 
565 void
piglit_require_GLSL_version(int version)566 piglit_require_GLSL_version(int version)
567 {
568 	bool es;
569 	int major, minor;
570 
571 	piglit_require_GLSL();
572 
573 	piglit_get_glsl_version(&es, &major, &minor);
574 
575 	if (es || 100 * major + minor < version) {
576 		printf("GLSL %d.%d not supported.\n",
577 		       version / 100, version % 100);
578 		piglit_report_result(PIGLIT_SKIP);
579 	}
580 }
581 
582 void
piglit_require_vertex_shader(void)583 piglit_require_vertex_shader(void)
584 {
585 	if (piglit_get_gl_version() < 20
586 	    && !(piglit_is_extension_supported("GL_ARB_shader_objects")
587 		 && piglit_is_extension_supported("GL_ARB_vertex_shader"))) {
588 		printf("GLSL vertex shaders are not supported.\n");
589 		piglit_report_result(PIGLIT_SKIP);
590 	}
591 }
592 
593 void
piglit_require_fragment_shader(void)594 piglit_require_fragment_shader(void)
595 {
596 	if (piglit_get_gl_version() < 20
597 	    && !(piglit_is_extension_supported("GL_ARB_shader_objects")
598 		 && piglit_is_extension_supported("GL_ARB_fragment_shader"))) {
599 		printf("GLSL fragment shaders are not supported.\n");
600 		piglit_report_result(PIGLIT_SKIP);
601 	}
602 }
603 
604 /* Same function as link_check_status but for program pipeline */
605 static GLboolean
program_pipeline_check_status(GLuint pipeline,FILE * output)606 program_pipeline_check_status(GLuint pipeline, FILE *output)
607 {
608 	GLchar *info = NULL;
609 	GLint size;
610 	GLint ok;
611 
612 	piglit_require_extension("GL_ARB_separate_shader_objects");
613 
614 	glValidateProgramPipeline(pipeline);
615 	glGetProgramPipelineiv(pipeline, GL_VALIDATE_STATUS, &ok);
616 
617 	/* Some drivers return a size of 1 for an empty log.  This is the size
618 	 * of a log that contains only a terminating NUL character.
619 	 */
620 	glGetProgramPipelineiv(pipeline, GL_INFO_LOG_LENGTH, &size);
621 	if (size > 1) {
622 		info = malloc(size);
623 		glGetProgramPipelineInfoLog(pipeline, size, NULL, info);
624 	}
625 
626 	if (!ok) {
627 		fprintf(output, "Failed to validate the pipeline: %s\n",
628 			(info != NULL) ? info : "<empty log>");
629 	}
630 	else if (0 && info != NULL) {
631 		/* Enable this to get extra linking info.
632 		 * Even if there's no link errors, the info log may
633 		 * have some remarks.
634 		 */
635 		printf("Pipeline validation warning: %s\n", info);
636 	}
637 
638 	free(info);
639 
640 	return ok;
641 }
642 
643 GLboolean
piglit_program_pipeline_check_status(GLuint pipeline)644 piglit_program_pipeline_check_status(GLuint pipeline)
645 {
646 	return program_pipeline_check_status(pipeline, stderr);
647 }
648 
649 GLboolean
piglit_program_pipeline_check_status_quiet(GLuint pipeline)650 piglit_program_pipeline_check_status_quiet(GLuint pipeline)
651 {
652 	return program_pipeline_check_status(pipeline, stdout);
653 }
654