Browse Source

Add tests and fix bugs with container and root signature validation (#5)

* Fix ValidateDxilBitcode PSV generation for Root Signature validation.
- also fix validation for empty patch constant signature.
* Test container and root signature validation.
* Fix DXASSERT when PC in/out patch sig mismatch HS.
Tex Riddell 8 years ago
parent
commit
627763e563

+ 7 - 1
lib/HLSL/DxilGenerationPass.cpp

@@ -474,7 +474,6 @@ static DxilSignatureElement *FindArgInSignature(Argument &arg, llvm::StringRef s
       return &SE;
     }
   }
-  DXASSERT(0, "must find a match");
   return nullptr;
 }
 
@@ -650,6 +649,13 @@ void DxilGenerationPass::ProcessArgument(Function *func,
         sigKind != DXIL::SignatureKind::PatchConstant) {
       pSE = FindArgInSignature(arg, paramAnnotation.GetSemanticString(),
                                interpMode, sigPoint->GetKind(), *pSig);
+      if (!pSE) {
+        m_pHLModule->GetModule()->getContext().emitError(
+          Twine("Signature element ") + semanticStr +
+          Twine(", referred to by patch constant function, is not found in corresponding hull shader ") + 
+          (sigKind == DXIL::SignatureKind::Input ? "input." : "output."));
+        return;
+      }
       m_patchConstantInputsSigMap[arg.getArgNo()] = pSE;
     } else {
       std::unique_ptr<DxilSignatureElement> SE = pSig->CreateElement();

+ 17 - 8
lib/HLSL/DxilValidation.cpp

@@ -4049,6 +4049,10 @@ static void VerifyBlobPartMatches(_In_ ValidationContext &ValCtx,
     return;
   }
 
+  if (Size == 0) {
+    return;
+  }
+
   CComPtr<IMalloc> pMalloc;
   IFT(CoGetMalloc(1, &pMalloc));
   CComPtr<AbstractMemoryStream> pOutputStream;
@@ -4163,15 +4167,14 @@ HRESULT ValidateDxilContainerParts(llvm::Module *pModule,
     return DXC_E_CONTAINER_INVALID;
   }
 
-  std::string diagStr;
-  raw_string_ostream DiagStream(diagStr);
-  DiagnosticPrinterRawOStream DiagPrinter(DiagStream);
-
   DxilModule *pDxilModule = DxilModule::TryGetDxilModule(pModule);
   if (!pDxilModule) {
     return DXC_E_IR_VERIFICATION_FAILED;
   }
 
+  std::string diagStr;
+  raw_string_ostream DiagStream(diagStr);
+  DiagnosticPrinterRawOStream DiagPrinter(DiagStream);
   ValidationContext ValCtx(*pModule, pDebugModule, *pDxilModule, DiagPrinter);
 
   DXIL::ShaderKind ShaderKind = pDxilModule->GetShaderModel()->GetKind();
@@ -4241,9 +4244,10 @@ HRESULT ValidateDxilContainerParts(llvm::Module *pModule,
   if (FourCCFound.find(DFCC_OutputSignature) == FourCCFound.end()) {
     VerifySignatureMatches(ValCtx, DXIL::SignatureKind::Output, nullptr, 0);
   }
-  if (bTess && FourCCFound.find(DFCC_PatchConstantSignature) == FourCCFound.end())
+  if (bTess && FourCCFound.find(DFCC_PatchConstantSignature) == FourCCFound.end() &&
+      pDxilModule->GetPatchConstantSignature().GetElements().size())
   {
-    VerifySignatureMatches(ValCtx, DXIL::SignatureKind::PatchConstant, nullptr, 0);
+    ValCtx.EmitFormatError(ValidationRule::ContainerPartMissing, "Program Patch Constant Signature");
   }
   if (FourCCFound.find(DFCC_FeatureInfo) == FourCCFound.end()) {
     // Could be optional, but RS1 runtime doesn't handle this case properly.
@@ -4356,7 +4360,12 @@ HRESULT ValidateDxilBitcode(
   if (!dxilModule.GetRootSignature().IsEmpty()) {
     unique_ptr<DxilPartWriter> pWriter(NewPSVWriter(dxilModule));
     DXASSERT_NOMSG(pWriter->size());
-    unique_ptr<unsigned char[]> pPSVData(new unsigned char[pWriter->size()]);
+    CComPtr<IMalloc> pMalloc;
+    IFT(CoGetMalloc(1, &pMalloc));
+    CComPtr<AbstractMemoryStream> pOutputStream;
+    IFT(CreateMemoryStream(pMalloc, &pOutputStream));
+    pOutputStream->Reserve(pWriter->size());
+    pWriter->write(pOutputStream);
     const DxilVersionedRootSignatureDesc* pDesc = dxilModule.GetRootSignature().GetDesc();
     RootSignatureHandle RS;
     try {
@@ -4369,7 +4378,7 @@ HRESULT ValidateDxilBitcode(
       }
       IFTBOOL(VerifyRootSignatureWithShaderPSV(pDesc,
                                                dxilModule.GetShaderModel()->GetKind(),
-                                               pPSVData.get(), pWriter->size(),
+                                               pOutputStream->GetPtr(), pWriter->size(),
                                                DiagStream), DXC_E_INCORRECT_ROOT_SIGNATURE);
     } catch (...) {
       return DXC_E_INCORRECT_ROOT_SIGNATURE;

+ 2 - 0
tools/clang/unittests/HLSL/CMakeLists.txt

@@ -10,6 +10,8 @@ set( LLVM_LINK_COMPONENTS
   dxcsupport
   hlsl
   option
+  bitwriter
+  analysis
   )
 
 add_clang_library(clang-hlsl-tests SHARED

+ 45 - 0
tools/clang/unittests/HLSL/CompilerTest.cpp

@@ -573,6 +573,7 @@ public:
   TEST_METHOD(CodeGenResourceInTBV2)
   TEST_METHOD(CodeGenCBufferStructArray)
   TEST_METHOD(PreprocessWhenValidThenOK)
+  TEST_METHOD(WhenSigMismatchPCFunctionThenFail)
 
   // Dx11 Sample
   TEST_METHOD(CodeGenDX11Sample_2Dquadshaders_Blurx_Ps)
@@ -3691,3 +3692,47 @@ TEST_F(CompilerTest, PreprocessWhenValidThenOK) {
     "\n"
     "int BAR;\n", text.c_str());
 }
+
+TEST_F(CompilerTest, WhenSigMismatchPCFunctionThenFail) {
+  CComPtr<IDxcCompiler> pCompiler;
+  CComPtr<IDxcOperationResult> pResult;
+  CComPtr<IDxcBlobEncoding> pSource;
+
+  VERIFY_SUCCEEDED(CreateCompiler(&pCompiler));
+  CreateBlobFromText(
+    "struct PSSceneIn \n\
+    { \n\
+      float4 pos  : SV_Position; \n\
+      float2 tex  : TEXCOORD0; \n\
+      float3 norm : NORMAL; \n\
+    }; \n"
+    "struct HSPerPatchData {  \n\
+      float edges[ 3 ] : SV_TessFactor; \n\
+      float inside : SV_InsideTessFactor; \n\
+      float foo : FOO; \n\
+    }; \n"
+    "HSPerPatchData HSPerPatchFunc( InputPatch< PSSceneIn, 3 > points, \n\
+      OutputPatch<PSSceneIn, 3> outpoints) { \n\
+      HSPerPatchData d = (HSPerPatchData)0; \n\
+      d.edges[ 0 ] = points[0].tex.x + outpoints[0].tex.x; \n\
+      d.edges[ 1 ] = 1; \n\
+      d.edges[ 2 ] = 1; \n\
+      d.inside = 1; \n\
+      return d; \n\
+    } \n"
+    "[domain(\"tri\")] \n\
+    [partitioning(\"fractional_odd\")] \n\
+    [outputtopology(\"triangle_cw\")] \n\
+    [patchconstantfunc(\"HSPerPatchFunc\")] \n\
+    [outputcontrolpoints(3)] \n"
+    "void main(const uint id : SV_OutputControlPointID, \n\
+               const InputPatch< PSSceneIn, 3 > points ) { \n\
+    } \n"
+    , &pSource);
+
+  VERIFY_SUCCEEDED(pCompiler->Compile(pSource, L"source.hlsl", L"main",
+    L"hs_6_0", nullptr, 0, nullptr, 0, nullptr, &pResult));
+  std::string failLog(VerifyOperationFailed(pResult));
+  VERIFY_ARE_NOT_EQUAL(string::npos, failLog.find(
+    "Signature element SV_Position, referred to by patch constant function, is not found in corresponding hull shader output."));
+}

+ 409 - 42
tools/clang/unittests/HLSL/ValidationTest.cpp

@@ -16,14 +16,17 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Regex.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "dxc/HLSL/DxilContainer.h"
 
 #include <atlbase.h>
+#include "dxc/Support/FileIOHelper.h"
 
 #include "WexTestClass.h"
 #include "DxcTestUtils.h"
 #include "HlslTestUtils.h"
 
 using namespace std;
+using namespace hlsl;
 
 void CheckOperationSucceeded(IDxcOperationResult *pResult, IDxcBlob **ppBlob) {
   HRESULT status;
@@ -189,6 +192,17 @@ public:
   TEST_METHOD(WhenMetaFlagsUsageDeclThenOK);
   TEST_METHOD(WhenMetaFlagsUsageThenFail);
 
+  TEST_METHOD(WhenRootSigMismatchThenFail);
+  TEST_METHOD(WhenRootSigCompatThenSucceed);
+  TEST_METHOD(WhenProgramOutSigMissingThenFail);
+  TEST_METHOD(WhenProgramOutSigUnexpectedThenFail);
+  TEST_METHOD(WhenProgramSigMismatchThenFail);
+  TEST_METHOD(WhenProgramInSigMissingThenFail);
+  TEST_METHOD(WhenProgramSigMismatchThenFail2);
+  TEST_METHOD(WhenProgramPCSigMissingThenFail);
+  TEST_METHOD(WhenPSVMismatchThenFail);
+  TEST_METHOD(WhenFeatureInfoMismatchThenFail);
+
   dxc::DxcDllSupport m_dllSupport;
 
   void TestCheck(LPCWSTR name) {
@@ -201,12 +215,13 @@ public:
     }
   }
 
-  bool CheckOperationResultMsg(IDxcOperationResult *pResult,
-                               const char *pErrorMsg, bool maySucceedAnyway,
+  bool CheckOperationResultMsgs(IDxcOperationResult *pResult,
+                               llvm::ArrayRef<LPCSTR> pErrorMsgs, bool maySucceedAnyway,
                                bool bRegex) {
     HRESULT status;
     VERIFY_SUCCEEDED(pResult->GetStatus(&status));
-    if (pErrorMsg == nullptr) {
+    if (pErrorMsgs.empty() || 
+        (pErrorMsgs.size() == 1 && !pErrorMsgs[0])) {
       VERIFY_SUCCEEDED(status);
     }
     else {
@@ -216,27 +231,29 @@ public:
       //VERIFY_FAILED(status);
       CComPtr<IDxcBlobEncoding> text;
       VERIFY_SUCCEEDED(pResult->GetErrorBuffer(&text));
-      if (bRegex) {
-        llvm::Regex RE(pErrorMsg);
-        std::string reErrors;
-        VERIFY_IS_TRUE(RE.isValid(reErrors));
-        VERIFY_IS_TRUE(RE.match(llvm::StringRef((const char *)text->GetBufferPointer(), text->GetBufferSize())));
-      } else {
-        const char *pStart = (const char *)text->GetBufferPointer();
-        const char *pEnd = pStart + text->GetBufferSize();
-        const char *pMatch = std::search(pStart, pEnd, pErrorMsg, pErrorMsg + strlen(pErrorMsg));
-        if (pEnd == pMatch) {
-          WEX::Logging::Log::Comment(WEX::Common::String().Format(
-              L"Unable to find '%S' in text:\r\n%.*S", pErrorMsg, (pEnd - pStart),
-              pStart));
+      for (auto pErrorMsg : pErrorMsgs) {
+        if (bRegex) {
+          llvm::Regex RE(pErrorMsg);
+          std::string reErrors;
+          VERIFY_IS_TRUE(RE.isValid(reErrors));
+          VERIFY_IS_TRUE(RE.match(llvm::StringRef((const char *)text->GetBufferPointer(), text->GetBufferSize())));
+        } else {
+          const char *pStart = (const char *)text->GetBufferPointer();
+          const char *pEnd = pStart + text->GetBufferSize();
+          const char *pMatch = std::search(pStart, pEnd, pErrorMsg, pErrorMsg + strlen(pErrorMsg));
+          if (pEnd == pMatch) {
+            WEX::Logging::Log::Comment(WEX::Common::String().Format(
+                L"Unable to find '%S' in text:\r\n%.*S", pErrorMsg, (pEnd - pStart),
+                pStart));
+          }
+          VERIFY_ARE_NOT_EQUAL(pEnd, pMatch);
         }
-        VERIFY_ARE_NOT_EQUAL(pEnd, pMatch);
       }
     }
     return true;
   }
 
-  void CheckValidationMsg(IDxcBlob *pBlob, const char *pErrorMsg, bool bRegex = false) {
+  void CheckValidationMsgs(IDxcBlob *pBlob, llvm::ArrayRef<LPCSTR> pErrorMsgs, bool bRegex = false) {
     CComPtr<IDxcValidator> pValidator;
     CComPtr<IDxcOperationResult> pResult;
 
@@ -247,10 +264,10 @@ public:
     VERIFY_SUCCEEDED(m_dllSupport.CreateInstance(CLSID_DxcValidator, &pValidator));
     VERIFY_SUCCEEDED(pValidator->Validate(pBlob, DxcValidatorFlags_Default, &pResult));
 
-    CheckOperationResultMsg(pResult, pErrorMsg, false, bRegex);
+    CheckOperationResultMsgs(pResult, pErrorMsgs, false, bRegex);
   }
 
-  void CheckValidationMsg(const char *pBlob, size_t blobSize, const char *pErrorMsg, bool bRegex = false) {
+  void CheckValidationMsgs(const char *pBlob, size_t blobSize, llvm::ArrayRef<LPCSTR> pErrorMsgs, bool bRegex = false) {
     if (!m_dllSupport.IsEnabled()) {
       VERIFY_SUCCEEDED(m_dllSupport.Initialize());
     }
@@ -258,7 +275,7 @@ public:
     CComPtr<IDxcBlobEncoding> pBlobEncoding; // Encoding doesn't actually matter, it's binary.
     VERIFY_SUCCEEDED(m_dllSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary));
     VERIFY_SUCCEEDED(pLibrary->CreateBlobWithEncodingFromPinned((LPBYTE)pBlob, blobSize, CP_UTF8, &pBlobEncoding));
-    CheckValidationMsg(pBlobEncoding, pErrorMsg, bRegex);
+    CheckValidationMsgs(pBlobEncoding, pErrorMsgs, bRegex);
   }
 
   void CompileSource(IDxcBlobEncoding *pSource, LPCSTR pShaderModel,
@@ -277,6 +294,9 @@ public:
     VERIFY_SUCCEEDED(pCompiler->Compile(pSource, L"hlsl.hlsl", L"main",
                                         shWide, nullptr, 0, nullptr, 0, nullptr,
                                         &pResult));
+    HRESULT hr;
+    VERIFY_SUCCEEDED(pResult->GetStatus(&hr));
+    VERIFY_SUCCEEDED(hr);
     VERIFY_SUCCEEDED(pResult->GetResult(pResultBlob));
   }
 
@@ -314,13 +334,11 @@ public:
         m_dllSupport.CreateInstance(CLSID_DxcAssembler, &pAssembler));
     VERIFY_SUCCEEDED(pAssembler->AssembleToContainer(pText, &pAssembleResult));
 
-    for (auto pErrorMsg : pErrorMsgs) {
-      if (!CheckOperationResultMsg(pAssembleResult, pErrorMsg, true, bRegex)) {
-        // Assembly succeeded, try validation.
-        CComPtr<IDxcBlob> pBlob;
-        VERIFY_SUCCEEDED(pAssembleResult->GetResult(&pBlob));
-        CheckValidationMsg(pBlob, pErrorMsg, bRegex);
-      }
+    if (!CheckOperationResultMsgs(pAssembleResult, pErrorMsgs, true, bRegex)) {
+      // Assembly succeeded, try validation.
+      CComPtr<IDxcBlob> pBlob;
+      VERIFY_SUCCEEDED(pAssembleResult->GetResult(&pBlob));
+      CheckValidationMsgs(pBlob, pErrorMsgs, bRegex);
     }
   }
 
@@ -385,21 +403,81 @@ public:
     VERIFY_SUCCEEDED(
         m_dllSupport.CreateInstance(CLSID_DxcAssembler, &pAssembler));
     VERIFY_SUCCEEDED(pAssembler->AssembleToContainer(pText, &pAssembleResult));
-    for (auto pErrorMsg : pErrorMsgs) {
-      if (!CheckOperationResultMsg(pAssembleResult, pErrorMsg, true, bRegex)) {
-        // Assembly succeeded, try validation.
-        CComPtr<IDxcBlob> pBlob;
-        VERIFY_SUCCEEDED(pAssembleResult->GetResult(&pBlob));
-        CheckValidationMsg(pBlob, pErrorMsg, bRegex);
+    if (!CheckOperationResultMsgs(pAssembleResult, pErrorMsgs, true, bRegex)) {
+      // Assembly succeeded, try validation.
+      CComPtr<IDxcBlob> pBlob;
+      VERIFY_SUCCEEDED(pAssembleResult->GetResult(&pBlob));
+      CheckValidationMsgs(pBlob, pErrorMsgs, bRegex);
+    }
+  }
+
+  // compile one or two sources, validate module from 1 with container parts from 2, check messages
+  void ReplaceContainerPartsCheckMsgs(LPCSTR pSource1, LPCSTR pSource2, LPCSTR pShaderModel,
+                                     llvm::ArrayRef<DxilFourCC> PartsToReplace,
+                                     llvm::ArrayRef<LPCSTR> pErrorMsgs) {
+    CComPtr<IDxcBlob> pProgram1, pProgram2;
+    CompileSource(pSource1, pShaderModel, &pProgram1);
+    VERIFY_IS_NOT_NULL(pProgram1);
+    if (pSource2) {
+      CompileSource(pSource2, pShaderModel, &pProgram2);
+      VERIFY_IS_NOT_NULL(pProgram2);
+    } else {
+      pProgram2 = pProgram1;
+    }
+
+    // construct container with moudle from pProgram1 with other parts from pProgram2:
+    const DxilContainerHeader *pHeader1 = IsDxilContainerLike(pProgram1->GetBufferPointer(), pProgram1->GetBufferSize());
+    VERIFY_IS_NOT_NULL(pHeader1);
+    const DxilContainerHeader *pHeader2 = IsDxilContainerLike(pProgram2->GetBufferPointer(), pProgram2->GetBufferSize());
+    VERIFY_IS_NOT_NULL(pHeader2);
+
+    DxilContainerWriter *pContainerWriter = NewDxilContainerWriter();
+
+    // Add desired parts from first container
+    for (auto pPart : pHeader1) {
+      for (auto dfcc : PartsToReplace) {
+        if (dfcc == pPart->PartFourCC) {
+          pPart = nullptr;
+          break;
+        }
+      }
+      if (!pPart)
+        continue;
+      pContainerWriter->AddPart(pPart->PartFourCC, pPart->PartSize, [=](AbstractMemoryStream *pStream) {
+        ULONG cbWritten = 0;
+        pStream->Write(GetDxilPartData(pPart), pPart->PartSize, &cbWritten);
+      });
+    }
+
+    // Add desired parts from second container
+    for (auto pPart : pHeader2) {
+      for (auto dfcc : PartsToReplace) {
+        if (dfcc == pPart->PartFourCC) {
+          pContainerWriter->AddPart(pPart->PartFourCC, pPart->PartSize, [=](AbstractMemoryStream *pStream) {
+            ULONG cbWritten = 0;
+            pStream->Write(GetDxilPartData(pPart), pPart->PartSize, &cbWritten);
+          });
+          break;
+        }
       }
     }
+
+    // Write the container
+    CComPtr<IMalloc> pMalloc;
+    VERIFY_SUCCEEDED(CoGetMalloc(1, &pMalloc));
+    CComPtr<AbstractMemoryStream> pOutputStream;
+    VERIFY_SUCCEEDED(CreateMemoryStream(pMalloc, &pOutputStream));
+    pOutputStream->Reserve(pContainerWriter->size());
+    pContainerWriter->write(pOutputStream);
+
+    CheckValidationMsgs((const char *)pOutputStream->GetPtr(), pOutputStream->GetPtrSize(), pErrorMsgs, /*bRegex*/false);
   }
 };
 
 TEST_F(ValidationTest, WhenCorrectThenOK) {
   CComPtr<IDxcBlob> pProgram;
   CompileSource("float4 main() : SV_Target { return 1; }", "ps_6_0", &pProgram);
-  CheckValidationMsg(pProgram, nullptr);
+  CheckValidationMsgs(pProgram, nullptr);
 }
 
 // Lots of these going on below for simplicity in setting up payloads.
@@ -414,7 +492,7 @@ TEST_F(ValidationTest, WhenMisalignedThenFail) {
   const char blob[] = {
     'B', 'C',
   };
-  CheckValidationMsg(blob, _countof(blob), "Invalid bitcode size");
+  CheckValidationMsgs(blob, _countof(blob), "Invalid bitcode size");
 }
 
 TEST_F(ValidationTest, WhenEmptyFileThenFail) {
@@ -422,7 +500,7 @@ TEST_F(ValidationTest, WhenEmptyFileThenFail) {
   const char blob[] = {
     'B', 'C', 0xc0, 0xde
   };
-  CheckValidationMsg(blob, _countof(blob), "Malformed IR file");
+  CheckValidationMsgs(blob, _countof(blob), "Malformed IR file");
 }
 
 TEST_F(ValidationTest, WhenIncorrectMagicThenFail) {
@@ -430,14 +508,14 @@ TEST_F(ValidationTest, WhenIncorrectMagicThenFail) {
   const char blob[] = {
     'B', 'C', 0xc0, 0xdd
   };
-  CheckValidationMsg(blob, _countof(blob), "Invalid bitcode signature");
+  CheckValidationMsgs(blob, _countof(blob), "Invalid bitcode signature");
 }
 
 TEST_F(ValidationTest, WhenIncorrectTargetTripleThenFail) {
   const char blob[] = {
     'B', 'C', 0xc0, 0xde
   };
-  CheckValidationMsg(blob, _countof(blob), "Malformed IR file");
+  CheckValidationMsgs(blob, _countof(blob), "Malformed IR file");
 }
 
 TEST_F(ValidationTest, WhenMultipleModulesThenFail) {
@@ -450,7 +528,7 @@ TEST_F(ValidationTest, WhenMultipleModulesThenFail) {
     // Trigger the case we're looking for now
     0x21, 0x0c, 0x00, 0x00, // Enter sub-block, BlockID = 8, Code Size=3, padding x2
   };
-  CheckValidationMsg(blob, _countof(blob), "Unused bits in buffer");
+  CheckValidationMsgs(blob, _countof(blob), "Unused bits in buffer");
 }
 
 TEST_F(ValidationTest, WhenUnexpectedEOFThenFail) {
@@ -460,7 +538,7 @@ TEST_F(ValidationTest, WhenUnexpectedEOFThenFail) {
     0x21, 0x0c, 0x00, 0x00, // Enter sub-block, BlockID = 8, Code Size=3, padding x2
     0x00, 0x00, 0x00, 0x00, // NumWords = 0
   };
-  CheckValidationMsg(blob, _countof(blob), "Invalid record");
+  CheckValidationMsgs(blob, _countof(blob), "Invalid record");
 }
 
 TEST_F(ValidationTest, WhenUnknownBlocksThenFail) {
@@ -468,7 +546,7 @@ TEST_F(ValidationTest, WhenUnknownBlocksThenFail) {
     'B', 'C', 0xc0, 0xde,   // Signature
     0x31, 0x00, 0x00, 0x00  // Enter sub-block, BlockID != 8
   };
-  CheckValidationMsg(blob, _countof(blob), "Unrecognized block found");
+  CheckValidationMsgs(blob, _countof(blob), "Unrecognized block found");
 }
 
 TEST_F(ValidationTest, WhenInstrDisallowedThenFail) {
@@ -2020,6 +2098,295 @@ float4 main(float4 f4 : Input, out float d0 : SV_Depth, out float d1 : SV_Target
     /*bRegex*/true);
 }
 
+// TODO: Write more root signature tests
+
+TEST_F(ValidationTest, WhenRootSigMismatchThenFail) {
+  ReplaceContainerPartsCheckMsgs(
+    "float c; [RootSignature ( \"RootConstants(b0, num32BitConstants = 1)\" )] float4 main() : semantic { return c; }",
+    "[RootSignature ( \"\" )] float4 main() : semantic { return 0; }",
+    "vs_6_0",
+    {DFCC_RootSignature},
+    {
+      "Root Signature in DXIL container is not compatible with shader.",
+      "Validation failed."
+    }
+  );
+}
+
+TEST_F(ValidationTest, WhenRootSigCompatThenSucceed) {
+  ReplaceContainerPartsCheckMsgs(
+    "[RootSignature ( \"\" )] float4 main() : semantic { return 0; }",
+    "float c; [RootSignature ( \"RootConstants(b0, num32BitConstants = 1)\" )] float4 main() : semantic { return c; }",
+    "vs_6_0",
+    {DFCC_RootSignature},
+    {}
+  );
+}
+
+
+#define VERTEX_STRUCT1 \
+    "struct PSSceneIn \n\
+    { \n\
+      float4 pos  : SV_Position; \n\
+      float2 tex  : TEXCOORD0; \n\
+      float3 norm : NORMAL; \n\
+    }; \n"
+#define VERTEX_STRUCT2 \
+    "struct PSSceneIn \n\
+    { \n\
+      float4 pos  : SV_Position; \n\
+      float2 tex  : TEXCOORD0; \n\
+    }; \n"
+#define PC_STRUCT1 "struct HSPerPatchData {  \n\
+      float edges[ 3 ] : SV_TessFactor; \n\
+      float inside : SV_InsideTessFactor; \n\
+      float foo : FOO; \n\
+    }; \n"
+#define PC_STRUCT2 "struct HSPerPatchData {  \n\
+      float edges[ 3 ] : SV_TessFactor; \n\
+      float inside : SV_InsideTessFactor; \n\
+    }; \n"
+#define PC_FUNC "HSPerPatchData HSPerPatchFunc( InputPatch< PSSceneIn, 3 > points, \n\
+      OutputPatch<PSSceneIn, 3> outpoints) { \n\
+      HSPerPatchData d = (HSPerPatchData)0; \n\
+      d.edges[ 0 ] = points[0].tex.x + outpoints[0].tex.x; \n\
+      d.edges[ 1 ] = 1; \n\
+      d.edges[ 2 ] = 1; \n\
+      d.inside = 1; \n\
+      return d; \n\
+    } \n"
+#define PC_FUNC_NOOUT "HSPerPatchData HSPerPatchFunc( InputPatch< PSSceneIn, 3 > points ) { \n\
+      HSPerPatchData d = (HSPerPatchData)0; \n\
+      d.edges[ 0 ] = points[0].tex.x; \n\
+      d.edges[ 1 ] = 1; \n\
+      d.edges[ 2 ] = 1; \n\
+      d.inside = 1; \n\
+      return d; \n\
+    } \n"
+#define PC_FUNC_NOIN "HSPerPatchData HSPerPatchFunc( OutputPatch<PSSceneIn, 3> outpoints) { \n\
+      HSPerPatchData d = (HSPerPatchData)0; \n\
+      d.edges[ 0 ] = outpoints[0].tex.x; \n\
+      d.edges[ 1 ] = 1; \n\
+      d.edges[ 2 ] = 1; \n\
+      d.inside = 1; \n\
+      return d; \n\
+    } \n"
+#define HS_ATTR "[domain(\"tri\")] \n\
+    [partitioning(\"fractional_odd\")] \n\
+    [outputtopology(\"triangle_cw\")] \n\
+    [patchconstantfunc(\"HSPerPatchFunc\")] \n\
+    [outputcontrolpoints(3)] \n"
+#define HS_FUNC \
+    "PSSceneIn main(const uint id : SV_OutputControlPointID, \n\
+                    const InputPatch< PSSceneIn, 3 > points ) { \n\
+      return points[ id ]; \n\
+    } \n"
+#define HS_FUNC_NOOUT \
+    "void main(const uint id : SV_OutputControlPointID, \n\
+               const InputPatch< PSSceneIn, 3 > points ) { \n\
+    } \n"
+#define HS_FUNC_NOIN \
+    "PSSceneIn main( const uint id : SV_OutputControlPointID ) { \n\
+      return (PSSceneIn)0; \n\
+    } \n"
+#define DS_FUNC \
+    "[domain(\"tri\")] PSSceneIn main(const float3 bary : SV_DomainLocation, \n\
+                                      const OutputPatch<PSSceneIn, 3> patch, \n\
+                                      const HSPerPatchData perPatchData) { \n\
+      PSSceneIn v = patch[0]; \n\
+      v.pos = patch[0].pos * bary.x; \n\
+      v.pos += patch[1].pos * bary.y; \n\
+      v.pos += patch[2].pos * bary.z; \n\
+      return v; \n\
+    } \n"
+#define DS_FUNC_NOPC \
+    "[domain(\"tri\")] PSSceneIn main(const float3 bary : SV_DomainLocation, \n\
+                                      const OutputPatch<PSSceneIn, 3> patch) { \n\
+      PSSceneIn v = patch[0]; \n\
+      v.pos = patch[0].pos * bary.x; \n\
+      v.pos += patch[1].pos * bary.y; \n\
+      v.pos += patch[2].pos * bary.z; \n\
+      return v; \n\
+    } \n"
+
+TEST_F(ValidationTest, WhenProgramOutSigMissingThenFail) {
+  ReplaceContainerPartsCheckMsgs(
+    VERTEX_STRUCT1
+    PC_STRUCT1
+    PC_FUNC
+    HS_ATTR
+    HS_FUNC,
+
+    VERTEX_STRUCT1
+    PC_STRUCT1
+    PC_FUNC_NOOUT
+    HS_ATTR
+    HS_FUNC_NOOUT,
+
+    "hs_6_0",
+    {DFCC_InputSignature, DFCC_OutputSignature, DFCC_PatchConstantSignature},
+    {
+      "Container part 'Program Output Signature' does not match expected for module.",
+      "Validation failed."
+    }
+  );
+}
+
+TEST_F(ValidationTest, WhenProgramOutSigUnexpectedThenFail) {
+  ReplaceContainerPartsCheckMsgs(
+    VERTEX_STRUCT1
+    PC_STRUCT1
+    PC_FUNC_NOOUT
+    HS_ATTR
+    HS_FUNC_NOOUT,
+
+    VERTEX_STRUCT1
+    PC_STRUCT1
+    PC_FUNC
+    HS_ATTR
+    HS_FUNC,
+
+    "hs_6_0",
+    {DFCC_InputSignature, DFCC_OutputSignature, DFCC_PatchConstantSignature},
+    {
+      "Container part 'Program Output Signature' does not match expected for module.",
+      "Validation failed."
+    }
+  );
+}
+
+TEST_F(ValidationTest, WhenProgramSigMismatchThenFail) {
+  ReplaceContainerPartsCheckMsgs(
+    VERTEX_STRUCT1
+    PC_STRUCT1
+    PC_FUNC
+    HS_ATTR
+    HS_FUNC,
+
+    VERTEX_STRUCT2
+    PC_STRUCT2
+    PC_FUNC
+    HS_ATTR
+    HS_FUNC,
+
+    "hs_6_0",
+    {DFCC_InputSignature, DFCC_OutputSignature, DFCC_PatchConstantSignature},
+    {
+      "Container part 'Program Input Signature' does not match expected for module.",
+      "Container part 'Program Output Signature' does not match expected for module.",
+      "Container part 'Program Patch Constant Signature' does not match expected for module.",
+      "Validation failed."
+    }
+  );
+}
+
+TEST_F(ValidationTest, WhenProgramInSigMissingThenFail) {
+  ReplaceContainerPartsCheckMsgs(
+    VERTEX_STRUCT1
+    PC_STRUCT1
+    PC_FUNC
+    HS_ATTR
+    HS_FUNC,
+
+    // Compiling the HS_FUNC_NOIN produces the following error
+    //error: validation errors
+    //HS input control point count must be [1..32].  0 specified
+    VERTEX_STRUCT1
+    PC_STRUCT1
+    PC_FUNC_NOIN
+    HS_ATTR
+    HS_FUNC_NOIN,
+    "hs_6_0",
+    {DFCC_InputSignature, DFCC_OutputSignature, DFCC_PatchConstantSignature},
+    {
+      "Container part 'Program Input Signature' does not match expected for module.",
+      "Validation failed."
+    }
+  );
+}
+
+TEST_F(ValidationTest, WhenProgramSigMismatchThenFail2) {
+  ReplaceContainerPartsCheckMsgs(
+    VERTEX_STRUCT1
+    PC_STRUCT1
+    DS_FUNC,
+
+    VERTEX_STRUCT2
+    PC_STRUCT2
+    DS_FUNC,
+
+    "ds_6_0",
+    {DFCC_InputSignature, DFCC_OutputSignature, DFCC_PatchConstantSignature},
+    {
+      "Container part 'Program Input Signature' does not match expected for module.",
+      "Container part 'Program Output Signature' does not match expected for module.",
+      "Container part 'Program Patch Constant Signature' does not match expected for module.",
+      "Validation failed."
+    }
+  );
+}
+
+TEST_F(ValidationTest, WhenProgramPCSigMissingThenFail) {
+  ReplaceContainerPartsCheckMsgs(
+    VERTEX_STRUCT1
+    PC_STRUCT1
+    DS_FUNC,
+
+    VERTEX_STRUCT2
+    PC_STRUCT2
+    DS_FUNC_NOPC,
+
+    "ds_6_0",
+    {DFCC_InputSignature, DFCC_OutputSignature, DFCC_PatchConstantSignature},
+    {
+      "Container part 'Program Input Signature' does not match expected for module.",
+      "Container part 'Program Output Signature' does not match expected for module.",
+      "Missing part 'Program Patch Constant Signature' required by module.",
+      "Validation failed."
+    }
+  );
+}
+
+#undef VERTEX_STRUCT1
+#undef VERTEX_STRUCT2
+#undef PC_STRUCT1
+#undef PC_STRUCT2
+#undef PC_FUNC
+#undef PC_FUNC_NOOUT
+#undef PC_FUNC_NOIN
+#undef HS_ATTR
+#undef HS_FUNC
+#undef HS_FUNC_NOOUT
+#undef HS_FUNC_NOIN
+#undef DS_FUNC
+#undef DS_FUNC_NOPC
+
+TEST_F(ValidationTest, WhenPSVMismatchThenFail) {
+  ReplaceContainerPartsCheckMsgs(
+    "float c; [RootSignature ( \"RootConstants(b0, num32BitConstants = 1)\" )] float4 main() : semantic { return c; }",
+    "[RootSignature ( \"\" )] float4 main() : semantic { return 0; }",
+    "vs_6_0",
+    {DFCC_PipelineStateValidation},
+    {
+      "Container part 'Pipeline State Validation' does not match expected for module.",
+      "Validation failed."
+    }
+  );
+}
+
+TEST_F(ValidationTest, WhenFeatureInfoMismatchThenFail) {
+  ReplaceContainerPartsCheckMsgs(
+    "float4 main(uint2 foo : FOO) : SV_Target { return asdouble(foo.x, foo.y) * 2.0; }",
+    "float4 main() : SV_Target { return 0; }",
+    "ps_6_0",
+    {DFCC_FeatureInfo},
+    {
+      "Container part 'Feature Info' does not match expected for module.",
+      "Validation failed."
+    }
+  );
+}
+