Skip to content

Commit

Permalink
Merge pull request #723 from Sergio0694/dev/d2d-generator-tweaks
Browse files Browse the repository at this point in the history
Check cancellation for D2D linked shaders, share code
  • Loading branch information
Sergio0694 authored Dec 17, 2023
2 parents 23ef666 + 86b39c2 commit 212e974
Show file tree
Hide file tree
Showing 16 changed files with 350 additions and 340 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<DefineConstants>$(DefineConstants);D2D1_SOURCE_GENERATOR</DefineConstants>
</PropertyGroup>

<PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
using System;
using System.Collections.Immutable;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading;
using ComputeSharp.D2D1.Shaders.Translation;
using ComputeSharp.D2D1.SourceGenerators.Models;
using ComputeSharp.SourceGeneration.Extensions;
using ComputeSharp.SourceGeneration.Helpers;
using ComputeSharp.SourceGeneration.Models;
using Microsoft.CodeAnalysis;
using Windows.Win32;
using Windows.Win32.Graphics.Direct3D;
using static ComputeSharp.SourceGeneration.Diagnostics.DiagnosticDescriptors;

namespace ComputeSharp.D2D1.SourceGenerators;
Expand All @@ -23,11 +15,6 @@ partial class D2DPixelShaderDescriptorGenerator
/// </summary>
internal static partial class HlslBytecode
{
/// <summary>
/// The shared cache of <see cref="HlslBytecodeInfo"/> values.
/// </summary>
private static readonly DynamicCache<HlslBytecodeInfoKey, HlslBytecodeInfo> HlslBytecodeCache = new();

/// <summary>
/// Extracts the requested shader profile for the current shader.
/// </summary>
Expand Down Expand Up @@ -178,142 +165,5 @@ public static bool IsSimpleInputShader(

return isSimpleInputShader;
}

/// <summary>
/// Gets the <see cref="HlslBytecodeInfo"/> instance for the input shader info.
/// </summary>
/// <param name="key">The <see cref="HlslBytecodeInfoKey"/> instance for the shader to compile.</param>
/// <param name="token">The <see cref="CancellationToken"/> used to cancel the operation, if needed.</param>
/// <returns>The <see cref="HlslBytecodeInfo"/> instance for the current shader.</returns>
public static HlslBytecodeInfo GetInfo(ref HlslBytecodeInfoKey key, CancellationToken token)
{
static unsafe HlslBytecodeInfo GetInfo(HlslBytecodeInfoKey key, CancellationToken token)
{
// Check if the compilation is not enabled (eg. if there's been errors earlier in the pipeline).
// In this case, skip the compilation, as diagnostic will be emitted for those anyway.
// Compiling would just add overhead and result in more errors, as the HLSL would be invalid.
if (!key.IsCompilationEnabled)
{
return HlslBytecodeInfo.Missing.Instance;
}

try
{
token.ThrowIfCancellationRequested();

// Compile the shader bytecode using the effective parameters
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(d3DBlob.Get());

token.ThrowIfCancellationRequested();

byte* buffer = (byte*)d3DBlob.Get()->GetBufferPointer();
int length = checked((int)d3DBlob.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, D3DCompiler.PrettifyFxcErrorMessage(e.Message));
}
catch (FxcCompilationException e)
{
return new HlslBytecodeInfo.CompilerError(D3DCompiler.PrettifyFxcErrorMessage(e.Message));
}
}

// Get or create the HLSL bytecode compilation result for the input key. The dynamic cache
// will take care of retrieving an existing cached value if the same shader has been compiled
// already with the same parameters. After this call, callers must use the updated key value.
return HlslBytecodeCache.GetOrCreate(ref key, GetInfo, token);
}

/// <summary>
/// Gets any diagnostics from a processed <see cref="HlslBytecodeInfo"/> instance.
/// </summary>
/// <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 GetInfoDiagnostics(
INamedTypeSymbol structDeclarationSymbol,
HlslBytecodeInfo info,
ImmutableArrayBuilder<DiagnosticInfo> diagnostics)
{
DiagnosticInfo? diagnostic = null;

if (info is HlslBytecodeInfo.Win32Error win32Error)
{
diagnostic = DiagnosticInfo.Create(
HlslBytecodeFailedWithWin32Exception,
structDeclarationSymbol,
structDeclarationSymbol,
win32Error.HResult,
win32Error.Message);
}
else if (info is HlslBytecodeInfo.CompilerError fxcError)
{
diagnostic = DiagnosticInfo.Create(
HlslBytecodeFailedWithFxcCompilationException,
structDeclarationSymbol,
structDeclarationSymbol,
fxcError.Message);
}

if (diagnostic is not null)
{
diagnostics.Add(diagnostic);
}
}

