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