Skip to content

Commit

Permalink
Merge pull request #722 from Sergio0694/dev/requires-double-precision…
Browse files Browse the repository at this point in the history
…-support

Add [RequiresDoublePrecisionSupport] attribute
  • Loading branch information
Sergio0694 authored Dec 17, 2023
2 parents d98e57d + e27b7e4 commit 23ef666
Show file tree
Hide file tree
Showing 19 changed files with 260 additions and 59 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<DefineConstants>$(DefineConstants);D2D1_GENERATOR</DefineConstants>
</PropertyGroup>

<PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,20 +202,20 @@ static unsafe HlslBytecodeInfo GetInfo(HlslBytecodeInfoKey key, CancellationToke
token.ThrowIfCancellationRequested();

// Compile the shader bytecode using the effective parameters
using ComPtr<ID3DBlob> dxcBlobBytecode = D3DCompiler.Compile(
using ComPtr<ID3DBlob> d3DBlob = D3DCompiler.Compile(
key.HlslSource.AsSpan(),
key.ShaderProfile,
key.CompileOptions);

token.ThrowIfCancellationRequested();

// Check whether double precision operations are required
bool requiresDoublePrecisionSupport = D3DCompiler.IsDoublePrecisionSupportRequired(dxcBlobBytecode.Get());
bool requiresDoublePrecisionSupport = D3DCompiler.IsDoublePrecisionSupportRequired(d3DBlob.Get());

token.ThrowIfCancellationRequested();

byte* buffer = (byte*)dxcBlobBytecode.Get()->GetBufferPointer();
int length = checked((int)dxcBlobBytecode.Get()->GetBufferSize());
byte* buffer = (byte*)d3DBlob.Get()->GetBufferPointer();
int length = checked((int)d3DBlob.Get()->GetBufferSize());

byte[] array = new ReadOnlySpan<byte>(buffer, length).ToArray();

Expand Down Expand Up @@ -257,14 +257,17 @@ public static void GetInfoDiagnostics(
diagnostic = DiagnosticInfo.Create(
HlslBytecodeFailedWithWin32Exception,
structDeclarationSymbol,
[structDeclarationSymbol, win32Error.HResult, win32Error.Message]);
structDeclarationSymbol,
win32Error.HResult,
win32Error.Message);
}
else if (info is HlslBytecodeInfo.CompilerError fxcError)
{
diagnostic = DiagnosticInfo.Create(
HlslBytecodeFailedWithFxcCompilationException,
structDeclarationSymbol,
[structDeclarationSymbol, fxcError.Message]);
structDeclarationSymbol,
fxcError.Message);
}

if (diagnostic is not null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,14 +140,19 @@ public static void GetInfoDiagnostics(
diagnostic = DiagnosticInfo.Create(
D2DPixelShaderSourceCompilationFailedWithWin32Exception,
methodSymbol,
[methodSymbol, methodSymbol.ContainingType, win32Error.HResult, win32Error.Message]);
methodSymbol,
methodSymbol.ContainingType,
win32Error.HResult,
win32Error.Message);
}
else if (info is HlslBytecodeInfo.CompilerError fxcError)
{
diagnostic = DiagnosticInfo.Create(
D2DPixelShaderSourceCompilationFailedWithFxcCompilationException,
methodSymbol,
[methodSymbol, methodSymbol.ContainingType, fxcError.Message]);
methodSymbol,
methodSymbol.ContainingType,
fxcError.Message);
}

