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