1
+ using Microsoft . CodeAnalysis ;
2
+ using Microsoft . CodeAnalysis . CSharp ;
3
+ using Microsoft . CodeAnalysis . CSharp . Syntax ;
4
+ using static Microsoft . CodeAnalysis . CSharp . SyntaxFactory ;
5
+
6
+ namespace Silk . NET . SilkTouch . Mods ;
7
+
8
+ /// <summary>
9
+ /// Applies Vulkan-specific transformations.
10
+ /// </summary>
11
+ public class TransformVulkan : IMod
12
+ {
13
+ /// <inheritdoc />
14
+ public async Task ExecuteAsync ( IModContext ctx , CancellationToken ct = default )
15
+ {
16
+ var proj = ctx . SourceProject ;
17
+ if ( proj == null )
18
+ {
19
+ return ;
20
+ }
21
+
22
+ var compilation = await proj . GetCompilationAsync ( ct ) ;
23
+ if ( compilation == null )
24
+ {
25
+ return ;
26
+ }
27
+
28
+ var rewriter = new Rewriter ( ) ;
29
+ foreach ( var docId in proj ? . DocumentIds ?? [ ] )
30
+ {
31
+ var doc = proj ! . GetDocument ( docId ) ?? throw new InvalidOperationException ( "Document missing" ) ;
32
+ proj = doc . WithSyntaxRoot (
33
+ rewriter . Visit ( await doc . GetSyntaxRootAsync ( ct ) ) ? . NormalizeWhitespace ( )
34
+ ?? throw new InvalidOperationException ( "Visit returned null." )
35
+ ) . Project ;
36
+ }
37
+
38
+ ctx . SourceProject = proj ;
39
+ }
40
+
41
+ private class Rewriter : CSharpSyntaxRewriter
42
+ {
43
+ private const string MethodClassName = "Vk" ;
44
+
45
+ private const string InstanceTypeName = "InstanceHandle" ;
46
+ private const string InstanceFieldName = "_currentInstance" ;
47
+ private const string InstancePropertyName = "CurrentInstance" ;
48
+
49
+ private const string DeviceTypeName = "DeviceHandle" ;
50
+ private const string DeviceFieldName = "_currentDevice" ;
51
+ private const string DevicePropertyName = "CurrentDevice" ;
52
+
53
+ private const string VkCreateInstanceNativeName = "vkCreateInstance" ;
54
+ private const string VkCreateDeviceNativeName = "vkCreateDevice" ;
55
+
56
+ private const string VkResultName = "Result" ;
57
+ private const string VkResultSuccessName = "Success" ;
58
+
59
+ public override SyntaxNode ? VisitClassDeclaration ( ClassDeclarationSyntax node )
60
+ {
61
+ if ( node . Identifier . ValueText != MethodClassName )
62
+ {
63
+ return base . VisitClassDeclaration ( node ) ;
64
+ }
65
+
66
+ var instanceField = FieldDeclaration (
67
+ VariableDeclaration ( NullableType ( IdentifierName ( InstanceTypeName ) ) )
68
+ . AddVariables ( VariableDeclarator ( InstanceFieldName ) )
69
+ ) . AddModifiers ( Token ( SyntaxKind . PrivateKeyword ) ) ;
70
+
71
+
72
+ var deviceField = FieldDeclaration (
73
+ VariableDeclaration ( NullableType ( IdentifierName ( DeviceTypeName ) ) )
74
+ . AddVariables ( VariableDeclarator ( DeviceFieldName ) )
75
+ ) . AddModifiers ( Token ( SyntaxKind . PrivateKeyword ) ) ;
76
+
77
+ var instanceProperty = CreateProperty ( InstanceTypeName , InstancePropertyName , InstanceFieldName ) ;
78
+
79
+ var deviceProperty = CreateProperty ( DeviceTypeName , DevicePropertyName , DeviceFieldName ) ;
80
+
81
+ node = node . WithMembers ( [
82
+ instanceField ,
83
+ deviceField ,
84
+ instanceProperty ,
85
+ deviceProperty ,
86
+ ..node . Members . SelectMany ( RewriteMember )
87
+ ] ) ;
88
+
89
+ return base . VisitClassDeclaration ( node ) ;
90
+ }
91
+
92
+ private IEnumerable < MemberDeclarationSyntax > RewriteMember ( MemberDeclarationSyntax member )
93
+ {
94
+ if ( member is not MethodDeclarationSyntax method )
95
+ {
96
+ // yield return member;
97
+ yield break ;
98
+ }
99
+
100
+ if ( ! method . AttributeLists . GetNativeFunctionInfo ( out _ , out var entryPoint , out _ ) || entryPoint == null )
101
+ {
102
+ // yield return member;
103
+ yield break ;
104
+ }
105
+
106
+ if ( ! method . Modifiers . Any ( modifier => modifier . IsKind ( SyntaxKind . ExternKeyword ) ) )
107
+ {
108
+ // yield return member;
109
+ yield break ;
110
+ }
111
+
112
+ if ( entryPoint != VkCreateInstanceNativeName && entryPoint != VkCreateDeviceNativeName )
113
+ {
114
+ // yield return member;
115
+ yield break ;
116
+ }
117
+
118
+ var methodName = method . Identifier . ValueText ;
119
+ var privateMethodName = $ "{ methodName } Internal";
120
+
121
+ // Output the original method, but private
122
+ yield return method
123
+ . WithIdentifier ( Identifier ( privateMethodName ) )
124
+ . WithModifiers ( [
125
+ Token ( SyntaxKind . PrivateKeyword ) ,
126
+ ..member . Modifiers . Where ( modifier =>
127
+ ! SyntaxFacts . IsAccessibilityModifier ( modifier . Kind ( ) ) )
128
+ ] ) ;
129
+
130
+ // Add a new public method that stores the VkInstance or VkDevice
131
+ var resultName = "result" ;
132
+ yield return method
133
+ . WithAttributeLists ( [
134
+ ..method . AttributeLists
135
+ . Select ( list =>
136
+ AttributeList ( [
137
+ ..list . Attributes . Where ( attribute =>
138
+ ! attribute . IsAttribute ( "System.Runtime.InteropServices.DllImport" ) )
139
+ ] ) )
140
+ . Where ( list => list . Attributes . Count != 0 )
141
+ ] )
142
+ . WithModifiers ( [
143
+ ..member . Modifiers . Where ( modifier => ! modifier . IsKind ( SyntaxKind . ExternKeyword ) )
144
+ ] )
145
+ . WithBody (
146
+ Block (
147
+ LocalDeclarationStatement ( VariableDeclaration ( IdentifierName ( VkResultName ) ) ) ,
148
+ IfStatement (
149
+ BinaryExpression (
150
+ SyntaxKind . EqualsExpression ,
151
+ IdentifierName ( resultName ) ,
152
+ MemberAccessExpression (
153
+ SyntaxKind . SimpleMemberAccessExpression ,
154
+ IdentifierName ( VkResultName ) ,
155
+ IdentifierName ( VkResultSuccessName )
156
+ )
157
+ ) ,
158
+ Block (
159
+ ExpressionStatement (
160
+ AssignmentExpression (
161
+ SyntaxKind . SimpleAssignmentExpression ,
162
+ IdentifierName ( DevicePropertyName ) ,
163
+ IdentifierName ( DevicePropertyName ) ) ) )
164
+ ) ,
165
+ ReturnStatement ( IdentifierName ( resultName ) ) ) ) ;
166
+ // List(new StatementSyntax[]
167
+ // {
168
+ // // var result = CreateDeviceInternal(physicalDevice, pCreateInfo, pAllocator, pDevice);
169
+ // LocalDeclarationStatement(
170
+ // VariableDeclaration(
171
+ // IdentifierName("var")
172
+ // ).WithVariables(
173
+ // SingletonSeparatedList(
174
+ // VariableDeclarator(
175
+ // Identifier("result")
176
+ // ).WithInitializer(
177
+ // EqualsValueClause(
178
+ // InvocationExpression(
179
+ // IdentifierName("CreateDeviceInternal")
180
+ // ).WithArgumentList(
181
+ // ArgumentList(SeparatedList<ArgumentSyntax>(new SyntaxNodeOrToken[]
182
+ // {
183
+ // Argument(IdentifierName("physicalDevice")),
184
+ // Token(SyntaxKind.CommaToken),
185
+ // Argument(IdentifierName("pCreateInfo")),
186
+ // Token(SyntaxKind.CommaToken),
187
+ // Argument(IdentifierName("pAllocator")),
188
+ // Token(SyntaxKind.CommaToken),
189
+ // Argument(IdentifierName("pDevice"))
190
+ // }))
191
+ // )
192
+ // )
193
+ // )
194
+ // )
195
+ // )
196
+ // ),
197
+ // // if (result != 0) { CurrentDevice = *pDevice; }
198
+ // IfStatement(
199
+ // BinaryExpression(
200
+ // SyntaxKind.NotEqualsExpression,
201
+ // IdentifierName("result"),
202
+ // LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(0))
203
+ // ),
204
+ // Block(
205
+ // SingletonList<StatementSyntax>(
206
+ // ExpressionStatement(
207
+ // AssignmentExpression(
208
+ // SyntaxKind.SimpleAssignmentExpression,
209
+ // IdentifierName("CurrentDevice"),
210
+ // PrefixUnaryExpression(
211
+ // SyntaxKind.PointerIndirectionExpression,
212
+ // IdentifierName("pDevice")
213
+ // )
214
+ // )
215
+ // )
216
+ // )
217
+ // )
218
+ // ),
219
+ // // return result;
220
+ // ReturnStatement(IdentifierName("result"))
221
+ // })
222
+ // ));
223
+ }
224
+
225
+ /// <summary>
226
+ /// This generates the VkInstance and VkDevice properties.
227
+ /// </summary>>
228
+ private static PropertyDeclarationSyntax CreateProperty ( string typeName , string propertyName , string fieldName )
229
+ {
230
+ return PropertyDeclaration ( NullableType ( IdentifierName ( typeName ) ) , propertyName )
231
+ . AddModifiers ( Token ( SyntaxKind . PublicKeyword ) )
232
+ . AddAccessorListAccessors (
233
+ AccessorDeclaration ( SyntaxKind . GetAccessorDeclaration )
234
+ . WithExpressionBody ( ArrowExpressionClause ( IdentifierName ( fieldName ) ) )
235
+ . WithSemicolonToken ( Token ( SyntaxKind . SemicolonToken ) ) ,
236
+ AccessorDeclaration ( SyntaxKind . SetAccessorDeclaration )
237
+ . WithBody (
238
+ Block (
239
+ IfStatement (
240
+ BinaryExpression (
241
+ SyntaxKind . LogicalAndExpression ,
242
+ BinaryExpression (
243
+ SyntaxKind . NotEqualsExpression ,
244
+ IdentifierName ( fieldName ) ,
245
+ LiteralExpression ( SyntaxKind . NullLiteralExpression )
246
+ ) ,
247
+ BinaryExpression (
248
+ SyntaxKind . NotEqualsExpression ,
249
+ IdentifierName ( fieldName ) ,
250
+ IdentifierName ( "value" )
251
+ )
252
+ ) ,
253
+ ThrowStatement (
254
+ ObjectCreationExpression (
255
+ IdentifierName ( "InvalidOperationException" )
256
+ )
257
+ . WithArgumentList (
258
+ ArgumentList (
259
+ SingletonSeparatedList (
260
+ Argument (
261
+ LiteralExpression (
262
+ SyntaxKind . StringLiteralExpression ,
263
+ Literal (
264
+ $ "{ propertyName } has already been set. Please create a new API instance so that the loaded function pointers can be kept separate.")
265
+ )
266
+ )
267
+ )
268
+ )
269
+ )
270
+ )
271
+ ) ,
272
+ ExpressionStatement (
273
+ AssignmentExpression (
274
+ SyntaxKind . SimpleAssignmentExpression ,
275
+ IdentifierName ( fieldName ) ,
276
+ IdentifierName ( "value" )
277
+ )
278
+ ) ) ) ) ;
279
+ }
280
+ }
281
+ }
0 commit comments