Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
As such, this needs to be changed before a new release as well.
-->
<PropertyGroup>
<ComputeSharpPackageVersion>3.1.0</ComputeSharpPackageVersion>
<ComputeSharpPackageVersion>3.2.0</ComputeSharpPackageVersion>
<IsCommitOnReleaseBranch>false</IsCommitOnReleaseBranch>
</PropertyGroup>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using ComputeSharp.D2D1.SourceGenerators.Models;
using ComputeSharp.SourceGeneration.Extensions;
using ComputeSharp.SourceGeneration.Helpers;

namespace ComputeSharp.D2D1.SourceGenerators;

/// <inheritdoc/>
partial class D2DPixelShaderDescriptorGenerator
{
/// <summary>
/// A helper with all logic to generate the <c>EffectFactory</c> property.
/// </summary>
private static class EffectFactory
{
/// <summary>
/// Writes the <c>EffectFactory</c> property.
/// </summary>
/// <param name="info">The input <see cref="D2D1ShaderInfo"/> instance with gathered shader info.</param>
/// <param name="writer">The <see cref="IndentedTextWriter"/> instance to write into.</param>
public static void WriteSyntax(D2D1ShaderInfo info, IndentedTextWriter writer)
{
writer.WriteLine("/// <inheritdoc/>");
writer.WriteGeneratedAttributes(GeneratorName);
writer.WriteLine($"static unsafe nint global::ComputeSharp.D2D1.Descriptors.ID2D1PixelShaderDescriptor<{info.Hierarchy.Hierarchy[0].QualifiedName}>.EffectFactory");

using (writer.WriteBlock())
{
writer.WriteLine("[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]");
writer.WriteLine("get");

using (writer.WriteBlock())
{
writer.WriteLine($$"""
[global::System.Runtime.InteropServices.UnmanagedCallersOnly(CallConvs = [typeof(global::System.Runtime.CompilerServices.CallConvStdcall)])]
static int EffectFactory(void** effectImpl)
{
return global::ComputeSharp.D2D1.Interop.D2D1PixelShaderEffect.CreateEffectUnsafe<{{info.Hierarchy.Hierarchy[0].QualifiedName}}>(effectImpl);
}

return (nint)(delegate* unmanaged[Stdcall]<void**, int>)&EffectFactory;
""", isMultiline: true);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
declaredMembers.Add(EffectMetadata.WriteEffectDescriptionSyntax);
declaredMembers.Add(EffectMetadata.WriteEffectCategorySyntax);
declaredMembers.Add(EffectMetadata.WriteEffectAuthorSyntax);
declaredMembers.Add(EffectFactory.WriteSyntax);
declaredMembers.Add(NumericProperties.WriteConstantBufferSizeSyntax);
declaredMembers.Add(NumericProperties.WriteInputCountSyntax);
declaredMembers.Add(NumericProperties.WriteResourceTextureCountSyntax);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,19 @@ public interface ID2D1PixelShaderDescriptor<T>
/// </remarks>
static abstract string? EffectAuthor { get; }

/// <summary>
/// Gets a pointer to the effect factory for the current shader.
/// </summary>
/// <remarks>
/// <para>
/// This only applies to effects created from <see cref="D2D1PixelShaderEffect"/>.
/// </para>
/// <para>
/// 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>.
/// </para>
/// </remarks>
static abstract nint EffectFactory { get; }

/// <summary>
/// Gets the size in bytes of the constant buffer for the current shader.
/// </summary>
Expand Down
28 changes: 26 additions & 2 deletions src/ComputeSharp.D2D1/Shaders/Interop/D2D1PixelShaderEffect.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,30 @@ public static ref readonly Guid GetEffectId<T>()
return T.EffectAuthor;
}

/// <summary>
/// Creates a D2D effect instance for a given pixel shader type.
/// </summary>
/// <typeparam name="T">The type of D2D1 pixel shader to create a D2D effect instance for.</typeparam>
/// <param name="effectImpl">The effect implementation returned by the factory.</param>
/// <returns>The error code for the operation.</returns>
/// <remarks>
/// <para>
/// 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>
/// 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
/// should only ever be used implicitly by the ComputeSharp infrastructure, or in advanced scenarios where effects are registered manually.
/// </para>
/// <para>
/// This method is guaranteed to never throw an exception, and can be used directly in a method exported to native code.
/// The main use case scenario for calling <see cref="CreateEffectUnsafe"/> is to produce the factory for
/// <a href="https://learn.microsoft.com/windows/win32/api/d2d1_1/nf-d2d1_1-id2d1factory1-registereffectfromstring"><c>ID2D1Factory1::RegisterEffectFromString</c></a>.
/// </para>
/// </remarks>
public static int CreateEffectUnsafe<T>(void** effectImpl)
where T : unmanaged, ID2D1PixelShader, ID2D1PixelShaderDescriptor<T>
{
return PixelShaderEffect.Factory(PixelShaderEffect.Globals<T>.Instance, (IUnknown**)effectImpl);
}

/// <summary>
/// Registers an effect from an input D2D1 pixel shader, by calling <c>ID2D1Factory1::RegisterEffectFromString</c>.
/// </summary>
Expand Down Expand Up @@ -184,7 +208,7 @@ public static void RegisterForD2D1Factory1<T>(void* d2D1Factory1, out Guid effec
propertyXml: (ushort*)pXml,
bindings: d2D1PropertyBinding,
bindingsCount: (uint)(D2D1PixelShaderEffectProperty.NumberOfAlwaysAvailableProperties + T.ResourceTextureCount),
effectFactory: PixelShaderEffect.Globals<T>.Instance.Factory).Assert();
effectFactory: (delegate* unmanaged<IUnknown**, HRESULT>)T.EffectFactory).Assert();
}

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

// Effect factory
writer.Write((nint)PixelShaderEffect.Globals<T>.Instance.Factory);
writer.Write(T.EffectFactory);

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

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using ComputeSharp.D2D1.Descriptors;
using ComputeSharp.Win32;

Expand All @@ -11,29 +9,11 @@ namespace ComputeSharp.D2D1.Interop.Effects;
/// </summary>
internal unsafe partial struct PixelShaderEffect
{
/// <summary>
/// A wrapper for an effect factory.
/// </summary>
/// <param name="effectImpl">The resulting effect factory.</param>
/// <returns>The <c>HRESULT</c> for the operation.</returns>
/// <remarks>
/// The return type is intentionally <see langword="void"/><c>*</c> because delegate targets are considered
/// visible for reflection, so not using <see cref="IUnknown"/> avoids metadata for it being rooted unnecessarily.
/// </remarks>
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate int FactoryDelegate(void** effectImpl);

/// <summary>
/// A base type with global values for pixel shader effects.
/// </summary>
/// <param name="effectFactory">The <see cref="FactoryDelegate"/> wrapper for the shader factory.</param>
public abstract class Globals(FactoryDelegate effectFactory)
public abstract class Globals
{
/// <summary>
/// Gets the <see cref="FactoryDelegate"/> wrapper for the shader factory.
/// </summary>
private readonly FactoryDelegate effectFactory = effectFactory;

/// <inheritdoc cref="ID2D1PixelShaderDescriptor{T}.EffectId"/>
public abstract ref readonly Guid EffectId { get; }

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

/// <inheritdoc cref="ID2D1PixelShaderDescriptor{T}.HlslBytecode"/>
public abstract ReadOnlyMemory<byte> HlslBytecode { get; }

/// <summary>
/// Gets the factory for the current effect.
/// </summary>
public delegate* unmanaged<IUnknown**, HRESULT> Factory
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => (delegate* unmanaged<IUnknown**, HRESULT>)Marshal.GetFunctionPointerForDelegate(this.effectFactory);
}
}

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

Expand Down Expand Up @@ -147,11 +117,5 @@ public override ReadOnlyMemory<byte> HlslBytecode
return this.hlslBytecode = D2D1PixelShader.LoadBytecode<T>();
}
}

/// <inheritdoc cref="FactoryDelegate"/>
private static int CreateEffect(void** effectImpl)
{
return PixelShaderEffect.Factory(Instance, (IUnknown**)effectImpl);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ internal unsafe partial struct PixelShaderEffect
/// <param name="globals">The <see cref="Globals"/> instance to use.</param>
/// <param name="effectImpl">The resulting effect instance.</param>
/// <returns>The <see cref="HRESULT"/> for the operation.</returns>
private static int Factory(Globals globals, IUnknown** effectImpl)
public static int Factory(Globals globals, IUnknown** effectImpl)
{
PixelShaderEffect* @this = null;
GCHandle globalsHandle = default;
Expand Down
Loading