Skip to content

Commit 1141f48

Browse files
authored
Merge pull request #907 from Sergio0694/dev/remove-delegate-marshalling
Add `ID2D1PixelShaderDescriptor<T>.EffectFactory` and remove runtime delegate marshalling
2 parents 2bd64c9 + 7a7d4e7 commit 1141f48

File tree

9 files changed

+205
-41
lines changed

9 files changed

+205
-41
lines changed

build/Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
As such, this needs to be changed before a new release as well.
7373
-->
7474
<PropertyGroup>
75-
<ComputeSharpPackageVersion>3.1.0</ComputeSharpPackageVersion>
75+
<ComputeSharpPackageVersion>3.2.0</ComputeSharpPackageVersion>
7676
<IsCommitOnReleaseBranch>false</IsCommitOnReleaseBranch>
7777
</PropertyGroup>
7878

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using ComputeSharp.D2D1.SourceGenerators.Models;
2+
using ComputeSharp.SourceGeneration.Extensions;
3+
using ComputeSharp.SourceGeneration.Helpers;
4+
5+
namespace ComputeSharp.D2D1.SourceGenerators;
6+
7+
/// <inheritdoc/>
8+
partial class D2DPixelShaderDescriptorGenerator
9+
{
10+
/// <summary>
11+
/// A helper with all logic to generate the <c>EffectFactory</c> property.
12+
/// </summary>
13+
private static class EffectFactory
14+
{
15+
/// <summary>
16+
/// Writes the <c>EffectFactory</c> property.
17+
/// </summary>
18+
/// <param name="info">The input <see cref="D2D1ShaderInfo"/> instance with gathered shader info.</param>
19+
/// <param name="writer">The <see cref="IndentedTextWriter"/> instance to write into.</param>
20+
public static void WriteSyntax(D2D1ShaderInfo info, IndentedTextWriter writer)
21+
{
22+
writer.WriteLine("/// <inheritdoc/>");
23+
writer.WriteGeneratedAttributes(GeneratorName);
24+
writer.WriteLine($"static unsafe nint global::ComputeSharp.D2D1.Descriptors.ID2D1PixelShaderDescriptor<{info.Hierarchy.Hierarchy[0].QualifiedName}>.EffectFactory");
25+
26+
using (writer.WriteBlock())
27+
{
28+
writer.WriteLine("[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]");
29+
writer.WriteLine("get");
30+
31+
using (writer.WriteBlock())
32+
{
33+
writer.WriteLine($$"""
34+
[global::System.Runtime.InteropServices.UnmanagedCallersOnly(CallConvs = [typeof(global::System.Runtime.CompilerServices.CallConvStdcall)])]
35+
static int EffectFactory(void** effectImpl)
36+
{
37+
return global::ComputeSharp.D2D1.Interop.D2D1PixelShaderEffect.CreateEffectUnsafe<{{info.Hierarchy.Hierarchy[0].QualifiedName}}>(effectImpl);
38+
}
39+
40+
return (nint)(delegate* unmanaged[Stdcall]<void**, int>)&EffectFactory;
41+
""", isMultiline: true);
42+
}
43+
}
44+
}
45+
}
46+
}

src/ComputeSharp.D2D1.SourceGenerators/D2DPixelShaderDescriptorGenerator.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
225225
declaredMembers.Add(EffectMetadata.WriteEffectDescriptionSyntax);
226226
declaredMembers.Add(EffectMetadata.WriteEffectCategorySyntax);
227227
declaredMembers.Add(EffectMetadata.WriteEffectAuthorSyntax);
228+
declaredMembers.Add(EffectFactory.WriteSyntax);
228229
declaredMembers.Add(NumericProperties.WriteConstantBufferSizeSyntax);
229230
declaredMembers.Add(NumericProperties.WriteInputCountSyntax);
230231
declaredMembers.Add(NumericProperties.WriteResourceTextureCountSyntax);

src/ComputeSharp.D2D1/Interfaces/Descriptors/ID2D1PixelShaderDescriptor{T}.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,19 @@ public interface ID2D1PixelShaderDescriptor<T>
5252
/// </remarks>
5353
static abstract string? EffectAuthor { get; }
5454

