| #!/bin/bash |
| |
| # This script validates shaders (if successfully compiled) using spirv-val. |
| # It is not meant to preclude the possible addition of the validator to |
| # glslang. |
| |
| declare -r EXE='../build/install/bin/glslangValidator' |
| |
| # search common locations for spirv-tools: keep first one |
| for toolsdir in '../External/spirv-tools/build/tools' '../../SPIRV-Tools/build/tools/bin' '/usr/local/bin'; do |
| [[ -z "$VAL" && -x "${toolsdir}/spirv-val" ]] && declare -r VAL="${toolsdir}/spirv-val" |
| [[ -z "$DIS" && -x "${toolsdir}/spirv-dis" ]] && declare -r DIS="${toolsdir}/spirv-dis" |
| done |
| |
| declare -r gtests='../gtests/Hlsl.FromFile.cpp ../gtests/Spv.FromFile.cpp' |
| |
| declare -r targetenv='vulkan1.0' |
| |
| function fatal() { echo "ERROR: $@"; exit 5; } |
| |
| function usage |
| { |
| echo |
| echo "Usage: $(basename $0) [options...] shaders..." |
| echo |
| echo " Validates shaders (if successfully compiled) through spirv-val." |
| echo |
| echo "General options:" |
| echo " --help prints this text" |
| echo " --no-color disables output colorization" |
| echo " --dump-asm dumps all successfully compiled shader assemblies" |
| echo " --dump-val dumps all validation results" |
| echo " --dump-comp dumps all compilation logs" |
| echo "Spam reduction options:" |
| echo " --no-summary disables result summaries" |
| echo " --skip-ok do not print successful validations" |
| echo " --skip-comperr do not print compilation errors" |
| echo " --skip-valerr do not print validation errors" |
| echo " --quiet synonym for --skip-ok --skip-comperr --skip-valerr --no-summary" |
| echo " --terse print terse single line progress summary" |
| echo "Disassembly options:" |
| echo " --raw-id uses raw ids for disassembly" |
| echo |
| echo "Usage examples. Note most non-hlsl tests fail to compile for expected reasons." |
| echo " Exercise all hlsl.* files:" |
| echo " $(basename $0) hlsl.*" |
| echo " Exercise all hlsl.* files, tersely:" |
| echo " $(basename $0) --terse hlsl.*" |
| echo " Print validator output for myfile.frag:" |
| echo " $(basename $0) --quiet --dump-val myfile.frag" |
| echo " Exercise hlsl.* files, only printing validation errors:" |
| echo " $(basename $0) --skip-ok --skip-comperr hlsl.*" |
| |
| exit 5 |
| } |
| |
| function status() |
| { |
| printf "%-40s: %b\n" "$1" "$2" |
| } |
| |
| # make sure we can find glslang |
| [[ -x "$EXE" ]] || fatal "Unable to locate $(basename "$EXE") executable" |
| [[ -x "$VAL" ]] || fatal "Unable to locate spirv-val executable" |
| [[ -x "$DIS" ]] || fatal "Unable to locate spirv-dis executable" |
| |
| for gtest in $gtests; do |
| [[ -r "$gtest" ]] || fatal "Unable to locate source file: $(basename $gtest)" |
| done |
| |
| # temp files |
| declare -r spvfile='out.spv' \ |
| complog='comp.out' \ |
| vallog='val.out' \ |
| dislog='dis.out' \ |
| |
| # options |
| declare opt_vallog=false \ |
| opt_complog=false \ |
| opt_dislog=false \ |
| opt_summary=true \ |
| opt_stat_comperr=true \ |
| opt_stat_ok=true \ |
| opt_stat_valerr=true \ |
| opt_color=true \ |
| opt_raw_id=false \ |
| opt_quiet=false \ |
| opt_terse=false |
| |
| # clean up on exit |
| trap "rm -f ${spvfile} ${complog} ${vallog} ${dislog}" EXIT |
| |
| # Language guesser: there is no fixed mapping from filenames to language, |
| # so this examines the file and return one of: |
| # hlsl |
| # glsl |
| # bin |
| # unknown |
| # This is easier WRT future expansion than a big explicit list. |
| function FindLanguage() |
| { |
| local test="$1" |
| |
| # If it starts with hlsl, assume it's hlsl. |
| if [[ "$test" == *hlsl.* ]]; then |
| echo hlsl |
| return |
| fi |
| |
| if [[ "$test" == *.spv ]]; then |
| echo bin |
| return; |
| fi |
| |
| # If it doesn't start with spv., assume it's GLSL. |
| if [[ ! "$test" == spv.* && ! "$test" == remap.* ]]; then |
| echo glsl |
| return |
| fi |
| |
| # Otherwise, attempt to guess from shader contents, since there's no |
| # fixed mapping of filenames to languages. |
| local contents="$(cat "$test")" |
| |
| if [[ "$contents" == *#version* ]]; then |
| echo glsl |
| return |
| fi |
| |
| if [[ "$contents" == *SamplerState* || |
| "$contents" == *cbuffer* || |
| "$contents" == *SV_* ]]; then |
| echo hlsl |
| return |
| fi |
| |
| echo unknown |
| } |
| |
| # Attempt to discover entry point |
| function FindEntryPoint() |
| { |
| local test="$1" |
| |
| # if it's not hlsl, always use main |
| if [[ "$language" != 'hlsl' ]]; then |
| echo 'main' |
| return |
| fi |
| |
| # Try to find it in test sources |
| awk -F '[ (){",]+' -e "\$2 == \"${test}\" { print \$3; found=1; } END { if (found==0) print \"main\"; } " $gtests |
| } |
| |
| # command line options |
| while [ $# -gt 0 ] |
| do |
| case "$1" in |
| # -c) glslang="$2"; shift 2;; |
| --help|-?) usage;; |
| --no-color) opt_color=false; shift;; |
| --no-summary) opt_summary=false; shift;; |
| --skip-ok) opt_stat_ok=false; shift;; |
| --skip-comperr) opt_stat_comperr=false; shift;; |
| --skip-valerr) opt_stat_valerr=false; shift;; |
| --dump-asm) opt_dislog=true; shift;; |
| --dump-val) opt_vallog=true; shift;; |
| --dump-comp) opt_complog=true; shift;; |
| --raw-id) opt_raw_id=true; shift;; |
| --quiet) opt_quiet=true; shift;; |
| --terse) opt_quiet=true |
| opt_terse=true |
| shift;; |
| --*) fatal "Unknown command line option: $1";; |
| *) break;; |
| esac |
| done |
| |
| # this is what quiet means |
| if $opt_quiet; then |
| opt_stat_ok=false |
| opt_stat_comperr=false |
| opt_stat_valerr=false |
| $opt_terse || opt_summary=false |
| fi |
| |
| if $opt_color; then |
| declare -r white="\e[1;37m" cyan="\e[1;36m" red="\e[0;31m" no_color="\e[0m" |
| else |
| declare -r white="" cyan="" red="" no_color="" |
| fi |
| |
| # stats |
| declare -i count_ok=0 count_err=0 count_nocomp=0 count_total=0 |
| |
| declare -r dashsep='------------------------------------------------------------------------' |
| |
| testfiles=(${@}) |
| # if no shaders given, look for everything in current directory |
| [[ ${#testfiles[*]} == 0 ]] && testfiles=(*.frag *.vert *.tesc *.tese *.geom *.comp) |
| |
| $opt_summary && printf "\nValidating: ${#testfiles[*]} shaders\n\n" |
| |
| # Loop through the shaders we were given, compiling them if we can. |
| for test in ${testfiles[*]} |
| do |
| if [[ ! -r "$test" ]]; then |
| $opt_quiet || status "$test" "${red}FILE NOT FOUND${no_color}" |
| continue |
| fi |
| |
| ((++count_total)) |
| |
| $opt_terse && printf "\r[%-3d/%-3d : ${white}comperr=%-3d ${red}valerr=%-3d ${cyan}ok=%-3d${no_color}]" \ |
| ${count_total} ${#testfiles[*]} ${count_nocomp} ${count_err} ${count_ok} |
| |
| language="$(FindLanguage $test)" |
| entry="$(FindEntryPoint $test)" |
| langops='' |
| |
| case "$language" in |
| hlsl) langops='-D --hlsl-iomap --hlsl-offsets';; |
| glsl) ;; |
| bin) continue;; # skip binaries |
| *) $opt_quiet || status "$test" "${red}UNKNOWN LANGUAGE${no_color}"; continue;; |
| esac |
| |
| # compile the test file |
| if compout=$("$EXE" -e "$entry" $langops -V -o "$spvfile" "$test" 2>&1) |
| then |
| # successful compilation: validate |
| if valout=$("$VAL" --target-env ${targetenv} "$spvfile" 2>&1) |
| then |
| # validated OK |
| $opt_stat_ok && status "$test" "${cyan}OK${no_color}" |
| ((++count_ok)) |
| else |
| # validation failure |
| $opt_stat_valerr && status "$test" "${red}VAL ERROR${no_color}" |
| printf "%s\n%s:\n%s\n" "$dashsep" "$test" "$valout" >> "$vallog" |
| ((++count_err)) |
| fi |
| |
| if $opt_dislog; then |
| printf "%s\n%s:\n" "$dashsep" "$test" >> "$dislog" |
| $opt_raw_id && id_opt=--raw-id |
| "$DIS" ${id_opt} "$spvfile" >> "$dislog" |
| fi |
| else |
| # compile failure |
| $opt_stat_comperr && status "$test" "${white}COMP ERROR${no_color}" |
| printf "%s\n%s\n" "$dashsep" "$compout" >> "$complog" |
| ((++count_nocomp)) |
| fi |
| done |
| |
| $opt_terse && echo |
| |
| # summarize |
| $opt_summary && printf "\nSummary: ${white}${count_nocomp} compile errors${no_color}, ${red}${count_err} validation errors${no_color}, ${cyan}${count_ok} successes${no_color}\n" |
| |
| # dump logs |
| $opt_vallog && [[ -r $vallog ]] && cat "$vallog" |
| $opt_complog && [[ -r $complog ]] && cat "$complog" |
| $opt_dislog && [[ -r $dislog ]] && cat "$dislog" |
| |
| # exit code |
| [[ ${count_err} -gt 0 ]] && exit 1 |
| exit 0 |