Skip to content

Commit

Permalink
Merge pull request #720 from Sergio0694/dev/d2d-enable-runtime-compil…
Browse files Browse the repository at this point in the history
…ation

Add [D2DEnableRuntimeCompilation] and analyzers
  • Loading branch information
Sergio0694 authored Dec 16, 2023
2 parents 347ae52 + 8474eec commit c71770f
Show file tree
Hide file tree
Showing 17 changed files with 410 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,7 @@ CMPSD2D0072 | ComputeSharp.D2D1.Shaders | Error | [Documentation](https://github
CMPSD2D0073 | ComputeSharp.D2D1.Shaders | Error | [Documentation](https://github.com/Sergio0694/ComputeSharp)
CMPSD2D0074 | ComputeSharp.D2D1.Shaders | Error | [Documentation](https://github.com/Sergio0694/ComputeSharp)
CMPSD2D0075 | ComputeSharp.D2D1.Shaders | Error | [Documentation](https://github.com/Sergio0694/ComputeSharp)
CMPSD2D0076 | ComputeSharp.D2D1.Shaders | Error | [Documentation](https://github.com/Sergio0694/ComputeSharp)
CMPSD2D0077 | ComputeSharp.D2D1.Shaders | Info | [Documentation](https://github.com/Sergio0694/ComputeSharp)
CMPSD2D0078 | ComputeSharp.D2D1.Shaders | Warning | [Documentation](https://github.com/Sergio0694/ComputeSharp)
CMPSD2D0079 | ComputeSharp.D2D1.Shaders | Warning | [Documentation](https://github.com/Sergio0694/ComputeSharp)
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System.Collections.Immutable;
using ComputeSharp.SourceGeneration.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using static ComputeSharp.SourceGeneration.Diagnostics.DiagnosticDescriptors;

namespace ComputeSharp.D2D1.SourceGenerators;

/// <summary>
/// A diagnostic analyzer that generates diagnostics for the [D2DEnableRuntimeCompilation] attribute, when used on an assembly.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class D2DEnableRuntimeCompilationOnAssemblyAnalyzer : DiagnosticAnalyzer
{
/// <inheritdoc/>
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(D2DRuntimeCompilationOnAssemblyNotNecessary);

/// <inheritdoc/>
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
context.EnableConcurrentExecution();

context.RegisterCompilationAction(static context =>
{
IAssemblySymbol assemblySymbol = context.Compilation.Assembly;

// Emit a diagnostic if an assembly has both [D2DShaderProfile] and [D2DEnableRuntimeCompilation] (the latter is useless)
if (assemblySymbol.TryGetAttributeWithFullyQualifiedMetadataName("ComputeSharp.D2D1.D2DShaderProfileAttribute", out _) &&
assemblySymbol.TryGetAttributeWithFullyQualifiedMetadataName("ComputeSharp.D2D1.D2DEnableRuntimeCompilationAttribute", out AttributeData? attributeData))
{
context.ReportDiagnostic(Diagnostic.Create(
D2DRuntimeCompilationOnAssemblyNotNecessary,
attributeData.GetLocation(),
assemblySymbol));
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
using System.Collections.Immutable;
using System.Linq;
using ComputeSharp.SourceGeneration.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using static ComputeSharp.SourceGeneration.Diagnostics.DiagnosticDescriptors;

namespace ComputeSharp.D2D1.SourceGenerators;

/// <summary>
/// A diagnostic analyzer that generates diagnostics for the [D2DEnableRuntimeCompilation] attribute, when used on a type.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class D2DEnableRuntimeCompilationOnTypeAnalyzer : DiagnosticAnalyzer
{
/// <inheritdoc/>
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(
D2DRuntimeCompilationDisabled,
D2DRuntimeCompilationAlreadyEnabled,
D2DRuntimeCompilationOnTypeNotNecessary);

/// <inheritdoc/>
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
context.EnableConcurrentExecution();

context.RegisterCompilationStartAction(static context =>
{
// Get the [D2DShaderProfile], [D2DEnableRuntimeCompilation] and [D2DGeneratedPixelShaderDescriptor] symbols
if (context.Compilation.GetTypeByMetadataName("ComputeSharp.D2D1.D2DShaderProfileAttribute") is not { } d2DShaderProfileAttributeSymbol ||
context.Compilation.GetTypeByMetadataName("ComputeSharp.D2D1.D2DEnableRuntimeCompilationAttribute") is not { } d2DEnableRuntimeCompilationAttributeSymbol ||
context.Compilation.GetTypeByMetadataName("ComputeSharp.D2D1.D2DGeneratedPixelShaderDescriptorAttribute") is not { } d2DGeneratedPixelShaderDescriptorAttributeSymbol)
{
return;
}

context.RegisterSymbolAction(context =>
{
// Only struct types are possible targets
if (context.Symbol is not INamedTypeSymbol { TypeKind: TypeKind.Struct } typeSymbol)
{
return;
}

// Skip the type if it's not a generated shader descriptor target
if (!typeSymbol.HasAttributeWithType(d2DGeneratedPixelShaderDescriptorAttributeSymbol))
{
return;
}

// This analyzer will detect the following cases:
// 1) Shader not precompiled, and missing [D2DEnableRuntimeCompilation] (D2DRuntimeCompilationDisabled)
// 2) Shader with [D2DEnableRuntimeCompilation] on the assembly and also on the shader type (D2DRuntimeCompilationAlreadyEnabled)
// 3) Shader being precompiled, but also with [D2DEnableRuntimeCompilation] (D2DRuntimeCompilationNotNecessary)
bool hasD2DShaderProfileAttributeOnAssembly = typeSymbol.ContainingAssembly.HasAttributeWithType(d2DShaderProfileAttributeSymbol);
bool hasD2DShaderProfileAttributeOnType = typeSymbol.HasAttributeWithType(d2DShaderProfileAttributeSymbol);
bool hasD2DEnableRuntimeCompilationAttributeOnAssembly = typeSymbol.ContainingAssembly.HasAttributeWithType(d2DEnableRuntimeCompilationAttributeSymbol);
bool hasD2DEnableRuntimeCompilationAttributeOnType = typeSymbol.TryGetAttributeWithType(
d2DEnableRuntimeCompilationAttributeSymbol,
out AttributeData? d2DEnableRuntimeCompilationAttributeData);

// If [D2DEnableRuntimeCompilation] is present on the type and also on the assembly, emit a diagnostic (2)
if (hasD2DEnableRuntimeCompilationAttributeOnAssembly && hasD2DEnableRuntimeCompilationAttributeOnType)
{
context.ReportDiagnostic(Diagnostic.Create(
D2DRuntimeCompilationAlreadyEnabled,
d2DEnableRuntimeCompilationAttributeData!.GetLocation(),
typeSymbol));
}

// Check if the shader is precompiled:
// - If it is, we should check that it's not annotated with [D2DEnableRuntimeCompilation]
// - If it is not, we should check that it is annotated with [D2DEnableRuntimeCompilation]
if (hasD2DShaderProfileAttributeOnType || hasD2DShaderProfileAttributeOnAssembly)
{
// Emit a diagnostic if [D2DEnableRuntimeCompilation] is present on the type (3)
if (hasD2DEnableRuntimeCompilationAttributeOnType)
{
context.ReportDiagnostic(Diagnostic.Create(
D2DRuntimeCompilationOnTypeNotNecessary,
d2DEnableRuntimeCompilationAttributeData!.GetLocation(),
typeSymbol));
}
}
else if (!hasD2DEnableRuntimeCompilationAttributeOnType && !hasD2DEnableRuntimeCompilationAttributeOnAssembly)
{
// Emit a diagnostic if there is no [D2DEnableRuntimeCompilation] set anywhere (1)
context.ReportDiagnostic(Diagnostic.Create(
D2DRuntimeCompilationDisabled,
typeSymbol.Locations.FirstOrDefault(),
typeSymbol));
}
}, SymbolKind.NamedType);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1125,4 +1125,69 @@ partial class DiagnosticDescriptors
isEnabledByDefault: true,
description: "Methods from the Math and MathF types cannot be used in a shader, and equivalent APIs from the Hlsl type should be used instead.",
helpLinkUri: "https://github.com/Sergio0694/ComputeSharp");

/// <summary>
/// Gets a <see cref="DiagnosticDescriptor"/> for a shader not precompiled but with runtime compilation disabled.
/// <para>
/// Format: <c>"The shader {0} is not precompiled, but runtime compilation is not enabled (either precompile the shader by using [D2DShaderProfile], which is recommended, or enable runtime compilation using [D2DEnableRuntimeCompilation])"</c>.
/// </para>
/// </summary>
public static readonly DiagnosticDescriptor D2DRuntimeCompilationDisabled = new(
id: "CMPSD2D0076",
title: "D2D runtime compilation disabled",
messageFormat: "The shader {0} is not precompiled, but runtime compilation is not enabled (either precompile the shader by using [D2DShaderProfile], which is recommended, or enable runtime compilation using [D2DEnableRuntimeCompilation])",
category: "ComputeSharp.D2D1.Shaders",
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: "Shaders must either be precompiled using [D2DShaderProfile], which is recommended, or runtime compilation for shaders must be enabled using [D2DEnableRuntimeCompilation].",
helpLinkUri: "https://github.com/Sergio0694/ComputeSharp");

/// <summary>
/// Gets a <see cref="DiagnosticDescriptor"/> for an unnecessary [D2DEnableRuntimeCompilation] use.
/// <para>
/// Format: <c>"The shader {0} is annotated with [D2DEnableRuntimeCompilation], but its containing assembly is already annotated with this attribute (so using it again on the shader type is unnecessary)"</c>.
/// </para>
/// </summary>
public static readonly DiagnosticDescriptor D2DRuntimeCompilationAlreadyEnabled = new(
id: "CMPSD2D0077",
title: "D2D runtime compilation already enabled",
messageFormat: "The shader {0} is annotated with [D2DEnableRuntimeCompilation], but its containing assembly is already annotated with this attribute (so using it again on the shader type is unnecessary)",
category: "ComputeSharp.D2D1.Shaders",
defaultSeverity: DiagnosticSeverity.Info,
isEnabledByDefault: true,
description: "If an assembly is annotated with [D2DEnableRuntimeCompilation], that applies to all shader types within it. That means that using [D2DEnableRuntimeCompilation] on shader types too is unnecessary.",
helpLinkUri: "https://github.com/Sergio0694/ComputeSharp");

/// <summary>
/// Gets a <see cref="DiagnosticDescriptor"/> for an unnecessary [D2DEnableRuntimeCompilation] use on .
/// <para>
/// Format: <c>"The shader {0} is annotated with [D2DEnableRuntimeCompilation], but it is also being precompiled, as it has [D2DShaderProfile] on the type or containing assembly (so using [D2DEnableRuntimeCompilation] is unnecessary)"</c>.
/// </para>
/// </summary>
public static readonly DiagnosticDescriptor D2DRuntimeCompilationOnTypeNotNecessary = new(
id: "CMPSD2D0078",
title: "D2D runtime compilation on type is not necessary",
messageFormat: "The shader {0} is annotated with [D2DEnableRuntimeCompilation], but it is also being precompiled, as it has [D2DShaderProfile] on the type or containing assembly (so using [D2DEnableRuntimeCompilation] is unnecessary)",
category: "ComputeSharp.D2D1.Shaders",
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: "If a shader is precompiled (ie. it has [D2DShaderProfile] on the type declaration or on its containing assembly), also using [D2DEnableRuntimeCompilation] is unnecessary.",
helpLinkUri: "https://github.com/Sergio0694/ComputeSharp");

/// <summary>
/// Gets a <see cref="DiagnosticDescriptor"/> for an unnecessary [D2DEnableRuntimeCompilation] use on an assembly.
/// <para>
/// Format: <c>"The assembly {0} is annotated with [D2DEnableRuntimeCompilation], but it is also has an assembly-level [D2DShaderProfile] attribute, which will cause all shaders to be precompiled (so using [D2DEnableRuntimeCompilation] is unnecessary)"</c>.
/// </para>
/// </summary>
public static readonly DiagnosticDescriptor D2DRuntimeCompilationOnAssemblyNotNecessary = new(
id: "CMPSD2D0079",
title: "D2D runtime compilation on assembly is not necessary",
messageFormat: "The assembly {0} is annotated with [D2DEnableRuntimeCompilation], but it is also has an assembly-level [D2DShaderProfile] attribute, which will cause all shaders to be precompiled (so using [D2DEnableRuntimeCompilation] is unnecessary)",
category: "ComputeSharp.D2D1.Shaders",
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: "If an assembly is using [D2DShaderProfile] (meaning that all shaders declared within it will be precompiled), also using [D2DEnableRuntimeCompilation] is unnecessary.",
helpLinkUri: "https://github.com/Sergio0694/ComputeSharp",
customTags: WellKnownDiagnosticTags.CompilationEnd);
}
12 changes: 3 additions & 9 deletions src/ComputeSharp.D2D1.WinUI/CanvasEffect.EffectGraph.cs
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,7 @@ public void SetOutputNode(IEffectNode effectNode)
/// This interface only implemented by <see cref="EffectNode{T}"/> and it's not meant to be implemented by external types.
/// </para>
/// </remarks>
protected interface IEffectNode
{
}
protected interface IEffectNode;

/// <summary>
/// A marker interface for a generic effect node that was registered from an <see cref="EffectGraph"/> value.
Expand All @@ -246,16 +244,12 @@ protected interface IEffectNode
/// </para>
/// </remarks>
protected interface IEffectNode<out T> : IEffectNode
where T : ICanvasImage
{
}
where T : ICanvasImage;

/// <summary>
/// A marker type for an effect node that can be registered and retrieved from an <see cref="EffectGraph"/> value.
/// </summary>
/// <typeparam name="T">The type of <see cref="ICanvasImage"/> associated with the current effect node.</typeparam>
protected sealed class EffectNode<T> : IEffectNode<T>
where T : class, ICanvasImage
{
}
where T : class, ICanvasImage;
}
27 changes: 27 additions & 0 deletions src/ComputeSharp.D2D1/Attributes/D2DEnableRuntimeCompilation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;

namespace ComputeSharp.D2D1;

/// <summary>
/// An attribute that indicates that runtime compilation of D2D shaders is enabled.
/// This attribute is required for all shaders that are not compiled at build time.
/// </summary>
/// <remarks>
/// <para>
/// Precompiling shaders at build time is recommended, as it enables additional validation
/// and diagnostics, and provides better performance at runtime when dispatching the shaders.
/// </para>
/// <para>
/// This option can be used in advanced scenarios, for instance when trying to minimize
/// binary size (which should only be done after carefully measuring the actual difference).
/// </para>
/// <para>
/// Note that not using this attribute does not mean that trying to compile shaders at runtime will
/// fail. The purpose of this attribute is to allow declaring shader types that are not precompiled.
/// All other features, such as manually compiling a shader type with specific compile options or
/// with a different shader profile, or manually compiling a shader from source at runtime, are
/// always supported, regardless of whether nor not this attribute is used.
/// </para>
/// </remarks>
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Assembly, AllowMultiple = false)]
public sealed class D2DEnableRuntimeCompilationAttribute : Attribute;
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,4 @@ namespace ComputeSharp.D2D1;
/// </para>
/// </summary>
[AttributeUsage(AttributeTargets.Struct, AllowMultiple = false)]
public sealed class D2DGeneratedPixelShaderDescriptorAttribute : Attribute
{
}
public sealed class D2DGeneratedPixelShaderDescriptorAttribute : Attribute;
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,4 @@ namespace ComputeSharp.D2D1;
/// <para>For more info, see <see href="https://docs.microsoft.com/en-us/windows/win32/direct2d/hlsl-helpers"/>.</para>
/// </remarks>
[AttributeUsage(AttributeTargets.Struct, AllowMultiple = false)]
public sealed class D2DRequiresScenePositionAttribute : Attribute
{
}
public sealed class D2DRequiresScenePositionAttribute : Attribute;
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,4 @@ namespace ComputeSharp.SourceGeneration.Diagnostics;
/// <summary>
/// A container for all <see cref="DiagnosticDescriptor"/> instances for errors reported by analyzers in this project.
/// </summary>
internal static partial class DiagnosticDescriptors
{
}
internal static partial class DiagnosticDescriptors;
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,4 @@ namespace ComputeSharp;
/// </para>
/// </summary>
[AttributeUsage(AttributeTargets.Struct, AllowMultiple = false)]
public sealed class GeneratedComputeShaderDescriptorAttribute : Attribute
{
}
public sealed class GeneratedComputeShaderDescriptorAttribute : Attribute;
4 changes: 1 addition & 3 deletions src/ComputeSharp/Attributes/GloballyCoherentAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,4 @@ namespace ComputeSharp;
/// </para>
/// </remarks>
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public sealed class GloballyCoherentAttribute : Attribute
{
}
public sealed class GloballyCoherentAttribute : Attribute;
Loading

0 comments on commit c71770f

Please sign in to comment.