diff --git a/CMakeLists.txt b/CMakeLists.txt index f232b59f..18d44842 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -112,12 +112,6 @@ macro(spirv_cross_add_library name config_name) export(TARGETS ${name} FILE ${config_name}Config.cmake) endmacro() -macro(add_spirv_cross_test test-name) - add_test(NAME ${test-name} ${ARGN}) - set_tests_properties(${test-name} PROPERTIES - ENVIRONMENT PATH=${CMAKE_CURRENT_SOURCE_DIR}/external/glslang-build/output/bin:${CMAKE_CURRENT_SOURCE_DIR}/external/spirv-tools-build/output/bin:$ENV{PATH}) -endmacro() - set(spirv-cross-core-sources ${CMAKE_CURRENT_SOURCE_DIR}/GLSL.std.450.h ${CMAKE_CURRENT_SOURCE_DIR}/spirv_common.hpp @@ -268,8 +262,11 @@ if (SPIRV_CROSS_CLI) find_program(spirv-cross-spirv-val NAMES spirv-val PATHS ${CMAKE_CURRENT_SOURCE_DIR}/external/spirv-tools-build/output/bin NO_DEFAULT_PATH) + find_program(spirv-cross-spirv-opt NAMES spirv-opt + PATHS ${CMAKE_CURRENT_SOURCE_DIR}/external/spirv-tools-build/output/bin + NO_DEFAULT_PATH) - if ((${spirv-cross-glslang} MATCHES "NOTFOUND") OR (${spirv-cross-spirv-as} MATCHES "NOTFOUND") OR (${spirv-cross-spirv-val} MATCHES "NOTFOUND")) + if ((${spirv-cross-glslang} MATCHES "NOTFOUND") OR (${spirv-cross-spirv-as} MATCHES "NOTFOUND") OR (${spirv-cross-spirv-val} MATCHES "NOTFOUND") OR (${spirv-cross-spirv-opt} MATCHES "NOTFOUND")) set(SPIRV_CROSS_ENABLE_TESTS OFF) message("Could not find glslang or SPIRV-Tools build under external/. Run ./checkout_glslang_spirv_tools.sh and ./build_glslang_spirv_tools.sh. Testing will be disabled.") else() @@ -278,8 +275,15 @@ if (SPIRV_CROSS_CLI) message("Found glslangValidator in: ${spirv-cross-glslang}.") message("Found spirv-as in: ${spirv-cross-spirv-as}.") message("Found spirv-val in: ${spirv-cross-spirv-val}.") + message("Found spirv-opt in: ${spirv-cross-spirv-opt}.") endif() + set(spirv-cross-externals + --glslang "${spirv-cross-glslang}" + --spirv-as "${spirv-cross-spirv-as}" + --spirv-opt "${spirv-cross-spirv-opt}" + --spirv-val "${spirv-cross-spirv-val}") + if (${PYTHONINTERP_FOUND} AND SPIRV_CROSS_ENABLE_TESTS) if (${PYTHON_VERSION_MAJOR} GREATER 2) add_executable(spirv-cross-c-api-test tests-other/c_api_test.c) @@ -288,46 +292,56 @@ if (SPIRV_CROSS_CLI) if (CMAKE_COMPILER_IS_GNUCXX OR (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")) target_compile_options(spirv-cross-c-api-test PRIVATE -std=c89 -Wall -Wextra) endif() - add_spirv_cross_test(spirv-cross-c-api-test + add_test(NAME spirv-cross-c-api-test COMMAND $ ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/c_api_test.spv) - add_spirv_cross_test(spirv-cross-test + add_test(NAME spirv-cross-test COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --parallel + ${spirv-cross-externals} ${CMAKE_CURRENT_SOURCE_DIR}/shaders WORKING_DIRECTORY $) - add_spirv_cross_test(spirv-cross-test-no-opt + add_test(NAME spirv-cross-test-no-opt COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --parallel + ${spirv-cross-externals} ${CMAKE_CURRENT_SOURCE_DIR}/shaders-no-opt WORKING_DIRECTORY $) - add_spirv_cross_test(spirv-cross-test-metal + add_test(NAME spirv-cross-test-metal COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --metal --parallel + ${spirv-cross-externals} ${CMAKE_CURRENT_SOURCE_DIR}/shaders-msl WORKING_DIRECTORY $) - add_spirv_cross_test(spirv-cross-test-metal-no-opt + add_test(NAME spirv-cross-test-metal-no-opt COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --metal --parallel + ${spirv-cross-externals} ${CMAKE_CURRENT_SOURCE_DIR}/shaders-msl-no-opt WORKING_DIRECTORY $) - add_spirv_cross_test(spirv-cross-test-hlsl + add_test(NAME spirv-cross-test-hlsl COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --hlsl --parallel + ${spirv-cross-externals} ${CMAKE_CURRENT_SOURCE_DIR}/shaders-hlsl WORKING_DIRECTORY $) - add_spirv_cross_test(spirv-cross-test-hlsl-no-opt + add_test(NAME spirv-cross-test-hlsl-no-opt COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --hlsl --parallel + ${spirv-cross-externals} ${CMAKE_CURRENT_SOURCE_DIR}/shaders-hlsl-no-opt WORKING_DIRECTORY $) - add_spirv_cross_test(spirv-cross-test-opt + add_test(NAME spirv-cross-test-opt COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --opt --parallel + ${spirv-cross-externals} ${CMAKE_CURRENT_SOURCE_DIR}/shaders WORKING_DIRECTORY $) - add_spirv_cross_test(spirv-cross-test-metal-opt + add_test(NAME spirv-cross-test-metal-opt COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --metal --opt --parallel + ${spirv-cross-externals} ${CMAKE_CURRENT_SOURCE_DIR}/shaders-msl WORKING_DIRECTORY $) - add_spirv_cross_test(spirv-cross-test-hlsl-opt + add_test(NAME spirv-cross-test-hlsl-opt COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --hlsl --opt --parallel + ${spirv-cross-externals} ${CMAKE_CURRENT_SOURCE_DIR}/shaders-hlsl WORKING_DIRECTORY $) - add_spirv_cross_test(spirv-cross-test-reflection + add_test(NAME spirv-cross-test-reflection COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --reflect --parallel + ${spirv-cross-externals} ${CMAKE_CURRENT_SOURCE_DIR}/shaders-reflection WORKING_DIRECTORY $) endif() diff --git a/test_shaders.py b/test_shaders.py index cf233e60..4f31cf10 100755 --- a/test_shaders.py +++ b/test_shaders.py @@ -16,6 +16,13 @@ import multiprocessing import errno from functools import partial +class Paths(): + def __init__(self, glslang, spirv_as, spirv_val, spirv_opt): + self.glslang = glslang + self.spirv_as = spirv_as + self.spirv_val = spirv_val + self.spirv_opt = spirv_opt + def remove_file(path): #print('Removing file:', path) os.remove(path) @@ -131,21 +138,21 @@ def validate_shader_msl(shader, opt): print('Error compiling Metal shader: ' + msl_path) raise RuntimeError('Failed to compile Metal shader') -def cross_compile_msl(shader, spirv, opt): +def cross_compile_msl(shader, spirv, opt, paths): spirv_path = create_temporary() msl_path = create_temporary(os.path.basename(shader)) - spirv_cmd = ['spirv-as', '-o', spirv_path, shader] + spirv_cmd = [paths.spirv_as, '-o', spirv_path, shader] if '.preserve.' in shader: spirv_cmd.append('--preserve-numeric-ids') if spirv: subprocess.check_call(spirv_cmd) else: - subprocess.check_call(['glslangValidator', '--target-env', 'vulkan1.1', '-V', '-o', spirv_path, shader]) + subprocess.check_call([paths.glslang, '--target-env', 'vulkan1.1', '-V', '-o', spirv_path, shader]) if opt: - subprocess.check_call(['spirv-opt', '--skip-validation', '-O', '-o', spirv_path, spirv_path]) + subprocess.check_call([paths.spirv_opt, '--skip-validation', '-O', '-o', spirv_path, spirv_path]) spirv_cross_path = './spirv-cross' @@ -166,7 +173,7 @@ def cross_compile_msl(shader, spirv, opt): subprocess.check_call(msl_args) if not shader_is_invalid_spirv(msl_path): - subprocess.check_call(['spirv-val', '--target-env', 'vulkan1.1', spirv_path]) + subprocess.check_call([paths.spirv_val, '--target-env', 'vulkan1.1', spirv_path]) return (spirv_path, msl_path) @@ -201,8 +208,8 @@ def shader_to_win_path(shader): return shader ignore_fxc = False -def validate_shader_hlsl(shader, force_no_external_validation): - subprocess.check_call(['glslangValidator', '-e', 'main', '-D', '--target-env', 'vulkan1.1', '-V', shader]) +def validate_shader_hlsl(shader, force_no_external_validation, paths): + subprocess.check_call([paths.glslang, '-e', 'main', '-D', '--target-env', 'vulkan1.1', '-V', shader]) is_no_fxc = '.nofxc.' in shader global ignore_fxc if (not ignore_fxc) and (not force_no_external_validation) and (not is_no_fxc): @@ -231,21 +238,21 @@ def shader_to_sm(shader): else: return '50' -def cross_compile_hlsl(shader, spirv, opt, force_no_external_validation): +def cross_compile_hlsl(shader, spirv, opt, force_no_external_validation, paths): spirv_path = create_temporary() hlsl_path = create_temporary(os.path.basename(shader)) - spirv_cmd = ['spirv-as', '-o', spirv_path, shader] + spirv_cmd = [paths.spirv_as, '-o', spirv_path, shader] if '.preserve.' in shader: spirv_cmd.append('--preserve-numeric-ids') if spirv: subprocess.check_call(spirv_cmd) else: - subprocess.check_call(['glslangValidator', '--target-env', 'vulkan1.1', '-V', '-o', spirv_path, shader]) + subprocess.check_call([paths.glslang, '--target-env', 'vulkan1.1', '-V', '-o', spirv_path, shader]) if opt: - subprocess.check_call(['spirv-opt', '--skip-validation', '-O', '-o', spirv_path, spirv_path]) + subprocess.check_call([paths.spirv_opt, '--skip-validation', '-O', '-o', spirv_path, spirv_path]) spirv_cross_path = './spirv-cross' @@ -253,27 +260,27 @@ def cross_compile_hlsl(shader, spirv, opt, force_no_external_validation): subprocess.check_call([spirv_cross_path, '--entry', 'main', '--output', hlsl_path, spirv_path, '--hlsl-enable-compat', '--hlsl', '--shader-model', sm]) if not shader_is_invalid_spirv(hlsl_path): - subprocess.check_call(['spirv-val', '--target-env', 'vulkan1.1', spirv_path]) + subprocess.check_call([paths.spirv_val, '--target-env', 'vulkan1.1', spirv_path]) - validate_shader_hlsl(hlsl_path, force_no_external_validation) + validate_shader_hlsl(hlsl_path, force_no_external_validation, paths) return (spirv_path, hlsl_path) -def cross_compile_reflect(shader, spirv, opt): +def cross_compile_reflect(shader, spirv, opt, paths): spirv_path = create_temporary() reflect_path = create_temporary(os.path.basename(shader)) - spirv_cmd = ['spirv-as', '-o', spirv_path, shader] + spirv_cmd = [paths.spirv_as, '-o', spirv_path, shader] if '.preserve.' in shader: spirv_cmd.append('--preserve-numeric-ids') if spirv: subprocess.check_call(spirv_cmd) else: - subprocess.check_call(['glslangValidator', '--target-env', 'vulkan1.1', '-V', '-o', spirv_path, shader]) + subprocess.check_call([paths.glslang, '--target-env', 'vulkan1.1', '-V', '-o', spirv_path, shader]) if opt: - subprocess.check_call(['spirv-opt', '--skip-validation', '-O', '-o', spirv_path, spirv_path]) + subprocess.check_call([paths.spirv_opt, '--skip-validation', '-O', '-o', spirv_path, spirv_path]) spirv_cross_path = './spirv-cross' @@ -281,33 +288,33 @@ def cross_compile_reflect(shader, spirv, opt): subprocess.check_call([spirv_cross_path, '--entry', 'main', '--output', reflect_path, spirv_path, '--reflect']) return (spirv_path, reflect_path) -def validate_shader(shader, vulkan): +def validate_shader(shader, vulkan, paths): if vulkan: - subprocess.check_call(['glslangValidator', '--target-env', 'vulkan1.1', '-V', shader]) + subprocess.check_call([paths.glslang, '--target-env', 'vulkan1.1', '-V', shader]) else: - subprocess.check_call(['glslangValidator', shader]) + subprocess.check_call([paths.glslang, shader]) -def cross_compile(shader, vulkan, spirv, invalid_spirv, eliminate, is_legacy, flatten_ubo, sso, flatten_dim, opt): +def cross_compile(shader, vulkan, spirv, invalid_spirv, eliminate, is_legacy, flatten_ubo, sso, flatten_dim, opt, paths): spirv_path = create_temporary() glsl_path = create_temporary(os.path.basename(shader)) if vulkan or spirv: vulkan_glsl_path = create_temporary('vk' + os.path.basename(shader)) - spirv_cmd = ['spirv-as', '-o', spirv_path, shader] + spirv_cmd = [paths.spirv_as, '-o', spirv_path, shader] if '.preserve.' in shader: spirv_cmd.append('--preserve-numeric-ids') if spirv: subprocess.check_call(spirv_cmd) else: - subprocess.check_call(['glslangValidator', '--target-env', 'vulkan1.1', '-V', '-o', spirv_path, shader]) + subprocess.check_call([paths.glslang, '--target-env', 'vulkan1.1', '-V', '-o', spirv_path, shader]) if opt and (not invalid_spirv): - subprocess.check_call(['spirv-opt', '--skip-validation', '-O', '-o', spirv_path, spirv_path]) + subprocess.check_call([paths.spirv_opt, '--skip-validation', '-O', '-o', spirv_path, spirv_path]) if not invalid_spirv: - subprocess.check_call(['spirv-val', '--target-env', 'vulkan1.1', spirv_path]) + subprocess.check_call([paths.spirv_val, '--target-env', 'vulkan1.1', spirv_path]) extra_args = [] if eliminate: @@ -326,14 +333,14 @@ def cross_compile(shader, vulkan, spirv, invalid_spirv, eliminate, is_legacy, fl # A shader might not be possible to make valid GLSL from, skip validation for this case. if not ('nocompat' in glsl_path): subprocess.check_call([spirv_cross_path, '--entry', 'main', '--output', glsl_path, spirv_path] + extra_args) - validate_shader(glsl_path, False) + validate_shader(glsl_path, False, paths) else: remove_file(glsl_path) glsl_path = None if vulkan or spirv: subprocess.check_call([spirv_cross_path, '--entry', 'main', '--vulkan-semantics', '--output', vulkan_glsl_path, spirv_path] + extra_args) - validate_shader(vulkan_glsl_path, True) + validate_shader(vulkan_glsl_path, True, paths) # SPIR-V shaders might just want to validate Vulkan GLSL output, we don't always care about the output. if not vulkan: remove_file(vulkan_glsl_path) @@ -481,7 +488,7 @@ def shader_is_flatten_dimensions(shader): def shader_is_noopt(shader): return '.noopt.' in shader -def test_shader(stats, shader, update, keep, opt): +def test_shader(stats, shader, update, keep, opt, paths): joined_path = os.path.join(shader[0], shader[1]) vulkan = shader_is_vulkan(shader[1]) desktop = shader_is_desktop(shader[1]) @@ -495,7 +502,7 @@ def test_shader(stats, shader, update, keep, opt): noopt = shader_is_noopt(shader[1]) print('Testing shader:', joined_path) - spirv, glsl, vulkan_glsl = cross_compile(joined_path, vulkan, is_spirv, invalid_spirv, eliminate, is_legacy, flatten_ubo, sso, flatten_dim, opt and (not noopt)) + spirv, glsl, vulkan_glsl = cross_compile(joined_path, vulkan, is_spirv, invalid_spirv, eliminate, is_legacy, flatten_ubo, sso, flatten_dim, opt and (not noopt), paths) # Only test GLSL stats if we have a shader following GL semantics. if stats and (not vulkan) and (not is_spirv) and (not desktop): @@ -519,12 +526,12 @@ def test_shader(stats, shader, update, keep, opt): a.append(str(i)) print(','.join(a), file = stats) -def test_shader_msl(stats, shader, update, keep, opt, force_no_external_validation): +def test_shader_msl(stats, shader, update, keep, opt, force_no_external_validation, paths): joined_path = os.path.join(shader[0], shader[1]) print('\nTesting MSL shader:', joined_path) is_spirv = shader_is_spirv(shader[1]) noopt = shader_is_noopt(shader[1]) - spirv, msl = cross_compile_msl(joined_path, is_spirv, opt and (not noopt)) + spirv, msl = cross_compile_msl(joined_path, is_spirv, opt and (not noopt), paths) regression_check(shader, msl, update, keep, opt) # Uncomment the following line to print the temp SPIR-V file path. @@ -540,34 +547,34 @@ def test_shader_msl(stats, shader, update, keep, opt, force_no_external_validati remove_file(spirv) -def test_shader_hlsl(stats, shader, update, keep, opt, force_no_external_validation): +def test_shader_hlsl(stats, shader, update, keep, opt, force_no_external_validation, paths): joined_path = os.path.join(shader[0], shader[1]) print('Testing HLSL shader:', joined_path) is_spirv = shader_is_spirv(shader[1]) noopt = shader_is_noopt(shader[1]) - spirv, hlsl = cross_compile_hlsl(joined_path, is_spirv, opt and (not noopt), force_no_external_validation) + spirv, hlsl = cross_compile_hlsl(joined_path, is_spirv, opt and (not noopt), force_no_external_validation, paths) regression_check(shader, hlsl, update, keep, opt) remove_file(spirv) -def test_shader_reflect(stats, shader, update, keep, opt): +def test_shader_reflect(stats, shader, update, keep, opt, paths): joined_path = os.path.join(shader[0], shader[1]) print('Testing shader reflection:', joined_path) is_spirv = shader_is_spirv(shader[1]) noopt = shader_is_noopt(shader[1]) - spirv, reflect = cross_compile_reflect(joined_path, is_spirv, opt and (not noopt)) + spirv, reflect = cross_compile_reflect(joined_path, is_spirv, opt and (not noopt), paths) regression_check_reflect(shader, reflect, update, keep, opt) remove_file(spirv) -def test_shader_file(relpath, stats, shader_dir, update, keep, opt, force_no_external_validation, backend): +def test_shader_file(relpath, stats, shader_dir, update, keep, opt, force_no_external_validation, backend, paths): try: if backend == 'msl': - test_shader_msl(stats, (shader_dir, relpath), update, keep, opt, force_no_external_validation) + test_shader_msl(stats, (shader_dir, relpath), update, keep, opt, force_no_external_validation, paths) elif backend == 'hlsl': - test_shader_hlsl(stats, (shader_dir, relpath), update, keep, opt, force_no_external_validation) + test_shader_hlsl(stats, (shader_dir, relpath), update, keep, opt, force_no_external_validation, paths) elif backend == 'reflect': - test_shader_reflect(stats, (shader_dir, relpath), update, keep, opt) + test_shader_reflect(stats, (shader_dir, relpath), update, keep, opt, paths) else: - test_shader(stats, (shader_dir, relpath), update, keep, opt) + test_shader(stats, (shader_dir, relpath), update, keep, opt, paths) return None except Exception as e: return e @@ -581,6 +588,8 @@ def test_shaders_helper(stats, backend, args): relpath = os.path.relpath(path, args.folder) all_files.append(relpath) + paths = Paths(args.glslang, args.spirv_as, args.spirv_val, args.spirv_opt) + # The child processes in parallel execution mode don't have the proper state for the global args variable, so # at this point we need to switch to explicit arguments if args.parallel: @@ -591,7 +600,7 @@ def test_shaders_helper(stats, backend, args): results.append(pool.apply_async(test_shader_file, args = (f, stats, args.folder, args.update, args.keep, args.opt, args.force_no_external_validation, - backend))) + backend, paths))) for res in results: error = res.get() @@ -602,7 +611,7 @@ def test_shaders_helper(stats, backend, args): sys.exit(1) else: for i in all_files: - e = test_shader_file(i, stats, args.folder, args.update, args.keep, args.opt, args.force_no_external_validation, backend) + e = test_shader_file(i, stats, args.folder, args.update, args.keep, args.opt, args.force_no_external_validation, backend, paths) if e is not None: print('Error:', e) sys.exit(1) @@ -649,6 +658,18 @@ def main(): parser.add_argument('--parallel', action = 'store_true', help = 'Execute tests in parallel. Useful for doing regression quickly, but bad for debugging and stat output.') + parser.add_argument('--glslang', + default = 'glslangValidator', + help = 'Explicit path to glslangValidator') + parser.add_argument('--spirv-as', + default = 'spirv-as', + help = 'Explicit path to spirv-as') + parser.add_argument('--spirv-val', + default = 'spirv-val', + help = 'Explicit path to spirv-val') + parser.add_argument('--spirv-opt', + default = 'spirv-opt', + help = 'Explicit path to spirv-opt') args = parser.parse_args() if not args.folder: diff --git a/tests-other/c_api_test.c b/tests-other/c_api_test.c index 98109178..2d643c7e 100644 --- a/tests-other/c_api_test.c +++ b/tests-other/c_api_test.c @@ -1,5 +1,9 @@ /* Smoke test for the C API. */ +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif + #include #include #include