/// <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,
Example #2
0
        /// <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);
    }
Example #4
0
    /// <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));
    }
Example #5
0
        /// <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
            })
Example #6
0
        /// <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);
        }
Example #7
0
 /// <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;
 }
Example #8
0
    /// <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,