Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@
<Compile Include="..\ComputeSharp.D2D1\Shaders\Translation\ASCII.cs" Link="ComputeSharp.D2D1\Shaders\Translation\ASCII.cs" />
<Compile Include="..\ComputeSharp.D2D1\Shaders\Translation\D3DCompiler.cs" Link="ComputeSharp.D2D1\Shaders\Translation\D3DCompiler.cs" />
<Compile Include="..\ComputeSharp.D2D1\Shaders\Translation\D3DCompiler.ID3DInclude.cs" Link="ComputeSharp.D2D1\Shaders\Translation\D3DCompiler.ID3DInclude.cs" />
<Compile Include="..\ComputeSharp.D2D1\Shaders\Translation\Dxbc.cs" Link="ComputeSharp.D2D1\Shaders\Translation\Dxbc.cs" />
<Compile Include="..\ComputeSharp.D2D1\Shaders\Translation\Dxbc.Checksum.cs" Link="ComputeSharp.D2D1\Shaders\Translation\Dxbc.Checksum.cs" />
<Compile Include="..\ComputeSharp.D2D1\Shaders\Translation\Headers\D2D1EffectHelpers.cs" Link="ComputeSharp.D2D1\Shaders\Translation\Headers\D2D1EffectHelpers.cs" />

<!-- ComPtr<T> and HRESULT extension from D3D12 generator -->
Expand Down
29 changes: 29 additions & 0 deletions src/ComputeSharp.D2D1/Attributes/Enums/D2D1CompileOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#if SOURCE_GENERATOR
using D3DCOMPILE = Windows.Win32.PInvoke;
#else
using System.Diagnostics.CodeAnalysis;
using ComputeSharp.D2D1.Interop;
using ComputeSharp.Win32;

Expand Down Expand Up @@ -155,6 +156,34 @@ public enum D2D1CompileOptions
/// </remarks>
WarningsAreErrors = (int)D3DCOMPILE.D3DCOMPILE_WARNINGS_ARE_ERRORS,

/// <summary>
/// Declares minimum precision support in the compiled bytecode, which can allow Direct2D to link the
/// resulting effect. This flag has no effect unless <see cref="EnableLinking"/> is also set.
/// </summary>
/// <remarks>
/// <para>
/// Setting <see cref="EnableLinking"/> embeds an export function in the compiled bytecode, but Direct2D
/// will generally still not link effects created from it. Additionally declaring minimum precision support
/// has been observed to make linking engage, which can remove one rendering pass, and the intermediate
/// surface that goes with it, for each effect that ends up being linked.
/// </para>
/// <para>
/// Specifically, this flag appends a shader feature info blob declaring
/// <c>D3D_SHADER_FEATURE_MINIMUM_PRECISION</c> to the compiled bytecode. The compiled shader instructions
/// are left untouched, so no computation is lowered to a reduced precision. Note that Direct2D may still
/// run the bytecode through a minimum precision conversion, depending on the target being rendered to.
/// </para>
/// <para>
/// This relies on behavior that is neither documented nor guaranteed, and that may stop working at any
/// time. It should only be used after measuring that it actually improves performance, and shaders using
/// it should be validated to still produce correct results.
/// </para>
/// </remarks>
#if !SOURCE_GENERATOR
[Experimental("CMPSEXP0001", UrlFormat = "https://github.com/Sergio0694/ComputeSharp")]
#endif
DeclareMinimumPrecisionSupport = 1 << 29,

/// <summary>
/// Strips the reflection data from the generated shader bytecode. The bytecode size will be smaller, but trying
/// to perform reflection on the shader will return inaccurate results. It is recommend if not reflection is used.
Expand Down
9 changes: 8 additions & 1 deletion src/ComputeSharp.D2D1/Shaders/Interop/D2D1ShaderCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
using ComputeSharp.D2D1.Shaders.Translation;
using ComputeSharp.Win32;

// The compiler is responsible for implementing DeclareMinimumPrecisionSupport, so it has to reference it
#pragma warning disable CMPSEXP0001

namespace ComputeSharp.D2D1.Interop;