/// <summary>
/// Gets the diagnostics for when double precision support is configured incorrectly.
/// </summary>
/// <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)
{
// If we have no compiled HLSL bytecode, there is nothing more to do
if (info is not HlslBytecodeInfo.Success success)
{
return;
}

bool hasD2DRequiresDoublePrecisionSupportAttribute = structDeclarationSymbol.TryGetAttributeWithFullyQualifiedMetadataName(
"ComputeSharp.D2D1.D2DRequiresDoublePrecisionSupportAttribute",
out AttributeData? attributeData);

// Check the two cases where diagnostics are necessary:
// - The shader does not have [D2DRequiresDoublePrecisionSupport], but it needs it
// - The shader has [D2DRequiresDoublePrecisionSupport], but it does not need it
if (!hasD2DRequiresDoublePrecisionSupportAttribute && success.RequiresDoublePrecisionSupport)
{
diagnostics.Add(DiagnosticInfo.Create(
MissingD2DRequiresDoublePrecisionSupportAttribute,
structDeclarationSymbol,
structDeclarationSymbol));
}
else if (hasD2DRequiresDoublePrecisionSupportAttribute && !success.RequiresDoublePrecisionSupport)
{
diagnostics.Add(DiagnosticInfo.Create(
UnnecessaryD2DRequiresDoublePrecisionSupportAttribute,
attributeData!.GetLocation(),
structDeclarationSymbol));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -163,13 +163,13 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
isCompilationEnabled);

// Get the existing compiled shader, or compile the processed HLSL code
HlslBytecodeInfo hlslInfo = HlslBytecode.GetInfo(ref hlslInfoKey, token);
HlslBytecodeInfo hlslInfo = HlslBytecodeSyntaxProcessor.GetInfo(ref hlslInfoKey, token);

token.ThrowIfCancellationRequested();

// Append any diagnostic for the shader compilation
HlslBytecode.GetInfoDiagnostics(typeSymbol, hlslInfo, diagnostics);
HlslBytecode.GetDoublePrecisionSupportDiagnostics(typeSymbol, hlslInfo, diagnostics);
HlslBytecodeSyntaxProcessor.GetInfoDiagnostics(typeSymbol, hlslInfo, diagnostics);
HlslBytecodeSyntaxProcessor.GetDoublePrecisionSupportDiagnostics(typeSymbol, hlslInfo, diagnostics);

token.ThrowIfCancellationRequested();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
using ComputeSharp.SourceGeneration.Extensions;
using ComputeSharp.SourceGeneration.Helpers;
using ComputeSharp.SourceGeneration.Models;
using ComputeSharp.SourceGeneration.SyntaxProcessors;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static ComputeSharp.D2D1.SourceGenerators.D2DPixelShaderDescriptorGenerator;

namespace ComputeSharp.D2D1.SourceGenerators;

Expand Down Expand Up @@ -55,7 +55,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
isCompilationEnabled);

