Skip to content

Commit

Permalink
Checkpoint, working on rewrite of the struct generator
Browse files Browse the repository at this point in the history
  • Loading branch information
Rover656 committed May 13, 2023
1 parent fdfbaf4 commit 31cc152
Show file tree
Hide file tree
Showing 106 changed files with 3,974 additions and 1,065 deletions.
184 changes: 184 additions & 0 deletions SilkyWebGPU.BaseSourceGenerators/Base/BaseExtensionMethodGenerator.cs
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)
{
}
}
6 changes: 6 additions & 0 deletions SilkyWebGPU.BaseSourceGenerators/Base/BaseStructGenerator.cs
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 SilkyWebGPU.BaseSourceGenerators/Base/GlobalConstants.cs
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();
}
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 SilkyWebGPU.BaseSourceGenerators/Base/Method/MethodDefinition.cs
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.
}
Loading

0 comments on commit 31cc152

Please sign in to comment.