Skip to content

Commit

Permalink
Remove shader linking heuristics in generator
Browse files Browse the repository at this point in the history
  • Loading branch information
Sergio0694 committed Jun 8, 2024
1 parent 8fbd765 commit b71ea33
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 77 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using ComputeSharp.SourceGeneration.Extensions;
using Microsoft.CodeAnalysis;

Expand Down Expand Up @@ -76,83 +75,12 @@ public static D2D1ShaderProfile GetEffectiveShaderProfile(D2D1ShaderProfile? sha
/// Gets the effective compile options to use.
/// </summary>
/// <param name="compileOptions">The requested compile options.</param>
/// <param name="isLinkingSupported">Whether the input shader supports linking.</param>
/// <returns>The effective compile options.</returns>
public static D2D1CompileOptions GetEffectiveCompileOptions(D2D1CompileOptions? compileOptions, bool isLinkingSupported)
public static D2D1CompileOptions GetEffectiveCompileOptions(D2D1CompileOptions? compileOptions)
{
// If an explicit set of compile options is provided, use that directly. Otherwise, use the default
// options plus the option to enable linking only if the shader can potentially support it.
return compileOptions ?? (D2D1CompileOptions.Default | (isLinkingSupported ? D2D1CompileOptions.EnableLinking : 0));
}

/// <summary>
/// Extracts the metadata definition for the current shader.
/// </summary>
/// <param name="compilation">The input <see cref="Compilation"/> object currently in use.</param>
/// <param name="structDeclarationSymbol">The input <see cref="INamedTypeSymbol"/> instance to process.</param>
/// <param name="inputCount">The number of inputs for the shader.</param>
/// <returns>Whether the shader only has simple inputs.</returns>
public static bool IsSimpleInputShader(
Compilation compilation,
INamedTypeSymbol structDeclarationSymbol,
int inputCount)
{
return IsSimpleInputShader(
structDeclarationSymbol,
compilation.GetTypeByMetadataName("ComputeSharp.D2D1.D2DInputSimpleAttribute")!,
inputCount);
}

/// <summary>
/// Extracts the metadata definition for the current shader.
/// </summary>
/// <param name="structDeclarationSymbol">The input <see cref="INamedTypeSymbol"/> instance to process.</param>
/// <param name="d2DInputSimpleSymbolAttributeSymbol">The symbol for the <c>[D2DInputSimple]</c> attribute.</param>
/// <param name="inputCount">The number of inputs for the shader.</param>
/// <returns>Whether the shader only has simple inputs.</returns>
public static bool IsSimpleInputShader(
INamedTypeSymbol structDeclarationSymbol,
INamedTypeSymbol d2DInputSimpleSymbolAttributeSymbol,
int inputCount)
{
// We cannot trust the input count to be valid at this point (it may be invalid and
// with diagnostic already emitted for it). So first, just clamp it in the right range.
inputCount = Math.Max(0, Math.Min(8, inputCount));

// If there are no inputs, the shader is as if only had simple inputs
if (inputCount == 0)
{
return true;
}

// Build a map of all simple inputs (unmarked inputs default to being complex).
// We can never have more than 8 inputs, and if there are it means the shader is
// not valid. Just ignore them, and the generator will emit a separate diagnostic.
Span<bool> simpleInputsMap = stackalloc bool[8];

// We first start with all inputs marked as complex (ie. not simple)
simpleInputsMap.Clear();

foreach (AttributeData attributeData in structDeclarationSymbol.GetAttributes())
{
// Only retrieve indices of simple inputs that are in range. If an input is out of
// range, the diagnostic for it will already be emitted by a previous generator step.
if (SymbolEqualityComparer.Default.Equals(attributeData.AttributeClass, d2DInputSimpleSymbolAttributeSymbol) &&
attributeData.ConstructorArguments is [{ Value: >= 0 and < 8 and int index }])
{
simpleInputsMap[index] = true;
}
}

bool isSimpleInputShader = true;

// Validate all inputs in our range (filtered by the allowed one)
foreach (bool isSimpleInput in simpleInputsMap[..inputCount])
{
isSimpleInputShader &= isSimpleInput;
}

return isSimpleInputShader;
return compileOptions ?? D2D1CompileOptions.Default;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,10 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
token.ThrowIfCancellationRequested();

// Get the shader profile and linking info for the HLSL bytecode properties
bool isLinkingSupported = HlslBytecode.IsSimpleInputShader(context.SemanticModel.Compilation, typeSymbol, inputCount);
D2D1ShaderProfile? requestedShaderProfile = HlslBytecode.GetRequestedShaderProfile(typeSymbol);
D2D1CompileOptions? requestedCompileOptions = HlslBytecode.GetRequestedCompileOptions(typeSymbol);
D2D1ShaderProfile effectiveShaderProfile = HlslBytecode.GetEffectiveShaderProfile(requestedShaderProfile, out bool isCompilationEnabled);
D2D1CompileOptions effectiveCompileOptions = HlslBytecode.GetEffectiveCompileOptions(requestedCompileOptions, isLinkingSupported);
D2D1CompileOptions effectiveCompileOptions = HlslBytecode.GetEffectiveCompileOptions(requestedCompileOptions);

token.ThrowIfCancellationRequested();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections.Immutable;
using ComputeSharp.SourceGeneration.Extensions;
using Microsoft.CodeAnalysis;
Expand Down Expand Up @@ -54,7 +55,7 @@ public override void Initialize(AnalysisContext context)
}

// Emit a diagnostic if the compile options are not valid for the shader type
if (!D2DPixelShaderDescriptorGenerator.HlslBytecode.IsSimpleInputShader(typeSymbol, d2DInputSimpleAttributeSymbol, inputCount))
if (!IsSimpleInputShader(typeSymbol, d2DInputSimpleAttributeSymbol, inputCount))
{
context.ReportDiagnostic(Diagnostic.Create(
InvalidD2D1PixelOptionsTrivialSamplingOnShaderType,
Expand All @@ -64,4 +65,56 @@ public override void Initialize(AnalysisContext context)
}, SymbolKind.NamedType);
});
}