if (diagnostic is not null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1194,21 +1194,21 @@ partial class DiagnosticDescriptors
/// <summary>
/// Gets a <see cref="DiagnosticDescriptor"/> for a shader missing [D2DRequiresDoublePrecisionSupport].
/// <para>
/// Format: <c>"The shader {0} requires double precision support, but it does not have the [D2DRequiresDoublePrecisionSupport] attribute on it (adding the attribute is necessary to explicitly opt-in into that functionality)"</c>.
/// Format: <c>"The shader {0} requires double precision support, but it does not have the [D2DRequiresDoublePrecisionSupport] attribute on it (adding the attribute is necessary to explicitly opt-in to that functionality)"</c>.
/// </para>
/// </summary>
public static readonly DiagnosticDescriptor MissingD2DRequiresDoublePrecisionSupportAttribute = new(
id: "CMPSD2D0080",
title: "Missing [D2DRequiresDoublePrecisionSupport] attribute",
messageFormat: "The shader {0} requires double precision support, but it does not have the [D2DRequiresDoublePrecisionSupport] attribute on it (adding the attribute is necessary to explicitly opt-in into that functionality)",
messageFormat: "The shader {0} requires double precision support, but it does not have the [D2DRequiresDoublePrecisionSupport] attribute on it (adding the attribute is necessary to explicitly opt-in to that functionality)",
category: "ComputeSharp.D2D1.Shaders",
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: "Shaders performing double precision operations must be annotated with [D2DRequiresDoublePrecisionSupport] to explicitly opt-in into that functionality.",
description: "Shaders performing double precision operations must be annotated with [D2DRequiresDoublePrecisionSupport] to explicitly opt-in to that functionality.",
helpLinkUri: "https://github.com/Sergio0694/ComputeSharp");

/// <summary>
/// Gets a <see cref="DiagnosticDescriptor"/> for a shader is unnecessarily using [UnnecessaryD2DRequiresDoublePrecisionSupportAttribute].
/// Gets a <see cref="DiagnosticDescriptor"/> for a shader is unnecessarily using [D2DRequiresDoublePrecisionSupport].
/// <para>
/// Format: <c>"The shader {0} does not require double precision support, but it has the [D2DRequiresDoublePrecisionSupport] attribute on it (using the attribute is not needed if the shader is not performing any double precision operations)"</c>.
/// </para>
Expand All @@ -1224,7 +1224,7 @@ partial class DiagnosticDescriptors
helpLinkUri: "https://github.com/Sergio0694/ComputeSharp");

/// <summary>
/// Gets a <see cref="DiagnosticDescriptor"/> for a shader is using [UnnecessaryD2DRequiresDoublePrecisionSupportAttribute] incorrectly.
/// Gets a <see cref="DiagnosticDescriptor"/> for a shader is using [D2DRequiresDoublePrecisionSupport] incorrectly.
/// <para>
/// Format: <c>"The shader {0} has the [D2DRequiresDoublePrecisionSupport] attribute on it, but it is not precompiled, so validation for use of double precision operations cannot be performed"</c>.
/// </para>
Expand Down
2 changes: 1 addition & 1 deletion src/ComputeSharp.D2D1.SourceGenerators/Fxc/D3DCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public static unsafe bool IsDoublePrecisionSupportRequired(ID3DBlob* d3DBlob)
pInterface: &iidOfID3D11ShaderReflection,
ppReflector: (void**)d3D11ShaderReflection.GetAddressOf()).Assert();

ulong doublePrecisionFlags = DirectX.D3D_SHADER_REQUIRES_DOUBLES | DirectX.D3D_SHADER_REQUIRES_11_1_DOUBLE_EXTENSIONS;
const ulong doublePrecisionFlags = DirectX.D3D_SHADER_REQUIRES_DOUBLES | DirectX.D3D_SHADER_REQUIRES_11_1_DOUBLE_EXTENSIONS;

return (d3D11ShaderReflection.Get()->GetRequiresFlags() & doublePrecisionFlags) != 0;
}
Expand Down
13 changes: 2 additions & 11 deletions src/ComputeSharp.SourceGeneration.Hlsl/Models/HlslBytecodeInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,8 @@ internal abstract record HlslBytecodeInfo
/// A successfully compiled HLSL shader.
/// </summary>
/// <param name="Bytecode">The resulting HLSL bytecode.</param>
public sealed record Success(
#if D3D12_GENERATOR
EquatableArray<byte> Bytecode)
#else

#pragma warning disable CS1573
EquatableArray<byte> Bytecode,
bool RequiresDoublePrecisionSupport) // Whether the shader requires support for double precision operations.
#pragma warning restore CS1573
#endif
: HlslBytecodeInfo;
/// <param name="RequiresDoublePrecisionSupport">Whether the shader requires support for double precision operations.</param>
public sealed record Success(EquatableArray<byte> Bytecode, bool RequiresDoublePrecisionSupport) : HlslBytecodeInfo;