/// <summary>
Expand Down Expand Up @@ -68,10 +71,12 @@ public static unsafe ReadOnlyMemory<byte> Compile(
// Check the additional compile options (not provided by FXC directly)
bool enableLinking = (options & D2D1CompileOptions.EnableLinking) == D2D1CompileOptions.EnableLinking;
bool stripReflectionData = (options & D2D1CompileOptions.StripReflectionData) == D2D1CompileOptions.StripReflectionData;
bool declareMinimumPrecisionSupport = (options & D2D1CompileOptions.DeclareMinimumPrecisionSupport) == D2D1CompileOptions.DeclareMinimumPrecisionSupport;

// Remove the additional options to make them blittable to flags
options &= ~D2D1CompileOptions.EnableLinking;
options &= ~D2D1CompileOptions.StripReflectionData;
options &= ~D2D1CompileOptions.DeclareMinimumPrecisionSupport;

// Compile the standalone D2D1 full shader
using ComPtr<ID3DBlob> d3DBlobFullShader = D3DCompiler.Compile(
Expand Down Expand Up @@ -102,7 +107,9 @@ public static unsafe ReadOnlyMemory<byte> Compile(
stripReflectionData: stripReflectionData);

// Embed it as private data if requested
using ComPtr<ID3DBlob> d3DBlobLinked = D3DCompiler.SetD3DPrivateData(d3DBlobFullShader.Get(), d3DBlobFunction.Get());
using ComPtr<ID3DBlob> d3DBlobLinked = D3DCompiler.SetD3DPrivateData(
shaderBytecode: D3DCompiler.GetBytecode(d3DBlobFullShader.Get(), declareMinimumPrecisionSupport),
exportBlob: d3DBlobFunction.Get());

void* blobLinkedPtr = d3DBlobLinked.Get()->GetBufferPointer();
nuint blobLinkedSize = d3DBlobLinked.Get()->GetBufferSize();
Expand Down
58 changes: 43 additions & 15 deletions src/ComputeSharp.D2D1/Shaders/Translation/D3DCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
using ComputeSharp.Win32;
#endif

// The compiler is responsible for implementing DeclareMinimumPrecisionSupport, so it has to reference it
#pragma warning disable CMPSEXP0001

namespace ComputeSharp.D2D1.Shaders.Translation;

/// <summary>
Expand Down Expand Up @@ -55,9 +58,11 @@ public static ComPtr<ID3DBlob> Compile(

bool enableLinking = (options & D2D1CompileOptions.EnableLinking) == D2D1CompileOptions.EnableLinking;
bool stripReflectionData = (options & D2D1CompileOptions.StripReflectionData) == D2D1CompileOptions.StripReflectionData;
bool declareMinimumPrecisionSupport = (options & D2D1CompileOptions.DeclareMinimumPrecisionSupport) == D2D1CompileOptions.DeclareMinimumPrecisionSupport;

options &= ~D2D1CompileOptions.EnableLinking;
options &= ~D2D1CompileOptions.StripReflectionData;
options &= ~D2D1CompileOptions.DeclareMinimumPrecisionSupport;

try
{
Expand Down Expand Up @@ -95,7 +100,9 @@ public static ComPtr<ID3DBlob> Compile(
stripReflectionData: stripReflectionData);

// Embed it as private data if requested
using ComPtr<ID3DBlob> d3DBlobLinked = SetD3DPrivateData(d3DBlobFullShader.Get(), d3DBlobFunction.Get());
using ComPtr<ID3DBlob> d3DBlobLinked = SetD3DPrivateData(
shaderBytecode: GetBytecode(d3DBlobFullShader.Get(), declareMinimumPrecisionSupport),
exportBlob: d3DBlobFunction.Get());

return d3DBlobLinked.Move();
}
Expand Down Expand Up @@ -220,29 +227,50 @@ public static ComPtr<ID3DBlob> Compile(
}

/// <summary>
/// Embeds the bytecode for an exported shader as private data into another shader bytecode.
/// Gets the bytecode of a full shader, optionally declaring minimum precision support in it.
/// </summary>
/// <param name="shaderBlob">The bytecode for the full shader.</param>
/// <param name="exportBlob">The bytecode for the shader function to export.</param>
/// <returns>An <see cref="ID3DBlob"/> instance with the combined data of <paramref name="shaderBlob"/> and <paramref name="exportBlob"/>.</returns>
public static ComPtr<ID3DBlob> SetD3DPrivateData(ID3DBlob* shaderBlob, ID3DBlob* exportBlob)
/// <param name="declareMinimumPrecisionSupport">Whether to declare minimum precision support in the bytecode.</param>
/// <returns>The bytecode for the full shader.</returns>
/// <remarks>
/// Patched bytecode is a complete, valid DXBC container with an updated checksum.
/// </remarks>
public static ReadOnlySpan<byte> GetBytecode(ID3DBlob* shaderBlob, bool declareMinimumPrecisionSupport)
{
void* shaderPtr = shaderBlob->GetBufferPointer();
nuint shaderSize = shaderBlob->GetBufferSize();
ReadOnlySpan<byte> bytecode = new(shaderBlob->GetBufferPointer(), (int)shaderBlob->GetBufferSize());

if (declareMinimumPrecisionSupport)
{
return Dxbc.CreateWithMinimumPrecisionShaderFeatureFlag(bytecode);
}

return bytecode;
}

/// <summary>
/// Embeds the bytecode for an exported shader as private data into another shader bytecode.
/// </summary>
/// <param name="shaderBytecode">The bytecode for the full shader.</param>
/// <param name="exportBlob">The bytecode for the shader function to export.</param>
/// <returns>An <see cref="ID3DBlob"/> instance with the combined data of <paramref name="shaderBytecode"/> and <paramref name="exportBlob"/>.</returns>
public static ComPtr<ID3DBlob> SetD3DPrivateData(ReadOnlySpan<byte> shaderBytecode, ID3DBlob* exportBlob)
{
void* exportPtr = exportBlob->GetBufferPointer();
nuint exportSize = exportBlob->GetBufferSize();

using ComPtr<ID3DBlob> resultBlob = default;

DirectX.D3DSetBlobPart(
pSrcData: shaderPtr,
SrcDataSize: shaderSize,
Part: D3D_BLOB_PART.D3D_BLOB_PRIVATE_DATA,
Flags: 0,
pPart: exportPtr,
PartSize: exportSize,
ppNewShader: resultBlob.GetAddressOf()).Assert();
fixed (byte* shaderPtr = shaderBytecode)
{
DirectX.D3DSetBlobPart(
pSrcData: shaderPtr,
SrcDataSize: (nuint)shaderBytecode.Length,
Part: D3D_BLOB_PART.D3D_BLOB_PRIVATE_DATA,
Flags: 0,
pPart: exportPtr,
PartSize: exportSize,
ppNewShader: resultBlob.GetAddressOf()).Assert();
}

return resultBlob.Move();
}
Expand Down
Loading
Loading