55+
/// <summary>
56+
/// Gets a pointer to the effect factory for the current shader.
57+
/// </summary>
58+
/// <remarks>
59+
/// <para>
60+
/// This only applies to effects created from <see cref="D2D1PixelShaderEffect"/>.
61+
/// </para>
62+
/// <para>
63+
/// The returned function pointer should have a signature matching <a href="https://learn.microsoft.com/windows/win32/api/d2d1_1/nc-d2d1_1-pd2d1_effect_factory"><c>PD2D1_EFFECT_FACTORY</c></a>.
64+
/// </para>
65+
/// </remarks>
66+
static abstract nint EffectFactory { get; }
67+
5568
/// <summary>
5669
/// Gets the size in bytes of the constant buffer for the current shader.
5770
/// </summary>

src/ComputeSharp.D2D1/Shaders/Interop/D2D1PixelShaderEffect.cs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,30 @@ public static ref readonly Guid GetEffectId<T>()
7272
return T.EffectAuthor;
7373
}
7474

75+
/// <summary>
76+
/// Creates a D2D effect instance for a given pixel shader type.
77+
/// </summary>
78+
/// <typeparam name="T">The type of D2D1 pixel shader to create a D2D effect instance for.</typeparam>
79+
/// <param name="effectImpl">The effect implementation returned by the factory.</param>
80+
/// <returns>The error code for the operation.</returns>
81+
/// <remarks>
82+
/// <para>
83+
/// This method provides the <a href="https://learn.microsoft.com/windows/win32/api/d2d1_1/nc-d2d1_1-pd2d1_effect_factory"><c>PD2D1_EFFECT_FACTORY</c></a>
84+
/// implementation for a given pixel shader type <typeparamref name="T"/>. It is only meant to be used to register an effect with this shader type, and
85+
/// should only ever be used implicitly by the ComputeSharp infrastructure, or in advanced scenarios where effects are registered manually.
86+
/// </para>
87+
/// <para>
88+
/// This method is guaranteed to never throw an exception, and can be used directly in a method exported to native code.
89+
/// The main use case scenario for calling <see cref="CreateEffectUnsafe"/> is to produce the factory for
90+
/// <a href="https://learn.microsoft.com/windows/win32/api/d2d1_1/nf-d2d1_1-id2d1factory1-registereffectfromstring"><c>ID2D1Factory1::RegisterEffectFromString</c></a>.
91+
/// </para>
92+
/// </remarks>
93+
public static int CreateEffectUnsafe<T>(void** effectImpl)
94+
where T : unmanaged, ID2D1PixelShader, ID2D1PixelShaderDescriptor<T>
95+
{
96+
return PixelShaderEffect.Factory(PixelShaderEffect.Globals<T>.Instance, (IUnknown**)effectImpl);
97+
}
98+
7599
/// <summary>
76100
/// Registers an effect from an input D2D1 pixel shader, by calling <c>ID2D1Factory1::RegisterEffectFromString</c>.
77101
/// </summary>
@@ -184,7 +208,7 @@ public static void RegisterForD2D1Factory1<T>(void* d2D1Factory1, out Guid effec
184208
propertyXml: (ushort*)pXml,
185209
bindings: d2D1PropertyBinding,
186210
bindingsCount: (uint)(D2D1PixelShaderEffectProperty.NumberOfAlwaysAvailableProperties + T.ResourceTextureCount),
187-
effectFactory: PixelShaderEffect.Globals<T>.Instance.Factory).Assert();
211+
effectFactory: (delegate* unmanaged<IUnknown**, HRESULT>)T.EffectFactory).Assert();
188212
}
189213

190214
effectId = T.EffectId;
@@ -376,7 +400,7 @@ public static ReadOnlyMemory<byte> GetRegistrationBlob<T>(out Guid effectId)
376400
}
377401

378402
// Effect factory
379-
writer.Write((nint)PixelShaderEffect.Globals<T>.Instance.Factory);
403+
writer.Write(T.EffectFactory);
380404

381405
byte[] registrationBlob = writer.WrittenSpan.ToArray();
382406

