Skip to content

Commit c058cb6

Browse files
committed
Add tests for 'DeclareMinimumPrecisionSupport'
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: a62113de-a3b2-457b-abce-70ee666c9e6f
1 parent 9531055 commit c058cb6

2 files changed

Lines changed: 129 additions & 0 deletions

File tree

tests/ComputeSharp.D2D1.Tests/D2D1PixelShaderTests.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99

1010
#pragma warning disable IDE0044, IDE0059, IDE0161
1111

12+
// Some tests are specifically validating the experimental DeclareMinimumPrecisionSupport option
13+
#pragma warning disable CMPSEXP0001
14+
1215
[D2DInputCount(0)]
1316
[D2DGeneratedPixelShaderDescriptor]
1417
[AutoConstructor]
@@ -710,6 +713,35 @@ public float4 Execute()
710713
}
711714
}
712715

716+
[TestMethod]
717+
public void LoadBytecode_DeclareMinimumPrecisionSupportIsAppliedCorrectly()
718+
{
719+
ReadOnlyMemory<byte> hlslBytecode1 = D2D1PixelShader.LoadBytecode<ReferenceShaderWithDefaultCompileOptions>(out _, out D2D1CompileOptions compileOptions1);
720+
ReadOnlyMemory<byte> hlslBytecode2 = D2D1PixelShader.LoadBytecode<ReferenceShaderWithDeclareMinimumPrecisionSupport>(out _, out D2D1CompileOptions compileOptions2);
721+
722+
Assert.AreEqual(D2D1CompileOptions.Default, compileOptions1);
723+
Assert.AreEqual(D2D1CompileOptions.Default | D2D1CompileOptions.DeclareMinimumPrecisionSupport, compileOptions2);
724+
725+
// Same check as in D2D1ShaderCompilerTests.CompileInvertEffectWithDeclareMinimumPrecisionSupport
726+
Assert.AreEqual(hlslBytecode1.Length + 20, hlslBytecode2.Length);
727+
}
728+
729+
[D2DInputCount(1)]
730+
[D2DInputSimple(0)]
731+
[D2DShaderProfile(D2D1ShaderProfile.PixelShader50)]
732+
[D2DCompileOptions(D2D1CompileOptions.Default | D2D1CompileOptions.DeclareMinimumPrecisionSupport)]
733+
[D2DGeneratedPixelShaderDescriptor]
734+
public readonly partial struct ReferenceShaderWithDeclareMinimumPrecisionSupport : ID2D1PixelShader
735+
{
736+
public float4 Execute()
737+
{
738+
float4 color = D2D.GetInput(0);
739+
float3 rgb = Hlsl.Saturate(1.0f - color.RGB);
740+
741+
return new(rgb, 1);
742+
}
743+
}
744+
713745
[TestMethod]
714746
public void GetConstantBufferSize_Empty()
715747
{

tests/ComputeSharp.D2D1.Tests/D2D1ShaderCompilerTests.cs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
using System;
2+
using System.Buffers.Binary;
23
using ComputeSharp.D2D1.Interop;
34
using Microsoft.VisualStudio.TestTools.UnitTesting;
45

6+
// These tests are specifically validating the experimental DeclareMinimumPrecisionSupport option
7+
#pragma warning disable CMPSEXP0001
8+
59
namespace ComputeSharp.D2D1.Tests;
610

711
[TestClass]
@@ -315,4 +319,97 @@ public void CompileShaderWithWarning_Suppressed()
315319

316320
Assert.IsTrue(bytecode.Length > 0);
317321
}
322+
323+
[TestMethod]
324+
public void CompileInvertEffectWithDeclareMinimumPrecisionSupport()
325+
{
326+
ReadOnlyMemory<byte> bytecode = D2D1ShaderCompiler.Compile(
327+
InvertEffectSource.AsSpan(),
328+
"PSMain".AsSpan(),
329+
D2D1ShaderProfile.PixelShader40Level93,
330+
D2D1CompileOptions.Default);
331+
332+
ReadOnlyMemory<byte> bytecodeWithRetention = D2D1ShaderCompiler.Compile(
333+
InvertEffectSource.AsSpan(),
334+
"PSMain".AsSpan(),
335+
D2D1ShaderProfile.PixelShader40Level93,
336+
D2D1CompileOptions.Default | D2D1CompileOptions.DeclareMinimumPrecisionSupport);
337+
338+
// The only difference is the appended shader feature info blob: one entry in the table of
339+
// blob offsets (4 bytes), the header of the blob (8 bytes), and its payload (8 bytes).
340+
Assert.AreEqual(bytecode.Length + 20, bytecodeWithRetention.Length);
341+
342+
// Compiling succeeds only if D3DSetBlobPart accepted the patched container, and the resulting
343+
// bytecode is only usable if the checksum was recomputed over the patched contents.
344+
Assert.IsTrue(IsWellFormedDxbcContainer(bytecodeWithRetention.Span));
345+
}
346+
347+
[TestMethod]
348+
public void CompileInvertEffectWithDeclareMinimumPrecisionSupportAndNoLinking()
349+
{
350+
ReadOnlyMemory<byte> bytecode = D2D1ShaderCompiler.Compile(
351+
InvertEffectSource.AsSpan(),
352+
"PSMain".AsSpan(),
353+
D2D1ShaderProfile.PixelShader40Level93,
354+
D2D1CompileOptions.Default & ~D2D1CompileOptions.EnableLinking);
355+
356+
ReadOnlyMemory<byte> bytecodeWithRetention = D2D1ShaderCompiler.Compile(
357+
InvertEffectSource.AsSpan(),
358+
"PSMain".AsSpan(),
359+
D2D1ShaderProfile.PixelShader40Level93,
360+
(D2D1CompileOptions.Default & ~D2D1CompileOptions.EnableLinking) | D2D1CompileOptions.DeclareMinimumPrecisionSupport);
361+
362+
// There is no export function to reach without linking, so the option is ignored
363+
CollectionAssert.AreEqual(bytecode.ToArray(), bytecodeWithRetention.ToArray());
364+
}
365+
366+
/// <summary>
367+
/// The HLSL source for a simple invert effect, shared by tests comparing compilation options.
368+
/// </summary>
369+
private const string InvertEffectSource = """
370+
#define D2D_INPUT_COUNT 1
371+
#define D2D_INPUT0_SIMPLE
372+
373+
#include "d2d1effecthelpers.hlsli"
374+
375+
D2D_PS_ENTRY(PSMain)
376+
{
377+
float4 color = D2DGetInput(0);
378+
float3 rgb = saturate(1.0 - color.rgb);
379+
return float4(rgb, 1);
380+
}
381+
""";
382+
383+
/// <summary>
384+
/// Checks that a buffer is a DXBC container with a consistent size and set of blob offsets.
385+
/// </summary>
386+
/// <param name="bytecode">The DXBC container to inspect.</param>
387+
/// <returns>Whether <paramref name="bytecode"/> is a well formed DXBC container.</returns>
388+
private static bool IsWellFormedDxbcContainer(ReadOnlySpan<byte> bytecode)
389+
{
390+
if (bytecode.Length < 32 || !bytecode.StartsWith("DXBC"u8))
391+
{
392+
return false;
393+
}
394+
395+
if (BinaryPrimitives.ReadUInt32LittleEndian(bytecode.Slice(24)) != (uint)bytecode.Length)
396+
{
397+
return false;
398+
}
399+
400+
uint blobCount = BinaryPrimitives.ReadUInt32LittleEndian(bytecode.Slice(28));
401+
402+
for (int i = 0; i < blobCount; i++)
403+
{
404+
uint blobOffset = BinaryPrimitives.ReadUInt32LittleEndian(bytecode.Slice(32 + (i * 4)));
405+
uint blobSize = BinaryPrimitives.ReadUInt32LittleEndian(bytecode.Slice((int)blobOffset + 4));
406+
407+
if (blobOffset + 8 + blobSize > bytecode.Length)
408+
{
409+
return false;
410+
}
411+
}
412+
413+
return true;
414+
}
318415
}

0 commit comments

Comments
 (0)