/// <summary>
/// Checks whether a given shader only has simple inputs.
/// </summary>
/// <param name="structDeclarationSymbol">The input <see cref="INamedTypeSymbol"/> instance to process.</param>
/// <param name="d2DInputSimpleSymbolAttributeSymbol">The symbol for the <c>[D2DInputSimple]</c> attribute.</param>
/// <param name="inputCount">The number of inputs for the shader.</param>
/// <returns>Whether the shader only has simple inputs.</returns>
private static bool IsSimpleInputShader(
INamedTypeSymbol structDeclarationSymbol,
INamedTypeSymbol d2DInputSimpleSymbolAttributeSymbol,
int inputCount)
{
// We cannot trust the input count to be valid at this point (it may be invalid and
// with diagnostic already emitted for it). So first, just clamp it in the right range.
inputCount = Math.Max(0, Math.Min(8, inputCount));

// If there are no inputs, the shader is as if only had simple inputs
if (inputCount == 0)
{
return true;
}

// Build a map of all simple inputs (unmarked inputs default to being complex).
// We can never have more than 8 inputs, and if there are it means the shader is
// not valid. Just ignore them, and the generator will emit a separate diagnostic.
Span<bool> simpleInputsMap = stackalloc bool[8];

// We first start with all inputs marked as complex (ie. not simple)
simpleInputsMap.Clear();

foreach (AttributeData attributeData in structDeclarationSymbol.GetAttributes())
{
// Only retrieve indices of simple inputs that are in range. If an input is out of
// range, the diagnostic for it will already be emitted by a previous generator step.
if (SymbolEqualityComparer.Default.Equals(attributeData.AttributeClass, d2DInputSimpleSymbolAttributeSymbol) &&
attributeData.ConstructorArguments is [{ Value: >= 0 and < 8 and int index }])
{
simpleInputsMap[index] = true;
}
}

bool isSimpleInputShader = true;

// Validate all inputs in our range (filtered by the allowed one)
foreach (bool isSimpleInput in simpleInputsMap[..inputCount])
{
isSimpleInputShader &= isSimpleInput;
}

return isSimpleInputShader;
}
}

0 comments on commit b71ea33

Please sign in to comment.