src/ComputeSharp.D2D1/Shaders/Interop/Effects/PixelShaderEffect.Globals{T}.cs

Lines changed: 1 addition & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
using System;
2-
using System.Runtime.CompilerServices;
3-
using System.Runtime.InteropServices;
42
using ComputeSharp.D2D1.Descriptors;
53
using ComputeSharp.Win32;
64

@@ -11,29 +9,11 @@ namespace ComputeSharp.D2D1.Interop.Effects;
119
/// </summary>
1210
internal unsafe partial struct PixelShaderEffect
1311
{
14-
/// <summary>
15-
/// A wrapper for an effect factory.
16-
/// </summary>
17-
/// <param name="effectImpl">The resulting effect factory.</param>
18-
/// <returns>The <c>HRESULT</c> for the operation.</returns>
19-
/// <remarks>
20-
/// The return type is intentionally <see langword="void"/><c>*</c> because delegate targets are considered
21-
/// visible for reflection, so not using <see cref="IUnknown"/> avoids metadata for it being rooted unnecessarily.
22-
/// </remarks>
23-
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
24-
public delegate int FactoryDelegate(void** effectImpl);
25-
2612
/// <summary>
2713
/// A base type with global values for pixel shader effects.
2814
/// </summary>
29-
/// <param name="effectFactory">The <see cref="FactoryDelegate"/> wrapper for the shader factory.</param>
30-
public abstract class Globals(FactoryDelegate effectFactory)
15+
public abstract class Globals
3116
{
32-
/// <summary>
33-
/// Gets the <see cref="FactoryDelegate"/> wrapper for the shader factory.
34-
/// </summary>
35-
private readonly FactoryDelegate effectFactory = effectFactory;
36-
3717
/// <inheritdoc cref="ID2D1PixelShaderDescriptor{T}.EffectId"/>
3818
public abstract ref readonly Guid EffectId { get; }
3919

@@ -66,15 +46,6 @@ public abstract class Globals(FactoryDelegate effectFactory)
6646

6747
/// <inheritdoc cref="ID2D1PixelShaderDescriptor{T}.HlslBytecode"/>
6848
public abstract ReadOnlyMemory<byte> HlslBytecode { get; }
69-
70-
/// <summary>
71-
/// Gets the factory for the current effect.
72-
/// </summary>
73-
public delegate* unmanaged<IUnknown**, HRESULT> Factory
74-
{
75-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
76-
get => (delegate* unmanaged<IUnknown**, HRESULT>)Marshal.GetFunctionPointerForDelegate(this.effectFactory);
77-
}
7849
}
7950

8051
/// <summary>
@@ -93,7 +64,6 @@ public sealed class Globals<T> : Globals
9364
/// Creates a new <see cref="Globals"/> instance for shaders of type <typeparamref name="T"/>.
9465
/// </summary>
9566
private Globals()
96-
: base(CreateEffect)
9767
{
9868
}
9969

@@ -147,11 +117,5 @@ public override ReadOnlyMemory<byte> HlslBytecode
147117
return this.hlslBytecode = D2D1PixelShader.LoadBytecode<T>();
148118
}
149119
}
150-
151-
/// <inheritdoc cref="FactoryDelegate"/>
152-
private static int CreateEffect(void** effectImpl)
153-
{
154-
return PixelShaderEffect.Factory(Instance, (IUnknown**)effectImpl);
155-
}
156120
}
157121
}

src/ComputeSharp.D2D1/Shaders/Interop/Effects/PixelShaderEffect.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ internal unsafe partial struct PixelShaderEffect
103103
/// <param name="globals">The <see cref="Globals"/> instance to use.</param>
104104
/// <param name="effectImpl">The resulting effect instance.</param>
105105
/// <returns>The <see cref="HRESULT"/> for the operation.</returns>
106-
private static int Factory(Globals globals, IUnknown** effectImpl)
106+
public static int Factory(Globals globals, IUnknown** effectImpl)
107107
{
108108
PixelShaderEffect* @this = null;
109109
GCHandle globalsHandle = default;

0 commit comments

Comments
 (0)