[PIX] Add pass to log DXR 1.0 shader invocation info into a UAV (#5301)

This PR adds a new PIX pass that instruments DXR 1.0 shaders
(intersection, any/closest hit and miss). The new pass emits each shader
invocation's [ray system
values](https://microsoft.github.io/DirectX-Specs/d3d/Raytracing.html#system-value-intrinsics)
and DispatchRaysIndex into a UAV that can be read back later. This will
enable new DXR debugging features in PIX.
This commit is contained in:
Austin Kinross 2023-06-16 14:01:18 -07:00 коммит произвёл GitHub
Родитель 5e080a7729
Коммит 6a3e2485de
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
8 изменённых файлов: 302 добавлений и 5 удалений

Просмотреть файл

@ -26,6 +26,7 @@ ModulePass *createDxilForceEarlyZPass();
ModulePass *createDxilDebugInstrumentationPass();
ModulePass *createDxilShaderAccessTrackingPass();
ModulePass *createDxilPIXAddTidToAmplificationShaderPayloadPass();
ModulePass *createDxilPIXDXRInvocationsLogPass();
void initializeDxilAddPixelHitInstrumentationPass(llvm::PassRegistry&);
void initializeDxilDbgValueToDbgDeclarePass(llvm::PassRegistry&);
@ -38,5 +39,6 @@ void initializeDxilForceEarlyZPass(llvm::PassRegistry&);
void initializeDxilDebugInstrumentationPass(llvm::PassRegistry&);
void initializeDxilShaderAccessTrackingPass(llvm::PassRegistry&);
void initializeDxilPIXAddTidToAmplificationShaderPayloadPass(llvm::PassRegistry&);
void initializeDxilPIXDXRInvocationsLogPass(llvm::PassRegistry&);
}

Просмотреть файл

