blob: 58289767b73431b39fb02f7ac80441bfaa44288b [file] [log] [blame]
///////////////////////////////////////////////////////////////////////////////
// //
// PixTest.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. //
// //
// Provides tests for the PIX-specific components //
// //
///////////////////////////////////////////////////////////////////////////////
#ifndef UNICODE
#define UNICODE
#endif
#include <algorithm>
#include <cassert>
#include <cfloat>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <vector>
#include "dxc/DxilContainer/DxilContainer.h"
#include "dxc/DxilContainer/DxilRuntimeReflection.h"
#include "dxc/DxilRootSignature/DxilRootSignature.h"
#include "dxc/Support/WinIncludes.h"
#include "dxc/dxcapi.h"
#include "dxc/dxcpix.h"
#ifdef _WIN32
#include <atlfile.h>
#endif
#include "dxc/DXIL/DxilModule.h"
#include "dxc/Test/DxcTestUtils.h"
#include "dxc/Test/HLSLTestData.h"
#include "dxc/Test/HlslTestUtils.h"
#include "dxc/DXIL/DxilUtil.h"
#include "dxc/Support/Global.h"
#include "dxc/Support/HLSLOptions.h"
#include "dxc/Support/Unicode.h"
#include "dxc/Support/dxcapi.use.h"
#include "dxc/Support/microcom.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/ModuleSlotTracker.h"
#include "llvm/IR/Operator.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MSFileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include <fstream>
#include <../lib/DxilDia/DxcPixLiveVariables_FragmentIterator.h>
#include <dxc/DxilPIXPasses/DxilPIXVirtualRegisters.h>
#include "PixTestUtils.h"
using namespace std;
using namespace hlsl;
using namespace hlsl_test;
using namespace pix_test;
static std::vector<std::string> Tokenize(const std::string &str,
const char *delimiters) {
std::vector<std::string> tokens;
std::string copy = str;
for (auto i = strtok(&copy[0], delimiters); i != nullptr;
i = strtok(nullptr, delimiters)) {
tokens.push_back(i);
}
return tokens;
}
#ifdef _WIN32
class PixTest {
#else
class PixTest : public ::testing::Test {
#endif
public:
BEGIN_TEST_CLASS(PixTest)
TEST_CLASS_PROPERTY(L"Parallel", L"true")
TEST_METHOD_PROPERTY(L"Priority", L"0")
END_TEST_CLASS()
TEST_CLASS_SETUP(InitSupport);
TEST_METHOD(CompileDebugDisasmPDB)
TEST_METHOD(AddToASPayload)
TEST_METHOD(AddToASGroupSharedPayload)
TEST_METHOD(AddToASGroupSharedPayload_MeshletCullSample)
TEST_METHOD(SignatureModification_Empty)
TEST_METHOD(SignatureModification_VertexIdAlready)
TEST_METHOD(SignatureModification_SomethingElseFirst)
TEST_METHOD(PixStructAnnotation_Lib_DualRaygen)
TEST_METHOD(PixStructAnnotation_Lib_RaygenAllocaStructAlignment)
TEST_METHOD(PixStructAnnotation_Simple)
TEST_METHOD(PixStructAnnotation_CopiedStruct)
TEST_METHOD(PixStructAnnotation_MixedSizes)
TEST_METHOD(PixStructAnnotation_StructWithinStruct)
TEST_METHOD(PixStructAnnotation_1DArray)
TEST_METHOD(PixStructAnnotation_2DArray)
TEST_METHOD(PixStructAnnotation_EmbeddedArray)
TEST_METHOD(PixStructAnnotation_FloatN)
TEST_METHOD(PixStructAnnotation_SequentialFloatN)
TEST_METHOD(PixStructAnnotation_EmbeddedFloatN)
TEST_METHOD(PixStructAnnotation_Matrix)
TEST_METHOD(PixStructAnnotation_MemberFunction)
TEST_METHOD(PixStructAnnotation_BigMess)
TEST_METHOD(PixStructAnnotation_AlignedFloat4Arrays)
TEST_METHOD(PixStructAnnotation_Inheritance)
TEST_METHOD(PixStructAnnotation_ResourceAsMember)
TEST_METHOD(PixStructAnnotation_WheresMyDbgValue)
TEST_METHOD(VirtualRegisters_InstructionCounts)
TEST_METHOD(VirtualRegisters_AlignedOffsets)
TEST_METHOD(RootSignatureUpgrade_SubObjects)
TEST_METHOD(RootSignatureUpgrade_Annotation)
TEST_METHOD(DxilPIXDXRInvocationsLog_SanityTest)
TEST_METHOD(DebugInstrumentation_TextOutput)
TEST_METHOD(DebugInstrumentation_BlockReport)
dxc::DxcDllSupport m_dllSupport;
VersionSupportInfo m_ver;
HRESULT CreateContainerBuilder(IDxcContainerBuilder **ppResult) {
return m_dllSupport.CreateInstance(CLSID_DxcContainerBuilder, ppResult);
}
std::string GetOption(std::string &cmd, char *opt) {
std::string option = cmd.substr(cmd.find(opt));
option = option.substr(option.find_first_of(' '));
option = option.substr(option.find_first_not_of(' '));
return option.substr(0, option.find_first_of(' '));
}
CComPtr<IDxcBlob> ExtractDxilPart(IDxcBlob *pProgram) {
CComPtr<IDxcLibrary> pLib;
VERIFY_SUCCEEDED(m_dllSupport.CreateInstance(CLSID_DxcLibrary, &pLib));
const hlsl::DxilContainerHeader *pContainer = hlsl::IsDxilContainerLike(
pProgram->GetBufferPointer(), pProgram->GetBufferSize());
VERIFY_IS_NOT_NULL(pContainer);
hlsl::DxilPartIterator partIter =
std::find_if(hlsl::begin(pContainer), hlsl::end(pContainer),
hlsl::DxilPartIsType(hlsl::DFCC_DXIL));
const hlsl::DxilProgramHeader *pProgramHeader =
(const hlsl::DxilProgramHeader *)hlsl::GetDxilPartData(*partIter);
uint32_t bitcodeLength;
const char *pBitcode;
CComPtr<IDxcBlob> pDxilBits;
hlsl::GetDxilProgramBitcode(pProgramHeader, &pBitcode, &bitcodeLength);
VERIFY_SUCCEEDED(pLib->CreateBlobFromBlob(
pProgram, pBitcode - (char *)pProgram->GetBufferPointer(),
bitcodeLength, &pDxilBits));
return pDxilBits;
}
PassOutput RunValueToDeclarePass(IDxcBlob *dxil, int startingLineNumber = 0) {
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"-dxil-dbg-value-to-dbg-declare");
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 {
std::move(pOptimizedModule), {}, Tokenize(outputText.c_str(), "\n")};
}
PassOutput RunDebugPass(IDxcBlob *dxil, int UAVSize = 1024 * 1024) {
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"-dxil-dbg-value-to-dbg-declare");
Options.push_back(L"-dxil-annotate-with-virtual-regs");
std::wstring debugArg =
L"-hlsl-dxil-debug-instrumentation,UAVSize=" + std::to_wstring(UAVSize);
Options.push_back(debugArg.c_str());
CComPtr<IDxcBlob> pOptimizedModule;
CComPtr<IDxcBlobEncoding> pText;
VERIFY_SUCCEEDED(pOptimizer->RunOptimizer(
dxil, Options.data(), Options.size(), &pOptimizedModule, &pText));
std::string outputText = BlobToUtf8(pText);
return {
std::move(pOptimizedModule), {}, Tokenize(outputText.c_str(), "\n")};
}
CComPtr<IDxcBlob> FindModule(hlsl::DxilFourCC fourCC, IDxcBlob *pSource) {
const UINT32 BC_C0DE = ((INT32)(INT8)'B' | (INT32)(INT8)'C' << 8 |
(INT32)0xDEC0 << 16); // BC0xc0de in big endian
const char *pBitcode = nullptr;
const hlsl::DxilPartHeader *pDxilPartHeader =
(hlsl::DxilPartHeader *)
pSource->GetBufferPointer(); // Initialize assuming that source is
// starting with DXIL part
if (BC_C0DE == *(UINT32 *)pSource->GetBufferPointer()) {
return pSource;
}
if (hlsl::IsValidDxilContainer(
(hlsl::DxilContainerHeader *)pSource->GetBufferPointer(),
pSource->GetBufferSize())) {
hlsl::DxilContainerHeader *pDxilContainerHeader =
(hlsl::DxilContainerHeader *)pSource->GetBufferPointer();
pDxilPartHeader =
*std::find_if(begin(pDxilContainerHeader), end(pDxilContainerHeader),
hlsl::DxilPartIsType(fourCC));
}
if (fourCC == pDxilPartHeader->PartFourCC) {
UINT32 pBlobSize;
const hlsl::DxilProgramHeader *pDxilProgramHeader =
(const hlsl::DxilProgramHeader *)(pDxilPartHeader + 1);
hlsl::GetDxilProgramBitcode(pDxilProgramHeader, &pBitcode, &pBlobSize);
UINT32 offset =
(UINT32)(pBitcode - (const char *)pSource->GetBufferPointer());
CComPtr<IDxcLibrary> library;
IFT(m_dllSupport.CreateInstance(CLSID_DxcLibrary, &library));
CComPtr<IDxcBlob> targetBlob;
library->CreateBlobFromBlob(pSource, offset, pBlobSize, &targetBlob);
return targetBlob;
}
return {};
}
void ReplaceDxilBlobPart(const void *originalShaderBytecode,
SIZE_T originalShaderLength, IDxcBlob *pNewDxilBlob,
IDxcBlob **ppNewShaderOut) {
CComPtr<IDxcLibrary> pLibrary;
IFT(m_dllSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary));
CComPtr<IDxcBlob> pNewContainer;
// Use the container assembler to build a new container from the
// recently-modified DXIL bitcode. This container will contain new copies of
// things like input signature etc., which will supersede the ones from the
// original compiled shader's container.
{
CComPtr<IDxcAssembler> pAssembler;
IFT(m_dllSupport.CreateInstance(CLSID_DxcAssembler, &pAssembler));
CComPtr<IDxcOperationResult> pAssembleResult;
VERIFY_SUCCEEDED(
pAssembler->AssembleToContainer(pNewDxilBlob, &pAssembleResult));
CComPtr<IDxcBlobEncoding> pAssembleErrors;
VERIFY_SUCCEEDED(pAssembleResult->GetErrorBuffer(&pAssembleErrors));
if (pAssembleErrors && pAssembleErrors->GetBufferSize() != 0) {
OutputDebugStringA(
static_cast<LPCSTR>(pAssembleErrors->GetBufferPointer()));
VERIFY_SUCCEEDED(E_FAIL);
}
VERIFY_SUCCEEDED(pAssembleResult->GetResult(&pNewContainer));
}
// Now copy over the blobs from the original container that won't have been
// invalidated by changing the shader code itself, using the container
// reflection API
{
// Wrap the original code in a container blob
CComPtr<IDxcBlobEncoding> pContainer;
VERIFY_SUCCEEDED(pLibrary->CreateBlobWithEncodingFromPinned(
static_cast<LPBYTE>(const_cast<void *>(originalShaderBytecode)),
static_cast<UINT32>(originalShaderLength), CP_ACP, &pContainer));
CComPtr<IDxcContainerReflection> pReflection;
IFT(m_dllSupport.CreateInstance(CLSID_DxcContainerReflection,
&pReflection));
// Load the reflector from the original shader
VERIFY_SUCCEEDED(pReflection->Load(pContainer));
UINT32 partIndex;
if (SUCCEEDED(pReflection->FindFirstPartKind(hlsl::DFCC_PrivateData,
&partIndex))) {
CComPtr<IDxcBlob> pPart;
VERIFY_SUCCEEDED(pReflection->GetPartContent(partIndex, &pPart));
CComPtr<IDxcContainerBuilder> pContainerBuilder;
IFT(m_dllSupport.CreateInstance(CLSID_DxcContainerBuilder,
&pContainerBuilder));
VERIFY_SUCCEEDED(pContainerBuilder->Load(pNewContainer));
VERIFY_SUCCEEDED(
pContainerBuilder->AddPart(hlsl::DFCC_PrivateData, pPart));
CComPtr<IDxcOperationResult> pBuildResult;
VERIFY_SUCCEEDED(pContainerBuilder->SerializeContainer(&pBuildResult));
CComPtr<IDxcBlobEncoding> pBuildErrors;
VERIFY_SUCCEEDED(pBuildResult->GetErrorBuffer(&pBuildErrors));
if (pBuildErrors && pBuildErrors->GetBufferSize() != 0) {
OutputDebugStringA(
reinterpret_cast<LPCSTR>(pBuildErrors->GetBufferPointer()));
VERIFY_SUCCEEDED(E_FAIL);
}
VERIFY_SUCCEEDED(pBuildResult->GetResult(&pNewContainer));
}
}
*ppNewShaderOut = pNewContainer.Detach();
}
class ModuleAndHangersOn {
std::unique_ptr<llvm::LLVMContext> llvmContext;
std::unique_ptr<llvm::Module> llvmModule;
DxilModule *dxilModule;
public:
ModuleAndHangersOn(IDxcBlob *pBlob) {
// Verify we have a valid dxil container.
const DxilContainerHeader *pContainer = IsDxilContainerLike(
pBlob->GetBufferPointer(), pBlob->GetBufferSize());
VERIFY_IS_NOT_NULL(pContainer);
VERIFY_IS_TRUE(IsValidDxilContainer(pContainer, pBlob->GetBufferSize()));
// Get Dxil part from container.
DxilPartIterator it =
std::find_if(begin(pContainer), end(pContainer),
DxilPartIsType(DFCC_ShaderDebugInfoDXIL));
VERIFY_IS_FALSE(it == end(pContainer));
const DxilProgramHeader *pProgramHeader =
reinterpret_cast<const DxilProgramHeader *>(GetDxilPartData(*it));
VERIFY_IS_TRUE(IsValidDxilProgramHeader(pProgramHeader, (*it)->PartSize));
// Get a pointer to the llvm bitcode.
const char *pIL;
uint32_t pILLength;
GetDxilProgramBitcode(pProgramHeader, &pIL, &pILLength);
// Parse llvm bitcode into a module.
std::unique_ptr<llvm::MemoryBuffer> pBitcodeBuf(
llvm::MemoryBuffer::getMemBuffer(llvm::StringRef(pIL, pILLength), "",
false));
llvmContext.reset(new llvm::LLVMContext);
llvm::ErrorOr<std::unique_ptr<llvm::Module>> pModule(
llvm::parseBitcodeFile(pBitcodeBuf->getMemBufferRef(), *llvmContext));
if (std::error_code ec = pModule.getError()) {
VERIFY_FAIL();
}
llvmModule = std::move(pModule.get());
dxilModule = DxilModule::TryGetDxilModule(llvmModule.get());
}
DxilModule &GetDxilModule() { return *dxilModule; }
};
struct AggregateOffsetAndSize {
unsigned countOfMembers;
unsigned offset;
unsigned size;
};
struct AllocaWrite {
std::string memberName;
uint32_t regBase;
uint32_t regSize;
uint64_t index;
};
struct TestableResults {
std::vector<AggregateOffsetAndSize> OffsetAndSizes;
std::vector<AllocaWrite> AllocaWrites;
};
TestableResults TestStructAnnotationCase(const char *hlsl,
const wchar_t *optimizationLevel,
bool validateCoverage = true,
const wchar_t *profile = L"as_6_5");
void ValidateAllocaWrite(std::vector<AllocaWrite> const &allocaWrites,
size_t index, const char *name);
CComPtr<IDxcBlob> RunShaderAccessTrackingPass(IDxcBlob *blob);
std::string RunDxilPIXAddTidToAmplificationShaderPayloadPass(IDxcBlob *blob);
CComPtr<IDxcBlob> RunDxilPIXMeshShaderOutputPass(IDxcBlob *blob);
CComPtr<IDxcBlob> RunDxilPIXDXRInvocationsLog(IDxcBlob *blob);
};
bool PixTest::InitSupport() {
if (!m_dllSupport.IsEnabled()) {
VERIFY_SUCCEEDED(m_dllSupport.Initialize());
m_ver.Initialize(m_dllSupport);
}
return true;
}
TEST_F(PixTest, CompileDebugDisasmPDB) {
const char *hlsl = R"(
[RootSignature("")]
float main(float pos : A) : SV_Target {
float x = abs(pos);
float y = sin(pos);
float z = x + y;
return z;
}
)";
CComPtr<IDxcLibrary> pLib;
VERIFY_SUCCEEDED(m_dllSupport.CreateInstance(CLSID_DxcLibrary, &pLib));
CComPtr<IDxcCompiler> pCompiler;
CComPtr<IDxcCompiler2> pCompiler2;
CComPtr<IDxcOperationResult> pResult;
CComPtr<IDxcBlobEncoding> pSource;
CComPtr<IDxcBlob> pProgram;
CComPtr<IDxcBlob> pPdbBlob;
CComHeapPtr<WCHAR> pDebugName;
VERIFY_SUCCEEDED(CreateCompiler(m_dllSupport, &pCompiler));
VERIFY_SUCCEEDED(pCompiler.QueryInterface(&pCompiler2));
CreateBlobFromText(m_dllSupport, hlsl, &pSource);
LPCWSTR args[] = {L"/Zi", L"/Qembed_debug"};
VERIFY_SUCCEEDED(pCompiler2->CompileWithDebug(
pSource, L"source.hlsl", L"main", L"ps_6_0", args, _countof(args),
nullptr, 0, nullptr, &pResult, &pDebugName, &pPdbBlob));
VERIFY_SUCCEEDED(pResult->GetResult(&pProgram));
// Test that disassembler can consume a PDB container
CComPtr<IDxcBlobEncoding> pDisasm;
VERIFY_SUCCEEDED(pCompiler->Disassemble(pPdbBlob, &pDisasm));
}
CComPtr<IDxcBlob> PixTest::RunShaderAccessTrackingPass(IDxcBlob *blob) {
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=");
CComPtr<IDxcBlob> pOptimizedModule;
CComPtr<IDxcBlobEncoding> pText;
VERIFY_SUCCEEDED(pOptimizer->RunOptimizer(
blob, Options.data(), Options.size(), &pOptimizedModule, &pText));
CComPtr<IDxcAssembler> pAssembler;
VERIFY_SUCCEEDED(
m_dllSupport.CreateInstance(CLSID_DxcAssembler, &pAssembler));
CComPtr<IDxcOperationResult> pAssembleResult;
VERIFY_SUCCEEDED(
pAssembler->AssembleToContainer(pOptimizedModule, &pAssembleResult));
HRESULT hr;
VERIFY_SUCCEEDED(pAssembleResult->GetStatus(&hr));
VERIFY_SUCCEEDED(hr);
CComPtr<IDxcBlob> pNewContainer;
VERIFY_SUCCEEDED(pAssembleResult->GetResult(&pNewContainer));
return pNewContainer;
}
CComPtr<IDxcBlob> PixTest::RunDxilPIXMeshShaderOutputPass(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"-opt-mod-passes");
Options.push_back(L"-hlsl-dxil-pix-meshshader-output-instrumentation,expand-"
L"payload=1,UAVSize=8192");
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;
}
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);
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-add-tid-to-as-payload,dispatchArgY=1,dispatchArgZ=2");
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 outputText;
}
TEST_F(PixTest, AddToASPayload) {
const char *hlsl = R"(
struct MyPayload
{
float f1;
float f2;
};
[numthreads(1, 1, 1)]
void ASMain(uint gid : SV_GroupID)
{
MyPayload payload;
payload.f1 = (float)gid / 4.f;
payload.f2 = (float)gid * 4.f;
DispatchMesh(1, 1, 1, payload);
}
struct PSInput
{
float4 position : SV_POSITION;
};
[outputtopology("triangle")]
[numthreads(3,1,1)]
void MSMain(
in payload MyPayload small,
in uint tid : SV_GroupThreadID,
in uint3 dtid : SV_DispatchThreadID,
out vertices PSInput verts[3],
out indices uint3 triangles[1])
{
SetMeshOutputCounts(3, 1);
verts[tid].position = float4(small.f1, small.f2, 0, 0);
triangles[0] = uint3(0, 1, 2);
}
)";
auto as = Compile(m_dllSupport, hlsl, L"as_6_6", {}, L"ASMain");
RunDxilPIXAddTidToAmplificationShaderPayloadPass(as);
auto ms = Compile(m_dllSupport, hlsl, L"ms_6_6", {}, L"MSMain");
RunDxilPIXMeshShaderOutputPass(ms);
}
unsigned FindOrAddVSInSignatureElementForInstanceOrVertexID(
hlsl::DxilSignature &InputSignature, hlsl::DXIL::SemanticKind semanticKind);
TEST_F(PixTest, SignatureModification_Empty) {
DxilSignature sig(DXIL::ShaderKind::Vertex, DXIL::SignatureKind::Input,
false);
FindOrAddVSInSignatureElementForInstanceOrVertexID(
sig, DXIL::SemanticKind::InstanceID);
FindOrAddVSInSignatureElementForInstanceOrVertexID(
sig, DXIL::SemanticKind::VertexID);
VERIFY_ARE_EQUAL(2ull, sig.GetElements().size());
VERIFY_ARE_EQUAL(sig.GetElement(0).GetKind(), DXIL::SemanticKind::InstanceID);
VERIFY_ARE_EQUAL(sig.GetElement(0).GetCols(), 1u);
VERIFY_ARE_EQUAL(sig.GetElement(0).GetRows(), 1u);
VERIFY_ARE_EQUAL(sig.GetElement(0).GetStartCol(), 0);
VERIFY_ARE_EQUAL(sig.GetElement(0).GetStartRow(), 0);
VERIFY_ARE_EQUAL(sig.GetElement(1).GetKind(), DXIL::SemanticKind::VertexID);
VERIFY_ARE_EQUAL(sig.GetElement(1).GetCols(), 1u);
VERIFY_ARE_EQUAL(sig.GetElement(1).GetRows(), 1u);
VERIFY_ARE_EQUAL(sig.GetElement(1).GetStartCol(), 0);
VERIFY_ARE_EQUAL(sig.GetElement(1).GetStartRow(), 1);
}
TEST_F(PixTest, SignatureModification_VertexIdAlready) {
DxilSignature sig(DXIL::ShaderKind::Vertex, DXIL::SignatureKind::Input,
false);
auto AddedElement =
llvm::make_unique<DxilSignatureElement>(DXIL::SigPointKind::VSIn);
AddedElement->Initialize(
Semantic::Get(DXIL::SemanticKind::VertexID)->GetName(),
hlsl::CompType::getU32(), DXIL::InterpolationMode::Constant, 1, 1, 0, 0,
0, {0});
AddedElement->SetKind(DXIL::SemanticKind::VertexID);
AddedElement->SetUsageMask(1);
sig.AppendElement(std::move(AddedElement));
FindOrAddVSInSignatureElementForInstanceOrVertexID(
sig, DXIL::SemanticKind::InstanceID);
FindOrAddVSInSignatureElementForInstanceOrVertexID(
sig, DXIL::SemanticKind::VertexID);
VERIFY_ARE_EQUAL(2ull, sig.GetElements().size());
VERIFY_ARE_EQUAL(sig.GetElement(0).GetKind(), DXIL::SemanticKind::VertexID);
VERIFY_ARE_EQUAL(sig.GetElement(0).GetCols(), 1u);
VERIFY_ARE_EQUAL(sig.GetElement(0).GetRows(), 1u);
VERIFY_ARE_EQUAL(sig.GetElement(0).GetStartCol(), 0);
VERIFY_ARE_EQUAL(sig.GetElement(0).GetStartRow(), 0);
VERIFY_ARE_EQUAL(sig.GetElement(1).GetKind(), DXIL::SemanticKind::InstanceID);
VERIFY_ARE_EQUAL(sig.GetElement(1).GetCols(), 1u);
VERIFY_ARE_EQUAL(sig.GetElement(1).GetRows(), 1u);
VERIFY_ARE_EQUAL(sig.GetElement(1).GetStartCol(), 0);
VERIFY_ARE_EQUAL(sig.GetElement(1).GetStartRow(), 1);
}
TEST_F(PixTest, SignatureModification_SomethingElseFirst) {
DxilSignature sig(DXIL::ShaderKind::Vertex, DXIL::SignatureKind::Input,
false);
auto AddedElement =
llvm::make_unique<DxilSignatureElement>(DXIL::SigPointKind::VSIn);
AddedElement->Initialize("One", hlsl::CompType::getU32(),
DXIL::InterpolationMode::Constant, 1, 6, 0, 0, 0,
{0});
AddedElement->SetKind(DXIL::SemanticKind::Arbitrary);
AddedElement->SetUsageMask(1);
sig.AppendElement(std::move(AddedElement));
FindOrAddVSInSignatureElementForInstanceOrVertexID(
sig, DXIL::SemanticKind::InstanceID);
FindOrAddVSInSignatureElementForInstanceOrVertexID(
sig, DXIL::SemanticKind::VertexID);
VERIFY_ARE_EQUAL(3ull, sig.GetElements().size());
// Not gonna check the first one cuz that would just be grading our own
// homework
VERIFY_ARE_EQUAL(sig.GetElement(1).GetKind(), DXIL::SemanticKind::InstanceID);
VERIFY_ARE_EQUAL(sig.GetElement(1).GetCols(), 1u);
VERIFY_ARE_EQUAL(sig.GetElement(1).GetRows(), 1u);
VERIFY_ARE_EQUAL(sig.GetElement(1).GetStartCol(), 0);
VERIFY_ARE_EQUAL(sig.GetElement(1).GetStartRow(), 1);
VERIFY_ARE_EQUAL(sig.GetElement(2).GetKind(), DXIL::SemanticKind::VertexID);
VERIFY_ARE_EQUAL(sig.GetElement(2).GetCols(), 1u);
VERIFY_ARE_EQUAL(sig.GetElement(2).GetRows(), 1u);
VERIFY_ARE_EQUAL(sig.GetElement(2).GetStartCol(), 0);
VERIFY_ARE_EQUAL(sig.GetElement(2).GetStartRow(), 2);
}
TEST_F(PixTest, AddToASGroupSharedPayload) {
const char *hlsl = R"(
struct Contained
{
uint j;
float af[3];
};
struct Bigger
{
half h;
void Init() { h = 1.f; }
Contained a[2];
};
struct MyPayload
{
uint i;
Bigger big[3];
};
groupshared MyPayload payload;
[numthreads(1, 1, 1)]
void main(uint gid : SV_GroupID)
{
DispatchMesh(1, 1, 1, payload);
}
)";
auto as = Compile(m_dllSupport, hlsl, L"as_6_6", {L"-Od"}, L"main");
RunDxilPIXAddTidToAmplificationShaderPayloadPass(as);
}
TEST_F(PixTest, AddToASGroupSharedPayload_MeshletCullSample) {
const char *hlsl = R"(
struct MyPayload
{
uint i[32];
};
groupshared MyPayload payload;
[numthreads(1, 1, 1)]
void main(uint gid : SV_GroupID)
{
DispatchMesh(1, 1, 1, payload);
}
)";
auto as = Compile(m_dllSupport, hlsl, L"as_6_6", {L"-Od"}, L"main");
RunDxilPIXAddTidToAmplificationShaderPayloadPass(as);
}
static llvm::DIType *PeelTypedefs(llvm::DIType *diTy) {
using namespace llvm;
const llvm::DITypeIdentifierMap EmptyMap;
while (1) {
DIDerivedType *diDerivedTy = dyn_cast<DIDerivedType>(diTy);
if (!diDerivedTy)
return diTy;
switch (diTy->getTag()) {
case dwarf::DW_TAG_member:
case dwarf::DW_TAG_inheritance:
case dwarf::DW_TAG_typedef:
case dwarf::DW_TAG_reference_type:
case dwarf::DW_TAG_const_type:
case dwarf::DW_TAG_restrict_type:
diTy = diDerivedTy->getBaseType().resolve(EmptyMap);
break;
default:
return diTy;
}
}
return diTy;
}
static unsigned GetDITypeSizeInBits(llvm::DIType *diTy) {
return PeelTypedefs(diTy)->getSizeInBits();
}
static unsigned GetDITypeAlignmentInBits(llvm::DIType *diTy) {
return PeelTypedefs(diTy)->getAlignInBits();
}
static bool FindStructMemberFromStore(llvm::StoreInst *S,
std::string *OutMemberName) {
using namespace llvm;
Value *Ptr = S->getPointerOperand();
AllocaInst *Alloca = nullptr;
auto &DL = S->getModule()->getDataLayout();
unsigned OffsetInAlloca = 0;
while (Ptr) {
if (auto AI = dyn_cast<AllocaInst>(Ptr)) {
Alloca = AI;
break;
} else if (auto Gep = dyn_cast<GEPOperator>(Ptr)) {
if (Gep->getNumIndices() < 2 || !Gep->hasAllConstantIndices() ||
0 != cast<ConstantInt>(Gep->getOperand(1))->getLimitedValue()) {
return false;
}
auto GepSrcPtr = Gep->getPointerOperand();
Type *GepSrcPtrTy = GepSrcPtr->getType()->getPointerElementType();
Type *PeelingType = GepSrcPtrTy;
for (unsigned i = 1; i < Gep->getNumIndices(); i++) {
uint64_t Idx =
cast<ConstantInt>(Gep->getOperand(1 + i))->getLimitedValue();
if (PeelingType->isStructTy()) {
auto StructTy = cast<StructType>(PeelingType);
unsigned Offset =
DL.getStructLayout(StructTy)->getElementOffsetInBits(Idx);
OffsetInAlloca += Offset;
PeelingType = StructTy->getElementType(Idx);
} else if (PeelingType->isVectorTy()) {
OffsetInAlloca +=
DL.getTypeSizeInBits(PeelingType->getVectorElementType()) * Idx;
PeelingType = PeelingType->getVectorElementType();
} else if (PeelingType->isArrayTy()) {
OffsetInAlloca +=
DL.getTypeSizeInBits(PeelingType->getArrayElementType()) * Idx;
PeelingType = PeelingType->getArrayElementType();
} else {
return false;
}
}
Ptr = GepSrcPtr;
} else {
return false;
}
}
// If there's not exactly one dbg.* inst, give up for now.
if (hlsl::dxilutil::mdv_user_empty(Alloca) ||
std::next(hlsl::dxilutil::mdv_users_begin(Alloca)) !=
hlsl::dxilutil::mdv_users_end(Alloca)) {
return false;
}
auto DI = dyn_cast<DbgDeclareInst>(*hlsl::dxilutil::mdv_users_begin(Alloca));
if (!DI)
return false;
DILocalVariable *diVar = DI->getVariable();
DIExpression *diExpr = DI->getExpression();
const llvm::DITypeIdentifierMap EmptyMap;
DIType *diType = diVar->getType().resolve(EmptyMap);
unsigned MemberOffset = OffsetInAlloca;
if (diExpr->isBitPiece()) {
MemberOffset += diExpr->getBitPieceOffset();
}
diType = PeelTypedefs(diType);
if (!isa<DICompositeType>(diType))
return false;
unsigned OffsetInDI = 0;
std::string MemberName;
//=====================================================
// Find the correct member based on size
while (diType) {
diType = PeelTypedefs(diType);
if (DICompositeType *diCompType = dyn_cast<DICompositeType>(diType)) {
if (diCompType->getTag() == dwarf::DW_TAG_structure_type ||
diCompType->getTag() == dwarf::DW_TAG_class_type) {
bool FoundCompositeMember = false;
for (DINode *Elem : diCompType->getElements()) {
auto diElemType = dyn_cast<DIType>(Elem);
if (!diElemType)
return false;
StringRef CurMemberName;
if (diElemType->getTag() == dwarf::DW_TAG_member) {
CurMemberName = diElemType->getName();
} else if (diElemType->getTag() == dwarf::DW_TAG_inheritance) {
} else {
return false;
}
unsigned CompositeMemberSize = GetDITypeSizeInBits(diElemType);
unsigned CompositeMemberAlignment =
GetDITypeAlignmentInBits(diElemType);
assert(CompositeMemberAlignment);
OffsetInDI =
llvm::RoundUpToAlignment(OffsetInDI, CompositeMemberAlignment);
if (OffsetInDI <= MemberOffset &&
MemberOffset < OffsetInDI + CompositeMemberSize) {
diType = diElemType;
if (CurMemberName.size()) {
if (MemberName.size())
MemberName += ".";
MemberName += CurMemberName;
}
FoundCompositeMember = true;
break;
}
// TODO: How will we match up the padding?
OffsetInDI += CompositeMemberSize;
}
if (!FoundCompositeMember)
return false;
}
// For arrays, just flatten it for now.
// TODO: multi-dimension array
else if (diCompType->getTag() == dwarf::DW_TAG_array_type) {
if (MemberOffset < OffsetInDI ||
MemberOffset >= OffsetInDI + diCompType->getSizeInBits())
return false;
DIType *diArrayElemType = diCompType->getBaseType().resolve(EmptyMap);
{
unsigned CurSize = diCompType->getSizeInBits();
unsigned CurOffset = MemberOffset - OffsetInDI;
for (DINode *SubrangeMD : diCompType->getElements()) {
DISubrange *Range = cast<DISubrange>(SubrangeMD);
unsigned ElemSize = CurSize / Range->getCount();
unsigned Idx = CurOffset / ElemSize;
CurOffset -= ElemSize * Idx;
CurSize = ElemSize;
MemberName += "[";
MemberName += std::to_string(Idx);
MemberName += "]";
}
}
unsigned ArrayElemSize = GetDITypeSizeInBits(diArrayElemType);
unsigned FlattenedIdx = (MemberOffset - OffsetInDI) / ArrayElemSize;
OffsetInDI += FlattenedIdx * ArrayElemSize;
diType = diArrayElemType;
} else {
return false;
}
} else if (DIBasicType *diBasicType = dyn_cast<DIBasicType>(diType)) {
if (OffsetInDI == MemberOffset) {
*OutMemberName = MemberName;
return true;
}
OffsetInDI += diBasicType->getSizeInBits();
return false;
} else {
return false;
}
}
return false;
}
// This function lives in lib\DxilPIXPasses\DxilAnnotateWithVirtualRegister.cpp
// Declared here so we can test it.
uint32_t CountStructMembers(llvm::Type const *pType);
PixTest::TestableResults PixTest::TestStructAnnotationCase(
const char *hlsl, const wchar_t *optimizationLevel, bool validateCoverage,
const wchar_t *profile) {
CComPtr<IDxcBlob> pBlob =
Compile(m_dllSupport, hlsl, profile,
{optimizationLevel, L"-HV", L"2018", L"-enable-16bit-types"});
CComPtr<IDxcBlob> pDxil = FindModule(DFCC_ShaderDebugInfoDXIL, pBlob);
PassOutput passOutput = RunAnnotationPasses(m_dllSupport, pDxil);
auto pAnnotated = passOutput.blob;
CComPtr<IDxcBlob> pAnnotatedContainer;
ReplaceDxilBlobPart(pBlob->GetBufferPointer(), pBlob->GetBufferSize(),
pAnnotated, &pAnnotatedContainer);
#if 0 // handy for debugging
auto disTextW = Disassemble(pAnnotatedContainer);
WEX::Logging::Log::Comment(disTextW.c_str());
#endif
ModuleAndHangersOn moduleEtc(pAnnotatedContainer);
PixTest::TestableResults ret;
// For every dbg.declare, run the member iterator and record what it finds:
auto entryPoints = moduleEtc.GetDxilModule().GetExportedFunctions();
for (auto &entryFunction : entryPoints) {
for (auto &block : entryFunction->getBasicBlockList()) {
for (auto &instruction : block.getInstList()) {
if (auto *dbgDeclare =
llvm::dyn_cast<llvm::DbgDeclareInst>(&instruction)) {
llvm::Value *Address = dbgDeclare->getAddress();
auto *AddressAsAlloca = llvm::dyn_cast<llvm::AllocaInst>(Address);
if (AddressAsAlloca != nullptr) {
auto *Expression = dbgDeclare->getExpression();
std::unique_ptr<dxil_debug_info::MemberIterator> iterator =
dxil_debug_info::CreateMemberIterator(
dbgDeclare,
moduleEtc.GetDxilModule().GetModule()->getDataLayout(),
AddressAsAlloca, Expression);
unsigned int startingBit = 0;
unsigned int coveredBits = 0;
unsigned int memberIndex = 0;
unsigned int memberCount = 0;
while (iterator->Next(&memberIndex)) {
memberCount++;
if (memberIndex == 0) {
startingBit = iterator->OffsetInBits(memberIndex);
coveredBits = iterator->SizeInBits(memberIndex);
} else {
coveredBits = std::max<unsigned int>(
coveredBits, iterator->OffsetInBits(memberIndex) +
iterator->SizeInBits(memberIndex));
}
}
AggregateOffsetAndSize OffsetAndSize = {};
OffsetAndSize.countOfMembers = memberCount;
OffsetAndSize.offset = startingBit;
OffsetAndSize.size = coveredBits;
ret.OffsetAndSizes.push_back(OffsetAndSize);
// Use this independent count of number of struct members to test
// the function that operates on the alloca type:
llvm::Type *pAllocaTy =
AddressAsAlloca->getType()->getElementType();
if (auto *AT = llvm::dyn_cast<llvm::ArrayType>(pAllocaTy)) {
// This is the case where a struct is passed to a function, and
// in these tests there should be only one struct behind the
// pointer.
VERIFY_ARE_EQUAL(AT->getNumElements(), 1u);
pAllocaTy = AT->getArrayElementType();
}
if (auto *ST = llvm::dyn_cast<llvm::StructType>(pAllocaTy)) {
uint32_t countOfMembers = CountStructMembers(ST);
// memberIndex might be greater, because the fragment iterator
// also includes contained derived types as fragments, in
// addition to the members of that contained derived types.
// CountStructMembers only counts the leaf-node types.
VERIFY_ARE_EQUAL(countOfMembers, memberCount);
} else if (pAllocaTy->isFloatingPointTy() ||
pAllocaTy->isIntegerTy()) {
// If there's only one member in the struct in the
// pass-to-function (by pointer) case, then the underlying type
// will have been reduced to the contained type.
VERIFY_ARE_EQUAL(1u, memberCount);
} else {
VERIFY_IS_TRUE(false);
}
}
}
}
}
// The member iterator should find a solid run of bits that is exactly
// covered by exactly one of the members found by the annotation pass:
if (validateCoverage) {
unsigned CurRegIdx = 0;
for (AggregateOffsetAndSize const &cover :
ret.OffsetAndSizes) // For each entry read from member iterators
// and dbg.declares
{
bool found = false;
for (ValueLocation const &valueLocation :
passOutput.valueLocations) // For each allocas and dxil values
{
if (CurRegIdx == (unsigned)valueLocation.base &&
(unsigned)valueLocation.count == cover.countOfMembers) {
VERIFY_IS_FALSE(found);
found = true;
}
}
VERIFY_IS_TRUE(found);
CurRegIdx += cover.countOfMembers;
}
}
// For every store operation to the struct alloca, check that the
// annotation pass correctly determined which alloca
for (auto &block : entryFunction->getBasicBlockList()) {
for (auto &instruction : block.getInstList()) {
if (auto *store = llvm::dyn_cast<llvm::StoreInst>(&instruction)) {
AllocaWrite NewAllocaWrite = {};
if (FindStructMemberFromStore(store, &NewAllocaWrite.memberName)) {
llvm::Value *index;
if (pix_dxil::PixAllocaRegWrite::FromInst(
store, &NewAllocaWrite.regBase, &NewAllocaWrite.regSize,
&index)) {
auto *asInt = llvm::dyn_cast<llvm::ConstantInt>(index);
NewAllocaWrite.index = asInt->getLimitedValue();
ret.AllocaWrites.push_back(NewAllocaWrite);
}
}
}
}
}
}
return ret;
}
void PixTest::ValidateAllocaWrite(std::vector<AllocaWrite> const &allocaWrites,
size_t index, const char *name) {
VERIFY_ARE_EQUAL(index,
allocaWrites[index].regBase + allocaWrites[index].index);
#ifndef NDEBUG
// Compilation may add a prefix to the struct member name:
VERIFY_IS_TRUE(
0 == strncmp(name, allocaWrites[index].memberName.c_str(), strlen(name)));
#endif
}
struct OptimizationChoice {
const wchar_t *Flag;
bool IsOptimized;
};
static const OptimizationChoice OptimizationChoices[] = {
{L"-Od", false},
{L"-O1", true},
};
TEST_F(PixTest, PixStructAnnotation_Lib_DualRaygen) {
if (m_ver.SkipDxilVersion(1, 5))
return;
for (auto choice : OptimizationChoices) {
auto optimization = choice.Flag;
const char *hlsl = R"(
RaytracingAccelerationStructure Scene : register(t0, space0);
RWTexture2D<float4> RenderTarget : register(u0);
struct SceneConstantBuffer
{
float4x4 projectionToWorld;
float4 cameraPosition;
float4 lightPosition;
float4 lightAmbientColor;
float4 lightDiffuseColor;
};
ConstantBuffer<SceneConstantBuffer> g_sceneCB : register(b0);
struct RayPayload
{
float4 color;
};
inline void GenerateCameraRay(uint2 index, out float3 origin, out float3 direction)
{
float2 xy = index + 0.5f; // center in the middle of the pixel.
float2 screenPos = xy;// / DispatchRaysDimensions().xy * 2.0 - 1.0;
// Invert Y for DirectX-style coordinates.
screenPos.y = -screenPos.y;
// Unproject the pixel coordinate into a ray.
float4 world = /*mul(*/float4(screenPos, 0, 1)/*, g_sceneCB.projectionToWorld)*/;
//world.xyz /= world.w;
origin = world.xyz; //g_sceneCB.cameraPosition.xyz;
direction = float3(1,0,0);//normalize(world.xyz - origin);
}
void RaygenCommon()
{
float3 rayDir;
float3 origin;
// Generate a ray for a camera pixel corresponding to an index from the dispatched 2D grid.
GenerateCameraRay(DispatchRaysIndex().xy, origin, rayDir);
// Trace the ray.
// Set the ray's extents.
RayDesc ray;
ray.Origin = origin;
ray.Direction = rayDir;
// Set TMin to a non-zero small value to avoid aliasing issues due to floating - point errors.
// TMin should be kept small to prevent missing geometry at close contact areas.
ray.TMin = 0.001;
ray.TMax = 10000.0;
RayPayload payload = { float4(0, 0, 0, 0) };
TraceRay(Scene, RAY_FLAG_CULL_BACK_FACING_TRIANGLES, ~0, 0, 1, 0, ray, payload);
// Write the raytraced color to the output texture.
// RenderTarget[DispatchRaysIndex().xy] = payload.color;
}
[shader("raygeneration")]
void Raygen0()
{
RaygenCommon();
}
[shader("raygeneration")]
void Raygen1()
{
RaygenCommon();
}
)";
// This is just a crash test until we decide what the right way forward
CComPtr<IDxcBlob> pBlob =
Compile(m_dllSupport, hlsl, L"lib_6_6", {optimization});
CComPtr<IDxcBlob> pDxil = FindModule(DFCC_ShaderDebugInfoDXIL, pBlob);
RunAnnotationPasses(m_dllSupport, pDxil);
}
}
TEST_F(PixTest, PixStructAnnotation_Lib_RaygenAllocaStructAlignment) {
if (m_ver.SkipDxilVersion(1, 5))
return;
const char *hlsl = R"(
RaytracingAccelerationStructure Scene : register(t0, space0);
RWTexture2D<float4> RenderTarget : register(u0);
struct SceneConstantBuffer
{
float4x4 projectionToWorld;
float4 cameraPosition;
float4 lightPosition;
float4 lightAmbientColor;
float4 lightDiffuseColor;
};
ConstantBuffer<SceneConstantBuffer> g_sceneCB : register(b0);
struct RayPayload
{
float4 color;
};
inline void GenerateCameraRay(uint2 index, out float3 origin, out float3 direction)
{
float2 xy = index + 0.5f; // center in the middle of the pixel.
float2 screenPos = xy;// / DispatchRaysDimensions().xy * 2.0 - 1.0;
// Invert Y for DirectX-style coordinates.
screenPos.y = -screenPos.y;
// Unproject the pixel coordinate into a ray.
float4 world = /*mul(*/float4(screenPos, 0, 1)/*, g_sceneCB.projectionToWorld)*/;
//world.xyz /= world.w;
origin = world.xyz; //g_sceneCB.cameraPosition.xyz;
direction = float3(1,0,0);//normalize(world.xyz - origin);
}
void RaygenCommon()
{
float3 rayDir;
float3 origin;
// Generate a ray for a camera pixel corresponding to an index from the dispatched 2D grid.
GenerateCameraRay(DispatchRaysIndex().xy, origin, rayDir);
// Trace the ray.
// Set the ray's extents.
RayDesc ray;
ray.Origin = origin;
ray.Direction = rayDir;
// Set TMin to a non-zero small value to avoid aliasing issues due to floating - point errors.
// TMin should be kept small to prevent missing geometry at close contact areas.
ray.TMin = 0.001;
ray.TMax = 10000.0;
RayPayload payload = { float4(0, 0, 0, 0) };
TraceRay(Scene, RAY_FLAG_CULL_BACK_FACING_TRIANGLES, ~0, 0, 1, 0, ray, payload);
// Write the raytraced color to the output texture.
// RenderTarget[DispatchRaysIndex().xy] = payload.color;
}
[shader("raygeneration")]
void Raygen()
{
RaygenCommon();
}
)";
auto Testables = TestStructAnnotationCase(hlsl, L"-Od", true, L"lib_6_6");
// Built-in type "RayDesc" has this structure: struct { float3 Origin; float
// TMin; float3 Direction; float TMax; } This is 8 floats, with members at
// offsets 0,3,4,7 respectively.
auto FindAtLeastOneOf = [=](char const *name, uint32_t index) {
VERIFY_IS_TRUE(std::find_if(Testables.AllocaWrites.begin(),
Testables.AllocaWrites.end(),
[&name, &index](AllocaWrite const &aw) {
return 0 == strcmp(aw.memberName.c_str(),
name) &&
aw.index == index;
}) != Testables.AllocaWrites.end());
};
FindAtLeastOneOf("Origin.x", 0);
FindAtLeastOneOf("TMin", 3);
FindAtLeastOneOf("Direction.x", 4);
FindAtLeastOneOf("TMax", 7);
}
TEST_F(PixTest, PixStructAnnotation_Simple) {
if (m_ver.SkipDxilVersion(1, 5))
return;
for (auto choice : OptimizationChoices) {
auto optimization = choice.Flag;
const char *hlsl = R"(
struct smallPayload
{
uint dummy;
};
[numthreads(1, 1, 1)]
void main()
{
smallPayload p;
p.dummy = 42;
DispatchMesh(1, 1, 1, p);
}
)";
auto Testables = TestStructAnnotationCase(hlsl, optimization);
if (!Testables.OffsetAndSizes.empty()) {
VERIFY_ARE_EQUAL(1u, Testables.OffsetAndSizes.size());
VERIFY_ARE_EQUAL(1u, Testables.OffsetAndSizes[0].countOfMembers);
VERIFY_ARE_EQUAL(0u, Testables.OffsetAndSizes[0].offset);
VERIFY_ARE_EQUAL(32u, Testables.OffsetAndSizes[0].size);
}
VERIFY_ARE_EQUAL(1u, Testables.AllocaWrites.size());
ValidateAllocaWrite(Testables.AllocaWrites, 0, "dummy");
}
}
TEST_F(PixTest, PixStructAnnotation_CopiedStruct) {
if (m_ver.SkipDxilVersion(1, 5))
return;
for (auto choice : OptimizationChoices) {
auto optimization = choice.Flag;
const char *hlsl = R"(
struct smallPayload
{
uint dummy;
};
[numthreads(1, 1, 1)]
void main()
{
smallPayload p;
p.dummy = 42;
smallPayload p2 = p;
DispatchMesh(1, 1, 1, p2);
}
)";
auto Testables = TestStructAnnotationCase(hlsl, optimization);
// 2 in unoptimized case (one for each instance of smallPayload)
// 1 in optimized case (cuz p2 aliases over p)
VERIFY_IS_TRUE(Testables.OffsetAndSizes.size() >= 1);
for (const auto &os : Testables.OffsetAndSizes) {
VERIFY_ARE_EQUAL(1u, os.countOfMembers);
VERIFY_ARE_EQUAL(0u, os.offset);
VERIFY_ARE_EQUAL(32u, os.size);
}
VERIFY_ARE_EQUAL(1u, Testables.AllocaWrites.size());
}
}
TEST_F(PixTest, PixStructAnnotation_MixedSizes) {
if (m_ver.SkipDxilVersion(1, 5))
return;
for (auto choice : OptimizationChoices) {
auto optimization = choice.Flag;
const char *hlsl = R"(
struct smallPayload
{
bool b1;
uint16_t sixteen;
uint32_t thirtytwo;
uint64_t sixtyfour;
};
[numthreads(1, 1, 1)]
void main()
{
smallPayload p;
p.b1 = true;
p.sixteen = 16;
p.thirtytwo = 32;
p.sixtyfour = 64;
DispatchMesh(1, 1, 1, p);
}
)";
auto Testables = TestStructAnnotationCase(hlsl, optimization);
if (!choice.IsOptimized) {
VERIFY_ARE_EQUAL(1u, Testables.OffsetAndSizes.size());
VERIFY_ARE_EQUAL(4u, Testables.OffsetAndSizes[0].countOfMembers);
VERIFY_ARE_EQUAL(0u, Testables.OffsetAndSizes[0].offset);
// 8 bytes align for uint64_t:
VERIFY_ARE_EQUAL(32u + 16u + 16u /*alignment for next field*/ + 32u +
32u /*alignment for max align*/ + 64u,
Testables.OffsetAndSizes[0].size);
} else {
VERIFY_ARE_EQUAL(4u, Testables.OffsetAndSizes.size());
VERIFY_ARE_EQUAL(1u, Testables.OffsetAndSizes[0].countOfMembers);
VERIFY_ARE_EQUAL(0u, Testables.OffsetAndSizes[0].offset);
VERIFY_ARE_EQUAL(32u, Testables.OffsetAndSizes[0].size);
VERIFY_ARE_EQUAL(1u, Testables.OffsetAndSizes[1].countOfMembers);
VERIFY_ARE_EQUAL(32u, Testables.OffsetAndSizes[1].offset);
VERIFY_ARE_EQUAL(16u, Testables.OffsetAndSizes[1].size);
VERIFY_ARE_EQUAL(1u, Testables.OffsetAndSizes[2].countOfMembers);
VERIFY_ARE_EQUAL(32u + 32u, Testables.OffsetAndSizes[2].offset);
VERIFY_ARE_EQUAL(32u, Testables.OffsetAndSizes[2].size);
VERIFY_ARE_EQUAL(1u, Testables.OffsetAndSizes[3].countOfMembers);
VERIFY_ARE_EQUAL(32u + 32u + 32u + /*padding for alignment*/ 32u,
Testables.OffsetAndSizes[3].offset);
VERIFY_ARE_EQUAL(64u, Testables.OffsetAndSizes[3].size);
}
VERIFY_ARE_EQUAL(4u, Testables.AllocaWrites.size());
ValidateAllocaWrite(Testables.AllocaWrites, 0, "b1");
ValidateAllocaWrite(Testables.AllocaWrites, 1, "sixteen");
ValidateAllocaWrite(Testables.AllocaWrites, 2, "thirtytwo");
ValidateAllocaWrite(Testables.AllocaWrites, 3, "sixtyfour");
}
}
TEST_F(PixTest, PixStructAnnotation_StructWithinStruct) {
if (m_ver.SkipDxilVersion(1, 5))
return;
for (auto choice : OptimizationChoices) {
auto optimization = choice.Flag;
const char *hlsl = R"(
struct Contained
{
uint32_t one;
uint32_t two;
};
struct smallPayload
{
uint32_t before;
Contained contained;
uint32_t after;
};
[numthreads(1, 1, 1)]
void main()
{
smallPayload p;
p.before = 0xb4;
p.contained.one = 1;
p.contained.two = 2;
p.after = 3;
DispatchMesh(1, 1, 1, p);
}
)";
auto Testables = TestStructAnnotationCase(hlsl, optimization);
if (!choice.IsOptimized) {
VERIFY_ARE_EQUAL(1u, Testables.OffsetAndSizes.size());
VERIFY_ARE_EQUAL(4u, Testables.OffsetAndSizes[0].countOfMembers);
VERIFY_ARE_EQUAL(0u, Testables.OffsetAndSizes[0].offset);
VERIFY_ARE_EQUAL(4u * 32u, Testables.OffsetAndSizes[0].size);
} else {
VERIFY_ARE_EQUAL(4u, Testables.OffsetAndSizes.size());
for (unsigned i = 0; i < 4; i++) {
VERIFY_ARE_EQUAL(1u, Testables.OffsetAndSizes[i].countOfMembers);
VERIFY_ARE_EQUAL(i * 32u, Testables.OffsetAndSizes[i].offset);
VERIFY_ARE_EQUAL(32u, Testables.OffsetAndSizes[i].size);
}
}
ValidateAllocaWrite(Testables.AllocaWrites, 0, "before");
ValidateAllocaWrite(Testables.AllocaWrites, 1, "contained.one");
ValidateAllocaWrite(Testables.AllocaWrites, 2, "contained.two");
ValidateAllocaWrite(Testables.AllocaWrites, 3, "after");
}
}
TEST_F(PixTest, PixStructAnnotation_1DArray) {
if (m_ver.SkipDxilVersion(1, 5))
return;
for (auto choice : OptimizationChoices) {
auto optimization = choice.Flag;
const char *hlsl = R"(
struct smallPayload
{
uint32_t Array[2];
};
[numthreads(1, 1, 1)]
void main()
{
smallPayload p;
p.Array[0] = 250;
p.Array[1] = 251;
DispatchMesh(1, 1, 1, p);
}
)";
auto Testables = TestStructAnnotationCase(hlsl, optimization);
if (!choice.IsOptimized) {
VERIFY_ARE_EQUAL(1u, Testables.OffsetAndSizes.size());
VERIFY_ARE_EQUAL(2u, Testables.OffsetAndSizes[0].countOfMembers);
VERIFY_ARE_EQUAL(0u, Testables.OffsetAndSizes[0].offset);
VERIFY_ARE_EQUAL(2u * 32u, Testables.OffsetAndSizes[0].size);
} else {
VERIFY_ARE_EQUAL(2u, Testables.OffsetAndSizes.size());
for (unsigned i = 0; i < 2; i++) {
VERIFY_ARE_EQUAL(1u, Testables.OffsetAndSizes[i].countOfMembers);
VERIFY_ARE_EQUAL(i * 32u, Testables.OffsetAndSizes[i].offset);
VERIFY_ARE_EQUAL(32u, Testables.OffsetAndSizes[i].size);
}
}
VERIFY_ARE_EQUAL(2u, Testables.AllocaWrites.size());
int Idx = 0;
ValidateAllocaWrite(Testables.AllocaWrites, Idx++, "Array[0]");
ValidateAllocaWrite(Testables.AllocaWrites, Idx++, "Array[1]");
}
}
TEST_F(PixTest, PixStructAnnotation_2DArray) {
if (m_ver.SkipDxilVersion(1, 5))
return;
for (auto choice : OptimizationChoices) {
auto optimization = choice.Flag;
const char *hlsl = R"(
struct smallPayload
{
uint32_t TwoDArray[2][3];
};
[numthreads(1, 1, 1)]
void main()
{
smallPayload p;
p.TwoDArray[0][0] = 250;
p.TwoDArray[0][1] = 251;
p.TwoDArray[0][2] = 252;
p.TwoDArray[1][0] = 253;
p.TwoDArray[1][1] = 254;
p.TwoDArray[1][2] = 255;
DispatchMesh(1, 1, 1, p);
}
)";
auto Testables = TestStructAnnotationCase(hlsl, optimization);
if (!choice.IsOptimized) {
VERIFY_ARE_EQUAL(1u, Testables.OffsetAndSizes.size());
VERIFY_ARE_EQUAL(6u, Testables.OffsetAndSizes[0].countOfMembers);
VERIFY_ARE_EQUAL(0u, Testables.OffsetAndSizes[0].offset);
VERIFY_ARE_EQUAL(2u * 3u * 32u, Testables.OffsetAndSizes[0].size);
} else {
VERIFY_ARE_EQUAL(6u, Testables.OffsetAndSizes.size());
for (unsigned i = 0; i < 6; i++) {
VERIFY_ARE_EQUAL(1u, Testables.OffsetAndSizes[i].countOfMembers);
VERIFY_ARE_EQUAL(i * 32u, Testables.OffsetAndSizes[i].offset);
VERIFY_ARE_EQUAL(32u, Testables.OffsetAndSizes[i].size);
}
}
VERIFY_ARE_EQUAL(6u, Testables.AllocaWrites.size());
int Idx = 0;
ValidateAllocaWrite(Testables.AllocaWrites, Idx++, "TwoDArray[0][0]");
ValidateAllocaWrite(Testables.AllocaWrites, Idx++, "TwoDArray[0][1]");
ValidateAllocaWrite(Testables.AllocaWrites, Idx++, "TwoDArray[0][2]");
ValidateAllocaWrite(Testables.AllocaWrites, Idx++, "TwoDArray[1][0]");
ValidateAllocaWrite(Testables.AllocaWrites, Idx++, "TwoDArray[1][1]");
ValidateAllocaWrite(Testables.AllocaWrites, Idx++, "TwoDArray[1][2]");
}
}
TEST_F(PixTest, PixStructAnnotation_EmbeddedArray) {
if (m_ver.SkipDxilVersion(1, 5))
return;
for (auto choice : OptimizationChoices) {
auto optimization = choice.Flag;
const char *hlsl = R"(
struct Contained
{
uint32_t array[3];
};
struct smallPayload
{
uint32_t before;
Contained contained;
uint32_t after;
};
[numthreads(1, 1, 1)]
void main()
{
smallPayload p;
p.before = 0xb4;
p.contained.array[0] = 0;
p.contained.array[1] = 1;
p.contained.array[2] = 2;
p.after = 3;
DispatchMesh(1, 1, 1, p);
}
)";
auto Testables = TestStructAnnotationCase(hlsl, optimization);
if (!choice.IsOptimized) {
VERIFY_ARE_EQUAL(1u, Testables.OffsetAndSizes.size());
VERIFY_ARE_EQUAL(5u, Testables.OffsetAndSizes[0].countOfMembers);
VERIFY_ARE_EQUAL(0u, Testables.OffsetAndSizes[0].offset);
VERIFY_ARE_EQUAL(5u * 32u, Testables.OffsetAndSizes[0].size);
} else {
VERIFY_ARE_EQUAL(5u, Testables.OffsetAndSizes.size());
for (unsigned i = 0; i < 5; i++) {
VERIFY_ARE_EQUAL(1u, Testables.OffsetAndSizes[i].countOfMembers);
VERIFY_ARE_EQUAL(i * 32u, Testables.OffsetAndSizes[i].offset);
VERIFY_ARE_EQUAL(32u, Testables.OffsetAndSizes[i].size);
}
}
ValidateAllocaWrite(Testables.AllocaWrites, 0, "before");
ValidateAllocaWrite(Testables.AllocaWrites, 1, "contained.array[0]");
ValidateAllocaWrite(Testables.AllocaWrites, 2, "contained.array[1]");
ValidateAllocaWrite(Testables.AllocaWrites, 3, "contained.array[2]");
ValidateAllocaWrite(Testables.AllocaWrites, 4, "after");
}
}
TEST_F(PixTest, PixStructAnnotation_FloatN) {
if (m_ver.SkipDxilVersion(1, 5))
return;
for (auto choice : OptimizationChoices) {
auto optimization = choice.Flag;
auto IsOptimized = choice.IsOptimized;
const char *hlsl = R"(
struct smallPayload
{
float2 f2;
};
[numthreads(1, 1, 1)]
void main()
{
smallPayload p;
p.f2 = float2(1,2);
DispatchMesh(1, 1, 1, p);
}
)";
auto Testables = TestStructAnnotationCase(hlsl, optimization);
if (IsOptimized) {
VERIFY_ARE_EQUAL(2u, Testables.OffsetAndSizes.size());
VERIFY_ARE_EQUAL(1u, Testables.OffsetAndSizes[0].countOfMembers);
VERIFY_ARE_EQUAL(1u, Testables.OffsetAndSizes[1].countOfMembers);
VERIFY_ARE_EQUAL(0u, Testables.OffsetAndSizes[0].offset);
VERIFY_ARE_EQUAL(32u, Testables.OffsetAndSizes[0].size);
VERIFY_ARE_EQUAL(32u, Testables.OffsetAndSizes[1].offset);
VERIFY_ARE_EQUAL(32u, Testables.OffsetAndSizes[1].size);
} else {
VERIFY_ARE_EQUAL(1u, Testables.OffsetAndSizes.size());
VERIFY_ARE_EQUAL(2u, Testables.OffsetAndSizes[0].countOfMembers);
VERIFY_ARE_EQUAL(0u, Testables.OffsetAndSizes[0].offset);
VERIFY_ARE_EQUAL(32u + 32u, Testables.OffsetAndSizes[0].size);
}
VERIFY_ARE_EQUAL(Testables.AllocaWrites.size(), 2u);
ValidateAllocaWrite(Testables.AllocaWrites, 0, "f2.x");
ValidateAllocaWrite(Testables.AllocaWrites, 1, "f2.y");
}
}
TEST_F(PixTest, PixStructAnnotation_SequentialFloatN) {
if (m_ver.SkipDxilVersion(1, 5))
return;
for (auto choice : OptimizationChoices) {
auto optimization = choice.Flag;
const char *hlsl = R"(
struct smallPayload
{
float3 color;
float3 dir;
};
[numthreads(1, 1, 1)]
void main()
{
smallPayload p;
p.color = float3(1,2,3);
p.dir = float3(4,5,6);
DispatchMesh(1, 1, 1, p);
}
)";
auto Testables = TestStructAnnotationCase(hlsl, optimization);
if (choice.IsOptimized) {
VERIFY_ARE_EQUAL(6u, Testables.OffsetAndSizes.size());
for (unsigned i = 0; i < 6; i++) {
VERIFY_ARE_EQUAL(1u, Testables.OffsetAndSizes[i].countOfMembers);
VERIFY_ARE_EQUAL(i * 32u, Testables.OffsetAndSizes[i].offset);
VERIFY_ARE_EQUAL(32u, Testables.OffsetAndSizes[i].size);
}
} else {
VERIFY_ARE_EQUAL(1u, Testables.OffsetAndSizes.size());
VERIFY_ARE_EQUAL(6u, Testables.OffsetAndSizes[0].countOfMembers);
VERIFY_ARE_EQUAL(0u, Testables.OffsetAndSizes[0].offset);
VERIFY_ARE_EQUAL(32u * 6u, Testables.OffsetAndSizes[0].size);
}
VERIFY_ARE_EQUAL(6u, Testables.AllocaWrites.size());
ValidateAllocaWrite(Testables.AllocaWrites, 0, "color.x");
ValidateAllocaWrite(Testables.AllocaWrites, 1, "color.y");
ValidateAllocaWrite(Testables.AllocaWrites, 2, "color.z");
ValidateAllocaWrite(Testables.AllocaWrites, 3, "dir.x");
ValidateAllocaWrite(Testables.AllocaWrites, 4, "dir.y");
ValidateAllocaWrite(Testables.AllocaWrites, 5, "dir.z");
}
}
TEST_F(PixTest, PixStructAnnotation_EmbeddedFloatN) {
if (m_ver.SkipDxilVersion(1, 5))
return;
for (auto choice : OptimizationChoices) {
auto optimization = choice.Flag;
const char *hlsl = R"(
struct Embedded
{
float2 f2;
};
struct smallPayload
{
uint32_t i32;
Embedded e;
};
[numthreads(1, 1, 1)]
void main()
{
smallPayload p;
p.i32 = 32;
p.e.f2 = float2(1,2);
DispatchMesh(1, 1, 1, p);
}
)";
auto Testables = TestStructAnnotationCase(hlsl, optimization);
if (choice.IsOptimized) {
VERIFY_ARE_EQUAL(3u, Testables.OffsetAndSizes.size());
for (unsigned i = 0; i < 3; i++) {
VERIFY_ARE_EQUAL(1u, Testables.OffsetAndSizes[i].countOfMembers);
VERIFY_ARE_EQUAL(i * 32u, Testables.OffsetAndSizes[i].offset);
VERIFY_ARE_EQUAL(32u, Testables.OffsetAndSizes[i].size);
}
} else {
VERIFY_ARE_EQUAL(1u, Testables.OffsetAndSizes.size());
VERIFY_ARE_EQUAL(3u, Testables.OffsetAndSizes[0].countOfMembers);
VERIFY_ARE_EQUAL(0u, Testables.OffsetAndSizes[0].offset);
VERIFY_ARE_EQUAL(32u * 3u, Testables.OffsetAndSizes[0].size);
}
VERIFY_ARE_EQUAL(3u, Testables.AllocaWrites.size());
ValidateAllocaWrite(Testables.AllocaWrites, 0, "i32");
ValidateAllocaWrite(Testables.AllocaWrites, 1, "e.f2.x");
ValidateAllocaWrite(Testables.AllocaWrites, 2, "e.f2.y");
}
}
TEST_F(PixTest, PixStructAnnotation_Matrix) {
if (m_ver.SkipDxilVersion(1, 5))
return;
for (auto choice : OptimizationChoices) {
auto optimization = choice.Flag;
const char *hlsl = R"(
struct smallPayload
{
float4x4 mat;
};
[numthreads(1, 1, 1)]
void main()
{
smallPayload p;
p.mat = float4x4( 1,2,3,4, 5,6,7,8, 9,10,11,12, 13,14,15, 16);
DispatchMesh(1, 1, 1, p);
}
)";
auto Testables = TestStructAnnotationCase(hlsl, optimization);
// Can't test member iterator until dbg.declare instructions are emitted
// when structs contain pointers-to-pointers
VERIFY_ARE_EQUAL(16u, Testables.AllocaWrites.size());
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
std::string expected = std::string("mat._") + std::to_string(i + 1) +
std::to_string(j + 1);
ValidateAllocaWrite(Testables.AllocaWrites, i * 4 + j,
expected.c_str());
}
}
}
}
TEST_F(PixTest, PixStructAnnotation_MemberFunction) {
if (m_ver.SkipDxilVersion(1, 5))
return;
for (auto choice : OptimizationChoices) {
auto optimization = choice.Flag;
const char *hlsl = R"(
RWStructuredBuffer<float> floatRWUAV: register(u0);
struct smallPayload
{
int i;
};
float2 signNotZero(float2 v)
{
return (v > 0.0f ? float(1).xx : float(-1).xx);
}
float2 unpackUnorm2(uint packed)
{
return (1.0 / 65535.0) * float2((packed >> 16) & 0xffff, packed & 0xffff);
}
float3 unpackOctahedralSnorm(float2 e)
{
float3 v = float3(e.xy, 1.0f - abs(e.x) - abs(e.y));
if (v.z < 0.0f) v.xy = (1.0f - abs(v.yx)) * signNotZero(v.xy);
return normalize(v);
}
float3 unpackOctahedralUnorm(float2 e)
{
return unpackOctahedralSnorm(e * 2.0f - 1.0f);
}
float2 unpackHalf2(uint packed)
{
return float2(f16tof32(packed >> 16), f16tof32(packed & 0xffff));
}
struct Gbuffer
{
float3 worldNormal;
float3 objectNormal; //offset:12
float linearZ; //24
float prevLinearZ; //28
float fwidthLinearZ; //32
float fwidthObjectNormal; //36
uint materialType; //40
uint2 materialParams0; //44
uint4 materialParams1; //52 <--------- this is the variable that's being covered twice (52*8 = 416 416)
uint instanceId; //68 <------- and there's one dword left over, as expected
void load(int2 pixelPos, Texture2DArray<uint4> gbTex)
{
uint4 data0 = gbTex.Load(int4(pixelPos, 0, 0));
uint4 data1 = gbTex.Load(int4(pixelPos, 1, 0));
uint4 data2 = gbTex.Load(int4(pixelPos, 2, 0));
worldNormal = unpackOctahedralUnorm(unpackUnorm2(data0.x));
linearZ = f16tof32((data0.y >> 8) & 0xffff);
materialType = (data0.y & 0xff);
materialParams0 = data0.zw;
materialParams1 = data1.xyzw;
instanceId = data2.x;
prevLinearZ = asfloat(data2.y);
objectNormal = unpackOctahedralUnorm(unpackUnorm2(data2.z));
float2 fwidth = unpackHalf2(data2.w);
fwidthLinearZ = fwidth.x;
fwidthObjectNormal = fwidth.y;
}
};
Gbuffer loadGbuffer(int2 pixelPos, Texture2DArray<uint4> gbTex)
{
Gbuffer output;
output.load(pixelPos, gbTex);
return output;
}
Texture2DArray<uint4> g_gbuffer : register(t0, space0);
[numthreads(1, 1, 1)]
void main()
{
const Gbuffer gbuffer = loadGbuffer(int2(0,0), g_gbuffer);
smallPayload p;
p.i = gbuffer.materialParams1.x + gbuffer.materialParams1.y + gbuffer.materialParams1.z + gbuffer.materialParams1.w;
DispatchMesh(1, 1, 1, p);
}
)";
auto Testables = TestStructAnnotationCase(hlsl, optimization, true);
// TODO: Make 'this' work
// Can't validate # of writes: rel and dbg are different
// VERIFY_ARE_EQUAL(43, Testables.AllocaWrites.size());
// Can't test individual writes until struct member names are returned:
// for (int i = 0; i < 51; ++i)
//{
// ValidateAllocaWrite(Testables.AllocaWrites, i, "");
//}
}
}
TEST_F(PixTest, PixStructAnnotation_BigMess) {
if (m_ver.SkipDxilVersion(1, 5))
return;
for (auto choice : OptimizationChoices) {
auto optimization = choice.Flag;
const char *hlsl = R"(
struct BigStruct
{
uint64_t bigInt;
double bigDouble;
};
struct EmbeddedStruct
{
uint32_t OneInt;
uint32_t TwoDArray[2][2];
};
struct smallPayload
{
uint dummy;
uint vertexCount;
uint primitiveCount;
EmbeddedStruct embeddedStruct;
#ifdef PAYLOAD_MATRICES
float4x4 mat;
#endif
uint64_t bigOne;
half littleOne;
BigStruct bigStruct[2];
uint lastCheck;
};
[numthreads(1, 1, 1)]
void main()
{
smallPayload p;
// Adding enough instructions to make the shader interesting to debug:
p.dummy = 42;
p.vertexCount = 3;
p.primitiveCount = 1;
p.embeddedStruct.OneInt = 123;
p.embeddedStruct.TwoDArray[0][0] = 252;
p.embeddedStruct.TwoDArray[0][1] = 253;
p.embeddedStruct.TwoDArray[1][0] = 254;
p.embeddedStruct.TwoDArray[1][1] = 255;
#ifdef PAYLOAD_MATRICES
p.mat = float4x4( 1,2,3,4, 5,6,7,8, 9,10,11,12, 13,14,15, 16);
#endif
p.bigOne = 123456789;
p.littleOne = 1.0;
p.bigStruct[0].bigInt = 10;
p.bigStruct[0].bigDouble = 2.0;
p.bigStruct[1].bigInt = 20;
p.bigStruct[1].bigDouble = 4.0;
p.lastCheck = 27;
DispatchMesh(1, 1, 1, p);
}
)";
auto Testables = TestStructAnnotationCase(hlsl, optimization);
if (!choice.IsOptimized) {
VERIFY_ARE_EQUAL(1u, Testables.OffsetAndSizes.size());
VERIFY_ARE_EQUAL(15u, Testables.OffsetAndSizes[0].countOfMembers);
VERIFY_ARE_EQUAL(0u, Testables.OffsetAndSizes[0].offset);
constexpr uint32_t BigStructBitSize = 64 * 2;
constexpr uint32_t EmbeddedStructBitSize = 32 * 5;
VERIFY_ARE_EQUAL(3u * 32u + EmbeddedStructBitSize + 64u + 16u +
16u /*alignment for next field*/ +
BigStructBitSize * 2u + 32u +
32u /*align to max align*/,
Testables.OffsetAndSizes[0].size);
} else {
VERIFY_ARE_EQUAL(15u, Testables.OffsetAndSizes.size());
// First 8 members
for (unsigned i = 0; i < 8; i++) {
VERIFY_ARE_EQUAL(1u, Testables.OffsetAndSizes[i].countOfMembers);
VERIFY_ARE_EQUAL(i * 32u, Testables.OffsetAndSizes[i].offset);
VERIFY_ARE_EQUAL(32u, Testables.OffsetAndSizes[i].size);
}
// bigOne
VERIFY_ARE_EQUAL(1u, Testables.OffsetAndSizes[8].countOfMembers);
VERIFY_ARE_EQUAL(256u, Testables.OffsetAndSizes[8].offset);
VERIFY_ARE_EQUAL(64u, Testables.OffsetAndSizes[8].size);
// littleOne
VERIFY_ARE_EQUAL(1u, Testables.OffsetAndSizes[9].countOfMembers);
VERIFY_ARE_EQUAL(320u, Testables.OffsetAndSizes[9].offset);
VERIFY_ARE_EQUAL(16u, Testables.OffsetAndSizes[9].size);
// Each member of BigStruct[2]
for (unsigned i = 0; i < 4; i++) {
int idx = i + 10;
VERIFY_ARE_EQUAL(1u, Testables.OffsetAndSizes[idx].countOfMembers);
VERIFY_ARE_EQUAL(384 + i * 64u, Testables.OffsetAndSizes[idx].offset);
VERIFY_ARE_EQUAL(64u, Testables.OffsetAndSizes[idx].size);
}
VERIFY_ARE_EQUAL(1u, Testables.OffsetAndSizes[14].countOfMembers);
VERIFY_ARE_EQUAL(640u, Testables.OffsetAndSizes[14].offset);
VERIFY_ARE_EQUAL(32u, Testables.OffsetAndSizes[14].size);
}
VERIFY_ARE_EQUAL(15u, Testables.AllocaWrites.size());
size_t Index = 0;
ValidateAllocaWrite(Testables.AllocaWrites, Index++, "dummy");
ValidateAllocaWrite(Testables.AllocaWrites, Index++, "vertexCount");
ValidateAllocaWrite(Testables.AllocaWrites, Index++, "primitiveCount");
ValidateAllocaWrite(Testables.AllocaWrites, Index++,
"embeddedStruct.OneInt");
ValidateAllocaWrite(Testables.AllocaWrites, Index++,
"embeddedStruct.TwoDArray[0][0]");
ValidateAllocaWrite(Testables.AllocaWrites, Index++,
"embeddedStruct.TwoDArray[0][1]");
ValidateAllocaWrite(Testables.AllocaWrites, Index++,
"embeddedStruct.TwoDArray[1][0]");
ValidateAllocaWrite(Testables.AllocaWrites, Index++,
"embeddedStruct.TwoDArray[1][1]");
ValidateAllocaWrite(Testables.AllocaWrites, Index++, "bigOne");
ValidateAllocaWrite(Testables.AllocaWrites, Index++, "littleOne");
ValidateAllocaWrite(Testables.AllocaWrites, Index++, "bigStruct[0].bigInt");
ValidateAllocaWrite(Testables.AllocaWrites, Index++,
"bigStruct[0].bigDouble");
ValidateAllocaWrite(Testables.AllocaWrites, Index++, "bigStruct[1].bigInt");
ValidateAllocaWrite(Testables.AllocaWrites, Index++,
"bigStruct[1].bigDouble");
ValidateAllocaWrite(Testables.AllocaWrites, Index++, "lastCheck");
}
}
TEST_F(PixTest, PixStructAnnotation_AlignedFloat4Arrays) {
if (m_ver.SkipDxilVersion(1, 5))
return;
for (auto choice : OptimizationChoices) {
auto optimization = choice.Flag;
const char *hlsl = R"(
struct LinearSHSampleData
{
float4 linearTerms[3];
float4 hdrColorAO;
float4 visibilitySH;
} g_lhSampleData;
struct smallPayload
{
LinearSHSampleData lhSampleData;
};
[numthreads(1, 1, 1)]
void main()
{
smallPayload p;
p.lhSampleData.linearTerms[0].x = g_lhSampleData.linearTerms[0].x;
DispatchMesh(1, 1, 1, p);
}
)";
auto Testables = TestStructAnnotationCase(hlsl, optimization);
// Can't test offsets and sizes until dbg.declare instructions are emitted
// when floatn is used
// (https://github.com/microsoft/DirectXShaderCompiler/issues/2920)
// VERIFY_ARE_EQUAL(20, Testables.AllocaWrites.size());
}
}
TEST_F(PixTest, PixStructAnnotation_Inheritance) {
if (m_ver.SkipDxilVersion(1, 5))
return;
for (auto choice : OptimizationChoices) {
auto optimization = choice.Flag;
const char *hlsl = R"(
struct Base
{
float floatValue;
};
typedef Base BaseTypedef;
struct Derived : BaseTypedef
{
int intValue;
};
[numthreads(1, 1, 1)]
void main()
{
Derived p;
p.floatValue = 1.;
p.intValue = 2;
DispatchMesh(1, 1, 1, p);
}
)";
auto Testables = TestStructAnnotationCase(hlsl, optimization);
// Can't test offsets and sizes until dbg.declare instructions are emitted
// when floatn is used
// (https://github.com/microsoft/DirectXShaderCompiler/issues/2920)
// VERIFY_ARE_EQUAL(20, Testables.AllocaWrites.size());
}
}
TEST_F(PixTest, PixStructAnnotation_ResourceAsMember) {
if (m_ver.SkipDxilVersion(1, 5))
return;
for (auto choice : OptimizationChoices) {
auto optimization = choice.Flag;
const char *hlsl = R"(
Buffer g_texture;
struct smallPayload
{
float value;
};
struct WithEmbeddedObject
{
Buffer texture;
};
void DispatchIt(WithEmbeddedObject eo)
{
smallPayload p;
p.value = eo.texture.Load(0);
DispatchMesh(1, 1, 1, p);
}
[numthreads(1, 1, 1)]
void main()
{
WithEmbeddedObject eo;
eo.texture = g_texture;
DispatchIt(eo);
}
)";
auto Testables = TestStructAnnotationCase(hlsl, optimization);
// Can't test offsets and sizes until dbg.declare instructions are emitted
// when floatn is used
// (https://github.com/microsoft/DirectXShaderCompiler/issues/2920)
// VERIFY_ARE_EQUAL(20, Testables.AllocaWrites.size());
}
}
TEST_F(PixTest, PixStructAnnotation_WheresMyDbgValue) {
if (m_ver.SkipDxilVersion(1, 5))
return;
for (auto choice : OptimizationChoices) {
auto optimization = choice.Flag;
const char *hlsl = R"(
struct smallPayload
{
float f1;
float2 f2;
};
[numthreads(1, 1, 1)]
void main()
{
smallPayload p;
p.f1 = 1;
p.f2 = float2(2,3);
DispatchMesh(1, 1, 1, p);
}
)";
auto Testables = TestStructAnnotationCase(hlsl, optimization);
// Can't test offsets and sizes until dbg.declare instructions are emitted
// when floatn is used
// (https://github.com/microsoft/DirectXShaderCompiler/issues/2920)
VERIFY_ARE_EQUAL(3u, Testables.AllocaWrites.size());
}
}
TEST_F(PixTest, VirtualRegisters_InstructionCounts) {
if (m_ver.SkipDxilVersion(1, 5))
return;
for (auto choice : OptimizationChoices) {
auto optimization = choice.Flag;
const char *hlsl = R"(
RaytracingAccelerationStructure Scene : register(t0, space0);
RWTexture2D<float4> RenderTarget : register(u0);
struct SceneConstantBuffer
{
float4x4 projectionToWorld;
float4 cameraPosition;
float4 lightPosition;
float4 lightAmbientColor;
float4 lightDiffuseColor;
};
ConstantBuffer<SceneConstantBuffer> g_sceneCB : register(b0);
struct RayPayload
{
float4 color;
};
inline void GenerateCameraRay(uint2 index, out float3 origin, out float3 direction)
{
float2 xy = index + 0.5f; // center in the middle of the pixel.
float2 screenPos = xy;// / DispatchRaysDimensions().xy * 2.0 - 1.0;
// Invert Y for DirectX-style coordinates.
screenPos.y = -screenPos.y;
// Unproject the pixel coordinate into a ray.
float4 world = /*mul(*/float4(screenPos, 0, 1)/*, g_sceneCB.projectionToWorld)*/;
//world.xyz /= world.w;
origin = world.xyz; //g_sceneCB.cameraPosition.xyz;
direction = float3(1,0,0);//normalize(world.xyz - origin);
}
void RaygenCommon()
{
float3 rayDir;
float3 origin;
// Generate a ray for a camera pixel corresponding to an index from the dispatched 2D grid.
GenerateCameraRay(DispatchRaysIndex().xy, origin, rayDir);
// Trace the ray.
// Set the ray's extents.
RayDesc ray;
ray.Origin = origin;
ray.Direction = rayDir;
// Set TMin to a non-zero small value to avoid aliasing issues due to floating - point errors.
// TMin should be kept small to prevent missing geometry at close contact areas.
ray.TMin = 0.001;
ray.TMax = 10000.0;
RayPayload payload = { float4(0, 0, 0, 0) };
TraceRay(Scene, RAY_FLAG_CULL_BACK_FACING_TRIANGLES, ~0, 0, 1, 0, ray, payload);
// Write the raytraced color to the output texture.
// RenderTarget[DispatchRaysIndex().xy] = payload.color;
}
[shader("raygeneration")]
void Raygen0()
{
RaygenCommon();
}
[shader("raygeneration")]
void Raygen1()
{
RaygenCommon();
}
typedef BuiltInTriangleIntersectionAttributes MyAttributes;
[shader("closesthit")]
void InnerClosestHitShader(inout RayPayload payload, in MyAttributes attr)
{
payload.color = float4(0,1,0,0);
}
[shader("miss")]
void MyMissShader(inout RayPayload payload)
{
payload.color = float4(1, 0, 0, 0);
})";
CComPtr<IDxcBlob> pBlob =
Compile(m_dllSupport, hlsl, L"lib_6_6", {optimization});
CComPtr<IDxcBlob> pDxil = FindModule(DFCC_ShaderDebugInfoDXIL, pBlob);
auto outputLines = RunAnnotationPasses(m_dllSupport, pDxil).lines;
const char instructionRangeLabel[] = "InstructionRange:";
// The numbering pass should have counted instructions for each
// "interesting" (to PIX) function and output its start and (end+1)
// instruction ordinal. End should always be a reasonable number of
// instructions (>10) and end should always be higher than start, and all
// four functions above should be represented.
int countOfInstructionRangeLines = 0;
for (auto const &line : outputLines) {
auto tokens = Tokenize(line, " ");
if (tokens.size() >= 4) {
if (tokens[0] == instructionRangeLabel) {
countOfInstructionRangeLines++;
int instructionStart = atoi(tokens[1].c_str());
int instructionEnd = atoi(tokens[2].c_str());
VERIFY_IS_TRUE(instructionEnd > 10);
VERIFY_IS_TRUE(instructionEnd > instructionStart);
auto found1 = tokens[3].find("Raygen0@@YAXXZ") != std::string::npos;
auto found2 = tokens[3].find("Raygen1@@YAXXZ") != std::string::npos;
auto foundClosest =
tokens[3].find("InnerClosestHit") != std::string::npos;
auto foundMiss = tokens[3].find("MyMiss") != std::string::npos;
VERIFY_IS_TRUE(found1 || found2 || foundClosest || foundMiss);
}
}
}
VERIFY_ARE_EQUAL(4, countOfInstructionRangeLines);
// Non-library target:
const char *PixelShader = R"(
[RootSignature("")]
float main(float pos : A) : SV_Target {
float x = abs(pos);
float y = sin(pos);
float z = x + y;
return z;
}
)";
pBlob = Compile(m_dllSupport, PixelShader, L"ps_6_6", {optimization});
pDxil = FindModule(DFCC_ShaderDebugInfoDXIL, pBlob);
outputLines = RunAnnotationPasses(m_dllSupport, pDxil).lines;
countOfInstructionRangeLines = 0;
for (auto const &line : outputLines) {
auto tokens = Tokenize(line, " ");
if (tokens.size() >= 4) {
if (tokens[0] == instructionRangeLabel) {
countOfInstructionRangeLines++;
int instructionStart = atoi(tokens[1].c_str());
int instructionEnd = atoi(tokens[2].c_str());
VERIFY_IS_TRUE(instructionStart == 0);
VERIFY_IS_TRUE(instructionEnd > 10);
VERIFY_IS_TRUE(instructionEnd > instructionStart);
auto foundMain = tokens[3].find("main") != std::string::npos;
VERIFY_IS_TRUE(foundMain);
}
}
}
VERIFY_ARE_EQUAL(1, countOfInstructionRangeLines);
// Now check that the initial value parameter works:
const int startingInstructionOrdinal = 1234;
outputLines =
RunAnnotationPasses(m_dllSupport, pDxil, startingInstructionOrdinal)
.lines;
countOfInstructionRangeLines = 0;
for (auto const &line : outputLines) {
auto tokens = Tokenize(line, " ");
if (tokens.size() >= 4) {
if (tokens[0] == instructionRangeLabel) {
countOfInstructionRangeLines++;
int instructionStart = atoi(tokens[1].c_str());
int instructionEnd = atoi(tokens[2].c_str());
VERIFY_IS_TRUE(instructionStart == startingInstructionOrdinal);
VERIFY_IS_TRUE(instructionEnd > instructionStart);
auto foundMain = tokens[3].find("main") != std::string::npos;
VERIFY_IS_TRUE(foundMain);
}
}
}
VERIFY_ARE_EQUAL(1, countOfInstructionRangeLines);
}
}
TEST_F(PixTest, VirtualRegisters_AlignedOffsets) {
if (m_ver.SkipDxilVersion(1, 5))
return;
{
const char *hlsl = R"(
cbuffer cbEveryFrame : register(b0)
{
int i32;
float f32;
};
struct VS_OUTPUT_ENV
{
float4 Pos : SV_Position;
float2 Tex : TEXCOORD0;
};
float4 main(VS_OUTPUT_ENV input) : SV_Target
{
// (BTW we load from i32 and f32 (which are resident in a cb) so that these local variables aren't optimized away)
bool i1 = i32 != 0;
min16uint u16 = (min16uint)(i32 / 4);
min16int s16 = (min16int)(i32/4) * -1; // signed s16 gets -8
min12int s12 = (min12int)(i32/8) * -1; // signed s12 gets -4
half h = (half) f32 / 2.f; // f32 is initialized to 32.0 in8he CB, so the 16-bit type now has "16.0" in it
min16float mf16 = (min16float) f32 / -2.f;
min10float mf10 = (min10float) f32 / -4.f;
return float4((float)(i1 + u16) / 2.f, (float)(s16 + s12) / -128.f, h / 128.f, mf16 / 128.f + mf10 / 256.f);
}
)";
// This is little more than a crash test, designed to exercise a previously
// over-active assert..
std::vector<std::pair<const wchar_t *, std::vector<const wchar_t *>>>
argSets = {
{L"ps_6_0", {L"-Od"}},
{L"ps_6_2", {L"-Od", L"-HV", L"2018", L"-enable-16bit-types"}}};
for (auto const &args : argSets) {
CComPtr<IDxcBlob> pBlob =
Compile(m_dllSupport, hlsl, args.first, args.second);
CComPtr<IDxcBlob> pDxil = FindModule(DFCC_ShaderDebugInfoDXIL, pBlob);
RunAnnotationPasses(m_dllSupport, pDxil);
}
}
}
static void VerifyOperationSucceeded(IDxcOperationResult *pResult) {
HRESULT result;
VERIFY_SUCCEEDED(pResult->GetStatus(&result));
if (FAILED(result)) {
CComPtr<IDxcBlobEncoding> pErrors;
VERIFY_SUCCEEDED(pResult->GetErrorBuffer(&pErrors));
CA2W errorsWide(BlobToUtf8(pErrors).c_str());
WEX::Logging::Log::Comment(errorsWide);
}
VERIFY_SUCCEEDED(result);
}
TEST_F(PixTest, RootSignatureUpgrade_SubObjects) {
const char *source = R"x(
GlobalRootSignature so_GlobalRootSignature =
{
"RootConstants(num32BitConstants=1, b8), "
};
StateObjectConfig so_StateObjectConfig =
{
STATE_OBJECT_FLAGS_ALLOW_LOCAL_DEPENDENCIES_ON_EXTERNAL_DEFINITONS
};
LocalRootSignature so_LocalRootSignature1 =
{
"RootConstants(num32BitConstants=3, b2), "
"UAV(u6),RootFlags(LOCAL_ROOT_SIGNATURE)"
};
LocalRootSignature so_LocalRootSignature2 =
{
"RootConstants(num32BitConstants=3, b2), "
"UAV(u8, flags=DATA_STATIC), "
"RootFlags(LOCAL_ROOT_SIGNATURE)"
};
RaytracingShaderConfig so_RaytracingShaderConfig =
{
128, // max payload size
32 // max attribute size
};
RaytracingPipelineConfig so_RaytracingPipelineConfig =
{
2 // max trace recursion depth
};
TriangleHitGroup MyHitGroup =
{
"MyAnyHit", // AnyHit
"MyClosestHit", // ClosestHit
};
SubobjectToExportsAssociation so_Association1 =
{
"so_LocalRootSignature1", // subobject name
"MyRayGen" // export association
};
SubobjectToExportsAssociation so_Association2 =
{
"so_LocalRootSignature2", // subobject name
"MyAnyHit" // export association
};
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";
CComPtr<IDxcCompiler> pCompiler;
VERIFY_SUCCEEDED(m_dllSupport.CreateInstance(CLSID_DxcCompiler, &pCompiler));
CComPtr<IDxcBlobEncoding> pSource;
Utf8ToBlob(m_dllSupport, source, &pSource);
CComPtr<IDxcOperationResult> pResult;
VERIFY_SUCCEEDED(pCompiler->Compile(pSource, L"source.hlsl", L"", L"lib_6_6",
nullptr, 0, nullptr, 0, nullptr,
&pResult));
VerifyOperationSucceeded(pResult);
CComPtr<IDxcBlob> compiled;
VERIFY_SUCCEEDED(pResult->GetResult(&compiled));
auto optimizedContainer = RunShaderAccessTrackingPass(compiled);
const char *pBlobContent =
reinterpret_cast<const char *>(optimizedContainer->GetBufferPointer());
unsigned blobSize = optimizedContainer->GetBufferSize();
const hlsl::DxilContainerHeader *pContainerHeader =
hlsl::IsDxilContainerLike(pBlobContent, blobSize);
const hlsl::DxilPartHeader *pPartHeader =
GetDxilPartByType(pContainerHeader, hlsl::DFCC_RuntimeData);
VERIFY_ARE_NOT_EQUAL(pPartHeader, nullptr);
hlsl::RDAT::DxilRuntimeData rdat(GetDxilPartData(pPartHeader),
pPartHeader->PartSize);
auto const subObjectTableReader = rdat.GetSubobjectTable();
// There are 9 subobjects in the HLSL above:
VERIFY_ARE_EQUAL(subObjectTableReader.Count(), 9u);
bool foundGlobalRS = false;
for (uint32_t i = 0; i < subObjectTableReader.Count(); ++i) {
auto subObject = subObjectTableReader[i];
hlsl::DXIL::SubobjectKind subobjectKind = subObject.getKind();
switch (subobjectKind) {
case hlsl::DXIL::SubobjectKind::GlobalRootSignature: {
foundGlobalRS = true;
VERIFY_IS_TRUE(0 ==
strcmp(subObject.getName(), "so_GlobalRootSignature"));
auto rootSigReader = subObject.getRootSignature();
DxilVersionedRootSignatureDesc const *rootSignature = nullptr;
DeserializeRootSignature(rootSigReader.getData(),
rootSigReader.sizeData(), &rootSignature);
VERIFY_ARE_EQUAL(rootSignature->Version,
DxilRootSignatureVersion::Version_1_1);
VERIFY_ARE_EQUAL(rootSignature->Desc_1_1.NumParameters, 2u);
VERIFY_ARE_EQUAL(rootSignature->Desc_1_1.pParameters[1].ParameterType,
DxilRootParameterType::UAV);
VERIFY_ARE_EQUAL(rootSignature->Desc_1_1.pParameters[1].ShaderVisibility,
DxilShaderVisibility::All);
VERIFY_ARE_EQUAL(
rootSignature->Desc_1_1.pParameters[1].Descriptor.RegisterSpace,
static_cast<uint32_t>(-2));
VERIFY_ARE_EQUAL(
rootSignature->Desc_1_1.pParameters[1].Descriptor.ShaderRegister, 0u);
DeleteRootSignature(rootSignature);
break;
}
}
}
VERIFY_IS_TRUE(foundGlobalRS);
}
TEST_F(PixTest, RootSignatureUpgrade_Annotation) {
const char *dynamicTextureAccess = R"x(
Texture1D<float4> tex[5] : register(t3);
SamplerState SS[3] : register(s2);
[RootSignature("DescriptorTable(SRV(t3, numDescriptors=5)),\
DescriptorTable(Sampler(s2, numDescriptors=3))")]
float4 main(int i : A, float j : B) : SV_TARGET
{
float4 r = tex[i].Sample(SS[i], i);
return r;
}
)x";
auto compiled = Compile(m_dllSupport, dynamicTextureAccess, L"ps_6_6");
auto pOptimizedContainer = RunShaderAccessTrackingPass(compiled);
const char *pBlobContent =
reinterpret_cast<const char *>(pOptimizedContainer->GetBufferPointer());
unsigned blobSize = pOptimizedContainer->GetBufferSize();
const hlsl::DxilContainerHeader *pContainerHeader =
hlsl::IsDxilContainerLike(pBlobContent, blobSize);
const hlsl::DxilPartHeader *pPartHeader =
GetDxilPartByType(pContainerHeader, hlsl::DFCC_RootSignature);
VERIFY_ARE_NOT_EQUAL(pPartHeader, nullptr);
hlsl::RootSignatureHandle RSH;
RSH.LoadSerialized((const uint8_t *)GetDxilPartData(pPartHeader),
pPartHeader->PartSize);
RSH.Deserialize();
auto const *desc = RSH.GetDesc();
bool foundGlobalRS = false;
VERIFY_ARE_EQUAL(desc->Version, hlsl::DxilRootSignatureVersion::Version_1_1);
VERIFY_ARE_EQUAL(desc->Desc_1_1.NumParameters, 3u);
for (unsigned int i = 0; i < desc->Desc_1_1.NumParameters; ++i) {
hlsl::DxilRootParameter1 const *param = desc->Desc_1_1.pParameters + i;
switch (param->ParameterType) {
case hlsl::DxilRootParameterType::UAV:
VERIFY_ARE_EQUAL(param->Descriptor.RegisterSpace,
static_cast<uint32_t>(-2));
VERIFY_ARE_EQUAL(param->Descriptor.ShaderRegister, 0u);
foundGlobalRS = true;
break;
}
}
VERIFY_IS_TRUE(foundGlobalRS);
}
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(m_dllSupport, source, L"lib_6_6", {});
RunDxilPIXDXRInvocationsLog(compiledLib);
}
TEST_F(PixTest, DebugInstrumentation_TextOutput) {
const char *source = R"x(
float4 main() : SV_Target {
return float4(0,0,0,0);
})x";
auto compiled = Compile(m_dllSupport, source, L"ps_6_0", {});
auto output = RunDebugPass(compiled, 8 /*ludicrously low UAV size limit*/);
bool foundStaticOverflow = false;
bool foundCounterOffset = false;
bool foundThreshold = false;
for (auto const &line : output.lines) {
if (line.find("StaticOverflow:12") != std::string::npos)
foundStaticOverflow = true;
if (line.find("InterestingCounterOffset:3") != std::string::npos)
foundCounterOffset = true;
if (line.find("OverflowThreshold:1") != std::string::npos)
foundThreshold = true;
}
VERIFY_IS_TRUE(foundStaticOverflow);
}
TEST_F(PixTest, DebugInstrumentation_BlockReport) {
const char *source = R"x(
RWStructuredBuffer<int> UAV: register(u0);
float4 main() : SV_Target {
// basic int variable
int v = UAV[0];
if(v == 0)
UAV[1] = v;
else
UAV[2] = v;
// float with indexed alloca
float f[2];
f[0] = UAV[4];
f[1] = UAV[5];
if(v == 2)
f[0] = v;
else
f[1] = v;
float farray2[2];
farray2[0] = UAV[4];
farray2[1] = UAV[5];
if(v == 4)
farray2[0] = v;
else
farray2[1] = v;
double d = UAV[8];
int64_t i64 = UAV[9];
return float4(d,i64,0,0);
})x";
auto compiled = Compile(m_dllSupport, source, L"ps_6_0", {L"-Od"});
auto output = RunDebugPass(compiled);
bool foundBlock = false;
bool foundRet = false;
bool foundUnnumberedVoidProllyADXNothing = false;
bool found32BitAssignment = false;
bool foundFloatAssignment = false;
bool foundDoubleAssignment = false;
bool found64BitAssignment = false;
bool found32BitAllocaStore = false;
for (auto const &line : output.lines) {
if (line.find("Block#") != std::string::npos) {
if (line.find("r,0,r;") != std::string::npos)
foundRet = true;
if (line.find("v,0,v;") != std::string::npos)
foundUnnumberedVoidProllyADXNothing = true;
if (line.find("3,3,a;") != std::string::npos)
found32BitAssignment = true;
if (line.find("d,13,a;") != std::string::npos)
foundDoubleAssignment = true;
if (line.find("f,19,a;") != std::string::npos)
foundFloatAssignment = true;
if (line.find("6,16,a;") != std::string::npos)
found64BitAssignment = true;
if (line.find("3,3,s,2+0;") != std::string::npos)
found32BitAllocaStore = true;
foundBlock = true;
}
}
VERIFY_IS_TRUE(foundBlock);
VERIFY_IS_TRUE(foundRet);
VERIFY_IS_TRUE(foundUnnumberedVoidProllyADXNothing);
VERIFY_IS_TRUE(found32BitAssignment);
VERIFY_IS_TRUE(found64BitAssignment);
VERIFY_IS_TRUE(foundFloatAssignment);
VERIFY_IS_TRUE(foundDoubleAssignment);
VERIFY_IS_TRUE(found32BitAllocaStore);
}