// Get the existing compiled shader, or compile the processed HLSL code
HlslBytecodeInfo hlslInfo = HlslBytecode.GetInfo(ref hlslInfoKey, token);
HlslBytecodeInfo hlslInfo = HlslBytecodeSyntaxProcessor.GetInfo(ref hlslInfoKey, token);

token.ThrowIfCancellationRequested();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ partial class DiagnosticDescriptors
/// Format: <c>"The shader of type {0} failed to compile due to an HLSL compiler error (Message: "{1}")"</c>.
/// </para>
/// </summary>
public static readonly DiagnosticDescriptor HlslBytecodeFailedWithFxcCompilationException = new(
public static readonly DiagnosticDescriptor HlslBytecodeFailedWithCompilationException = new(
id: "CMPSD2D0034",
title: "HLSL bytecode compilation failed due to an HLSL compiler error",
messageFormat: """The shader of type {0} failed to compile due to an HLSL compiler error (Message: "{1}")""",
Expand Down Expand Up @@ -1197,7 +1197,7 @@ partial class DiagnosticDescriptors
/// 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(
public static readonly DiagnosticDescriptor MissingRequiresDoublePrecisionSupportAttribute = 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 to that functionality)",
Expand All @@ -1213,7 +1213,7 @@ partial class DiagnosticDescriptors
/// 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>
/// </summary>
public static readonly DiagnosticDescriptor UnnecessaryD2DRequiresDoublePrecisionSupportAttribute = new(
public static readonly DiagnosticDescriptor UnnecessaryRequiresDoublePrecisionSupportAttribute = new(
id: "CMPSD2D0081",
title: "Unnecessary [D2DRequiresDoublePrecisionSupport] attribute",
messageFormat: "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)",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace System.Text;

/// <summary>
/// Extensions for the <see cref="Encoding"/> type.
/// </summary>
internal static class EncodingExtensions
{
/// <summary>
/// Encodes into a span of bytes a set of characters from the specified read-only span.
/// </summary>
/// <param name="encoding">The input <see cref="Encoding"/> instance to use.</param>
/// <param name="chars">The span containing the set of characters to encode.</param>
/// <param name="bytes">The byte span to hold the encoded bytes.</param>
/// <returns>The number of encoded bytes.</returns>
public static unsafe int GetBytes(this Encoding encoding, ReadOnlySpan<char> chars, Span<byte> bytes)
{
fixed (char* charsPtr = chars)
fixed (byte* bytesPtr = bytes)
{
return encoding.GetBytes(charsPtr, chars.Length, bytesPtr, bytes.Length);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System;
using System.Threading;
using ComputeSharp.D2D1.Shaders.Translation;
using ComputeSharp.D2D1.SourceGenerators.Models;
using Windows.Win32;
using Windows.Win32.Graphics.Direct3D;

namespace ComputeSharp.SourceGeneration.SyntaxProcessors;

/// <inheritdoc/>
partial class HlslBytecodeSyntaxProcessor
{
/// <inheritdoc/>
private static partial string GetRequiresDoublePrecisionSupportAttributeName()
{
return "ComputeSharp.D2D1.D2DRequiresDoublePrecisionSupportAttribute";
}

/// <inheritdoc/>
private static partial ComPtr<ID3DBlob> Compile(HlslBytecodeInfoKey key, CancellationToken token)
{
return D3DCompiler.Compile(
key.HlslSource.AsSpan(),
key.ShaderProfile,
key.CompileOptions,
token);
}

/// <inheritdoc/>
private static unsafe partial bool IsDoublePrecisionSupportRequired(ID3DBlob* d3DBlob)
{
return D3DCompiler.IsDoublePrecisionSupportRequired(d3DBlob);
}

/// <inheritdoc/>
private static partial string FixupErrorMessage(string message)
{
return D3DCompiler.PrettifyFxcErrorMessage(message);
}
}
39 changes: 25 additions & 14 deletions src/ComputeSharp.D2D1/Shaders/Translation/D3DCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Runtime.InteropServices;
using System.Text;
#if SOURCE_GENERATOR
using System.Threading;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.Graphics.Direct3D;
Expand Down Expand Up @@ -34,21 +35,23 @@ internal static unsafe partial class D3DCompiler
/// <param name="shaderProfile">The shader profile to use to compile the shader.</param>
/// <param name="options">The options to use to compile the shader.</param>
/// <returns>The bytecode for the compiled shader.</returns>
public static ComPtr<ID3DBlob> Compile(ReadOnlySpan<char> source, D2D1ShaderProfile shaderProfile, D2D1CompileOptions options)
{
int maxLength = Encoding.ASCII.GetMaxByteCount(source.Length);
byte[] buffer = ArrayPool<byte>.Shared.Rent(maxLength);
int writtenBytes;

public static ComPtr<ID3DBlob> Compile(
ReadOnlySpan<char> source,
D2D1ShaderProfile shaderProfile,
#if SOURCE_GENERATOR
fixed (char* pSource = source)
fixed (byte* pBuffer = buffer)
{
writtenBytes = Encoding.ASCII.GetBytes(pSource, source.Length, pBuffer, buffer.Length);
}
#pragma warning disable CS1573
D2D1CompileOptions options,
CancellationToken token) // The cancellation token used to cancel the operation.
#pragma warning restore CS1573
#else
writtenBytes = Encoding.ASCII.GetBytes(source, buffer);
D2D1CompileOptions options)
#endif
{
// Transcode the input HLSL source from Unicode to ASCII encoding
int maxLength = Encoding.ASCII.GetMaxByteCount(source.Length);
byte[] buffer = ArrayPool<byte>.Shared.Rent(maxLength);
int writtenBytes = Encoding.ASCII.GetBytes(source, buffer);
ReadOnlySpan<byte> sourceAscii = new(buffer, 0, writtenBytes);

bool enableLinking = (options & D2D1CompileOptions.EnableLinking) == D2D1CompileOptions.EnableLinking;
bool stripReflectionData = (options & D2D1CompileOptions.StripReflectionData) == D2D1CompileOptions.StripReflectionData;
Expand All @@ -58,9 +61,13 @@ public static ComPtr<ID3DBlob> Compile(ReadOnlySpan<char> source, D2D1ShaderProf

try
{
#if SOURCE_GENERATOR
token.ThrowIfCancellationRequested();
#endif

// Compile the standalone D2D1 full shader
using ComPtr<ID3DBlob> d3DBlobFullShader = Compile(
source: buffer.AsSpan(0, writtenBytes),
source: sourceAscii,
macro: ASCII.D2D_FULL_SHADER,
d2DEntry: ASCII.Execute,
entryPoint: ASCII.Execute,
Expand All @@ -73,9 +80,13 @@ public static ComPtr<ID3DBlob> Compile(ReadOnlySpan<char> source, D2D1ShaderProf
return d3DBlobFullShader.Move();
}

#if SOURCE_GENERATOR
token.ThrowIfCancellationRequested();
#endif

// Compile the export function
using ComPtr<ID3DBlob> d3DBlobFunction = Compile(
source: buffer.AsSpan(0, writtenBytes),
source: sourceAscii,
macro: ASCII.D2D_FUNCTION,
d2DEntry: ASCII.Execute,
entryPoint: default,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Models\HlslBytecodeInfo.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\Interfaces\IConstantBufferInfo.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Models\TypeAliases.cs" />
<Compile Include="$(MSBuildThisFileDirectory)SyntaxProcessors\HlslBytecodeSyntaxProcessor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)SyntaxProcessors\ConstantBufferSyntaxProcessor.Generation.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Helpers\HlslSourceHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)SyntaxProcessors\HlslDefinitionsSyntaxProcessor.cs" />
Expand Down
Loading

0 comments on commit 212e974

Please sign in to comment.