yum-archive/TaSTT-Whisper

High-performance GPGPU inference of OpenAI's Whisper automatic speech recognition (ASR) model

git clone https://git.yummers.dev/yum-archive/TaSTT-Whisper

KonstantinSource codes8c4603c

master
6.0 KiB244 linesraw
1using System.Runtime.CompilerServices;
2namespace CompressShaders;
3
4record struct sShaderBinary
5{
6	public string name;
7	public byte[] data;
8
9	public sShaderBinary( string path )
10	{
11		name = Path.GetFileNameWithoutExtension( path );
12		data = File.ReadAllBytes( path );
13	}
14
15	public bool wave64 => name.EndsWith( "64" );
16	public string uniqueName => wave64 ? name.Substring( 0, name.Length - 2 ) : name;
17}
18
19sealed class FoundShaders
20{
21	public readonly sShaderBinary[] binaries;
22	public readonly string[] names;
23	public readonly int[] wave32, wave64;
24
25	public FoundShaders( IEnumerable<sShaderBinary> found )
26	{
27		binaries = found
28			.OrderBy( b => b.name )
29			.ToArray();
30
31		names = binaries
32			.Select( b => b.uniqueName )
33			.Distinct()
34			.ToArray();
35
36		wave32 = new int[ names.Length ];
37		wave64 = new int[ names.Length ];
38		for( int i = 0; i < names.Length; i++ )
39		{
40			int i32 = findIndex( names[ i ], false );
41			int i64 = findIndex( names[ i ], true );
42			if( i32 >= 0 && i64 >= 0 )
43			{
44				wave32[ i ] = i32;
45				wave64[ i ] = i64;
46				continue;
47			}
48			if( i32 >= 0 )
49			{
50				wave32[ i ] = wave64[ i ] = i32;
51				continue;
52			}
53			throw new ApplicationException( $"Wave64 shader {names[ i ]} doesn't have the corresponding Wave32 one" );
54		}
55	}
56
57	int findIndex( string name, bool wave64 )
58	{
59		for( int i = 0; i < binaries.Length; i++ )
60		{
61			sShaderBinary sb = binaries[ i ];
62			if( sb.uniqueName != name )
63				continue;
64			if( sb.wave64 == wave64 )
65				return i;
66		}
67		return -1;
68	}
69}
70
71class Program
72{
73	static string getSolutionRoot( [CallerFilePath] string? path = null )
74	{
75		string? dir = Path.GetDirectoryName( path );
76		dir = Path.GetDirectoryName( dir );
77		dir = Path.GetDirectoryName( dir );
78		return dir ?? throw new ApplicationException();
79	}
80
81#if DEBUG
82	const string config = "Debug";
83#else
84	const string config = "Release";
85#endif
86
87	static string shadersBinDir( string root )
88	{
89		return Path.Combine( root, "ComputeShaders", "x64", config );
90	}
91
92	static IEnumerable<sShaderBinary> readShaders( string root )
93	{
94		string dir = shadersBinDir( root );
95		foreach( string path in Directory.EnumerateFiles( dir, "*.cso" ) )
96			yield return new sShaderBinary( path );
97	}
98
99	static void writeHeader( string root, IEnumerable<string> names )
100	{
101		string path = Path.Combine( root, "Whisper", "D3D", "shaderNames.h" );
102		using var stream = File.CreateText( path );
103		stream.WriteLine( @"// This header is generated by a tool
104#pragma once
105#include <stdint.h>
106
107namespace DirectCompute
108{
109	enum struct eComputeShader: uint16_t
110	{" );
111
112		int id = 0;
113		foreach( string name in names )
114		{
115			stream.WriteLine( "\t\t{0} = {1},", name, id );
116			id++;
117		}
118		stream.Write( @"	};
119
120	const char* computeShaderName( eComputeShader cs );
121}" );
122	}
123
124	static void writeCpp( string root, IEnumerable<string> names )
125	{
126		string path = Path.Combine( root, "Whisper", "D3D", "shaderNames.cpp" );
127		ShaderNames.write( path, names );
128	}
129
130	static void writePayloadIDs( StreamWriter stream, string varName, int[] ids )
131	{
132		stream.Write( @"
133static const std::array<uint8_t, {0}> {1} = {{", ids.Length, varName );
134
135		for( int i = 0; i < ids.Length; i++ )
136		{
137			if( 0 == i % 16 )
138				stream.Write( "\r\n\t" );
139			else
140				stream.Write( ' ' );
141			stream.Write( "{0},", ids[ i ] );
142		}
143		stream.Write( @"
144};" );
145	}
146
147	static void writePayload( string root, FoundShaders shaders, out int cbSource, out int cbCompressed )
148	{
149		MemoryStream ms = new MemoryStream();
150		List<int> offsets = new List<int>();
151		foreach( var bin in shaders.binaries )
152		{
153			offsets.Add( (int)ms.Length );
154			ms.Write( bin.data );
155		}
156		offsets.Add( (int)ms.Length );
157
158		byte[] dxbc = ms.ToArray();
159		byte[] compressed = Cabinet.compressBuffer( dxbc );
160		cbSource = dxbc.Length;
161		cbCompressed = compressed.Length;
162
163		string path = Path.Combine( root, "Whisper", "D3D", $"shaderData-{config}.inl" );
164		using var stream = File.CreateText( path );
165		stream.Write( @"// This source file is generated by a tool
166
167// This array contains concatenated and compressed DXBC binaries for all compiled compute shaders
168static const std::array<uint8_t, {0}> s_compressedShaders =
169{{", compressed.Length );
170
171		for( int i = 0; i < compressed.Length; i++ )
172		{
173			if( 0 == i % 16 )
174				stream.Write( "\r\n\t" );
175			else
176				stream.Write( ' ' );
177			stream.Write( "0x{0:X02},", compressed[ i ] );
178		}
179
180		stream.Write( @"
181}};
182
183// This array contains start offsets of shader binaries in the decompressed DXBC blob.
184// It includes one more entry for the end of the complete decompressed blob.
185static const std::array<uint32_t, {0}> s_shaderOffsets = {{", offsets.Count );
186
187		for( int i = 0; i < offsets.Count; i++ )
188		{
189			if( 0 == i % 16 )
190				stream.Write( "\r\n\t" );
191			else
192				stream.Write( ' ' );
193			stream.Write( "{0},", offsets[ i ] );
194		}
195		stream.Write( @"
196};" );
197
198		stream.Write( @"
199// Index = eComputeShader enum value, value = index of the shader binary to use on nVidia and Intel GPUs" );
200		writePayloadIDs( stream, "s_shaderBlobs32", shaders.wave32 );
201		stream.Write( @"
202// Index = eComputeShader enum value, value = index of the shader binary to use on AMD GPUs" );
203		writePayloadIDs( stream, "s_shaderBlobs64", shaders.wave64 );
204
205		ulong fp64Flags = 0;
206		for( int i = 0; i < shaders.binaries.Length; i++ )
207		{
208			bool fp64 = DetectFp64.usesFp64( shaders.binaries[ i ].data );
209			if( fp64 )
210				fp64Flags |= (ulong)1 << i;
211		}
212
213		stream.Write( @"
214// Bitmap of the shader binaries which use FP64 arithmetic instructions
215constexpr uint64_t fp64ShadersBitmap = 0x{0:X}ull;", fp64Flags );
216	}
217
218	static void mainImpl()
219	{
220		string root = getSolutionRoot();
221		LanguageCodes.produce( root );
222
223		FoundShaders shaders = new FoundShaders( readShaders( root ) );
224
225		writeHeader( root, shaders.names );
226		writeCpp( root, shaders.names );
227		writePayload( root, shaders, out int cbIn, out int cbOut );
228		Console.WriteLine( "Compressed {0} compute shaders, {1:F1} kb -> {2:F1} kb", shaders.binaries.Length, cbIn / 1024.0, cbOut / 1024.0 );
229	}
230
231	static int Main( string[] args )
232	{
233		try
234		{
235			mainImpl();
236			return 0;
237		}
238		catch( Exception ex )
239		{
240			Console.WriteLine( ex.Message );
241			return ex.HResult;
242		}
243	}
244}