Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions lib/DxilPIXPasses/DxilShaderAccessTracking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,17 @@ DxilResourceAndClass DxilShaderAccessTracking::DetermineAccessForHandleForLib(
}
}
}
if (ret.registerType == RegisterType::Invalid) {
auto const &Samplers = DM.GetSamplers();
for (auto &Sampler : Samplers) {
if (global == Sampler->GetGlobalSymbol()) {
binding =
hlsl::resource_helper::loadBindingFromResourceBase(Sampler.get());
ret.registerType = RegisterType::Sampler;
break;
}
}
}
if (ret.registerType != RegisterType::Invalid) {
ret.accessStyle = AccessStyle::FromRootSig;
ret.RegisterID = binding.rangeLowerBound;
Expand Down Expand Up @@ -898,6 +909,15 @@ bool DxilShaderAccessTracking::runOnModule(Module &M) {

// Special cases
switch (opCode) {
case DXIL::OpCode::AnnotateHandle:
// annotateHandle attaches type information. It is not a resource
// access. GetResourceFromHandle still walks through it when a
// later access uses the annotated handle.
continue;
case DXIL::OpCode::BarrierByMemoryHandle:
// A barrier orders accesses to a resource. It is not itself an
// access.
continue;
case DXIL::OpCode::GetDimensions:
// readWrite = ShaderAccessFlags::DescriptorRead; // TODO: Support
// GetDimensions
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// RUN: %dxc -T cs_6_8 -E main -Od %s | %opt -S -hlsl-dxil-pix-shader-access-instrumentation,config=U0:0:2i0;.0;256;512. | %FileCheck %s

// Barrier() on a resource handle orders accesses to that resource. It is
// not itself an access.
//
// The config puts the UAVs of space 0 at slot 0 onwards, so g_out is slot
// 0 and g_rw is slot 1. A slot is three dwords, so g_out's write dword is
// at byte 4 and g_rw's write dword is at byte 16.

// g_rw is only barriered, never accessed, so nothing is recorded against
// it.
// CHECK-NOT: bufferStore.i32(i32 69, %dx.types.Handle {{.*}}, i32 16,

// The store to g_out is a genuine write and is recorded.
// CHECK: call void @dx.op.bufferStore.i32(i32 69, %dx.types.Handle {{.*}}, i32 4,

// CHECK-NOT: bufferStore.i32(i32 69, %dx.types.Handle {{.*}}, i32 16,

RWByteAddressBuffer g_out : register(u0);
RWTexture2D<float4> g_rw : register(u1);

[numthreads(1, 1, 1)]
void main(uint index : SV_GroupIndex)
{
Barrier(g_rw, DEVICE_SCOPE);
g_out.Store(0, 1);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// RUN: %dxc -T lib_6_6 -Od %s | %opt -S -hlsl-dxil-pix-shader-access-instrumentation,config=S0:1:1i0;U0:2:1i0;.0;0;0. | %FileCheck %s

// annotateHandle attaches type information to a handle. It is not a
// memory operation. g_untouched is only passed to GetDimensions, which
// this pass skips, so the annotation is that resource's only handle use.
// Nothing is recorded against it.
//
// The config puts the SRV of space 0 at slot 1 and the UAV of space 0 at
// slot 2. A slot is three dwords, so g_untouched's read dword is at byte
// 12 and its write dword at byte 16. g_output's write dword is at byte 28.

// CHECK-NOT: bufferStore.i32(i32 69, %dx.types.Handle {{.*}}, i32 12,
// CHECK-NOT: bufferStore.i32(i32 69, %dx.types.Handle {{.*}}, i32 16,

// The store to g_output is a genuine access and is recorded:
// CHECK: call void @dx.op.bufferStore.i32(i32 69, %dx.types.Handle {{.*}}, i32 28,

// CHECK-NOT: bufferStore.i32(i32 69, %dx.types.Handle {{.*}}, i32 12,
// CHECK-NOT: bufferStore.i32(i32 69, %dx.types.Handle {{.*}}, i32 16,

Texture2D<float4> g_untouched : register(t0);
RWByteAddressBuffer g_output : register(u0);

[shader("raygeneration")]
void RayGen()
{
uint width, height;
g_untouched.GetDimensions(width, height);
g_output.Store(0, width + height);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// RUN: %dxc -T lib_6_6 -Od %s | %opt -S -hlsl-dxil-pix-shader-access-instrumentation,config=S0:1:1i0;U0:2:1i0;.256;512;1024. | %FileCheck %s

// annotateHandle is not an access, but a genuine access that uses an
// annotated handle still records the resource class from that annotation.
//
// Offsets with this config (SRV space 0 at slot 1, UAV space 0 at slot 2,
// three dwords per slot, descriptor-heap records at byte 256):
// g_input read slot 1, read dword -> 12
// g_input write slot 1, write dword -> 16 (must not appear)
// g_output write slot 2, write dword -> 28
// heapTexture read descriptor 3 -> 292
//
// A descriptor-heap record encodes shader kind in its top four bits and
// ResourceAccessStyle in the next four. RayGeneration is 7 and SRVRead is
// 5, so 0x75000000 == 1962934272.

// CHECK-NOT: bufferStore.i32(i32 69, %dx.types.Handle {{.*}}, i32 16,
// CHECK: call void @dx.op.bufferStore.i32(i32 69, %dx.types.Handle {{.*}}, i32 12,
// CHECK: call void @dx.op.bufferStore.i32(i32 69, %dx.types.Handle {{.*}}, i32 292, i32 undef, i32 1962934272,
// CHECK: call void @dx.op.bufferStore.i32(i32 69, %dx.types.Handle {{.*}}, i32 28,
// CHECK-NOT: bufferStore.i32(i32 69, %dx.types.Handle {{.*}}, i32 16,

ByteAddressBuffer g_input : register(t0);
RWByteAddressBuffer g_output : register(u0);

[shader("raygeneration")]
void RayGen()
{
Texture2D<float4> heapTexture = ResourceDescriptorHeap[3];
uint value = g_input.Load(0);
value += asuint(heapTexture.Load(int3(0, 0, 0)).x);
g_output.Store(0, value);
}
76 changes: 72 additions & 4 deletions tools/clang/unittests/HLSL/PixTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ class PixTest : public ::testing::Test {
TEST_METHOD(AccessTracking_ModificationReport_Read)
TEST_METHOD(AccessTracking_ModificationReport_Write)
TEST_METHOD(AccessTracking_ModificationReport_SM66)
TEST_METHOD(AccessTracking_SamplerAccessInLibrary)

TEST_METHOD(PixStructAnnotation_Lib_DualRaygen)

Expand Down Expand Up @@ -185,6 +186,7 @@ class PixTest : public ::testing::Test {
TEST_METHOD(Validation_ControlInvalidModuleFails)
TEST_METHOD(Validation_ControlBoilerplateOnlyFailureIsRejected)
TEST_METHOD(Validation_NonUniformResourceIndex_WaveOpsFlag)
TEST_METHOD(Validation_ShaderAccessTracking_DynamicallyIndexedResource)

dxc::DxCompilerDllLoader m_dllSupport;
VersionSupportInfo m_ver;
Expand Down Expand Up @@ -637,7 +639,8 @@ class PixTest : public ::testing::Test {
const wchar_t *profile = L"as_6_5");
void ValidateAllocaWrite(std::vector<AllocaWrite> const &allocaWrites,
size_t index, const char *name);
PassOutput RunShaderAccessTrackingPass(IDxcBlob *blob);
PassOutput RunShaderAccessTrackingPass(
IDxcBlob *blob, const wchar_t *config = L"U0:0:10i0;U0:1:2i0;.0;0;0.");
CComPtr<IDxcBlob>
RunDxilPIXAddTidToAmplificationShaderPayloadPass(IDxcBlob *blob);
CComPtr<IDxcBlob> RunDxilPIXMeshShaderOutputPass(IDxcBlob *blob);
Expand Down Expand Up @@ -921,14 +924,17 @@ TEST_F(PixTest, CompileDebugDisasmPDB) {
VERIFY_SUCCEEDED(pCompiler->Disassemble(pPdbBlob, &pDisasm));
}

PassOutput PixTest::RunShaderAccessTrackingPass(IDxcBlob *blob) {
PassOutput PixTest::RunShaderAccessTrackingPass(IDxcBlob *blob,
const wchar_t *config) {
CComPtr<IDxcOptimizer> pOptimizer;
VERIFY_SUCCEEDED(
m_dllSupport.CreateInstance(CLSID_DxcOptimizer, &pOptimizer));
std::vector<LPCWSTR> Options;
Options.push_back(L"-opt-mod-passes");
Options.push_back(L"-hlsl-dxil-pix-shader-access-instrumentation,config=U0:0:"
L"10i0;U0:1:2i0;.0;0;0.");
std::wstring passOption =
L"-hlsl-dxil-pix-shader-access-instrumentation,config=";
passOption += config;
Options.push_back(passOption.c_str());

CComPtr<IDxcBlob> pOptimizedModule;
CComPtr<IDxcBlobEncoding> pText;
Expand Down Expand Up @@ -1286,6 +1292,47 @@ float main() : SV_Target
ValidateAccessTrackingMods(hlsl, true);
}

std::vector<std::string> Split(std::string str, char delimeter);

static bool HasBufferStoreWithByteOffset(std::vector<std::string> const &lines,
unsigned byteOffset) {
std::string needle = "i32 " + std::to_string(byteOffset);
for (auto const &line : lines) {
if (line.find("dx.op.bufferStore") != std::string::npos &&
line.find(needle) != std::string::npos) {
return true;
}
}
Comment on lines +1299 to +1305
return false;
}

TEST_F(PixTest, AccessTracking_SamplerAccessInLibrary) {
if (m_ver.SkipDxilVersion(1, 6)) {
return;
}

const char *hlsl = R"(
Texture2D<float4> g_texture : register(t0);
SamplerState g_sampler : register(s2);
RWByteAddressBuffer g_output : register(u0);

[shader("raygeneration")]
void RayGen()
{
float4 value = g_texture.SampleLevel(g_sampler, float2(0, 0), 0);
g_output.Store(0, asuint(value.x));
}
)";

auto compiled = Compile(m_dllSupport, hlsl, L"lib_6_6", {L"-Od"});
auto output = RunShaderAccessTrackingPass(
compiled, L"S0:0:4i0;M0:20:4i0;U0:40:4i0;.0;0;0.");
auto lines = Split(Disassemble(output.blob), '\n');
Comment on lines +1327 to +1330
VERIFY_IS_TRUE(HasBufferStoreWithByteOffset(lines, 264));
VerifyInstrumentedModuleIsValid(
output.blob, "shader access tracking of a library sampler access");
}

TEST_F(PixTest, AddToASGroupSharedPayload) {

const char *hlsl = R"(
Expand Down Expand Up @@ -4394,3 +4441,24 @@ float4 main(float4 pos : SV_Position) : SV_Target
std::string::npos,
Disassemble(pOptimizedModule).find("dx.op.waveActiveAllEqual"));
}

TEST_F(PixTest, Validation_ShaderAccessTracking_DynamicallyIndexedResource) {
const char *source = R"x(
Texture2D textures[8] : register(t0);
SamplerState samp : register(s0);

cbuffer Constants : register(b0)
{
uint index;
};

float4 main(float4 pos : SV_Position) : SV_Target
{
return textures[index].Sample(samp, pos.xy);
})x";

auto compiled = Compile(m_dllSupport, source, L"ps_6_0", {L"-Od"});
auto output = RunShaderAccessTrackingPass(compiled);
VerifyInstrumentedModuleIsValid(
output.blob, "shader access tracking of a dynamically indexed resource");
}
Loading