Add experimental 'DeclareMinimumPrecisionSupport' compile option - #928
Draft
Sergio0694 wants to merge 3 commits into
Draft
Add experimental 'DeclareMinimumPrecisionSupport' compile option#928Sergio0694 wants to merge 3 commits into
Sergio0694 wants to merge 3 commits into
Conversation
Adds support for appending (or updating) the 'SFI0' shader feature info blob in a DXBC container, along with the modified MD5 checksum used by DXBC containers. Recomputing the checksum is required, as FXC APIs such as 'D3DSetBlobPart' validate it and reject any container that was modified after being compiled. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: a62113de-a3b2-457b-abce-70ee666c9e6f
Setting '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. This option appends a shader feature info blob declaring 'D3D_SHADER_FEATURE_MINIMUM_PRECISION' to the compiled bytecode. The compiled shader instructions are left untouched, so no computation is lowered to a reduced precision. This relies on behavior that is neither documented nor guaranteed, so the option is marked as experimental. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: a62113de-a3b2-457b-abce-70ee666c9e6f
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: a62113de-a3b2-457b-abce-70ee666c9e6f
There was a problem hiding this comment.
Pull request overview
This PR introduces an experimental D2D1CompileOptions.DeclareMinimumPrecisionSupport flag for ComputeSharp.D2D1 shader compilation. When used together with EnableLinking, it patches the produced DXBC container to declare minimum precision support via an SFI0 blob (and recomputes the DXBC checksum), which has been observed to make Direct2D effect shader linking engage.
Changes:
- Added DXBC container patching utilities (including checksum recomputation) to inject/modify the
SFI0shader feature info blob. - Wired the new experimental flag through both runtime compilation and source-generator-linked compilation paths.
- Added tests covering the +20 byte delta behavior and round-tripping through generated descriptors.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/ComputeSharp.D2D1.Tests/D2D1ShaderCompilerTests.cs | Adds unit tests for the new compile option and a DXBC well-formedness helper used by tests. |
| tests/ComputeSharp.D2D1.Tests/D2D1PixelShaderTests.cs | Adds an end-to-end generator path test ensuring the compile option round-trips and affects bytecode size as expected. |
| src/ComputeSharp.D2D1/Shaders/Translation/Dxbc.cs | Introduces DXBC inspection/patching logic to append/update the SFI0 blob for minimum precision support. |
| src/ComputeSharp.D2D1/Shaders/Translation/Dxbc.Checksum.cs | Implements the DXBC checksum recomputation algorithm required after patching containers. |
| src/ComputeSharp.D2D1/Shaders/Translation/D3DCompiler.cs | Applies the optional DXBC patching before embedding export blobs for linking. |
| src/ComputeSharp.D2D1/Shaders/Interop/D2D1ShaderCompiler.cs | Wires the new option through the public shader compiler API path. |
| src/ComputeSharp.D2D1/Attributes/Enums/D2D1CompileOptions.cs | Adds the experimental DeclareMinimumPrecisionSupport enum flag and documentation. |
| src/ComputeSharp.D2D1.SourceGenerators/ComputeSharp.D2D1.SourceGenerators.csproj | Links the new DXBC helper source files into the source generator project build. |
Comments suppressed due to low confidence (1)
tests/ComputeSharp.D2D1.Tests/D2D1ShaderCompilerTests.cs:360
- Same as above:
bytecodeWithRetentionis a misleading name (this is the minimum precision support option being toggled). Consider renaming for clarity.
ReadOnlyMemory<byte> bytecodeWithRetention = D2D1ShaderCompiler.Compile(
InvertEffectSource.AsSpan(),
"PSMain".AsSpan(),
D2D1ShaderProfile.PixelShader40Level93,
(D2D1CompileOptions.Default & ~D2D1CompileOptions.EnableLinking) | D2D1CompileOptions.DeclareMinimumPrecisionSupport);
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| namespace ComputeSharp.D2D1.Shaders.Translation; | ||
|
|
||
| /// <inheritdoc/> | ||
| partial class Dxbc |
Comment on lines
+400
to
+411
| uint blobCount = BinaryPrimitives.ReadUInt32LittleEndian(bytecode.Slice(28)); | ||
|
|
||
| for (int i = 0; i < blobCount; i++) | ||
| { | ||
| uint blobOffset = BinaryPrimitives.ReadUInt32LittleEndian(bytecode.Slice(32 + (i * 4))); | ||
| uint blobSize = BinaryPrimitives.ReadUInt32LittleEndian(bytecode.Slice((int)blobOffset + 4)); | ||
|
|
||
| if (blobOffset + 8 + blobSize > bytecode.Length) | ||
| { | ||
| return false; | ||
| } | ||
| } |
Comment on lines
+332
to
+345
| ReadOnlyMemory<byte> bytecodeWithRetention = D2D1ShaderCompiler.Compile( | ||
| InvertEffectSource.AsSpan(), | ||
| "PSMain".AsSpan(), | ||
| D2D1ShaderProfile.PixelShader40Level93, | ||
| D2D1CompileOptions.Default | D2D1CompileOptions.DeclareMinimumPrecisionSupport); | ||
|
|
||
| // The only difference is the appended shader feature info blob: one entry in the table of | ||
| // blob offsets (4 bytes), the header of the blob (8 bytes), and its payload (8 bytes). | ||
| Assert.AreEqual(bytecode.Length + 20, bytecodeWithRetention.Length); | ||
|
|
||
| // Compiling succeeds only if D3DSetBlobPart accepted the patched container, and the resulting | ||
| // bytecode is only usable if the checksum was recomputed over the patched contents. | ||
| Assert.IsTrue(IsWellFormedDxbcContainer(bytecodeWithRetention.Span)); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a new experimental
D2D1CompileOptionsflag that declares minimum precision support in the compiled shader bytecode.EnableLinkingalready compiles an export function and embeds it in the bytecode, which is what Direct2D effect shader linking needs. In practice though, Direct2D will generally still not link effects created from that bytecode, so every effect in a chain costs its own rendering pass and its own intermediate surface.Additionally declaring minimum precision support in the bytecode has been observed to make linking engage. This PR exposes that as an opt-in flag, so apps that can benefit from it are able to enable it and measure it, while the default compilation path stays exactly as it is today.
What the flag does
When
DeclareMinimumPrecisionSupportis set (andEnableLinkingis also set), the compiled bytecode gets a shader feature info blob (SFI0) declaringD3D_SHADER_FEATURE_MINIMUM_PRECISION.The compiled shader instructions are left completely untouched, so no computation is lowered to a reduced precision. Only the container metadata changes. The resulting bytecode is 20 bytes larger: 4 bytes for the new entry in the table of blob offsets, 8 bytes for the blob header, and 8 bytes for the payload.
The flag is a no-op without
EnableLinking, since there is no export function to link in that case. This is covered by a test asserting byte-identical output.Implementation
The new
Dxbctype appends (or updates, if one is already present) theSFI0blob in a DXBC container, and then recomputes the container checksum.Recomputing the checksum is required rather than optional:
D3DSetBlobPart, which is used right after to embed the export function, validates the checksum of its input and fails withE_FAILif the container was modified after being compiled. The checksum is the MD5 algorithm from RFC 1321 with a modified handling of the final block, as published in INF-0004 - Validator Hashing.Because
D3DSetBlobPartperforms that validation, it doubles as a strong correctness check: the tests only pass if the checksum implementation is byte-for-byte correct.API surface
The flag is not part of
DefaultorOptimizeForSize, so nothing changes for existing code.Alternatives that were tried and rejected
Two simpler approaches were measured first, and neither works:
min16floatin HLSL. FXC optimizes the annotation away, producing byte-identical bytecode with noSFI0blob at all. Verified onps_4_0_level_9_3,ps_4_0andps_5_0. Anything inert enough to be safe gets eliminated, and anything that survives does so precisely because it changes the numerics, so there is no usable middle ground.D3DCOMPILE_PARTIAL_PRECISION(/Gpp). Rejected outright by FXC belowps_5_0: "error X3534: partial precision is not supported for target ps_4_0_level_9_3. Min-precision types may offer similar functionality."Patching the container directly is therefore the only reliable option, which is what this PR does.
Caveats
This relies on behavior that is neither documented nor guaranteed, and that may stop working at any time. That is why the option is marked with
[Experimental]and is opt-in rather than being folded intoDefault.Direct2D may also still run the bytecode through a minimum precision conversion depending on the target being rendered to, so shaders using this option should be validated to still produce correct results, and the option should only be kept if it measurably improves performance for a given workload.
Testing
D2D1ShaderCompilerTests.CompileInvertEffectWithDeclareMinimumPrecisionSupport— asserts the exact 20 byte size delta and that the result is a well formed DXBC container with a consistent size and set of blob offsets.D2D1ShaderCompilerTests.CompileInvertEffectWithDeclareMinimumPrecisionSupportAndNoLinking— asserts the option is ignored withoutEnableLinking(byte-identical output).D2D1PixelShaderTests.LoadBytecode_DeclareMinimumPrecisionSupportIsAppliedCorrectly— covers the source generator path end to end, including that the option round-trips through the generated descriptor.Full
ComputeSharp.D2D1.Testssuite passes (148 passed, 4 pre-existing skips).Commits
Add 'Dxbc' helpers to patch shader feature flags— container patching and checksum, no behavior change.Add experimental 'DeclareMinimumPrecisionSupport' compile option— the option and its wiring into both compilation paths.Add tests for 'DeclareMinimumPrecisionSupport'— test coverage.