Skip to content

Commit b187f27

Browse files
committed
Implement most of TransformVulkan mod
1 parent 2188fcd commit b187f27

File tree

3 files changed

+283
-0
lines changed

3 files changed

+283
-0
lines changed

generator.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@
178178
"TransformProperties",
179179
"TransformEnums",
180180
"AddVTables"
181+
"TransformVulkan",
181182
],
182183
"ClangScraper": {
183184
"ClangSharpResponseFiles": [

sources/SilkTouch/SilkTouch/Mods/Common/ModLoader.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public class ModLoader
2727
nameof(MixKhronosData) => typeof(MixKhronosData),
2828
nameof(TransformHandles) => typeof(TransformHandles),
2929
nameof(TransformEnums) => typeof(TransformEnums),
30+
nameof(TransformVulkan) => typeof(TransformVulkan),
3031
nameof(ExtractNestedTyping) => typeof(ExtractNestedTyping),
3132
nameof(TransformProperties) => typeof(TransformProperties),
3233
nameof(ClangScraper) => typeof(ClangScraper),
Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
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

Comments
 (0)