yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
6dd5bd52e
master
1import os 2import shutil 3import glob 4import subprocess 5import argparse 6import sys 7import prettytable 8import json 9 10### Setup ### 11 12def clear_mkdir (dir ): 13if os .path .exists (dir ): 14shutil .rmtree (dir ) 15os .makedirs (dir ,exist_ok = True ) 16 17clear_mkdir ('modules' ) 18clear_mkdir ('targets' ) 19clear_mkdir ('targets/generated' ) 20 21target_choices = [ 22'spirv' ,# SPIRV directly 23'spirv-glsl' ,# SPIRV through synthesized GLSL 24'dxil' ,# DXIL with HLSL and DXC 25'dxil-embedded' # DXIL with precompiled modules 26] 27 28parser = argparse .ArgumentParser () 29parser .add_argument ('--target' ,type = str ,default = 'spirv' ,choices = target_choices ) 30parser .add_argument ('--samples' ,type = int ,default = 1 ) 31parser .add_argument ('--output' ,type = str ,default = 'benchmarks.json' ) 32 33args = parser .parse_args (sys .argv [1 :]) 34 35slangc = '..\\..\\build\\Release\\bin\\slangc.exe' 36target = args .target 37samples = args .samples 38 39if target == 'spirv' : 40target = 'spirv -emit-spirv-directly' 41target_ext = 'spirv' 42embed = False 43elif target == 'spirv-glsl' : 44target = 'spirv -emit-spirv-via-glsl' 45target_ext = 'spirv' 46embed = False 47elif target == 'dxil-embedded' : 48target_ext = 'dxil' 49embed = True 50elif target == 'dxil' : 51target_ext = 'dxil' 52embed = False 53 54f'slangc: { slangc } ' ) 55f'target: { target } ' ) 56f'samples: { samples } \n' ) 57 58### Utility ### 59 60def parse (results ): 61results = results .split ('\n' ) 62results = [r for r in results if r .startswith ('[*]' ) ] 63results = [r .split ()for r in results ] 64profile = {} 65for r in results : 66profile [r [1 ]]= float (r [- 1 ][:- 2 ]) 67return profile 68 69timings = {} 70def run (command ,key ): 71profile = {} 72for i in range (samples ): 73try : 74results = subprocess .check_output (command ,stderr = subprocess .STDOUT ,shell = True ).decode ('utf-8' ) 75except subprocess .CalledProcessError as exc : 76f"[Error] Failed to run command: { command } " ) 77exc .output .decode ('utf-8' )) 78return # Return without adding to timings 79 80p = parse (results ) 81if len (profile )== 0 : 82profile = p 83else : 84for k ,v in p .items (): 85profile .setdefault (k ,0 ) 86profile [k ]+= v 87 88# Only add to timings if we have data 89if profile : 90for k in profile : 91profile [k ]/= samples 92timings [key ]= profile 93else : 94f"[Warning] No timing data collected for { key } " ) 95 96def compile_cmd (file ,output ,stage = None ,entry = None ,emit = False ): 97cmd = f' { slangc } -report-perf-benchmark { file } ' 98 99if stage : 100cmd += f' -stage { stage } ' 101if entry : 102cmd += f' -entry { entry } ' 103else : 104cmd += f' -entry { stage } ' 105 106if emit : 107cmd += f' -target { target_ext } ' 108output += '.' + target_ext 109if target == 'dxil-embedded' : 110cmd += ' -profile lib_6_6' 111elif embed : 112cmd += ' -embed-dxil' 113 114cmd += f' -o { output } ' 115 116return cmd 117 118### Monolithic compilation ### 119 120hit = 'hit.slang' 121 122cmd = compile_cmd (hit ,f'targets/dxr-ch-mono' ,stage = 'closesthit' ,entry = 'MdlRadianceClosestHitProgram' ,emit = True ) 123run (cmd ,f'full/ { target_ext } /mono/closesthit' ) 124f'[I] compiled shadow (monolithic)' ) 125 126cmd = compile_cmd (hit ,f'targets/dxr-ah-mono' ,stage = 'anyhit' ,entry = 'MdlRadianceAnyHitProgram' ,emit = True ) 127run (cmd ,f'full/ { target_ext } /mono/anyhit' ) 128f'[I] compiled shadow (monolithic)' ) 129 130cmd = compile_cmd (hit ,f'targets/dxr-sh-mono' ,stage = 'anyhit' ,entry = 'MdlShadowAnyHitProgram' ,emit = True ) 131run (cmd ,f'full/ { target_ext } /mono/shadow' ) 132f'[I] compiled shadow (monolithic)' ) 133 134### Module precompilation ### 135 136modules = [] 137 138for file in glob .glob (f'*.slang' ): 139if not file .endswith ('hit.slang' ): 140basename = os .path .basename (file ) 141run (compile_cmd (file ,f'modules/ { basename } -module' ),'module/' + file ) 142f'[I] compiled { file } .' ) 143 144### Module whole compilation ### 145 146cmd = compile_cmd (hit ,f'targets/dxr-ch-modules' ,stage = 'closesthit' ,entry = 'MdlRadianceClosestHitProgram' ,emit = True ) 147run (cmd ,f'full/ { target_ext } /module/closesthit' ) 148f'[I] compiled closesthit (module)' ) 149 150cmd = compile_cmd (hit ,f'targets/dxr-ah-modules' ,stage = 'anyhit' ,entry = 'MdlRadianceAnyHitProgram' ,emit = True ) 151run (cmd ,f'full/ { target_ext } /module/anyhit' ) 152f'[I] compiled anyhit (module)' ) 153 154cmd = compile_cmd (hit ,f'targets/dxr-sh-modules' ,stage = 'anyhit' ,entry = 'MdlShadowAnyHitProgram' ,emit = True ) 155run (cmd ,f'full/ { target_ext } /module/shadow' ) 156f'[I] compiled shadow (module)' ) 157 158# Module precompilation time 159precompilation_time = 0 160for k in timings : 161if k .startswith ('module' ): 162precompilation_time += timings [k ]['compileInner' ] 163 164timings [f'full/ { target_ext } /precompilation' ]= {'compileInner' :precompilation_time } 165 166# Output to benchmark file 167json_data = [] 168for k ,v in timings .items (): 169if not k .startswith ('full' ): 170continue 171 172name = k .split ('/' )[1 :] 173name = ' : ' .join (reversed (name )) 174 175data = { 176'name' :name , 177'value' :v ['compileInner' ], 178'unit' :'milliseconds' 179 } 180 181json_data .append (data ) 182 183# TODO: append target to benchmark file name 184with open (args .output ,'w' )as file : 185json .dump (json_data ,file ,indent = 4 ) 186 187# Generate readable Markdown as well 1884 * '\n' ) 189'# Slang MDL benchmark results\n' ) 190'## Module precompilation time\n' ) 191precomp_key = f'full/ { target_ext } /precompilation' 192if precomp_key in timings : 193f'Total: ** { timings [ precomp_key ][ "compileInner" ] } ms**\n' ) 194else : 195"No precompilation data available\n" ) 196 197'## Module compilation for entry points\n' ) 198 199entries = ['Closest Hit' ,'Any Hit' ,'Shadow' ] 200prefixes = ['closesthit' ,'anyhit' ,'shadow' ] 201 202table = prettytable .PrettyTable () 203table .set_style (prettytable .MARKDOWN ) 204table .field_names = ['Entry' ,'Total' ] 205 206total = 0 207for entry ,prefix in zip (entries ,prefixes ): 208row = [entry ] 209key = f'full/ { target_ext } /module/ { prefix } ' 210if key in timings : 211db = timings [key ] 212spCompile = db .get ('compileInner' ,0 ) 213row .append (f' { spCompile :.3f } ms' ) 214table .add_row (row ) 215total += spCompile 216else : 217row .append ('Failed' ) 218table .add_row (row ) 219f"[Warning] Compilation failed for module/ { prefix } " ) 220 221f'Total: ** { total } ms**\n' ) 222table ,end = '\n\n' ) 223 224'## Monolithic compilation for entry points\n' ) 225 226table = prettytable .PrettyTable () 227table .set_style (prettytable .MARKDOWN ) 228table .field_names = ['Entry' ,'Total' ] 229 230total = 0 231for entry ,prefix in zip (entries ,prefixes ): 232row = [entry ] 233key = f'full/ { target_ext } /mono/ { prefix } ' 234if key in timings : 235db = timings [key ] 236spCompile = db .get ('compileInner' ,0 ) 237row .append (f' { spCompile :.3f } ms' ) 238table .add_row (row ) 239total += spCompile 240else : 241row .append ('Failed' ) 242table .add_row (row ) 243f"[Warning] Compilation failed for mono/ { prefix } " ) 244 245f'Total: ** { total } ms**\n' ) 246table ,end = '\n\n' )