11using System ;
2+ using System . Buffers . Binary ;
23using ComputeSharp . D2D1 . Interop ;
34using Microsoft . VisualStudio . TestTools . UnitTesting ;
45
6+ // These tests are specifically validating the experimental DeclareMinimumPrecisionSupport option
7+ #pragma warning disable CMPSEXP0001
8+
59namespace 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