/// <summary> /// Compiles a new HLSL shader from the input source code. /// </summary> /// <param name="hlslSourceAscii">The HLSL source code to compile (in ASCII).</param> /// <param name="entryPointAscii">The entry point of the shader being compiled (in ASCII).</param> /// <param name="shaderProfile">The shader profile to use to compile the shader.</param> /// <param name="options">The compiler options to use to compile the shader.</param> /// <returns>The bytecode for the compiled shader.</returns> /// <exception cref="FxcCompilationException">Thrown if the compilation fails.</exception> public static unsafe ReadOnlyMemory <byte> Compile( ReadOnlySpan <byte> hlslSourceAscii, ReadOnlySpan <byte> entryPointAscii, D2D1ShaderProfile shaderProfile, D2D1CompileOptions options) { // Check linking support bool enableLinking = (options & D2D1CompileOptions.EnableLinking) == D2D1CompileOptions.EnableLinking; // Remove the linking flag to make the options blittable to flags options &= ~D2D1CompileOptions.EnableLinking; // Compile the standalone D2D1 full shader using ComPtr <ID3DBlob> d3DBlobFullShader = D3DCompiler.CompileShader( source: hlslSourceAscii, macro: D3DCompiler.ASCII.D2D_FULL_SHADER, d2DEntry: entryPointAscii, entryPoint: entryPointAscii, target: D3DCompiler.ASCII.GetPixelShaderProfile(shaderProfile), flags: (uint)options); if (!enableLinking) { void *blobFullShaderPtr = d3DBlobFullShader.Get()->GetBufferPointer(); nuint blobFullShaderSize = d3DBlobFullShader.Get()->GetBufferSize(); return(new Span <byte>(blobFullShaderPtr, (int)blobFullShaderSize).ToArray()); } // Compile the export function using ComPtr <ID3DBlob> d3DBlobFunction = D3DCompiler.CompileShader( source: hlslSourceAscii, macro: D3DCompiler.ASCII.D2D_FUNCTION, d2DEntry: entryPointAscii, entryPoint: default,
/// <summary> /// Extracts the compile options for the current shader. /// </summary> /// <param name="diagnostics">The collection of produced <see cref="Diagnostic"/> instances.</param> /// <param name="structDeclarationSymbol">The input <see cref="INamedTypeSymbol"/> instance to process.</param> /// <returns>The compile options to use to compile the shader, if present.</returns> public static D2D1CompileOptions?GetCompileOptions(ImmutableArray <Diagnostic> .Builder diagnostics, INamedTypeSymbol structDeclarationSymbol) { if (structDeclarationSymbol.TryGetAttributeWithFullMetadataName("ComputeSharp.D2D1.D2DCompileOptionsAttribute", out AttributeData? attributeData)) { D2D1CompileOptions options = (D2D1CompileOptions)attributeData !.ConstructorArguments[0].Value !; if ((options & D2D1CompileOptions.PackMatrixColumnMajor) != 0) { diagnostics.Add( InvalidPackMatrixColumnMajorOption, structDeclarationSymbol, structDeclarationSymbol); } // PackMatrixRowMajor is always automatically enabled return(options | D2D1CompileOptions.PackMatrixRowMajor); } if (structDeclarationSymbol.ContainingAssembly.TryGetAttributeWithFullMetadataName("ComputeSharp.D2D1.D2DCompileOptionsAttribute", out attributeData)) { // No need to validate against PackMatrixColumnMajor as that's checked separately return((D2D1CompileOptions)attributeData !.ConstructorArguments[0].Value ! | D2D1CompileOptions.PackMatrixRowMajor); } return(null); }
/// <summary> /// Compiles a new HLSL shader from the input source code. /// </summary> /// <param name="hlslSource">The HLSL source code to compile.</param> /// <param name="entryPoint">The entry point of the shader being compiled.</param> /// <param name="shaderProfile">The shader profile to use to compile the shader.</param> /// <param name="options">The compiler options to use to compile the shader.</param> /// <returns>The bytecode for the compiled shader.</returns> /// <exception cref="FxcCompilationException">Thrown if the compilation fails.</exception> public static ReadOnlyMemory <byte> Compile( ReadOnlySpan <char> hlslSource, ReadOnlySpan <char> entryPoint, D2D1ShaderProfile shaderProfile, D2D1CompileOptions options) { // Encode the HLSL source to ASCII int maxSourceLength = Encoding.ASCII.GetMaxByteCount(hlslSource.Length); byte[] sourceBuffer = ArrayPool <byte> .Shared.Rent(maxSourceLength); int sourceWrittenBytes = Encoding.ASCII.GetBytes(hlslSource, sourceBuffer); // Encode the entry point to ASCII int maxEntryPointLength = Encoding.ASCII.GetMaxByteCount(entryPoint.Length); byte[] entryPointBuffer = ArrayPool <byte> .Shared.Rent(maxEntryPointLength); int entryPointWrittenBytes = Encoding.ASCII.GetBytes(entryPoint, entryPointBuffer); ReadOnlySpan <byte> sourceAscii = sourceBuffer.AsSpan(0, sourceWrittenBytes); ReadOnlySpan <byte> entryPointAscii = entryPointBuffer.AsSpan(0, entryPointWrittenBytes); ReadOnlyMemory <byte> bytecode = Compile(sourceAscii, entryPointAscii, shaderProfile, options); ArrayPool <byte> .Shared.Return(sourceBuffer); ArrayPool <byte> .Shared.Return(entryPointBuffer); return(bytecode); }
/// <summary> /// Loads the bytecode from an input D2D1 pixel shader. /// </summary> /// <typeparam name="T">The type of D2D1 pixel shader to load the bytecode for.</typeparam> /// <param name="options"> /// <para>The compile options to use to get the shader bytecode.</para> /// <para>For consistency with <see cref="D2DCompileOptionsAttribute"/>, <see cref="D2D1CompileOptions.PackMatrixRowMajor"/> will be automatically added.</para> /// </param> /// <returns>A <see cref="ReadOnlyMemory{T}"/> instance with the resulting shader bytecode.</returns> /// <exception cref="ArgumentException">Thrown if <see cref="D2D1CompileOptions.PackMatrixColumnMajor"/> is specified within <paramref name="options"/>.</exception> /// <remarks> /// <para> /// If precompiled shader with the requested options does not exist, the shader will be compiled with the input options. If additional compile /// options have been specified on the shader type <typeparamref name="T"/> (through <see cref="D2DCompileOptionsAttribute"/>), they will be ignored. /// </para> /// <para> /// If the shader needs to be recompiled, the shader profile that will be used is <see cref="D2D1ShaderProfile.PixelShader50"/>. /// </para> /// <para> /// If the input shader was precompiled, the returned <see cref="ReadOnlyMemory{T}"/> instance will wrap a pinned memory buffer (from the PE section). /// If the shader was compiled at runtime, the returned <see cref="ReadOnlyMemory{T}"/> instance will wrap a <see cref="byte"/> array with the bytecode. /// </para> /// </remarks> public static ReadOnlyMemory <byte> LoadBytecode <T>(D2D1CompileOptions options) where T : unmanaged, ID2D1PixelShader { if ((options & D2D1CompileOptions.PackMatrixColumnMajor) != 0) { ThrowHelper.ThrowArgumentException(nameof(options), "The PackMatrixColumnMajor compile options is not compatible with ComputeSharp.D2D1 shaders."); } return(LoadOrCompileBytecode <T>(null, options | D2D1CompileOptions.PackMatrixRowMajor)); }
/// <summary> /// Gets a <see cref="BlockSyntax"/> instance with the logic to try to get a compiled shader bytecode. /// </summary> /// <param name="sourceInfo">The source info for the shader to compile.</param> /// <param name="token">The <see cref="CancellationToken"/> used to cancel the operation, if needed.</param> /// <param name="options">The effective compile options used to create the shader bytecode.</param> /// <param name="diagnostic">The resulting diagnostic from the processing operation, if any.</param> /// <returns>The <see cref="ImmutableArray{T}"/> instance with the compiled shader bytecode.</returns> public static unsafe ImmutableArray <byte> GetBytecode( HlslShaderSourceInfo sourceInfo, CancellationToken token, out D2D1CompileOptions options, out DiagnosticInfo?diagnostic) { ImmutableArray <byte> bytecode = ImmutableArray <byte> .Empty; // No embedded shader was requested, or there were some 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 (sourceInfo is { HasErrors: true } or { ShaderProfile: null })
/// <summary> /// Gets a <see cref="Diagnostic"/>-s for invalid assembly-level <c>[D2D1CompileOptions]</c> attribute, if one is present. /// </summary> /// <param name="assemblySymbol">The input <see cref="IAssemblySymbol"/> instance to process.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param> /// <returns>The diagnostic for the attribute, if invalid.</returns> public static Diagnostic?GetAssemblyLevelCompileOptionsDiagnostics(IAssemblySymbol assemblySymbol, CancellationToken cancellationToken) { // In order to emit diagnostics for [D2D1CompileOptions] attributes at the assembly level, the following is needed: // - The type symbol for the assembly, to get the AttributeData object for the [D2D1CompileOptions] attribute, if used. // - The syntax node representing the attribute targeting the assembly, to get a location (this is retrieved from the AttributeData). // - The input D2D1CompileOptions value, which can be retrieved from the constructor arguments of the AttributeData object. if (assemblySymbol.TryGetAttributeWithFullMetadataName("ComputeSharp.D2D1.D2DCompileOptionsAttribute", out AttributeData? attributeData)) { D2D1CompileOptions options = (D2D1CompileOptions)attributeData !.ConstructorArguments[0].Value !; if ((options & D2D1CompileOptions.PackMatrixColumnMajor) != 0) { return(Diagnostic.Create( InvalidPackMatrixColumnMajorOption, attributeData.ApplicationSyntaxReference?.GetSyntax(cancellationToken).GetLocation(), assemblySymbol)); } } return(null); }
/// <summary> /// Creates a new <see cref="D2DCompileOptionsAttribute"/> instance with the specified parameters. /// </summary> /// <param name="options">The compiler options to use to compile the shader.</param> public D2DCompileOptionsAttribute(D2D1CompileOptions options) { Options = options; }
/// <summary> /// Compiles a new HLSL shader from the input source code. /// </summary> /// <param name="source">The HLSL source code to compile.</param> /// <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 = Encoding.ASCII.GetBytes(source, buffer); try { // Compile the standalone D2D1 full shader using ComPtr <ID3DBlob> d3DBlobFullShader = CompileShader( source: buffer.AsSpan(0, writtenBytes), macro: ASCII.D2D_FULL_SHADER, d2DEntry: ASCII.Execute, entryPoint: ASCII.Execute, target: ASCII.GetPixelShaderProfile(shaderProfile), flags: (uint)(options& ~D2D1CompileOptions.EnableLinking)); if ((options & D2D1CompileOptions.EnableLinking) == 0) { return(d3DBlobFullShader.Move()); } // Compile the export function using ComPtr <ID3DBlob> d3DBlobFunction = CompileShader( source: buffer.AsSpan(0, writtenBytes), macro: ASCII.D2D_FUNCTION, d2DEntry: ASCII.Execute, entryPoint: default,