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