Переглянути джерело

PIX SM 6.6 resource access tracking (#3594)

This change updates the existing PIX resource-tracking code to handle dynamic resources (and SM6.6's resource binding apparatus in general).
The output UAV is now segmented into three parts: the original formatted buffer at the beginning, for old-style createHandle resources, followed by a block for resource (texture, buffer etc) access, followed by a block for sampler access.
The latter two are divided into 8-byte records. The first dword records writes to a resource/sampler, the second reads.
The writes are encoded bit fields denoting the access performed by the shader.

(cherry picked from commit 880c1359c5bfaf6f8bab3aadc087a6359279f126)
Jeff Noyle 4 роки тому
батько
коміт
7a1c0bddca

+ 428 - 134
lib/DxilPIXPasses/DxilShaderAccessTracking.cpp

@@ -14,6 +14,8 @@
 
 #include "dxc/DXIL/DxilInstructions.h"
 #include "dxc/DXIL/DxilModule.h"
+#include "dxc/DXIL/DxilResourceBinding.h"
+#include "dxc/DXIL/DxilResourceProperties.h"
 #include "dxc/DxilPIXPasses/DxilPIXPasses.h"
 #include "dxc/HLSL/DxilGenerationPass.h"
 #include "dxc/HLSL/DxilSpanAllocator.h"
@@ -48,6 +50,8 @@ enum class ShaderAccessFlags : uint32_t {
   // attached to the UAV was accessed, but not necessarily the UAV resource.
   Counter = 1 << 2,
 
+  Sampler = 1 << 3,
+
   // Descriptor-only read (if any), but not the resource contents (if any).
   // Used for GetDimensions, samplers, and secondary texture for sampler
   // feedback.
@@ -141,10 +145,24 @@ struct SlotRange {
   unsigned numInvariableSlots;
 };
 
+enum class AccessStyle { None, FromRootSig, ResourceFromDescriptorHeap, SamplerFromDescriptorHeap };
 struct DxilResourceAndClass {
-  DxilResourceBase *resource;
+  AccessStyle accessStyle;
+  RegisterType registerType;
+  int RegisterSpace;
+  unsigned RegisterID;
   Value *index;
-  DXIL::ResourceClass resClass;
+  Value *dynamicallyBoundIndex;
+};
+
+enum class ResourceAccessStyle {
+  None,
+  Sampler,
+  UAVRead,
+  UAVWrite,
+  CBVRead,
+  SRVRead,
+  EndOfEnum
 };
 
 //---------------------------------------------------------------------------------------------------------------------------------
@@ -165,11 +183,23 @@ private:
   bool EmitResourceAccess(DxilResourceAndClass &res, Instruction *instruction,
                           OP *HlslOP, LLVMContext &Ctx,
                           ShaderAccessFlags readWrite);
+  DxilResourceAndClass GetResourceFromHandle(Value* resHandle, DxilModule& DM);
 
 private:
+  struct DynamicResourceBinding {
+    int HeapIndex;
+    bool HeapIsSampler; // else resource
+    std::string Name;
+  };
+
+  std::vector<DynamicResourceBinding> m_dynamicResourceBindings;
   bool m_CheckForDynamicIndexing = false;
+  int m_DynamicResourceDataOffset = -1;
+  int m_DynamicSamplerDataOffset = -1;
+  int m_OutputBufferSize = -1;
   std::map<RegisterTypeAndSpace, SlotRange> m_slotAssignments;
   std::map<llvm::Function *, CallInst *> m_FunctionToUAVHandle;
+  std::map<llvm::Function *, std::map<ResourceAccessStyle, Constant *>> m_FunctionToEncodedAccess;
   std::set<RSRegisterIdentifier> m_DynamicallyIndexedBindPoints;
 };
 
@@ -264,6 +294,11 @@ void DxilShaderAccessTracking::applyOptions(PassOptions O) {
 
       rt = ParseRegisterType(config);
     }
+    m_DynamicResourceDataOffset = DeserializeInt(config);
+    ValidateDelimiter(config, ';');
+    m_DynamicSamplerDataOffset = DeserializeInt(config);
+    ValidateDelimiter(config, ';');
+    m_OutputBufferSize = DeserializeInt(config);
   }
 }
 
@@ -302,110 +337,399 @@ void DxilShaderAccessTracking::EmitAccess(LLVMContext &Ctx, OP *HlslOP,
       });
 }
 