/// <summary>
/// An HLSL shader that failed to compile due to a Win32 error.
Expand Down
14 changes: 12 additions & 2 deletions src/ComputeSharp.SourceGeneration/Models/DiagnosticInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,17 @@ public Diagnostic ToDiagnostic()
/// <returns>A new <see cref="DiagnosticInfo"/> instance with the specified parameters.</returns>
public static DiagnosticInfo Create(DiagnosticDescriptor descriptor, Location? location, params object[] args)
{
return new(descriptor, location?.SourceTree, location?.SourceSpan ?? default, args.Select(static arg => arg.ToString()).ToImmutableArray());
// The returned DiagnosticInfo instances will be used inside incremental collections. Because of
// that, we pre-transform all arguments with ToString(), so they are guaranteed to be equatable
// from the start and not to keep compilations alive (if eg. they happen to be symbol objects).
EquatableArray<string> textArgs = args.Select(static arg => arg.ToString()).ToImmutableArray();

if (location is null)
{
return new(descriptor, null, default, textArgs);
}

return new(descriptor, location.SourceTree, location.SourceSpan, textArgs);
}

/// <summary>
Expand All @@ -54,7 +64,7 @@ public static DiagnosticInfo Create(DiagnosticDescriptor descriptor, Location? l
/// <returns>A new <see cref="DiagnosticInfo"/> instance with the specified parameters.</returns>
public static DiagnosticInfo Create(DiagnosticDescriptor descriptor, ISymbol symbol, params object[] args)
{
return Create(descriptor, symbol.Locations.First(), args);
return Create(descriptor, symbol.Locations.FirstOrDefault(), args);
}

/// <summary>
Expand Down
2 changes: 2 additions & 0 deletions src/ComputeSharp.SourceGenerators/AnalyzerReleases.Shipped.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,5 @@ CMPS0060 | ComputeSharp.Shaders | Error | [Documentation](https://github.com/Ser
CMPS0061 | ComputeSharp.Shaders | Error | [Documentation](https://github.com/Sergio0694/ComputeSharp)
CMPS0062 | ComputeSharp.Shaders | Error | [Documentation](https://github.com/Sergio0694/ComputeSharp)
CMPS0063 | ComputeSharp.Shaders | Error | [Documentation](https://github.com/Sergio0694/ComputeSharp)
CMPS0064 | ComputeSharp.Shaders | Error | [Documentation](https://github.com/Sergio0694/ComputeSharp)
CMPS0065 | ComputeSharp.Shaders | Warning | [Documentation](https://github.com/Sergio0694/ComputeSharp)
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
using System.Collections.Immutable;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using System.Threading;
using ComputeSharp.SourceGeneration.Extensions;
using ComputeSharp.SourceGeneration.Helpers;
using ComputeSharp.SourceGeneration.Models;
using ComputeSharp.SourceGenerators.Dxc;
using ComputeSharp.SourceGenerators.Models;
using Microsoft.CodeAnalysis;
using Windows.Win32;
using Windows.Win32.Graphics.Direct3D.Dxc;
using static ComputeSharp.SourceGeneration.Diagnostics.DiagnosticDescriptors;

namespace ComputeSharp.SourceGenerators;
Expand Down Expand Up @@ -53,19 +54,34 @@ static unsafe HlslBytecodeInfo GetInfo(HlslBytecodeInfoKey key, CancellationToke
token.ThrowIfCancellationRequested();

// Compile the shader bytecode
byte[] bytecode = DxcShaderCompiler.Instance.Compile(key.HlslSource.AsSpan(), key.CompileOptions, token);
using ComPtr<IDxcBlob> dxcBlob = DxcShaderCompiler.Instance.Compile(
key.HlslSource.AsSpan(),
key.CompileOptions,
token);

token.ThrowIfCancellationRequested();

return new HlslBytecodeInfo.Success(Unsafe.As<byte[], ImmutableArray<byte>>(ref bytecode));
// Check whether double precision operations are required
bool requiresDoublePrecisionSupport = DxcShaderCompiler.Instance.IsDoublePrecisionSupportRequired(dxcBlob.Get());

token.ThrowIfCancellationRequested();

byte* buffer = (byte*)dxcBlob.Get()->GetBufferPointer();
int length = checked((int)dxcBlob.Get()->GetBufferSize());

byte[] array = new ReadOnlySpan<byte>(buffer, length).ToArray();

ImmutableArray<byte> bytecode = Unsafe.As<byte[], ImmutableArray<byte>>(ref array);

return new HlslBytecodeInfo.Success(bytecode, requiresDoublePrecisionSupport);
}
catch (Win32Exception e)
{
return new HlslBytecodeInfo.Win32Error(e.NativeErrorCode, FixupExceptionMessage(e.Message));
return new HlslBytecodeInfo.Win32Error(e.NativeErrorCode, DxcShaderCompiler.FixupExceptionMessage(e.Message));
}
catch (DxcCompilationException e)
{
return new HlslBytecodeInfo.CompilerError(FixupExceptionMessage(e.Message));
return new HlslBytecodeInfo.CompilerError(DxcShaderCompiler.FixupExceptionMessage(e.Message));
}
}

Expand Down Expand Up @@ -108,14 +124,17 @@ public static void GetInfoDiagnostics(
diagnostic = DiagnosticInfo.Create(
HlslBytecodeFailedWithWin32Exception,
structDeclarationSymbol,
[structDeclarationSymbol, win32Error.HResult, win32Error.Message]);
structDeclarationSymbol,
win32Error.HResult,
win32Error.Message);
}
else if (info is HlslBytecodeInfo.CompilerError dxcError)
{
diagnostic = DiagnosticInfo.Create(
HlslBytecodeFailedWithDxcCompilationException,
structDeclarationSymbol,
[structDeclarationSymbol, dxcError.Message]);
structDeclarationSymbol,
dxcError.Message);
}

if (diagnostic is not null)
Expand All @@ -125,22 +144,41 @@ public static void GetInfoDiagnostics(
}

/// <summary>
/// Fixes up an exception message to improve the way it's displayed in VS.
/// Gets the diagnostics for when double precision support is configured incorrectly.
/// </summary>
/// <param name="message">The input exception message.</param>
/// <returns>The updated exception message.</returns>
private static string FixupExceptionMessage(string message)
/// <param name="structDeclarationSymbol">The input <see cref="INamedTypeSymbol"/> instance to process.</param>
/// <param name="info">The source <see cref="HlslBytecodeInfo"/> instance.</param>
/// <param name="diagnostics">The collection of produced <see cref="DiagnosticInfo"/> instances.</param>
public static void GetDoublePrecisionSupportDiagnostics(
INamedTypeSymbol structDeclarationSymbol,
HlslBytecodeInfo info,
ImmutableArrayBuilder<DiagnosticInfo> diagnostics)
{
// Add square brackets around error headers
message = Regex.Replace(message, @"^(error|warning):", static m => $"[{m.Groups[1].Value}]:", RegexOptions.Multiline);

// Remove lines with notes
message = Regex.Replace(message, @"^note:.+", string.Empty, RegexOptions.Multiline);
// If we have no compiled HLSL bytecode, there is nothing more to do
if (info is not HlslBytecodeInfo.Success success)
{
return;
}

// Remove syntax error indicators
message = Regex.Replace(message, @"^ +\^", string.Empty, RegexOptions.Multiline);
bool hasD2DRequiresDoublePrecisionSupportAttribute = structDeclarationSymbol.TryGetAttributeWithFullyQualifiedMetadataName(
"ComputeSharp.RequiresDoublePrecisionSupportAttribute",
out AttributeData? attributeData);

return message.NormalizeToSingleLine();
// Check the two cases where diagnostics are necessary (same as the D2D generator)
if (!hasD2DRequiresDoublePrecisionSupportAttribute && success.RequiresDoublePrecisionSupport)
{
diagnostics.Add(DiagnosticInfo.Create(
MissingRequiresDoublePrecisionSupportAttribute,
structDeclarationSymbol,
structDeclarationSymbol));
}
else if (hasD2DRequiresDoublePrecisionSupportAttribute && !success.RequiresDoublePrecisionSupport)
{
diagnostics.Add(DiagnosticInfo.Create(
UnnecessaryRequiresDoublePrecisionSupportAttribute,
attributeData!.GetLocation(),
structDeclarationSymbol));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
token.ThrowIfCancellationRequested();

HlslBytecode.GetInfoDiagnostics(typeSymbol, hlslInfo, diagnostics);
HlslBytecode.GetDoublePrecisionSupportDiagnostics(typeSymbol, hlslInfo, diagnostics);

token.ThrowIfCancellationRequested();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<DefineConstants>$(DefineConstants);D3D12_GENERATOR</DefineConstants>
</PropertyGroup>

<PropertyGroup>
Expand Down
Loading

0 comments on commit 23ef666

Please sign in to comment.