validate-shaders.sh 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. #!/bin/bash
  2. # This script validates shaders (if successfully compiled) using spirv-val.
  3. # It is not meant to preclude the possible addition of the validator to
  4. # glslang.
  5. declare -r EXE='../build/install/bin/glslangValidator'
  6. # search common locations for spirv-tools: keep first one
  7. for toolsdir in '../External/spirv-tools/build/tools' '../../SPIRV-Tools/build/tools/bin' '/usr/local/bin'; do
  8. [[ -z "$VAL" && -x "${toolsdir}/spirv-val" ]] && declare -r VAL="${toolsdir}/spirv-val"
  9. [[ -z "$DIS" && -x "${toolsdir}/spirv-dis" ]] && declare -r DIS="${toolsdir}/spirv-dis"
  10. done
  11. declare -r gtests='../gtests/Hlsl.FromFile.cpp ../gtests/Spv.FromFile.cpp'
  12. declare -r targetenv='vulkan1.0'
  13. function fatal() { echo "ERROR: $@"; exit 5; }
  14. function usage
  15. {
  16. echo
  17. echo "Usage: $(basename $0) [options...] shaders..."
  18. echo
  19. echo " Validates shaders (if successfully compiled) through spirv-val."
  20. echo
  21. echo "General options:"
  22. echo " --help prints this text"
  23. echo " --no-color disables output colorization"
  24. echo " --dump-asm dumps all successfully compiled shader assemblies"
  25. echo " --dump-val dumps all validation results"
  26. echo " --dump-comp dumps all compilation logs"
  27. echo "Spam reduction options:"
  28. echo " --no-summary disables result summaries"
  29. echo " --skip-ok do not print successful validations"
  30. echo " --skip-comperr do not print compilation errors"
  31. echo " --skip-valerr do not print validation errors"
  32. echo " --quiet synonym for --skip-ok --skip-comperr --skip-valerr --no-summary"
  33. echo " --terse print terse single line progress summary"
  34. echo "Disassembly options:"
  35. echo " --raw-id uses raw ids for disassembly"
  36. echo
  37. echo "Usage examples. Note most non-hlsl tests fail to compile for expected reasons."
  38. echo " Exercise all hlsl.* files:"
  39. echo " $(basename $0) hlsl.*"
  40. echo " Exercise all hlsl.* files, tersely:"
  41. echo " $(basename $0) --terse hlsl.*"
  42. echo " Print validator output for myfile.frag:"
  43. echo " $(basename $0) --quiet --dump-val myfile.frag"
  44. echo " Exercise hlsl.* files, only printing validation errors:"
  45. echo " $(basename $0) --skip-ok --skip-comperr hlsl.*"
  46. exit 5
  47. }
  48. function status()
  49. {
  50. printf "%-40s: %b\n" "$1" "$2"
  51. }
  52. # make sure we can find glslang
  53. [[ -x "$EXE" ]] || fatal "Unable to locate $(basename "$EXE") executable"
  54. [[ -x "$VAL" ]] || fatal "Unable to locate spirv-val executable"
  55. [[ -x "$DIS" ]] || fatal "Unable to locate spirv-dis executable"
  56. for gtest in $gtests; do
  57. [[ -r "$gtest" ]] || fatal "Unable to locate source file: $(basename $gtest)"
  58. done
  59. # temp files
  60. declare -r spvfile='out.spv' \
  61. complog='comp.out' \
  62. vallog='val.out' \
  63. dislog='dis.out' \
  64. # options
  65. declare opt_vallog=false \
  66. opt_complog=false \
  67. opt_dislog=false \
  68. opt_summary=true \
  69. opt_stat_comperr=true \
  70. opt_stat_ok=true \
  71. opt_stat_valerr=true \
  72. opt_color=true \
  73. opt_raw_id=false \
  74. opt_quiet=false \
  75. opt_terse=false
  76. # clean up on exit
  77. trap "rm -f ${spvfile} ${complog} ${vallog} ${dislog}" EXIT
  78. # Language guesser: there is no fixed mapping from filenames to language,
  79. # so this examines the file and return one of:
  80. # hlsl
  81. # glsl
  82. # bin
  83. # unknown
  84. # This is easier WRT future expansion than a big explicit list.
  85. function FindLanguage()
  86. {
  87. local test="$1"
  88. # If it starts with hlsl, assume it's hlsl.
  89. if [[ "$test" == *hlsl.* ]]; then
  90. echo hlsl
  91. return
  92. fi
  93. if [[ "$test" == *.spv ]]; then
  94. echo bin
  95. return;
  96. fi
  97. # If it doesn't start with spv., assume it's GLSL.
  98. if [[ ! "$test" == spv.* && ! "$test" == remap.* ]]; then
  99. echo glsl
  100. return
  101. fi
  102. # Otherwise, attempt to guess from shader contents, since there's no
  103. # fixed mapping of filenames to languages.
  104. local contents="$(cat "$test")"
  105. if [[ "$contents" == *#version* ]]; then
  106. echo glsl
  107. return
  108. fi
  109. if [[ "$contents" == *SamplerState* ||
  110. "$contents" == *cbuffer* ||
  111. "$contents" == *SV_* ]]; then
  112. echo hlsl
  113. return
  114. fi
  115. echo unknown
  116. }
  117. # Attempt to discover entry point
  118. function FindEntryPoint()
  119. {
  120. local test="$1"
  121. # if it's not hlsl, always use main
  122. if [[ "$language" != 'hlsl' ]]; then
  123. echo 'main'
  124. return
  125. fi
  126. # Try to find it in test sources
  127. awk -F '[ (){",]+' -e "\$2 == \"${test}\" { print \$3; found=1; } END { if (found==0) print \"main\"; } " $gtests
  128. }
  129. # command line options
  130. while [ $# -gt 0 ]
  131. do
  132. case "$1" in
  133. # -c) glslang="$2"; shift 2;;
  134. --help|-?) usage;;
  135. --no-color) opt_color=false; shift;;
  136. --no-summary) opt_summary=false; shift;;
  137. --skip-ok) opt_stat_ok=false; shift;;
  138. --skip-comperr) opt_stat_comperr=false; shift;;
  139. --skip-valerr) opt_stat_valerr=false; shift;;
  140. --dump-asm) opt_dislog=true; shift;;
  141. --dump-val) opt_vallog=true; shift;;
  142. --dump-comp) opt_complog=true; shift;;
  143. --raw-id) opt_raw_id=true; shift;;
  144. --quiet) opt_quiet=true; shift;;
  145. --terse) opt_quiet=true
  146. opt_terse=true
  147. shift;;
  148. --*) fatal "Unknown command line option: $1";;
  149. *) break;;
  150. esac
  151. done
  152. # this is what quiet means
  153. if $opt_quiet; then
  154. opt_stat_ok=false
  155. opt_stat_comperr=false
  156. opt_stat_valerr=false
  157. $opt_terse || opt_summary=false
  158. fi
  159. if $opt_color; then
  160. declare -r white="\e[1;37m" cyan="\e[1;36m" red="\e[0;31m" no_color="\e[0m"
  161. else
  162. declare -r white="" cyan="" red="" no_color=""
  163. fi
  164. # stats
  165. declare -i count_ok=0 count_err=0 count_nocomp=0 count_total=0
  166. declare -r dashsep='------------------------------------------------------------------------'
  167. testfiles=(${@})
  168. # if no shaders given, look for everything in current directory
  169. [[ ${#testfiles[*]} == 0 ]] && testfiles=(*.frag *.vert *.tesc *.tese *.geom *.comp)
  170. $opt_summary && printf "\nValidating: ${#testfiles[*]} shaders\n\n"
  171. # Loop through the shaders we were given, compiling them if we can.
  172. for test in ${testfiles[*]}
  173. do
  174. if [[ ! -r "$test" ]]; then
  175. $opt_quiet || status "$test" "${red}FILE NOT FOUND${no_color}"
  176. continue
  177. fi
  178. ((++count_total))
  179. $opt_terse && printf "\r[%-3d/%-3d : ${white}comperr=%-3d ${red}valerr=%-3d ${cyan}ok=%-3d${no_color}]" \
  180. ${count_total} ${#testfiles[*]} ${count_nocomp} ${count_err} ${count_ok}
  181. language="$(FindLanguage $test)"
  182. entry="$(FindEntryPoint $test)"
  183. langops=''
  184. case "$language" in
  185. hlsl) langops='-D --hlsl-iomap --hlsl-offsets';;
  186. glsl) ;;
  187. bin) continue;; # skip binaries
  188. *) $opt_quiet || status "$test" "${red}UNKNOWN LANGUAGE${no_color}"; continue;;
  189. esac
  190. # compile the test file
  191. if compout=$("$EXE" -e "$entry" $langops -V -o "$spvfile" "$test" 2>&1)
  192. then
  193. # successful compilation: validate
  194. if valout=$("$VAL" --target-env ${targetenv} "$spvfile" 2>&1)
  195. then
  196. # validated OK
  197. $opt_stat_ok && status "$test" "${cyan}OK${no_color}"
  198. ((++count_ok))
  199. else
  200. # validation failure
  201. $opt_stat_valerr && status "$test" "${red}VAL ERROR${no_color}"
  202. printf "%s\n%s:\n%s\n" "$dashsep" "$test" "$valout" >> "$vallog"
  203. ((++count_err))
  204. fi
  205. if $opt_dislog; then
  206. printf "%s\n%s:\n" "$dashsep" "$test" >> "$dislog"
  207. $opt_raw_id && id_opt=--raw-id
  208. "$DIS" ${id_opt} "$spvfile" >> "$dislog"
  209. fi
  210. else
  211. # compile failure
  212. $opt_stat_comperr && status "$test" "${white}COMP ERROR${no_color}"
  213. printf "%s\n%s\n" "$dashsep" "$compout" >> "$complog"
  214. ((++count_nocomp))
  215. fi
  216. done
  217. $opt_terse && echo
  218. # summarize
  219. $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"
  220. # dump logs
  221. $opt_vallog && [[ -r $vallog ]] && cat "$vallog"
  222. $opt_complog && [[ -r $complog ]] && cat "$complog"
  223. $opt_dislog && [[ -r $dislog ]] && cat "$dislog"
  224. # exit code
  225. [[ ${count_err} -gt 0 ]] && exit 1
  226. exit 0