@ -19,6 +19,7 @@ add_llvm_library(LLVMDxilPIXPasses
DxilPIXVirtualRegisters.cpp
PixPassHelpers.cpp
DxilPIXAddTidToAmplificationShaderPayload.cpp
DxilPIXDXRInvocationsLog.cpp
ADDITIONAL_HEADER_DIRS
${LLVM_MAIN_INCLUDE_DIR}/llvm/IR

Просмотреть файл

@ -0,0 +1,234 @@
///////////////////////////////////////////////////////////////////////////////
// //
// DxilPIXDXRInvocationsLog.cpp //
// Copyright (C) Microsoft Corporation. All rights reserved. //
// This file is distributed under the University of Illinois Open Source //
// License. See LICENSE.TXT for details. //
// //
///////////////////////////////////////////////////////////////////////////////
#include "dxc/DXIL/DxilFunctionProps.h"
#include "dxc/DXIL/DxilOperations.h"
#include "dxc/DXIL/DxilUtil.h"
#include "dxc/DXIL/DxilInstructions.h"
#include "dxc/DXIL/DxilModule.h"
#include "dxc/DxilPIXPasses/DxilPIXPasses.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Transforms/Utils/Local.h"
#include "PixPassHelpers.h"
using namespace llvm;
using namespace hlsl;
using namespace PIXPassHelpers;
class DxilPIXDXRInvocationsLog : public ModulePass {
uint64_t m_MaxNumEntriesInLog = 1;
public:
static char ID;
DxilPIXDXRInvocationsLog() : ModulePass(ID) {}
StringRef getPassName() const override {
return "DXIL Logs all non-RayGen DXR 1.0 invocations into a UAV";
}
void applyOptions(PassOptions O) override;
bool runOnModule(Module &M) override;
};
static DXIL::ShaderKind GetShaderKind(DxilModule const &DM, llvm::Function const *entryFunction)
{
DXIL::ShaderKind ShaderKind = DXIL::ShaderKind::Invalid;
if (!DM.HasDxilFunctionProps(entryFunction)) {
auto ShaderModel = DM.GetShaderModel();
ShaderKind = ShaderModel->GetKind();
}
else {
auto const& Props = DM.GetDxilFunctionProps(entryFunction);
ShaderKind = Props.shaderKind;
}
return ShaderKind;
}
void DxilPIXDXRInvocationsLog::applyOptions(PassOptions O) {
GetPassOptionUInt64(O, "maxNumEntriesInLog", &m_MaxNumEntriesInLog, 1); // Use a silly default value. PIX should set a better value here.
}
bool DxilPIXDXRInvocationsLog::runOnModule(Module &M) {
DxilModule &DM = M.GetOrCreateDxilModule();
LLVMContext &Ctx = M.getContext();
OP *HlslOP = DM.GetOP();
for (auto entryFunction : DM.GetExportedFunctions()) {
DXIL::ShaderKind ShaderKind = GetShaderKind(DM, entryFunction);
switch (ShaderKind) {
case DXIL::ShaderKind::Intersection:
case DXIL::ShaderKind::AnyHit:
case DXIL::ShaderKind::ClosestHit:
case DXIL::ShaderKind::Miss:
break;
default:
continue;
}
IRBuilder<> Builder(dxilutil::FirstNonAllocaInsertionPt(entryFunction));
// Add the UAVs that we're going to write to
CallInst* HandleForCountUAV = PIXPassHelpers::CreateUAV(DM, Builder, /* registerID */ 0, "PIX_CountUAV_Handle");
CallInst* HandleForUAV = PIXPassHelpers::CreateUAV(DM, Builder, /* registerID */ 1, "PIX_UAV_Handle");
DM.ReEmitDxilResources();
auto DispatchRaysIndexOpFunc = HlslOP->GetOpFunc(DXIL::OpCode::DispatchRaysIndex, Type::getInt32Ty(Ctx));
auto WorldRayOriginOpFunc = HlslOP->GetOpFunc(DXIL::OpCode::WorldRayOrigin, Type::getFloatTy(Ctx));
auto WorldRayDirectionOpFunc = HlslOP->GetOpFunc(DXIL::OpCode::WorldRayDirection, Type::getFloatTy(Ctx));
auto CurrentRayTFunc = HlslOP->GetOpFunc(DXIL::OpCode::RayTCurrent, Type::getFloatTy(Ctx));
auto MinRayTFunc = HlslOP->GetOpFunc(DXIL::OpCode::RayTMin, Type::getFloatTy(Ctx));
auto RayFlagsFunc = HlslOP->GetOpFunc(DXIL::OpCode::RayFlags, Type::getInt32Ty(Ctx));
auto *DispatchRaysIndexOpcode = HlslOP->GetU32Const((unsigned)DXIL::OpCode::DispatchRaysIndex);
auto *WorldRayOriginOpcode = HlslOP->GetU32Const((unsigned)DXIL::OpCode::WorldRayOrigin);
auto *WorldRayDirectionOpcode = HlslOP->GetU32Const((unsigned)DXIL::OpCode::WorldRayDirection);
auto *CurrentRayTOpcode = HlslOP->GetU32Const((unsigned)DXIL::OpCode::RayTCurrent);
auto *MinRayTOpcode = HlslOP->GetU32Const((unsigned)DXIL::OpCode::RayTMin);
auto *RayFlagsOpcode = HlslOP->GetU32Const((unsigned)DXIL::OpCode::RayFlags);
auto DispatchRaysX = Builder.CreateCall(DispatchRaysIndexOpFunc, { DispatchRaysIndexOpcode, HlslOP->GetI8Const(0) }, "DispatchRaysX");
auto DispatchRaysY = Builder.CreateCall(DispatchRaysIndexOpFunc, { DispatchRaysIndexOpcode, HlslOP->GetI8Const(1) }, "DispatchRaysY");
auto DispatchRaysZ = Builder.CreateCall(DispatchRaysIndexOpFunc, { DispatchRaysIndexOpcode, HlslOP->GetI8Const(2) }, "DispatchRaysZ");
auto WorldRayOriginX = Builder.CreateCall(WorldRayOriginOpFunc, { WorldRayOriginOpcode, HlslOP->GetI8Const(0) }, "WorldRayOriginX");
auto WorldRayOriginY = Builder.CreateCall(WorldRayOriginOpFunc, { WorldRayOriginOpcode, HlslOP->GetI8Const(1) }, "WorldRayOriginY");
auto WorldRayOriginZ = Builder.CreateCall(WorldRayOriginOpFunc, { WorldRayOriginOpcode, HlslOP->GetI8Const(2) }, "WorldRayOriginZ");
auto WorldRayDirectionX = Builder.CreateCall(WorldRayDirectionOpFunc, { WorldRayDirectionOpcode, HlslOP->GetI8Const(0) }, "WorldRayDirectionX");
auto WorldRayDirectionY = Builder.CreateCall(WorldRayDirectionOpFunc, { WorldRayDirectionOpcode, HlslOP->GetI8Const(1) }, "WorldRayDirectionY");
auto WorldRayDirectionZ = Builder.CreateCall(WorldRayDirectionOpFunc, { WorldRayDirectionOpcode, HlslOP->GetI8Const(2) }, "WorldRayDirectionZ");
auto CurrentRayT = Builder.CreateCall(CurrentRayTFunc, { CurrentRayTOpcode }, "CurrentRayT");
auto MinRayT = Builder.CreateCall(MinRayTFunc, { MinRayTOpcode }, "MinRayT");
auto RayFlags = Builder.CreateCall(RayFlagsFunc, { RayFlagsOpcode }, "RayFlags");
Function *AtomicOpFunc = HlslOP->GetOpFunc(OP::OpCode::AtomicBinOp, Type::getInt32Ty(Ctx));
Constant *AtomicBinOpcode = HlslOP->GetU32Const((unsigned)OP::OpCode::AtomicBinOp);
Constant *AtomicAdd = HlslOP->GetU32Const((unsigned)DXIL::AtomicBinOpCode::Add);
Function *UMinOpFunc = HlslOP->GetOpFunc(OP::OpCode::UMin, Type::getInt32Ty(Ctx));
Constant *UMinOpCode = HlslOP->GetU32Const((unsigned)OP::OpCode::UMin);
Function *StoreFuncFloat = HlslOP->GetOpFunc(OP::OpCode::BufferStore, Type::getFloatTy(Ctx));
Function *StoreFuncInt = HlslOP->GetOpFunc(OP::OpCode::BufferStore, Type::getInt32Ty(Ctx));
Constant *StoreOpcode = HlslOP->GetU32Const((unsigned)OP::OpCode::BufferStore);
Constant *WriteMask_XYZW = HlslOP->GetI8Const(15);
Constant *WriteMask_X = HlslOP->GetI8Const(1);
Constant *ShaderKindAsConstant = HlslOP->GetU32Const((uint32_t)ShaderKind);
Constant *MaxEntryIndexAsConstant = HlslOP->GetU32Const((uint32_t)m_MaxNumEntriesInLog - 1u);
Constant *Zero32Arg = HlslOP->GetU32Const(0);
Constant *One32Arg = HlslOP->GetU32Const(1);
UndefValue *UndefArg = UndefValue::get(Type::getInt32Ty(Ctx));
// Firstly we read this invocation's index within the invocations log buffer, and
// atomically increment it for the next invocation
auto *EntryIndex = Builder.CreateCall(
AtomicOpFunc,
{
AtomicBinOpcode, // i32, ; opcode
HandleForCountUAV, // %dx.types.Handle, ; resource handle
AtomicAdd, // i32, ; binary operation code
Zero32Arg, // i32, ; coordinate c0: byte offset
UndefArg, // i32, ; coordinate c1 (unused)
UndefArg, // i32, ; coordinate c2 (unused)
One32Arg // i32); increment value
},
"EntryIndexResult");
// Clamp the index so that we don't write off the end of the UAV. If we clamp, then it's up to PIX to replay the work
// again with a larger log buffer.
auto* EntryIndexClamped = Builder.CreateCall(UMinOpFunc, { UMinOpCode, EntryIndex, MaxEntryIndexAsConstant });
const auto numBytesPerEntry = 4 + (3 * 4) + (3 * 4) + (3 * 4) + 4 + 4 + 4; // See number of bytes we store per shader invocation below
auto EntryOffset = Builder.CreateMul(EntryIndexClamped, HlslOP->GetU32Const(numBytesPerEntry), "EntryOffset");
auto EntryOffsetPlus16 = Builder.CreateAdd(EntryOffset, HlslOP->GetU32Const(16), "EntryOffsetPlus16");
auto EntryOffsetPlus32 = Builder.CreateAdd(EntryOffset, HlslOP->GetU32Const(32), "EntryOffsetPlus32");
auto EntryOffsetPlus48 = Builder.CreateAdd(EntryOffset, HlslOP->GetU32Const(48), "EntryOffsetPlus48");
// Then we start storing the invocation's info into the main UAV buffer
(void)Builder.CreateCall(
StoreFuncInt,
{
StoreOpcode, // i32, ; opcode
HandleForUAV, // %dx.types.Handle, ; resource handle
EntryOffset, // i32, ; coordinate c0: byte offset
UndefArg, // i32, ; coordinate c1 (unused)
ShaderKindAsConstant, // i32, ; value v0
DispatchRaysX, // i32, ; value v1
DispatchRaysY, // i32, ; value v2
DispatchRaysZ, // i32, ; value v3
WriteMask_XYZW // i8 ;
});
(void)Builder.CreateCall(
StoreFuncFloat,
{
StoreOpcode, // i32, ; opcode
HandleForUAV, // %dx.types.Handle, ; resource handle
EntryOffsetPlus16, // i32, ; coordinate c0: byte offset
UndefArg, // i32, ; coordinate c1 (unused)
WorldRayOriginX, // f32, ; value v0
WorldRayOriginY, // f32, ; value v1
WorldRayOriginZ, // f32, ; value v2
WorldRayDirectionX, // f32, ; value v3
WriteMask_XYZW // i8 ;
});
(void)Builder.CreateCall(
StoreFuncFloat,
{
StoreOpcode, // i32, ; opcode
HandleForUAV, // %dx.types.Handle, ; resource handle
EntryOffsetPlus32, // i32, ; coordinate c0: byte offset
UndefArg, // i32, ; coordinate c1 (unused)
WorldRayDirectionY, // f32, ; value v0
WorldRayDirectionZ, // f32, ; value v1
MinRayT, // f32, ; value v2
CurrentRayT, // f32, ; value v3
WriteMask_XYZW // i8 ;
});
(void)Builder.CreateCall(
StoreFuncInt,
{
StoreOpcode, // i32, ; opcode
HandleForUAV, // %dx.types.Handle, ; resource handle
EntryOffsetPlus48, // i32, ; coordinate c0: byte offset
UndefArg, // i32, ; coordinate c1 (unused)
RayFlags, // i32, ; value v0
UndefArg, // i32, ; value v1
UndefArg, // i32, ; value v2
UndefArg, // i32, ; value v3
WriteMask_X // i8 ;
});
}
return true;
}
char DxilPIXDXRInvocationsLog::ID = 0;
ModulePass *llvm::createDxilPIXDXRInvocationsLogPass() {
return new DxilPIXDXRInvocationsLog();
}
INITIALIZE_PASS(DxilPIXDXRInvocationsLog, "hlsl-dxil-pix-dxr-invocations-log",
"HLSL DXIL Logs all non-RayGen DXR 1.0 invocations into a UAV",
false, false)

Просмотреть файл

@ -900,7 +900,7 @@ bool DxilShaderAccessTracking::runOnModule(Module &M) {
FOS << "ShouldAssumeDsvAccess";
}
}
int uavRegId = 0;
for (auto * F : instrumentableFunctions) {
DXIL::ShaderKind shaderKind = DXIL::ShaderKind::Invalid;
if (!DM.HasDxilFunctionProps(F)) {
@ -917,7 +917,7 @@ bool DxilShaderAccessTracking::runOnModule(Module &M) {
IRBuilder<> Builder(F->getEntryBlock().getFirstInsertionPt());
m_FunctionToUAVHandle[F] = PIXPassHelpers::CreateUAV(
DM, Builder, uavRegId++, "PIX_CountUAV_Handle");
DM, Builder, 0u, "PIX_CountUAV_Handle");
OP *HlslOP = DM.GetOP();
for (int accessStyle = static_cast<int>(ResourceAccessStyle::None);
accessStyle < static_cast<int>(ResourceAccessStyle::EndOfEnum);

Просмотреть файл

@ -273,7 +273,7 @@ llvm::CallInst *CreateUAV(DxilModule &DM, IRBuilder<> &Builder,
auto const *shaderModel = DM.GetShaderModel();
if (shaderModel->IsLib()) {
auto *Global = DM.GetModule()->getOrInsertGlobal("PIXUAV", UAVStructTy);
auto *Global = DM.GetModule()->getOrInsertGlobal(("PIXUAV" + std::to_string(registerId)).c_str(), UAVStructTy);
GlobalVariable *NewGV = cast<GlobalVariable>(Global);
NewGV->setConstant(true);
NewGV->setLinkage(GlobalValue::ExternalLinkage);
@ -293,7 +293,7 @@ llvm::CallInst *CreateUAV(DxilModule &DM, IRBuilder<> &Builder,
pUAV->SetGloballyCoherent(false);
pUAV->SetHasCounter(false);
pUAV->SetCompType(CompType::getI32());
pUAV->SetLowerBound(0);
pUAV->SetLowerBound(registerId);
pUAV->SetRangeSize(1);
pUAV->SetKind(DXIL::ResourceKind::RawBuffer);

Просмотреть файл

@ -1,7 +1,7 @@
// RUN: %dxc -EClosestHit -Tlib_6_5 %s | %opt -S -hlsl-dxil-pix-shader-access-instrumentation,config=U0:0:10i0;U0:1:2i0;.0;0;0. | %FileCheck %s
// Check we added the UAV:
// CHECK: @PIXUAV = external constant %struct.RWByteAddressBuffer, align 4
// CHECK: @PIXUAV0 = external constant %struct.RWByteAddressBuffer, align 4
// CHECK: load %struct.RWByteAddressBuffer, %struct.RWByteAddressBuffer* @PIXUAV
// CHECK: call %dx.types.Handle @dx.op.createHandleForLib.struct.RWByteAddressBuffer
//

Просмотреть файл

@ -245,6 +245,7 @@ public:
TEST_METHOD(RootSignatureUpgrade_Annotation)
TEST_METHOD(SymbolManager_Embedded2DArray)
TEST_METHOD(DxilPIXDXRInvocationsLog_SanityTest)
dxc::DxcDllSupport m_dllSupport;
VersionSupportInfo m_ver;
@ -1172,6 +1173,7 @@ static std::string ToString(std::wstring from)
std::string RunDxilPIXAddTidToAmplificationShaderPayloadPass(IDxcBlob *
blob);
CComPtr<IDxcBlob> RunDxilPIXMeshShaderOutputPass(IDxcBlob* blob);
CComPtr<IDxcBlob> RunDxilPIXDXRInvocationsLog(IDxcBlob* blob);
void CompileAndRunAnnotationAndGetDebugPart(
dxc::DxcDllSupport &dllSupport, const char *source, const wchar_t *profile,
IDxcBlob **ppDebugPart, std::vector<const wchar_t *> extraArgs = {});
@ -3087,6 +3089,28 @@ CComPtr<IDxcBlob> PixTest::RunDxilPIXMeshShaderOutputPass(IDxcBlob *blob) {
return pOptimizedModule;
}
CComPtr<IDxcBlob> PixTest::RunDxilPIXDXRInvocationsLog(IDxcBlob* blob) {
CComPtr<IDxcBlob> dxil = FindModule(DFCC_ShaderDebugInfoDXIL, blob);
CComPtr<IDxcOptimizer> pOptimizer;
VERIFY_SUCCEEDED(
m_dllSupport.CreateInstance(CLSID_DxcOptimizer, &pOptimizer));
std::vector<LPCWSTR> Options;
Options.push_back(L"-hlsl-dxil-pix-dxr-invocations-log,maxNumEntriesInLog=24");
CComPtr<IDxcBlob> pOptimizedModule;
CComPtr<IDxcBlobEncoding> pText;
VERIFY_SUCCEEDED(pOptimizer->RunOptimizer(
dxil, Options.data(), Options.size(), &pOptimizedModule, &pText));
std::string outputText;
if (pText->GetBufferSize() != 0) {
outputText = reinterpret_cast<const char*>(pText->GetBufferPointer());
}
return pOptimizedModule;
}
std::string
PixTest::RunDxilPIXAddTidToAmplificationShaderPayloadPass(IDxcBlob *blob) {
CComPtr<IDxcBlob> dxil = FindModule(DFCC_ShaderDebugInfoDXIL, blob);
@ -5197,4 +5221,38 @@ void ASMain()
ValidateStructMember(2, L"bigOne", 24*8);
}
TEST_F(PixTest, DxilPIXDXRInvocationsLog_SanityTest) {
const char* source = R"x(
struct MyPayload
{
float4 color;
};
[shader("raygeneration")]
void MyRayGen()
{
}
[shader("closesthit")]
void MyClosestHit(inout MyPayload payload, in BuiltInTriangleIntersectionAttributes attr)
{
}
[shader("anyhit")]
void MyAnyHit(inout MyPayload payload, in BuiltInTriangleIntersectionAttributes attr)
{
}
[shader("miss")]
void MyMiss(inout MyPayload payload)
{
}
)x";
auto compiledLib = Compile(source, L"lib_6_6", {});
RunDxilPIXDXRInvocationsLog(compiledLib);
}
#endif

Просмотреть файл

@ -2142,6 +2142,8 @@ class db_dxil(object):
add_pass('hlsl-dxil-PIX-add-tid-to-as-payload', 'DxilPIXAddTidToAmplificationShaderPayload', 'HLSL DXIL Add flat thread id to payload from AS to MS', [
{'n':'dispatchArgY','t':'int','c':1},
{'n':'dispatchArgZ','t':'int','c':1}])
add_pass('hlsl-dxil-pix-dxr-invocations-log', 'DxilPIXDXRInvocationsLog', 'HLSL DXIL Logs all non-RayGen DXR 1.0 invocations into a UAV', [
{'n':'maxNumEntriesInLog','t':'int','c':1}])
category_lib="dxil_gen"