1
1
using System ;
2
+ using System . Collections . Generic ;
2
3
using System . IO ;
4
+ using System . Linq ;
5
+ using System . Runtime . CompilerServices ;
6
+ using System . Runtime . InteropServices ;
3
7
using System . Text ;
4
8
using Serilog ;
5
9
using Silk . NET . Core . Native ;
@@ -19,6 +23,17 @@ public class Shader
19
23
20
24
public ShaderModule Module { get ; private set ; }
21
25
26
+ public const int MaxIncludeDepth = 10 ;
27
+
28
+ private List < ( string path , DateTime writeTime ) > _fileWriteTimes = new ( ) ;
29
+
30
+ public bool SourceFilesEdited
31
+ {
32
+ get
33
+ {
34
+ return _fileWriteTimes . Any ( tuple => tuple . writeTime != File . GetLastWriteTime ( tuple . path ) ) ;
35
+ }
36
+ }
22
37
23
38
public Shader ( RenderContext context , string shaderFilePath , ShaderKind shaderKind , bool optimize = true , string entryPoint = "main" )
24
39
{
@@ -72,6 +87,9 @@ private unsafe byte[] CompileShaderFile(string shaderPath, string entryPoint, Sh
72
87
CompilationResult * compilationResult = null ;
73
88
try
74
89
{
90
+ _fileWriteTimes . Clear ( ) ;
91
+ _fileWriteTimes . Add ( ( shaderPath , File . GetLastWriteTime ( shaderPath ) ) ) ;
92
+
75
93
string shaderText = File . ReadAllText ( shaderPath ) ;
76
94
byte [ ] inputFileNameBytes = Encoding . ASCII . GetBytes ( Path . GetFileName ( shaderPath ) ) ;
77
95
byte [ ] entryPointNameBytes = Encoding . ASCII . GetBytes ( entryPoint ) ;
@@ -83,7 +101,12 @@ private unsafe byte[] CompileShaderFile(string shaderPath, string entryPoint, Sh
83
101
{
84
102
shaderc . CompileOptionsSetOptimizationLevel ( compileOptions , OptimizationLevel . Performance ) ;
85
103
}
86
-
104
+
105
+ //Set functions for resolving #includes in shaders
106
+ var resolverFn = new PfnIncludeResolveFn ( ShaderIncludeResolve ) ;
107
+ var releaseFn = new PfnIncludeResultReleaseFn ( ShaderIncludeResultRelease ) ;
108
+ shaderc . CompileOptionsSetIncludeCallbacks ( compileOptions , resolverFn , releaseFn , null ) ;
109
+
87
110
fixed ( byte * inputFileName = inputFileNameBytes )
88
111
{
89
112
fixed ( byte * entryPointName = entryPointNameBytes )
@@ -133,6 +156,49 @@ private unsafe byte[] CompileShaderFile(string shaderPath, string entryPoint, Sh
133
156
}
134
157
}
135
158
}
159
+
160
+ //This and ShaderIncludeReleaseResult() are equivalent to the virtual functions on shaderc::CompileOptions::IncluderInterface in the C++ shaderc API
161
+ private unsafe IncludeResult * ShaderIncludeResolve ( void * interfacePtr , byte * requestedSourcePtr , int includeTypeInt , byte * requestingSourcePtr , UIntPtr includeDepthUintPtr )
162
+ {
163
+ string requestedSource = Marshal . PtrToStringAnsi ( ( IntPtr ) requestedSourcePtr ) ?? throw new Exception ( "Failed to resolve requestedSource in shader include handler." ) ;
164
+ ulong includeDepth = includeDepthUintPtr ;
165
+ if ( includeDepth > MaxIncludeDepth )
166
+ {
167
+ Log . Error ( $ "Exceeded maximum include depth of { MaxIncludeDepth } while compiling shader '{ Name } '.") ;
168
+ return null ;
169
+ }
170
+
171
+ string includeFullPath = $ "{ BuildConfig . ShadersDirectory } { requestedSource } ";
172
+ if ( requestedSource == Name || _fileWriteTimes . Any ( tuple => tuple . path == includeFullPath ) )
173
+ {
174
+ Log . Error ( $ "Circular include detected when compiling shader '{ Name } '.") ;
175
+ return null ;
176
+ }
177
+
178
+ if ( ! File . Exists ( includeFullPath ) )
179
+ {
180
+ Log . Error ( $ "Failed to find shader at include path '{ requestedSource } '.") ;
181
+ return null ;
182
+ }
183
+
184
+ IncludeResult * includeResult = ( IncludeResult * ) SilkMarshal . Allocate ( Unsafe . SizeOf < IncludeResult > ( ) ) ;
185
+ includeResult ->SourceName = ( byte * ) SilkMarshal . StringToPtr ( requestedSource ) ;
186
+ includeResult ->SourceNameLength = ( uint ) requestedSource . Length ;
187
+
188
+ string includedFileContents = File . ReadAllText ( includeFullPath ) ;
189
+ includeResult ->Content = ( byte * ) SilkMarshal . StringToPtr ( includedFileContents ) ;
190
+ includeResult ->ContentLength = ( uint ) includedFileContents . Length ;
191
+
192
+ _fileWriteTimes . Add ( ( includeFullPath , File . GetLastWriteTime ( includeFullPath ) ) ) ;
193
+ return includeResult ;
194
+ }
195
+
196
+ private unsafe void ShaderIncludeResultRelease ( void * interfacePtr , IncludeResult * data )
197
+ {
198
+ SilkMarshal . Free ( ( nint ) data ->SourceName ) ;
199
+ SilkMarshal . Free ( ( nint ) data ->Content ) ;
200
+ SilkMarshal . Free ( ( IntPtr ) data ) ;
201
+ }
136
202
137
203
private ShaderModule LoadAndCompileShaderFile ( string shaderPath , string entryPoint , ShaderKind shaderKind , bool optimize = true )
138
204
{
0 commit comments