+static ResourceAccessStyle AccessStyleFromAccessAndType(
+    AccessStyle accessStyle, 
+    RegisterType registerType,
+    ShaderAccessFlags readWrite)
+{
+    switch (accessStyle)
+    {
+    case AccessStyle::ResourceFromDescriptorHeap:
+        switch (registerType)
+        {
+        case RegisterType::CBV:
+          return ResourceAccessStyle::CBVRead;
+        case RegisterType::SRV:
+          return ResourceAccessStyle::SRVRead;
+        case RegisterType::UAV:
+            return readWrite == ShaderAccessFlags::Read ?
+                ResourceAccessStyle::UAVRead :
+                ResourceAccessStyle::UAVWrite;
+        default:
+          return ResourceAccessStyle::None;
+        }
+    case AccessStyle::SamplerFromDescriptorHeap:
+        return ResourceAccessStyle::Sampler;
+    default:
+        return ResourceAccessStyle::None;
+    }
+}
+
 bool DxilShaderAccessTracking::EmitResourceAccess(DxilResourceAndClass &res,
                                                   Instruction *instruction,
                                                   OP *HlslOP, LLVMContext &Ctx,
                                                   ShaderAccessFlags readWrite) {
-
-  RegisterTypeAndSpace typeAndSpace{RegisterTypeFromResourceClass(res.resClass),
-                                    res.resource->GetSpaceID()};
-
-  auto slot = m_slotAssignments.find(typeAndSpace);
-  // If the assignment isn't found, we assume it's not accessed
-  if (slot != m_slotAssignments.end()) {
-
-    IRBuilder<> Builder(instruction);
-    Value *slotIndex;
-
-    if (isa<ConstantInt>(res.index)) {
-      unsigned index = cast<ConstantInt>(res.index)->getLimitedValue();
-      if (index > slot->second.numSlots) {
-        // out-of-range accesses are written to slot zero:
-        slotIndex = HlslOP->GetU32Const(0);
+  IRBuilder<> Builder(instruction);
+  
+  if (res.accessStyle == AccessStyle::FromRootSig) {
+    RegisterTypeAndSpace typeAndSpace{
+        res.registerType, 
+        static_cast<unsigned>(res.RegisterSpace) // reserved spaces are -ve, but user spaces can only be +ve
+    };
+
+    auto slot = m_slotAssignments.find(typeAndSpace);
+    // If the assignment isn't found, we assume it's not accessed
+    if (slot != m_slotAssignments.end()) {
+
+        Value *slotIndex;
+    
+      if (isa<ConstantInt>(res.index)) {
+        unsigned index = cast<ConstantInt>(res.index)->getLimitedValue();
+        if (index > slot->second.numSlots) {
+          // out-of-range accesses are written to slot zero:
+          slotIndex = HlslOP->GetU32Const(0);
+        } else {
+          slotIndex = HlslOP->GetU32Const((slot->second.startSlot + index) *
+                                          DWORDsPerResource * BytesPerDWORD);
+        }
       } else {
-        slotIndex = HlslOP->GetU32Const((slot->second.startSlot + index) *
-                                        DWORDsPerResource * BytesPerDWORD);
+        RSRegisterIdentifier id{typeAndSpace.Type, typeAndSpace.Space,
+                                res.RegisterID};
+        m_DynamicallyIndexedBindPoints.emplace(std::move(id));
+    
+        // CompareWithSlotLimit will contain 1 if the access is out-of-bounds
+        // (both over- and and under-flow via the unsigned >= with slot count)
+        auto CompareWithSlotLimit = Builder.CreateICmpUGE(
+            res.index, HlslOP->GetU32Const(slot->second.numSlots),
+            "CompareWithSlotLimit");
+        auto CompareWithSlotLimitAsUint = Builder.CreateCast(
+            Instruction::CastOps::ZExt, CompareWithSlotLimit,
+            Type::getInt32Ty(Ctx), "CompareWithSlotLimitAsUint");
+    
+        // IsInBounds will therefore contain 0 if the access is out-of-bounds, and
+        // 1 otherwise.
+        auto IsInBounds = Builder.CreateSub(
+            HlslOP->GetU32Const(1), CompareWithSlotLimitAsUint, "IsInBounds");
+    
+        auto SlotDwordOffset = Builder.CreateAdd(
+            res.index, HlslOP->GetU32Const(slot->second.startSlot),
+            "SlotDwordOffset");
+        auto SlotByteOffset = Builder.CreateMul(
+            SlotDwordOffset,
+            HlslOP->GetU32Const(DWORDsPerResource * BytesPerDWORD),
+            "SlotByteOffset");
+    
+        // This will drive an out-of-bounds access slot down to 0
+        slotIndex = Builder.CreateMul(SlotByteOffset, IsInBounds, "slotIndex");
       }
-    } else {
-      RSRegisterIdentifier id{typeAndSpace.Type, typeAndSpace.Space,
-                              res.resource->GetID()};
-      m_DynamicallyIndexedBindPoints.emplace(std::move(id));
-
-      // CompareWithSlotLimit will contain 1 if the access is out-of-bounds
-      // (both over- and and under-flow via the unsigned >= with slot count)
-      auto CompareWithSlotLimit = Builder.CreateICmpUGE(
-          res.index, HlslOP->GetU32Const(slot->second.numSlots),
-          "CompareWithSlotLimit");
-      auto CompareWithSlotLimitAsUint = Builder.CreateCast(
-          Instruction::CastOps::ZExt, CompareWithSlotLimit,
-          Type::getInt32Ty(Ctx), "CompareWithSlotLimitAsUint");
-
-      // IsInBounds will therefore contain 0 if the access is out-of-bounds, and
-      // 1 otherwise.
-      auto IsInBounds = Builder.CreateSub(
-          HlslOP->GetU32Const(1), CompareWithSlotLimitAsUint, "IsInBounds");
-
-      auto SlotDwordOffset = Builder.CreateAdd(
-          res.index, HlslOP->GetU32Const(slot->second.startSlot),
-          "SlotDwordOffset");
-      auto SlotByteOffset = Builder.CreateMul(
-          SlotDwordOffset,
-          HlslOP->GetU32Const(DWORDsPerResource * BytesPerDWORD),
-          "SlotByteOffset");
-
-      // This will drive an out-of-bounds access slot down to 0
-      slotIndex = Builder.CreateMul(SlotByteOffset, IsInBounds, "slotIndex");
+    
+      EmitAccess(Ctx, HlslOP, Builder, slotIndex, readWrite);
+    
+      return true; // did modify
     }
+  }
+  else if (m_DynamicResourceDataOffset != -1) {
+      if (res.accessStyle == AccessStyle::ResourceFromDescriptorHeap ||
+          res.accessStyle == AccessStyle::SamplerFromDescriptorHeap)
+      {
+          Constant* BaseOfRecordsForType;
+          int LimitForType;
+          if (res.accessStyle == AccessStyle::ResourceFromDescriptorHeap) {
+              LimitForType = m_DynamicSamplerDataOffset - m_DynamicResourceDataOffset;
+              BaseOfRecordsForType =
+                  HlslOP->GetU32Const(m_DynamicResourceDataOffset);
+          } else {
+              LimitForType = m_OutputBufferSize - m_DynamicSamplerDataOffset;
+              BaseOfRecordsForType =
+                HlslOP->GetU32Const(m_DynamicSamplerDataOffset);
+          }
 
-    EmitAccess(Ctx, HlslOP, Builder, slotIndex, readWrite);
+          // Branchless limit: compare offset to size of data reserved for that type,
+          // resulting in a value of 0 or 1.
+          // Extend that 0/1 to an integer, and multiply the offset by that value.
+          // Result: expected offset, or 0 if too large.
+
+          // Add 1 to the index in order to skip over the zeroth entry: that's 
+          // reserved for "out of bounds" writes.
+          auto *IndexToWrite =
+              Builder.CreateAdd(res.dynamicallyBoundIndex, HlslOP->GetU32Const(1));
+
+          // Each record is two dwords:
+          // the first dword is for write access, the second for read.
+          Constant *SizeofRecord =
+              HlslOP->GetU32Const(2 * static_cast<unsigned int>(sizeof(uint32_t)));
+          auto *BaseOfRecord =
+              Builder.CreateMul(IndexToWrite, SizeofRecord);
+          Value* OffsetToWrite;
+          if (readWrite == ShaderAccessFlags::Write) {
+            OffsetToWrite = BaseOfRecord;
+          }
+          else {
+            OffsetToWrite = Builder.CreateAdd(BaseOfRecord, 
+                HlslOP->GetU32Const(static_cast<unsigned int>(sizeof(uint32_t))));
+          }
 
-    return true; // did modify
+          // Generate the 0 (out of bounds) or 1 (in-bounds) multiplier:
+          Constant *BufferLimit = HlslOP->GetU32Const(LimitForType);
+          auto *LimitBoolean =
+              Builder.CreateICmpULT(OffsetToWrite, BufferLimit);
+          
+          auto * LimitIntegerValue = Builder.CreateCast(
+              Instruction::CastOps::ZExt, LimitBoolean,
+              Type::getInt32Ty(Ctx));
+          
+          // Limit the offset to the out-of-bounds record if the above generated 0,
+          // or leave it as-is if the above generated 1:
+          auto *LimitedOffset = Builder.CreateMul(OffsetToWrite, LimitIntegerValue);
+          
+          // Offset into the range of records for this type of access (resource or sampler)
+          auto* Offset = Builder.CreateAdd(BaseOfRecordsForType, LimitedOffset);
+
+          ResourceAccessStyle accessStyle = AccessStyleFromAccessAndType(
+              res.accessStyle, 
+              res.registerType,
+              readWrite);
+
+          Constant* EncodedFlags = m_FunctionToEncodedAccess
+                                .at(Builder.GetInsertBlock()->getParent())
+                                .at(accessStyle);
+
+          Constant *ElementMask = HlslOP->GetI8Const(1);
+          Function *StoreFunc =
+              HlslOP->GetOpFunc(OP::OpCode::BufferStore, Type::getInt32Ty(Ctx));
+          Constant *StoreOpcode =
+              HlslOP->GetU32Const((unsigned)OP::OpCode::BufferStore);
+          UndefValue *UndefArg = UndefValue::get(Type::getInt32Ty(Ctx));
+          (void)Builder.CreateCall(
+              StoreFunc,
+              {
+                  StoreOpcode,                  // i32, ; opcode
+                  m_FunctionToUAVHandle.at(
+                      Builder.GetInsertBlock()
+                          ->getParent()),       // %dx.types.Handle, ; resource handle
+                  Offset,                // i32, ; coordinate c0: byte offset
+                  UndefArg,                     // i32, ; coordinate c1 (unused)
+                  EncodedFlags,                 // i32, ; value v0
+                  UndefArg,                     // i32, ; value v1
+                  UndefArg,                     // i32, ; value v2
+                  UndefArg,                     // i32, ; value v3
+                  ElementMask                   // i8 ; just the first value is used
+              });
+          return true; // did modify
+      }
   }
+
   return false; // did not modify
 }
 
-DxilResourceAndClass GetResourceFromHandle(Value *resHandle, DxilModule &DM) {
+DxilResourceAndClass 
+DxilShaderAccessTracking::GetResourceFromHandle(Value *resHandle,
+                                                DxilModule &DM) {
 
-  DxilResourceAndClass ret{nullptr, nullptr, DXIL::ResourceClass::Invalid};
+  DxilResourceAndClass ret{
+      AccessStyle::None, 
+      RegisterType::Terminator,
+      0,
+      0,
+      nullptr,
+      nullptr};
 
   CallInst *handle = cast<CallInst>(resHandle);
-  DxilInst_CreateHandle createHandle(handle);
 
-  // Dynamic rangeId is not supported - skip and let validation report the
-  // error.
-  if (!isa<ConstantInt>(createHandle.get_rangeId()))
-    return ret;
+  unsigned rangeId = -1;
 
-  unsigned rangeId =
-      cast<ConstantInt>(createHandle.get_rangeId())->getLimitedValue();
+  if (hlsl::OP::IsDxilOpFuncCallInst(handle, hlsl::OP::OpCode::CreateHandle))
+  {
+    DxilInst_CreateHandle createHandle(handle);
 
-  auto resClass =
-      static_cast<DXIL::ResourceClass>(createHandle.get_resourceClass_val());
+    // Dynamic rangeId is not supported - skip and let validation report the
+    // error.
+    if (isa<ConstantInt>(createHandle.get_rangeId())) {
+        rangeId = cast<ConstantInt>(createHandle.get_rangeId())->getLimitedValue();
 
-  switch (resClass) {
-  case DXIL::ResourceClass::SRV:
-    ret.resource = &DM.GetSRV(rangeId);
-    break;
-  case DXIL::ResourceClass::UAV:
-    ret.resource = &DM.GetUAV(rangeId);
-    break;
-  case DXIL::ResourceClass::CBuffer:
-    ret.resource = &DM.GetCBuffer(rangeId);
-    break;
-  case DXIL::ResourceClass::Sampler:
-    ret.resource = &DM.GetSampler(rangeId);
-    break;
-  default:
-    DXASSERT(0, "invalid res class");
-    return ret;
-  }
+        auto resClass = static_cast<DXIL::ResourceClass>(createHandle.get_resourceClass_val());
 
-  ret.index = createHandle.get_index();
-  ret.resClass = resClass;
+        DxilResourceBase* resource = nullptr;
+        RegisterType registerType = RegisterType::Invalid;
+        switch (resClass) {
+        case DXIL::ResourceClass::SRV:
+            resource = &DM.GetSRV(rangeId);
+            registerType = RegisterType::SRV;
+            break;
+        case DXIL::ResourceClass::UAV:
+            resource = &DM.GetUAV(rangeId);
+          registerType = RegisterType::UAV;
+          break;
+        case DXIL::ResourceClass::CBuffer:
+            resource = &DM.GetCBuffer(rangeId);
+            registerType = RegisterType::CBV;
+            break;
+        case DXIL::ResourceClass::Sampler:
+            resource = &DM.GetSampler(rangeId);
+            registerType = RegisterType::Sampler;
+            break;
+        }
+        if (resource != nullptr) {
+            ret.index = createHandle.get_index();
+            ret.registerType = registerType;
+            ret.accessStyle = AccessStyle::FromRootSig;
+            ret.RegisterID = resource->GetID();
+            ret.RegisterSpace = resource->GetSpaceID();
+        }
+    }
+  } else if (hlsl::OP::IsDxilOpFuncCallInst(handle, hlsl::OP::OpCode::AnnotateHandle)) {
+      DxilInst_AnnotateHandle annotateHandle(handle);
+      auto properties = hlsl::resource_helper::loadPropsFromAnnotateHandle(
+          annotateHandle, *DM.GetShaderModel());
+
+      auto* handleCreation = cast<CallInst>(annotateHandle.get_res());
+
+      if (hlsl::OP::IsDxilOpFuncCallInst(handleCreation, hlsl::OP::OpCode::CreateHandleFromBinding)) {
+          DxilInst_CreateHandleFromBinding createHandleFromBinding(handleCreation);
+          Constant* B = cast<Constant>(createHandleFromBinding.get_bind());
+          auto binding = hlsl::resource_helper::loadBindingFromConstant(*B);
+          ret.accessStyle = AccessStyle::FromRootSig;
+          ret.index = createHandleFromBinding.get_index();
+          ret.registerType = RegisterTypeFromResourceClass(
+              static_cast<hlsl::DXIL::ResourceClass>(binding.resourceClass));
+          ret.RegisterSpace = binding.spaceID;
+      } else if (hlsl::OP::IsDxilOpFuncCallInst(handleCreation, hlsl::OP::OpCode::CreateHandleFromHeap)) {
+          DxilInst_CreateHandleFromHeap createHandleFromHeap(handleCreation);
+          ret.accessStyle = createHandleFromHeap.get_samplerHeap_val()
+              ? AccessStyle::SamplerFromDescriptorHeap : AccessStyle::ResourceFromDescriptorHeap;
+          ret.dynamicallyBoundIndex = createHandleFromHeap.get_index();
+
+          ret.registerType = RegisterTypeFromResourceClass(properties.getResourceClass());
+
+          DynamicResourceBinding drb{};
+          drb.HeapIsSampler = createHandleFromHeap.get_samplerHeap_val();
+          drb.HeapIndex = -1;
+          drb.Name = "ShaderNameTodo";
+          if (auto * constInt = dyn_cast<ConstantInt>(createHandleFromHeap.get_index()))
+          {
+              drb.HeapIndex = constInt->getLimitedValue();
+          }
+          m_dynamicResourceBindings.emplace_back(std::move(drb));
+
+          return ret;
+      } else {
+          DXASSERT_NOMSG(false);
+      }
+  }
 
   return ret;
 }
 
+static bool IsDynamicResourceShaderModel(DxilModule& DM) {
+  return DM.GetShaderModel()->IsSMAtLeast(6, 6);
+}
+
+// Set up a UAV with structure of a single int
+static llvm::CallInst* CreateUAV(DxilModule & DM, IRBuilder<> & Builder, unsigned int UAVResourceHandle, unsigned int bind, const char * name)
+{
+    LLVMContext& Ctx = DM.GetModule()->getContext();
+
+    SmallVector<llvm::Type*, 1> Elements{ Type::getInt32Ty(Ctx) };
+    llvm::StructType* UAVStructTy =
+        llvm::StructType::create(Elements, "class.RWStructuredBuffer");
+    std::unique_ptr<DxilResource> pUAV =
+        llvm::make_unique<DxilResource>();
+    pUAV->SetGlobalName((std::string("PIX_CountUAVName")+ std::to_string(UAVResourceHandle)).c_str());
+    pUAV->SetGlobalSymbol(UndefValue::get(UAVStructTy->getPointerTo()));
+    pUAV->SetID(UAVResourceHandle);
+    pUAV->SetRW(true); //sets UAV class
+    pUAV->SetSpaceID((
+        unsigned int)-2); // This is the reserved-for-tools register space
+    pUAV->SetSampleCount(1);
+    pUAV->SetGloballyCoherent(false);
+    pUAV->SetHasCounter(false);
+    pUAV->SetCompType(CompType::getI32());
+    pUAV->SetLowerBound(0);
+    pUAV->SetRangeSize(1);
+    pUAV->SetKind(DXIL::ResourceKind::RawBuffer);
+
+    auto pAnnotation =
+        DM.GetTypeSystem().GetStructAnnotation(UAVStructTy);
+    if (pAnnotation == nullptr) {
+
+        pAnnotation = DM.GetTypeSystem().AddStructAnnotation(UAVStructTy);
+        pAnnotation->GetFieldAnnotation(0).SetCBufferOffset(0);
+        pAnnotation->GetFieldAnnotation(0).SetCompType(
+            hlsl::DXIL::ComponentType::I32);
+        pAnnotation->GetFieldAnnotation(0).SetFieldName("count");
+    }
+
+    OP *HlslOP = DM.GetOP();
+
+    // Create handle for the newly-added UAV
+    if (IsDynamicResourceShaderModel(DM)) {
+      Function *CreateHandleFromBindingOpFunc =
+          HlslOP->GetOpFunc(DXIL::OpCode::CreateHandleFromBinding, Type::getVoidTy(Ctx));
+      Constant *CreateHandleFromBindingOpcodeArg =
+          HlslOP->GetU32Const((unsigned)DXIL::OpCode::CreateHandleFromBinding);
+      DxilResourceBinding binding =
+          resource_helper::loadBindingFromResourceBase(pUAV.get());
+      Value *bindingV = resource_helper::getAsConstant(
+          binding, HlslOP->GetResourceBindingType(), *DM.GetShaderModel());
+
+      Value *registerIndex = HlslOP->GetU32Const(UAVResourceHandle);
+
+      Value *isUniformRes = HlslOP->GetI1Const(0);
+
+      Value *createHandleFromBindingArgs[] = { CreateHandleFromBindingOpcodeArg, bindingV, registerIndex,
+                                              isUniformRes};
+
+      auto * handle = Builder.CreateCall(
+          CreateHandleFromBindingOpFunc,
+          createHandleFromBindingArgs,
+          name);
+
+      Function *annotHandleFn = HlslOP->GetOpFunc(DXIL::OpCode::AnnotateHandle, Type::getVoidTy(Ctx));
+      Value *annotHandleArg = HlslOP->GetI32Const((unsigned)DXIL::OpCode::AnnotateHandle);
+      DxilResourceProperties RP = resource_helper::loadPropsFromResourceBase(pUAV.get());
+      Type *resPropertyTy = HlslOP->GetResourcePropertiesType();
+      Value *propertiesV = resource_helper::getAsConstant(RP, resPropertyTy, *DM.GetShaderModel());
+
+      unsigned int ID = DM.AddUAV(std::move(pUAV));
+      DXASSERT_LOCALVAR_NOMSG(ID, (unsigned)ID == UAVResourceHandle);
+
+      return Builder.CreateCall(annotHandleFn, {annotHandleArg, handle, propertiesV});
+    } else {
+      unsigned int ID = DM.AddUAV(std::move(pUAV));
+      DXASSERT_NOMSG((unsigned)ID == UAVResourceHandle);
+
+      Function* CreateHandleOpFunc = HlslOP->GetOpFunc(
+          DXIL::OpCode::CreateHandle, Type::getVoidTy(Ctx));
+      Constant* CreateHandleOpcodeArg =
+          HlslOP->GetU32Const((unsigned)DXIL::OpCode::CreateHandle);
+      Constant* UAVArg = HlslOP->GetI8Const(
+          static_cast<std::underlying_type<DxilResourceBase::Class>::type>(
+              DXIL::ResourceClass::UAV));
+      Constant* MetaDataArg =
+          HlslOP->GetU32Const(ID); // position of the metadata record in the
+                                   // corresponding metadata list
+      Constant* IndexArg = HlslOP->GetU32Const(0); //
+      Constant* FalseArg =
+          HlslOP->GetI1Const(0); // non-uniform resource index: false
+      return Builder.CreateCall(
+          CreateHandleOpFunc,
+          { CreateHandleOpcodeArg, UAVArg, MetaDataArg, IndexArg, FalseArg },
+          name);
+    }
+}
+
+static uint32_t EncodeShaderModel(DXIL::ShaderKind kind)
+{
+    DXASSERT_NOMSG(static_cast<int>(DXIL::ShaderKind::Invalid) <= 16);
+    return static_cast<uint32_t>(kind) << 28;
+}
+
+static uint32_t EncodeAccess(ResourceAccessStyle access) {
+    uint32_t encoded = static_cast<uint32_t>(access);
+    DXASSERT_NOMSG(encoded < 8);
+    return encoded << 24;
+}
+
 bool DxilShaderAccessTracking::runOnModule(Module &M) {
   // This pass adds instrumentation for shader access to resources
 
@@ -455,58 +779,19 @@ bool DxilShaderAccessTracking::runOnModule(Module &M) {
           unsigned int UAVResourceHandle =
               static_cast<unsigned int>(DM.GetUAVs().size());
 
-          // Set up a UAV with structure of a single int
-          SmallVector<llvm::Type *, 1> Elements{Type::getInt32Ty(Ctx)};
-          llvm::StructType *UAVStructTy =
-              llvm::StructType::create(Elements, "class.RWStructuredBuffer");
-          std::unique_ptr<DxilResource> pUAV =
-              llvm::make_unique<DxilResource>();
-          pUAV->SetGlobalName("PIX_CountUAVName");
-          pUAV->SetGlobalSymbol(UndefValue::get(UAVStructTy->getPointerTo()));
-          pUAV->SetID(UAVResourceHandle);
-          pUAV->SetSpaceID((
-              unsigned int)-2); // This is the reserved-for-tools register space
-          pUAV->SetSampleCount(1);
-          pUAV->SetGloballyCoherent(false);
-          pUAV->SetHasCounter(false);
-          pUAV->SetCompType(CompType::getI32());
-          pUAV->SetLowerBound(0);
-          pUAV->SetRangeSize(1);
-          pUAV->SetKind(DXIL::ResourceKind::RawBuffer);
-
-          auto pAnnotation =
-              DM.GetTypeSystem().GetStructAnnotation(UAVStructTy);
-          if (pAnnotation == nullptr) {
-
-            pAnnotation = DM.GetTypeSystem().AddStructAnnotation(UAVStructTy);
-            pAnnotation->GetFieldAnnotation(0).SetCBufferOffset(0);
-            pAnnotation->GetFieldAnnotation(0).SetCompType(
-                hlsl::DXIL::ComponentType::I32);
-            pAnnotation->GetFieldAnnotation(0).SetFieldName("count");
+          m_FunctionToUAVHandle[&F] = CreateUAV(DM, Builder, UAVResourceHandle, 0, "PIX_CountUAV_Handle");
+          auto const* shaderModel = DM.GetShaderModel();
+          auto shaderKind = shaderModel->GetKind();
+          OP *HlslOP = DM.GetOP();
+          for (int accessStyle = 1;
+              accessStyle < static_cast<int>(ResourceAccessStyle::EndOfEnum);
+              ++accessStyle)
+          {
+              ResourceAccessStyle style = static_cast<ResourceAccessStyle>(accessStyle);
+              m_FunctionToEncodedAccess[&F][style] =
+                  HlslOP->GetU32Const(EncodeShaderModel(shaderKind) |
+                      EncodeAccess(style));
           }
-
-          ID = DM.AddUAV(std::move(pUAV));
-
-          assert((unsigned)ID == UAVResourceHandle);
-
-          // Create handle for the newly-added UAV
-          Function *CreateHandleOpFunc = HlslOP->GetOpFunc(
-              DXIL::OpCode::CreateHandle, Type::getVoidTy(Ctx));
-          Constant *CreateHandleOpcodeArg =
-              HlslOP->GetU32Const((unsigned)DXIL::OpCode::CreateHandle);
-          Constant *UAVArg = HlslOP->GetI8Const(
-              static_cast<std::underlying_type<DxilResourceBase::Class>::type>(
-                  DXIL::ResourceClass::UAV));
-          Constant *MetaDataArg =
-              HlslOP->GetU32Const(ID); // position of the metadata record in the
-                                       // corresponding metadata list
-          Constant *IndexArg = HlslOP->GetU32Const(0); //
-          Constant *FalseArg =
-              HlslOP->GetI1Const(0); // non-uniform resource index: false
-          m_FunctionToUAVHandle[&F] = Builder.CreateCall(
-              CreateHandleOpFunc,
-              {CreateHandleOpcodeArg, UAVArg, MetaDataArg, IndexArg, FalseArg},
-              "PIX_CountUAV_Handle");
         }
       }
       DM.ReEmitDxilResources();
@@ -576,9 +861,11 @@ bool DxilShaderAccessTracking::runOnModule(Module &M) {
 
         for (unsigned iParam : handleParams) {
           auto res = GetResourceFromHandle(Call->getArgOperand(iParam), DM);
+          if (res.accessStyle == AccessStyle::None) {
+            continue;
+          }
           // Don't instrument the accesses to the UAV that we just added
-          if (res.resClass == DXIL::ResourceClass::UAV &&
-              res.resource->GetSpaceID() == (unsigned)-2) {
+          if (res.RegisterSpace  == -2) {
             break;
           }
           if (EmitResourceAccess(res, Call, HlslOP, Ctx, readWrite)) {
@@ -598,6 +885,13 @@ bool DxilShaderAccessTracking::runOnModule(Module &M) {
             << ';';
       }
       FOS << ".";
+
+      // todo: this will reflect dynamic resource names when the metadata exists
+      FOS << "DynamicallyBoundResources=";
+      for (auto const &drb : m_dynamicResourceBindings) {
+        FOS << (drb.HeapIsSampler ? 'S' : 'R') << drb.HeapIndex << ';';
+      }
+      FOS << ".";
     }
   }
 

+ 1 - 1
tools/clang/test/HLSLFileCheck/pix/AccessTracking.hlsl

@@ -1,4 +1,4 @@
-// RUN: %dxc -ECSMain -Tcs_6_0 %s | %opt -S -hlsl-dxil-pix-shader-access-instrumentation,config=S0:1:1i1;U0:2:10i0;.. | %FileCheck %s
+// RUN: %dxc -ECSMain -Tcs_6_0 %s | %opt -S -hlsl-dxil-pix-shader-access-instrumentation,config=S0:1:1i1;U0:2:10i0;.0;0;0. | %FileCheck %s
 
 // Check we added the UAV:
 // CHECK:  %PIX_CountUAV_Handle = call %dx.types.Handle @dx.op.createHandle(i32 57, i8 1, i32 1, i32 0, i1 false)

+ 1 - 1
tools/clang/test/HLSLFileCheck/pix/AccessTrackingForSamplerFeedback.hlsl

@@ -1,4 +1,4 @@
-// RUN: %dxc -Emain -Tcs_6_5 %s | %opt -S -hlsl-dxil-pix-shader-access-instrumentation,config=M0:0:1i0;S0:1:1i0;U0:2:10i0;.. | %FileCheck %s
+// RUN: %dxc -Emain -Tcs_6_5 %s | %opt -S -hlsl-dxil-pix-shader-access-instrumentation,config=M0:0:1i0;S0:1:1i0;U0:2:10i0;.0;0;0. | %FileCheck %s
 
 // Check we added the UAV:
 // CHECK:  %PIX_CountUAV_Handle = call %dx.types.Handle @dx.op.createHandle(i32 57, i8 1, i32 1, i32 0, i1 false)

+ 26 - 0
tools/clang/test/HLSLFileCheck/pix/DynamicResourceAccessTracking.hlsl

@@ -0,0 +1,26 @@
+// RUN: %dxc -EMain -Tcs_6_6 %s | %opt -S -hlsl-dxil-pix-shader-access-instrumentation,config=.256;512;1024. | %FileCheck %s
+
+static RWByteAddressBuffer DynamicBuffer = ResourceDescriptorHeap[1];
+[numthreads(1, 1, 1)]
+void Main()
+{
+    uint val = DynamicBuffer.Load(0u);
+    DynamicBuffer.Store(0u, val);
+}
+
+// check it's 6.6:
+// CHECK: call %dx.types.Handle @dx.op.createHandleFromBinding
+
+// Check we wrote to the PIX UAV:
+// CHECK: call void @dx.op.bufferStore.i32
+
+// Offset for buffer Load should be 256 + 8 (skip out-of-bounds record) + 8 (it's the 1th resource) + 4 (offset to the "read" field) = 276
+// The large integer is encoded flags for the ResourceAccessStyle (an enumerated type in lib\DxilPIXPasses\DxilShaderAccessTracking.cpp) for this access
+// CHECK:i32 276, i32 undef, i32 1375731712
+// CHECK:rawBufferLoad
+
+// Offset for buffer Store should be 256 + 8 (skip out-of-bounds record) + 8 (it's the 1th resource) + 0 (offset to the "write" field) = 272
+// The large integer is encoded flags for the ResourceAccessStyle (an enumerated type in lib\DxilPIXPasses\DxilShaderAccessTracking.cpp) for this access
+// CHECK:i32 272, i32 undef, i32 1392508928
+// CHECK:rawBufferStore
+

+ 19 - 0
tools/clang/test/HLSLFileCheck/pix/DynamicResourceOutOfBounds.hlsl

@@ -0,0 +1,19 @@
+// RUN: %dxc -EMain -Tcs_6_6 %s | %opt -S -hlsl-dxil-pix-shader-access-instrumentation,config=.256;272;1024. | %FileCheck %s
+
+static RWByteAddressBuffer DynamicBuffer = ResourceDescriptorHeap[1];
+[numthreads(1, 1, 1)]
+void Main()
+{
+    uint val = DynamicBuffer.Load(0u);
+    DynamicBuffer.Store(0u, val);
+}
+
+// check it's 6.6:
+// CHECK: call %dx.types.Handle @dx.op.createHandleFromBinding
+
+// The start of resource records has been passed in as 256. The limit of resource records is 272. 272-256 = 16.
+// 8 bytes per record means we have one record for out-of-bounds (that comes first), and one record for resource index 0.
+// The above HLSL references resource descriptor 1, so is out-of-bounds. Offset for out-of-bounds should thus be 256:
+// The large integer is encoded flags for the ResourceAccessStyle (an enumerated type in lib\DxilPIXPasses\DxilShaderAccessTracking.cpp) for this access
+// CHECK:i32 256, i32 undef, i32 1375731712
+// CHECK:rawBufferLoad

+ 26 - 0
tools/clang/test/HLSLFileCheck/pix/DynamicSamplerAccessTracking.hlsl

@@ -0,0 +1,26 @@
+// RUN: %dxc -EMain -Tps_6_6 %s | %opt -S -hlsl-dxil-pix-shader-access-instrumentation,config=S0:1:1i1;.256;512;1024. | %FileCheck %s
+
+static sampler sampler0 = SamplerDescriptorHeap[0];
+static sampler sampler3 = SamplerDescriptorHeap[3];
+Texture2D tx : register(t2);
+
+float4 Main() : SV_Target
+{
+    float4 a = tx.Sample(sampler0, float2(0,0));
+    float4 b = tx.Sample(sampler3, float2(0,0));
+    return a + b;
+}
+
+// check it's 6.6:
+// CHECK: call %dx.types.Handle @dx.op.createHandleFromBinding
+
+// The large integers are encoded flags for the ResourceAccessStyle (an enumerated type in lib\DxilPIXPasses\DxilShaderAccessTracking.cpp) for this access
+
+// Check we wrote sampler data to the PIX UAV. We told the pass to output starting at offset 512.
+// Add 8 to skip the "out of bounds" record. Add 4 to point to the "read" field within the next entry = 524
+// CHECK: call void @dx.op.bufferStore.i32(
+// CHECK:i32 524, i32 undef, i32 16777216
+
+// twice: 512 + 8 + 8*3+4 = 548
+// CHECK: call void @dx.op.bufferStore.i32(
+// CHECK:i32 548, i32 undef, i32 16777216

+ 24 - 0
tools/clang/test/HLSLFileCheck/pix/DynamicSamplerOutOfBounds.hlsl

@@ -0,0 +1,24 @@
+// RUN: %dxc -EMain -Tps_6_6 %s | %opt -S -hlsl-dxil-pix-shader-access-instrumentation,config=S0:1:1i1;.256;512;520. | %FileCheck %s
+
+static sampler sampler0 = SamplerDescriptorHeap[0];
+static sampler sampler3 = SamplerDescriptorHeap[3];
+Texture2D tx : register(t2);
+
+float4 Main() : SV_Target
+{
+    float4 a = tx.Sample(sampler0, float2(0,0));
+    float4 b = tx.Sample(sampler3, float2(0,0));
+    return a + b;
+}
+
+// check it's 6.6:
+// CHECK: call %dx.types.Handle @dx.op.createHandleFromBinding
+
+// The large integers are encoded flags for the ResourceAccessStyle (an enumerated type in lib\DxilPIXPasses\DxilShaderAccessTracking.cpp) for this access
+
+// The start of sampler records has been passed in as 512. The limit of the whole buffer is 520, leaving just one eight-byte record for the out-of-bounds record.
+// There are therefore no expected in-bounds references to samplers, so any such reference should go to the out-of-bounds offset at 512:
+
+// Out of bounds sampler access should be at offset 512
+// CHECK: call void @dx.op.bufferStore.i32(
+// CHECK:i32 512, i32 undef, i32 16777216

+ 1 - 1
tools/clang/test/HLSLFileCheck/pix/TraceRayInline.hlsl

@@ -1,4 +1,4 @@
-// RUN: %dxc -T vs_6_5 -E main %s | %opt -S -hlsl-dxil-pix-shader-access-instrumentation,config=S0:1:1i1;U0:2:10i0;.. | FileCheck %s
+// RUN: %dxc -T vs_6_5 -E main %s | %opt -S -hlsl-dxil-pix-shader-access-instrumentation,config=S0:1:1i1;U0:2:10i0;.0;0;0. | FileCheck %s
 
 // CHECK: call void @dx.op.rayQuery_TraceRayInline
 // CHECK: call void @dx.op.bufferStore.i32(i32 69, %dx.types.Handle

+ 1 - 1
tools/clang/test/HLSLFileCheck/pix/rawBufferStore.hlsl

@@ -1,4 +1,4 @@
-// RUN: %dxc -enable-16bit-types -Emain -Tcs_6_3 %s | %opt -S -hlsl-dxil-pix-shader-access-instrumentation,config=U0:2:10i0;.. | %FileCheck %s
+// RUN: %dxc -enable-16bit-types -Emain -Tcs_6_3 %s | %opt -S -hlsl-dxil-pix-shader-access-instrumentation,config=U0:2:10i0;.0;0;0. | %FileCheck %s
 
 // Check that the expected PIX UAV read-tracking is emitted (the atomicBinOp "|= 1") followed by the expected raw read:
 

+ 17 - 2
tools/clang/unittests/HLSL/CompilerTest.cpp

@@ -202,6 +202,7 @@ public:
   TEST_METHOD(BatchPasses)
   TEST_METHOD(BatchShaderTargets)
   TEST_METHOD(BatchValidation)
+  TEST_METHOD(BatchPIX)
 
   TEST_METHOD(SubobjectCodeGenErrors)
   BEGIN_TEST_METHOD(ManualFileCheckTest)
@@ -468,10 +469,20 @@ public:
         dumpStr = dumpPath + (wRelPath.m_psz + suitePath.size());
       }
 
-      WEX::Logging::Log::StartGroup(wRelPath);
+      class ScopedLogGroup
+      {
+        LPWSTR m_path;
+
+      public:
+          ScopedLogGroup(LPWSTR path)
+          : m_path(path)
+          { WEX::Logging::Log::StartGroup(m_path); }
+          ~ScopedLogGroup() { WEX::Logging::Log::EndGroup(m_path); }
+      };
+
+      ScopedLogGroup cleanup(wRelPath);
       CodeGenTestCheck(wRelPath, /*implicitDir*/ false,
         dumpStr.empty() ? nullptr : dumpStr.c_str());
-      WEX::Logging::Log::EndGroup(wRelPath);
 
       numTestsRun++;
     }
@@ -3138,6 +3149,10 @@ TEST_F(CompilerTest, BatchValidation) {
   CodeGenTestCheckBatchDir(L"validation");
 }
 
+TEST_F(CompilerTest, BatchPIX) {
+  CodeGenTestCheckBatchDir(L"PIX");
+}
+
 TEST_F(CompilerTest, BatchSamples) {
   CodeGenTestCheckBatchDir(L"samples");
 }