From: Nicolai Hähnle <nicolai.haeh...@amd.com> This commit add support to load a SPIR-V shader, possible since ARB_gl_spirv.
The SPIR-V shader is included on the shader_test script on new shader stage versions ([vertex shader spirv], [fragment shader spirv], etc). It is not in direct binary format, but in assembly format that it is assembled using spirv-tools. This is done by calling spirv-as, that is provided by setting the envvar PIGLIT_SPIRV_AS_BINARY. In this way we are adding an optional runtime dependency, instead of a build time one. Those sections are ignored unless [require] section includes "SPIRV YES" or "SPIRV ONLY". The only difference between the two is that "SPIRV ONLY" marks tests that can only be run on SPIR-V mode, as they are testing specific SPIR-V/ARB_gl_spirv features, like specializations. By default if those sections are present, shader_runner will try to load the SPIR-V shader (run in SPIR-V mode). This commit also adds a command line option "-glsl", that would try to force run the shader in GLSL mode even if the SPIR-V shader are available. If "SPIRV ONLY" is present, and force GLSL is attempted, the test will be skipped. Note that in GLSL mode, ARB_gl_spirv is not required. For that reason, we will not add ARB_gl_spirv on the [require] section of the tests. We check for this extension when assembling the SPIR-V shader. Signed-off-by: Nicolai Hähnle <nicolai.haeh...@amd.com> Signed-off-by: Alejandro Piñeiro <apinhe...@igalia.com> Signed-off-by: Neil Roberts <nrobe...@igalia.com> --- tests/shaders/shader_runner.c | 272 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 261 insertions(+), 11 deletions(-) diff --git a/tests/shaders/shader_runner.c b/tests/shaders/shader_runner.c index a6dbdedeb..b1bad8d9b 100644 --- a/tests/shaders/shader_runner.c +++ b/tests/shaders/shader_runner.c @@ -29,6 +29,7 @@ #include "piglit-util-gl.h" #include "piglit-vbo.h" #include "piglit-framework-gl/piglit_gl_framework.h" +#include "piglit-subprocess.h" #include "shader_runner_gles_workarounds.h" #include "parser_utils.h" @@ -39,7 +40,7 @@ static struct piglit_gl_test_config current_config; static void -get_required_config(const char *script_name, +get_required_config(const char *script_name, bool spirv, struct piglit_gl_test_config *config); static GLenum decode_drawing_mode(const char *mode_str); @@ -53,10 +54,15 @@ PIGLIT_GL_TEST_CONFIG_BEGIN config.window_visual = PIGLIT_GL_VISUAL_RGBA | PIGLIT_GL_VISUAL_DOUBLE; config.khr_no_error_support = PIGLIT_NO_ERRORS; - if (argc > 1) - get_required_config(argv[1], &config); - else + /* By default SPIR-V mode is false. It will not be enabled + * unless the script includes SPIRV YES or SPIRV ONLY lines at + * [require] section, so it will be handled later. + */ + if (argc > 1) { + get_required_config(argv[1], false, &config); + } else { config.supports_gl_compat_version = 10; + } current_config = config; @@ -143,6 +149,10 @@ static bool vbo_present = false; static bool link_ok = false; static bool prog_in_use = false; static bool sso_in_use = false; +static bool glsl_in_use = false; +static bool force_glsl = false; +static bool spirv_in_use = false; +static bool spirv_replaces_glsl = false; static GLchar *prog_err_info = NULL; static GLuint vao = 0; static GLuint draw_fbo, read_fbo; @@ -245,14 +255,20 @@ enum states { requirements, vertex_shader, vertex_shader_passthrough, + vertex_shader_spirv, vertex_program, tess_ctrl_shader, + tess_ctrl_shader_spirv, tess_eval_shader, + tess_eval_shader_spirv, geometry_shader, + geometry_shader_spirv, geometry_layout, fragment_shader, + fragment_shader_spirv, fragment_program, compute_shader, + compute_shader_spirv, vertex_data, test, }; @@ -436,6 +452,13 @@ compile_glsl(GLenum target) GLuint shader = glCreateShader(target); GLint ok; + if (spirv_in_use) { + printf("Cannot mix SPIRV and non-SPIRV shaders\n"); + return PIGLIT_FAIL; + } + + glsl_in_use = true; + switch (target) { case GL_VERTEX_SHADER: if (piglit_get_gl_version() < 20 && @@ -552,6 +575,7 @@ compile_glsl(GLenum target) return PIGLIT_PASS; } + static enum piglit_result compile_and_bind_program(GLenum target, const char *start, int len) { @@ -677,6 +701,139 @@ program_binary_save_restore(bool script_command) return true; } +static enum piglit_result +load_and_specialize_spirv(GLenum target, + const char *binary, unsigned size) +{ + if (glsl_in_use) { + printf("Cannot mix SPIR-V and non-SPIR-V shaders\n"); + return PIGLIT_FAIL; + } + + spirv_in_use = true; + + GLuint shader = glCreateShader(target); + + glShaderBinary(1, &shader, GL_SHADER_BINARY_FORMAT_SPIR_V_ARB, + binary, size); + + glSpecializeShaderARB(shader, "main", 0, NULL, NULL); + + GLint ok; + glGetShaderiv(shader, GL_COMPILE_STATUS, &ok); + + if (!ok) { + GLchar *info; + GLint size; + + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &size); + info = malloc(size); + + glGetShaderInfoLog(shader, size, NULL, info); + + printf("Failed to specialize %s: %s\n", + target_to_short_name(target), info); + + free(info); + return PIGLIT_FAIL; + } + + switch (target) { + case GL_VERTEX_SHADER: + vertex_shaders[num_vertex_shaders] = shader; + num_vertex_shaders++; + break; + case GL_TESS_CONTROL_SHADER: + tess_ctrl_shaders[num_tess_ctrl_shaders] = shader; + num_tess_ctrl_shaders++; + break; + case GL_TESS_EVALUATION_SHADER: + tess_eval_shaders[num_tess_eval_shaders] = shader; + num_tess_eval_shaders++; + break; + case GL_GEOMETRY_SHADER: + geometry_shaders[num_geometry_shaders] = shader; + num_geometry_shaders++; + break; + case GL_FRAGMENT_SHADER: + fragment_shaders[num_fragment_shaders] = shader; + num_fragment_shaders++; + break; + case GL_COMPUTE_SHADER: + compute_shaders[num_compute_shaders] = shader; + num_compute_shaders++; + break; + } + + return PIGLIT_PASS; +} + +static enum piglit_result +assemble_spirv(GLenum target) +{ + if (!piglit_is_extension_supported("GL_ARB_gl_spirv")) { + return PIGLIT_SKIP; + } + + char *arguments[] = { + getenv("PIGLIT_SPIRV_AS_BINARY"), + "-o", "-", + NULL + }; + + if (arguments[0] == NULL) + arguments[0] = "spirv-as"; + + /* Strip comments from the source */ + char *stripped_source = malloc(shader_string_size); + char *p = stripped_source; + bool at_start_of_line = true; + + for (const char *in = shader_string; + in < shader_string + shader_string_size; + in++) { + if (*in == '#' && at_start_of_line) { + const char *end; + end = memchr(in, + '\n', + shader_string + shader_string_size - in); + if (end == NULL) + break; + in = end; + } else { + at_start_of_line = *in == '\n'; + *(p++) = *in; + } + } + + uint8_t *binary_source; + size_t binary_source_length; + bool res = piglit_subprocess(arguments, + p - stripped_source, + (const uint8_t *) + stripped_source, + &binary_source_length, + &binary_source); + + free(stripped_source); + + if (!res) { + fprintf(stderr, "spirv-as failed\n"); + return PIGLIT_FAIL; + } + + enum piglit_result ret; + + ret = load_and_specialize_spirv(target, + (const char *) + binary_source, + binary_source_length); + + free(binary_source); + + return ret; +} + static enum piglit_result link_sso(GLenum target) { @@ -1015,6 +1172,22 @@ process_requirement(const char *line) sso_in_use = true; glGenProgramPipelines(1, &pipeline); + } else if (parse_str(line, "SPIRV", &line)) { + spirv_replaces_glsl = !force_glsl; + + if (parse_str(line, "ONLY", NULL)) { + if (force_glsl) { + printf("This shader is not compatible with GLSL\n"); + return PIGLIT_SKIP; + } + } else if (parse_str(line, "YES", NULL)) { + /* Empty. Everything already set. Just parsing + * correct options + */ + } else { + printf("Unknown SPIRV line in [require]\n"); + return PIGLIT_FAIL; + } } return PIGLIT_PASS; } @@ -1045,9 +1218,8 @@ process_geometry_layout(const char *line) } } - static enum piglit_result -leave_state(enum states state, const char *line) +leave_state(enum states state, const char *line, const char *script_name) { switch (state) { case none: @@ -1057,6 +1229,8 @@ leave_state(enum states state, const char *line) break; case vertex_shader: + if (spirv_replaces_glsl) + break; shader_string_size = line - shader_string; return compile_glsl(GL_VERTEX_SHADER); @@ -1068,22 +1242,54 @@ leave_state(enum states state, const char *line) shader_string, line - shader_string); + case vertex_shader_spirv: + if (!spirv_replaces_glsl) + break; + shader_string_size = line - shader_string; + return assemble_spirv(GL_VERTEX_SHADER); + case tess_ctrl_shader: + if (spirv_replaces_glsl) + break; shader_string_size = line - shader_string; return compile_glsl(GL_TESS_CONTROL_SHADER); + case tess_ctrl_shader_spirv: + if (!spirv_replaces_glsl) + break; + shader_string_size = line - shader_string; + return assemble_spirv(GL_TESS_CONTROL_SHADER); + case tess_eval_shader: + if (spirv_replaces_glsl) + break; shader_string_size = line - shader_string; return compile_glsl(GL_TESS_EVALUATION_SHADER); + case tess_eval_shader_spirv: + if (!spirv_replaces_glsl) + break; + shader_string_size = line - shader_string; + return assemble_spirv(GL_TESS_EVALUATION_SHADER); + case geometry_shader: + if (spirv_replaces_glsl) + break; shader_string_size = line - shader_string; return compile_glsl(GL_GEOMETRY_SHADER); + case geometry_shader_spirv: + if (!spirv_replaces_glsl) + break; + shader_string_size = line - shader_string; + return assemble_spirv(GL_GEOMETRY_SHADER); + case geometry_layout: break; case fragment_shader: + if (spirv_replaces_glsl) + break; shader_string_size = line - shader_string; return compile_glsl(GL_FRAGMENT_SHADER); @@ -1093,10 +1299,24 @@ leave_state(enum states state, const char *line) line - shader_string); break; + case fragment_shader_spirv: + if (!spirv_replaces_glsl) + break; + shader_string_size = line - shader_string; + return assemble_spirv(GL_FRAGMENT_SHADER); + case compute_shader: + if (spirv_replaces_glsl) + break; shader_string_size = line - shader_string; return compile_glsl(GL_COMPUTE_SHADER); + case compute_shader_spirv: + if (!spirv_replaces_glsl) + break; + shader_string_size = line - shader_string; + return assemble_spirv(GL_COMPUTE_SHADER); + case vertex_data: vertex_data_end = line; break; @@ -1263,7 +1483,6 @@ cleanup: return result; } - static enum piglit_result process_test_script(const char *script_name) { @@ -1283,7 +1502,7 @@ process_test_script(const char *script_name) while (line[0] != '\0') { if (line[0] == '[') { - result = leave_state(state, line); + result = leave_state(state, line, script_name); if (result != PIGLIT_PASS) return result; @@ -1300,15 +1519,27 @@ process_test_script(const char *script_name) shader_string = (char *) passthrough_vertex_shader_source; shader_string_size = strlen(shader_string); + } else if (parse_str(line, "[vertex shader spirv]", NULL)) { + state = vertex_shader_spirv; + shader_string = NULL; } else if (parse_str(line, "[tessellation control shader]", NULL)) { state = tess_ctrl_shader; shader_string = NULL; + } else if (parse_str(line, "[tessellation control shader spirv]", NULL)) { + state = tess_ctrl_shader_spirv; + shader_string = NULL; } else if (parse_str(line, "[tessellation evaluation shader]", NULL)) { state = tess_eval_shader; shader_string = NULL; + } else if (parse_str(line, "[tessellation evaluation shader spirv]", NULL)) { + state = tess_eval_shader_spirv; + shader_string = NULL; } else if (parse_str(line, "[geometry shader]", NULL)) { state = geometry_shader; shader_string = NULL; + } else if (parse_str(line, "[geometry shader spirv]", NULL)) { + state = geometry_shader_spirv; + shader_string = NULL; } else if (parse_str(line, "[geometry layout]", NULL)) { state = geometry_layout; shader_string = NULL; @@ -1318,9 +1549,15 @@ process_test_script(const char *script_name) } else if (parse_str(line, "[fragment program]", NULL)) { state = fragment_program; shader_string = NULL; + } else if (parse_str(line, "[fragment shader spirv]", NULL)) { + state = fragment_shader_spirv; + shader_string = NULL; } else if (parse_str(line, "[compute shader]", NULL)) { state = compute_shader; shader_string = NULL; + } else if (parse_str(line, "[compute shader spirv]", NULL)) { + state = compute_shader_spirv; + shader_string = NULL; } else if (parse_str(line, "[vertex data]", NULL)) { state = vertex_data; vertex_data_start = NULL; @@ -1360,6 +1597,12 @@ process_test_script(const char *script_name) case fragment_shader: case fragment_program: case compute_shader: + case vertex_shader_spirv: + case tess_ctrl_shader_spirv: + case tess_eval_shader_spirv: + case geometry_shader_spirv: + case fragment_shader_spirv: + case compute_shader_spirv: if (shader_string == NULL) shader_string = (char *) line; break; @@ -1381,7 +1624,7 @@ process_test_script(const char *script_name) line_num++; } - return leave_state(state, line); + return leave_state(state, line, script_name); } struct requirement_parse_results { @@ -1516,7 +1759,7 @@ choose_required_gl_version(struct requirement_parse_results *parse_results, * the GL and GLSL version requirements. Use these to guide context creation. */ static void -get_required_config(const char *script_name, +get_required_config(const char *script_name, bool spirv, struct piglit_gl_test_config *config) { struct requirement_parse_results parse_results; @@ -1525,6 +1768,12 @@ get_required_config(const char *script_name, parse_required_config(&parse_results, script_name); choose_required_gl_version(&parse_results, &required_gl_version); + if (spirv) { + required_gl_version.es = false; + required_gl_version.core = true; + required_gl_version.num = MAX2(required_gl_version.num, 33); + } + if (parse_results.found_size) { config->window_width = parse_results.size[0]; config->window_height = parse_results.size[1]; @@ -4105,7 +4354,7 @@ validate_current_gl_context(const char *filename) config.window_height = DEFAULT_WINDOW_HEIGHT; } - get_required_config(filename, &config); + get_required_config(filename, spirv_replaces_glsl, &config); if (!current_config.supports_gl_compat_version != !config.supports_gl_compat_version) @@ -4146,6 +4395,7 @@ piglit_init(int argc, char **argv) false); report_subtests = piglit_strip_arg(&argc, argv, "-report-subtests"); + force_glsl = piglit_strip_arg(&argc, argv, "-glsl"); if (argc < 2) { printf("usage: shader_runner <test.shader_test>\n"); exit(1); -- 2.14.1 _______________________________________________ Piglit mailing list Piglit@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/piglit