-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Checkpoint, working on rewrite of the struct generator
- Loading branch information
Showing
106 changed files
with
3,974 additions
and
1,065 deletions.
There are no files selected for viewing
184 changes: 184 additions & 0 deletions
184
SilkyWebGPU.BaseSourceGenerators/Base/BaseExtensionMethodGenerator.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using Microsoft.CodeAnalysis; | ||
using Rover656.SilkyWebGPU.BaseSourceGenerator.Method; | ||
|
||
// Funky namespace because we're going to share this file. | ||
namespace Rover656.SilkyWebGPU.BaseSourceGenerator; | ||
|
||
public class BaseExtensionMethodGenerator : ISourceGenerator | ||
{ | ||
private static string _referenceClass; | ||
private static string _extensionNs; | ||
private static string _apiHandle; | ||
|
||
public BaseExtensionMethodGenerator(string referenceClass, string extensionNamespace, string apiHandle) | ||
{ | ||
_referenceClass = referenceClass; | ||
_extensionNs = extensionNamespace; | ||
_apiHandle = apiHandle; | ||
} | ||
|
||
public void Execute(GeneratorExecutionContext context) | ||
{ | ||
// Get reference class | ||
var referenceClass = context.Compilation.GetTypeByMetadataName(_referenceClass); | ||
|
||
// Return if its not found | ||
if (referenceClass == null) | ||
return; | ||
|
||
// Collect methods for generation | ||
var methodsDefinitions = new List<MethodDefinition>(); | ||
foreach (var classMember in referenceClass.GetMembers()) | ||
{ | ||
if (classMember is IMethodSymbol classMethod) | ||
{ | ||
ProcessMethod(classMethod, ref methodsDefinitions); | ||
} | ||
} | ||
|
||
// Write output source | ||
var outputWriter = new StringBuilder($@"// <auto-generated/> | ||
using {GlobalConstants.ProjectNS}; | ||
using {GlobalConstants.ProjectNS}.Native; | ||
using {GlobalConstants.ProjectNS}.Native.Chain; | ||
using Silk.NET.WebGPU; | ||
using Silk.NET.WebGPU.Extensions.WGPU; | ||
namespace {_extensionNs}; | ||
public static partial class MethodExtensions | ||
{{"); | ||
|
||
foreach (var method in methodsDefinitions) | ||
{ | ||
outputWriter.AppendLine($@" | ||
public static unsafe {method.ReturnType} {method.Name}{method.TypeParameterDecl}(this {GlobalConstants.NativePtrType}<{method.Owner.Type}> {method.Owner.ParameterName}{method.ParameterDecl}) {method.ConstraintDecl} | ||
{{ | ||
if ({method.Owner.ParameterName}.IsNull()) throw new NullReferenceException(); | ||
{method.GetMethodCall(_apiHandle)} | ||
}}"); | ||
} | ||
|
||
outputWriter.Append('}'); | ||
|
||
context.AddSource($"MethodExtensions.generated.cs", outputWriter.ToString()); | ||
|
||
} | ||
|
||
private void ProcessMethod(IMethodSymbol classMethod, ref List<MethodDefinition> methodDefinitions) | ||
{ | ||
// Ignore parameterless methods | ||
if (classMethod.Parameters.Length <= 0) | ||
return; | ||
|
||
// Get the owner of this method | ||
if (!GetObjectOwner(classMethod, out var ownerDefinition)) | ||
return; | ||
|
||
// Check for method exclusion. | ||
if (ExcludeMethod(classMethod)) | ||
return; | ||
|
||
// Collect basic information (that does not change between variants) | ||
var baseDefinition = new MethodDefinition | ||
{ | ||
ReturnType = new MethodReturnType(classMethod), | ||
Owner = ownerDefinition, | ||
Name = classMethod.Name.Replace(ownerDefinition.Name, ""), | ||
OriginalName = classMethod.Name, | ||
Parameters = DefineParameters(classMethod), | ||
TypeParameters = DefineTypeParameters(classMethod), | ||
}; | ||
|
||
// Add default extension method definition | ||
methodDefinitions.Add(baseDefinition); | ||
|
||
// TODO: Generate variants without ref T0's. | ||
} | ||
|
||
private static bool GetObjectOwner(IMethodSymbol method, out ManagedOwnerDefinition ownerDefinition) | ||
{ | ||
// Default | ||
ownerDefinition = default; | ||
|
||
// We only expand methods that are owned by object's we recognize | ||
var objectParameter = method.Parameters[0]; | ||
if (objectParameter.Type is not IPointerTypeSymbol objectPointer) | ||
return false; | ||
|
||
// Get object type | ||
var objectType = objectPointer.PointedAtType; | ||
|
||
// If we don't recognize this type, ignore it | ||
if (!GlobalConstants.Objects.Contains(objectType.Name)) | ||
return false; | ||
|
||
// We have a match | ||
ownerDefinition = new ManagedOwnerDefinition | ||
{ | ||
Name = objectType.Name, | ||
Type = objectType.ToString(), | ||
ParameterName = objectParameter.Name, | ||
}; | ||
return true; | ||
} | ||
|
||
private static List<MethodParameterDefinition> DefineParameters(IMethodSymbol method) | ||
{ | ||
var parameters = new List<MethodParameterDefinition>(method.Parameters.Length - 1); | ||
for (var i = 1; i < method.Parameters.Length; i++) | ||
{ | ||
parameters.Add(new MethodParameterDefinition(method.Parameters[i])); | ||
} | ||
return parameters; | ||
} | ||
|
||
private static List<TypeParameterDefinition> DefineTypeParameters(IMethodSymbol method) | ||
{ | ||
var parameters = new List<TypeParameterDefinition>(method.TypeParameters.Length); | ||
for (var i = 0; i < method.TypeParameters.Length; i++) | ||
{ | ||
parameters.Add(new TypeParameterDefinition(method.TypeParameters[i])); | ||
} | ||
return parameters; | ||
} | ||
|
||
private static bool ExcludeMethod(IMethodSymbol method) | ||
{ | ||
for (var i = 1; i < method.Parameters.Length; i++) | ||
{ | ||
// Get parameter | ||
var parameter = method.Parameters[i]; | ||
|
||
// We disallow ref pointers. | ||
if (parameter.Type is IPointerTypeSymbol pointerParameter) | ||
{ | ||
if (parameter.RefKind == RefKind.Ref) | ||
{ | ||
// We don't deal with ref pointers, these are arrays and handled another way. | ||
return true; | ||
} | ||
|
||
if (pointerParameter.PointedAtType is IPointerTypeSymbol arrayType) | ||
{ | ||
// TODO: These arrays need the count parameter removed and redirected too. | ||
if (!GlobalConstants.Objects.Contains(arrayType.PointedAtType.Name)) | ||
{ | ||
// We don't deal with unknown array types. | ||
return true; | ||
} | ||
} | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
public void Initialize(GeneratorInitializationContext context) | ||
{ | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
namespace Rover656.SilkyWebGPU.BaseSourceGenerator; | ||
|
||
public class BaseStructGenerator | ||
{ | ||
|
||
} |
104 changes: 104 additions & 0 deletions
104
SilkyWebGPU.BaseSourceGenerators/Base/GlobalConstants.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
using System.Linq; | ||
|
||
namespace Rover656.SilkyWebGPU.BaseSourceGenerator; | ||
|
||
public static class GlobalConstants | ||
{ | ||
/// <summary> | ||
/// The root namespace of the project | ||
/// </summary> | ||
public const string ProjectNS = "Rover656.SilkyWebGPU"; | ||
|
||
public const string WebGpuNS = "Silk.NET.WebGPU"; | ||
public const string WgpuNS = $"{WebGpuNS}.Extensions.WGPU"; | ||
public const string DawnNS = $"{WebGpuNS}.Extensions.WGPU"; | ||
|
||
/// <summary> | ||
/// Native pointer type for wrapping objects. | ||
/// </summary> | ||
public const string NativePtrType = "WebGPUPtr"; | ||
|
||
/// <summary> | ||
/// Native array type. | ||
/// </summary> | ||
public const string NativeStructArrayType = "NativeArray"; | ||
|
||
/// <summary> | ||
/// Native pointer array type. | ||
/// </summary> | ||
public const string NativePointerArrayType = "NativePtrArray"; | ||
|
||
/// <summary> | ||
/// The base class for a chainable struct. | ||
/// </summary> | ||
public const string ChainableBaseClass = "ChainableStruct"; | ||
|
||
/// <summary> | ||
/// The base class for a chained struct. | ||
/// </summary> | ||
public const string ChainedBaseClass = "ChainedStruct"; | ||
|
||
/// <summary> | ||
/// The base class for a wrapped struct. | ||
/// </summary> | ||
public const string WrappedBaseClass = "WrappedStruct"; | ||
|
||
/// <summary> | ||
/// The standard set of objects that will be wrapped by WebGPUPtr's and given methods. | ||
/// </summary> | ||
public static readonly string[] Objects = | ||
{ | ||
"Adapter", "BindGroup", "BindGroupLayout", "Buffer", "CommandBuffer", "CommandEncoder", "ComputePassEncoder", | ||
"ComputePipeline", "Device", "Instance", "PipelineLayout", "QuerySet", "Queue", "RenderBundle", | ||
"RenderBundleEncoder", "RenderPassEncoder", "RenderPipeline", "Sampler", "ShaderModule", "Surface", "SwapChain", | ||
"Texture", "TextureView" | ||
}; | ||
|
||
public static readonly string[] ChainableStructs = | ||
{ | ||
"AdapterProperties", "BindGroupDescriptor", "BindGroupEntry", "BindGroupLayoutDescriptor", | ||
"BindGroupLayoutEntry", "BufferBindingLayout", "BufferDescriptor", "ColorTargetState", | ||
"CommandBufferDescriptor", "CommandEncoderDescriptor", "CompilationInfo", "CompilationMessage", | ||
"ComputePassDescriptor", "ComputePipelineDescriptor", "ConstantEntry", "DepthStencilState", "DeviceDescriptor", | ||
"FragmentState", "ImageCopyBuffer", "ImageCopyTexture", "InstanceDescriptor", "MultisampleState", | ||
"PipelineLayoutDescriptor", "PrimitiveState", "ProgrammableStageDescriptor", "QuerySetDescriptor", | ||
"QueueDescriptor", "RenderBundleDescriptor", "RenderBundleEncoderDescriptor", "RenderPassDescriptor", | ||
"RenderPipelineDescriptor", "RequestAdapterOptions", "RequiredLimits", "SamplerBindingLayout", | ||
"SamplerDescriptor", "ShaderModuleCompilationHint", "ShaderModuleDescriptor", "StorageTextureBindingLayout", | ||
"SupportedLimits", "SurfaceDescriptor", "SwapChainDescriptor", "TextureBindingLayout", "TextureDataLayout", | ||
"TextureDescriptor", "TextureViewDescriptor", "VertexState", | ||
}; | ||
|
||
/// <summary> | ||
/// All chained structs and their SType. | ||
/// </summary> | ||
public static readonly (string, string)[] ChainedStructs = | ||
{ | ||
("SurfaceDescriptorFromMetalLayer", "SType.SurfaceDescriptorFromMetalLayer"), | ||
("SurfaceDescriptorFromWindowsHWND", "SType.SurfaceDescriptorFromWindowsHwnd"), | ||
("SurfaceDescriptorFromXlibWindow", "SType.SurfaceDescriptorFromXlibWindow"), | ||
("SurfaceDescriptorFromCanvasHTMLSelector", "SType.SurfaceDescriptorFromCanvasHtmlselector"), | ||
("ShaderModuleSPIRVDescriptor", "SType.ShaderModuleSpirvdescriptor"), | ||
("ShaderModuleWGSLDescriptor", "SType.ShaderModuleWgsldescriptor"), | ||
("PrimitiveDepthClipControl", "SType.PrimitiveDepthClipControl"), | ||
("SurfaceDescriptorFromWaylandSurface", "SType.SurfaceDescriptorFromWaylandSurface"), | ||
("SurfaceDescriptorFromAndroidNativeWindow", "SType.SurfaceDescriptorFromAndroidNativeWindow"), | ||
("SurfaceDescriptorFromXcbWindow", "SType.SurfaceDescriptorFromXcbWindow"), | ||
("RenderPassDescriptorMaxDrawCount", "SType.RenderPassDescriptorMaxDrawCount"), | ||
}; | ||
|
||
/// <summary> | ||
/// Non-Chainable structs that require wrapping. | ||
/// </summary> | ||
public static readonly string[] WrappedStructs = | ||
{ | ||
"BlendComponent", "BlendState", "Color", "ComputePassTimestampWrite", "Extent3D", "Limits", "Origin3D", | ||
"RenderPassColorAttachment", "RenderPassDepthStencilAttachment", "RenderPassTimestampWrite", "StencilFaceState", | ||
"VertexAttribute", "VertexBufferLayout", | ||
}; | ||
|
||
/// <summary> | ||
/// All structs that have managed versions. | ||
/// </summary> | ||
public static readonly string[] ManagedStructs = ChainableStructs.Concat(ChainedStructs.Select(e => e.Item1)).Concat(WrappedStructs).ToArray(); | ||
} |
8 changes: 8 additions & 0 deletions
8
SilkyWebGPU.BaseSourceGenerators/Base/Method/ManagedOwnerDefinition.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
namespace Rover656.SilkyWebGPU.BaseSourceGenerator.Method; | ||
|
||
public struct ManagedOwnerDefinition | ||
{ | ||
public string Name; | ||
public string Type; | ||
public string ParameterName; | ||
} |
77 changes: 77 additions & 0 deletions
77
SilkyWebGPU.BaseSourceGenerators/Base/Method/MethodDefinition.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
|
||
namespace Rover656.SilkyWebGPU.BaseSourceGenerator.Method; | ||
|
||
public struct MethodDefinition | ||
{ | ||
public MethodReturnType ReturnType; | ||
public ManagedOwnerDefinition Owner; | ||
public string Name; | ||
public string OriginalName; | ||
public List<MethodParameterDefinition> Parameters; | ||
public List<TypeParameterDefinition> TypeParameters; | ||
|
||
public string ArgumentParameterDecl | ||
{ | ||
get | ||
{ | ||
if (Parameters.Count <= 0) | ||
return ""; | ||
|
||
var builder = new StringBuilder(", "); | ||
foreach (var parameter in Parameters) | ||
{ | ||
if (!string.IsNullOrEmpty(parameter.ArgumentPrefix)) | ||
builder.Append($"{parameter.ArgumentPrefix} "); | ||
builder.Append($"{parameter.Name}, "); | ||
} | ||
|
||
return builder.ToString().TrimEnd(',', ' '); | ||
} | ||
} | ||
|
||
public string ParameterDecl | ||
{ | ||
get | ||
{ | ||
if (Parameters.Count <= 0) | ||
return ""; | ||
|
||
var builder = new StringBuilder(", "); | ||
foreach (var parameter in Parameters) | ||
{ | ||
if (!string.IsNullOrEmpty(parameter.ParameterPrefix)) | ||
builder.Append($"{parameter.ParameterPrefix} "); | ||
builder.Append($"{parameter.Type} {parameter.Name}, "); | ||
} | ||
|
||
return builder.ToString().TrimEnd(',', ' '); | ||
} | ||
} | ||
|
||
public string GetMethodCall(string apiHandle) | ||
{ | ||
var builder = new StringBuilder(ReturnType.Type == "void" ? "" : "return "); | ||
|
||
if (ReturnType.ClassObject) builder.Append($"{ReturnType}.MakeStrong("); | ||
|
||
builder.Append($"{apiHandle}.{OriginalName}({Owner.ParameterName}{ArgumentParameterDecl})"); | ||
|
||
if (ReturnType.ClassObject) builder.Append(')'); | ||
builder.Append(';'); | ||
|
||
return builder.ToString(); | ||
} | ||
|
||
public string TypeParameterDecl => | ||
TypeParameters.Count <= 0 ? "" : $"<{string.Join(", ", TypeParameters.Select(e => e.Name))}>"; | ||
|
||
public string ConstraintDecl => TypeParameters.Count <= 0 | ||
? "" | ||
: string.Join(Environment.NewLine, TypeParameters.Select(e => e.ConstraintDecl)); | ||
|
||
// TODO: Get original definition string for inheritdoc. | ||
} |
Oops, something went wrong.