Xbox-ATG-Samples/PCSamples/Raytracing/SimpleRaytracingTriangle_PC12/SimpleRaytracingTriangle_PC...

788 строки
34 KiB
C++

//--------------------------------------------------------------------------------------
// SimpleRaytracingTriangle_PC12.cpp
//
// Advanced Technology Group (ATG)
// Copyright (C) Microsoft Corporation. All rights reserved.
//--------------------------------------------------------------------------------------
#include "pch.h"
#include "SimpleRaytracingTriangle_PC12.h"
#include "RayTracingHelper.h"
#include "CompiledShaders\SimpleRaytracing.hlsl.h"
extern void ExitSample() noexcept;
using namespace DirectX;
using Microsoft::WRL::ComPtr;
namespace
{
const wchar_t* c_hitGroupName = L"HitGroup";
const wchar_t* c_raygenShaderName = L"RayGenerationShader";
const wchar_t* c_closestHitShaderName = L"ClosestHitShader";
const wchar_t* c_missShaderName = L"MissShader";
enum GlobalRootSigIndex : uint32_t
{
GlobalRootSigIndex_OutputViewSlot = 0,
GlobalRootSigIndex_AccelerationStructureSlot,
GlobalRootSigIndex_Count
};
// Allocate upload heap buffer and fill with input data.
inline void AllocateUploadBuffer(
ID3D12Device* pDevice,
void* pData,
UINT64 datasize,
ID3D12Resource** ppResource,
const wchar_t* resourceName = nullptr)
{
auto uploadHeapProperties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD);
auto bufferDesc = CD3DX12_RESOURCE_DESC::Buffer(datasize);
DX::ThrowIfFailed(pDevice->CreateCommittedResource(
&uploadHeapProperties,
D3D12_HEAP_FLAG_NONE,
&bufferDesc,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(ppResource)));
if (resourceName)
{
(*ppResource)->SetName(resourceName);
}
if (pData)
{
void *pMappedData;
DX::ThrowIfFailed((*ppResource)->Map(0, nullptr, &pMappedData));
memcpy(pMappedData, pData, datasize);
(*ppResource)->Unmap(0, nullptr);
}
}
inline void AllocateUAVBuffer(
ID3D12Device* pDevice,
UINT64 bufferSize,
ID3D12Resource **ppResource,
D3D12_RESOURCE_STATES initialResourceState = D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
const wchar_t* resourceName = nullptr)
{
if (bufferSize == 0)
{
*ppResource = nullptr;
return;
}
auto defaultProperties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);
auto bufferDesc = CD3DX12_RESOURCE_DESC::Buffer(bufferSize, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS);
DX::ThrowIfFailed(pDevice->CreateCommittedResource(
&defaultProperties,
D3D12_HEAP_FLAG_NONE,
&bufferDesc,
initialResourceState,
nullptr,
IID_PPV_ARGS(ppResource)));
if (resourceName)
{
(*ppResource)->SetName(resourceName);
}
}
#ifdef _DEBUG
// Pretty-print a state object tree.
inline void PrintStateObjectDesc(const D3D12_STATE_OBJECT_DESC* desc)
{
std::wstringstream wstr;
wstr << L"\n";
wstr << L"--------------------------------------------------------------------\n";
wstr << L"| D3D12 State Object 0x" << static_cast<const void*>(desc) << L": ";
if (desc->Type == D3D12_STATE_OBJECT_TYPE_COLLECTION) wstr << L"Collection\n";
if (desc->Type == D3D12_STATE_OBJECT_TYPE_RAYTRACING_PIPELINE) wstr << L"Raytracing Pipeline\n";
auto ExportTree = [](UINT depth, UINT numExports, const D3D12_EXPORT_DESC* exports)
{
std::wostringstream woss;
for (UINT i = 0; i < numExports; i++)
{
woss << L"|";
if (depth > 0)
{
for (UINT j = 0; j < 2 * depth - 1; j++) woss << L" ";
}
woss << L" [" << i << L"]: ";
if (exports[i].ExportToRename) woss << exports[i].ExportToRename << L" --> ";
woss << exports[i].Name << L"\n";
}
return woss.str();
};
for (UINT i = 0; i < desc->NumSubobjects; i++)
{
wstr << L"| [" << i << L"]: ";
switch (desc->pSubobjects[i].Type)
{
case D3D12_STATE_SUBOBJECT_TYPE_GLOBAL_ROOT_SIGNATURE:
wstr << L"Global Root Signature 0x" << desc->pSubobjects[i].pDesc << L"\n";
break;
case D3D12_STATE_SUBOBJECT_TYPE_LOCAL_ROOT_SIGNATURE:
wstr << L"Local Root Signature 0x" << desc->pSubobjects[i].pDesc << L"\n";
break;
case D3D12_STATE_SUBOBJECT_TYPE_NODE_MASK:
wstr << L"Node Mask: 0x" << std::hex << std::setfill(L'0') << std::setw(8) << *static_cast<const UINT*>(desc->pSubobjects[i].pDesc) << std::setw(0) << std::dec << L"\n";
break;
case D3D12_STATE_SUBOBJECT_TYPE_DXIL_LIBRARY:
{
wstr << L"DXIL Library 0x";
auto lib = static_cast<const D3D12_DXIL_LIBRARY_DESC*>(desc->pSubobjects[i].pDesc);
wstr << lib->DXILLibrary.pShaderBytecode << L", " << lib->DXILLibrary.BytecodeLength << L" bytes\n";
wstr << ExportTree(1, lib->NumExports, lib->pExports);
break;
}
case D3D12_STATE_SUBOBJECT_TYPE_EXISTING_COLLECTION:
{
wstr << L"Existing Library 0x";
auto collection = static_cast<const D3D12_EXISTING_COLLECTION_DESC*>(desc->pSubobjects[i].pDesc);
wstr << collection->pExistingCollection << L"\n";
wstr << ExportTree(1, collection->NumExports, collection->pExports);
break;
}
case D3D12_STATE_SUBOBJECT_TYPE_SUBOBJECT_TO_EXPORTS_ASSOCIATION:
{
wstr << L"Subobject to Exports Association (Subobject [";
auto association = static_cast<const D3D12_SUBOBJECT_TO_EXPORTS_ASSOCIATION*>(desc->pSubobjects[i].pDesc);
UINT index = static_cast<UINT>(association->pSubobjectToAssociate - desc->pSubobjects);
wstr << index << L"])\n";
for (UINT j = 0; j < association->NumExports; j++)
{
wstr << L"| [" << j << L"]: " << association->pExports[j] << L"\n";
}
break;
}
case D3D12_STATE_SUBOBJECT_TYPE_DXIL_SUBOBJECT_TO_EXPORTS_ASSOCIATION:
{
wstr << L"DXIL Subobjects to Exports Association (";
auto association = static_cast<const D3D12_DXIL_SUBOBJECT_TO_EXPORTS_ASSOCIATION*>(desc->pSubobjects[i].pDesc);
wstr << association->SubobjectToAssociate << L")\n";
for (UINT j = 0; j < association->NumExports; j++)
{
wstr << L"| [" << j << L"]: " << association->pExports[j] << L"\n";
}
break;
}
case D3D12_STATE_SUBOBJECT_TYPE_RAYTRACING_SHADER_CONFIG:
{
wstr << L"Raytracing Shader Config\n";
auto config = static_cast<const D3D12_RAYTRACING_SHADER_CONFIG*>(desc->pSubobjects[i].pDesc);
wstr << L"| [0]: Max Payload Size: " << config->MaxPayloadSizeInBytes << L" bytes\n";
wstr << L"| [1]: Max Attribute Size: " << config->MaxAttributeSizeInBytes << L" bytes\n";
break;
}
case D3D12_STATE_SUBOBJECT_TYPE_RAYTRACING_PIPELINE_CONFIG:
{
wstr << L"Raytracing Pipeline Config\n";
auto config = static_cast<const D3D12_RAYTRACING_PIPELINE_CONFIG*>(desc->pSubobjects[i].pDesc);
wstr << L"| [0]: Max Recursion Depth: " << config->MaxTraceRecursionDepth << L"\n";
break;
}
case D3D12_STATE_SUBOBJECT_TYPE_HIT_GROUP:
{
wstr << L"Hit Group (";
auto hitGroup = static_cast<const D3D12_HIT_GROUP_DESC*>(desc->pSubobjects[i].pDesc);
wstr << (hitGroup->HitGroupExport ? hitGroup->HitGroupExport : L"[none]") << L")\n";
wstr << L"| [0]: Any Hit Import: " << (hitGroup->AnyHitShaderImport ? hitGroup->AnyHitShaderImport : L"[none]") << L"\n";
wstr << L"| [1]: Closest Hit Import: " << (hitGroup->ClosestHitShaderImport ? hitGroup->ClosestHitShaderImport : L"[none]") << L"\n";
wstr << L"| [2]: Intersection Import: " << (hitGroup->IntersectionShaderImport ? hitGroup->IntersectionShaderImport : L"[none]") << L"\n";
break;
}
}
wstr << L"|--------------------------------------------------------------------\n";
}
wstr << L"\n";
OutputDebugStringW(wstr.str().c_str());
}
#endif
}
Sample::Sample() noexcept(false)
{
m_deviceResources = std::make_unique<DX::DeviceResources>();
m_deviceResources->RegisterDeviceNotify(this);
}
Sample::~Sample()
{
if (m_deviceResources)
{
m_deviceResources->WaitForGpu();
}
// Force cleanup.
OnDeviceLost();
}
// Initialize the Direct3D resources required to run.
void Sample::Initialize(HWND window, int width, int height)
{
m_gamePad = std::make_unique<GamePad>();
m_keyboard = std::make_unique<Keyboard>();
m_deviceResources->SetWindow(window, width, height);
m_deviceResources->CreateDeviceResources();
CreateDeviceDependentResources();
m_deviceResources->CreateWindowSizeDependentResources();
CreateWindowSizeDependentResources();
}
#pragma region Frame Update
// Executes basic render loop.
void Sample::Tick()
{
Update();
Render();
}
// Updates the world.
void Sample::Update()
{
auto pad = m_gamePad->GetState(0);
if (pad.IsConnected())
{
m_gamePadButtons.Update(pad);
if (pad.IsViewPressed())
{
ExitSample();
}
}
else
{
m_gamePadButtons.Reset();
}
auto kb = m_keyboard->GetState();
m_keyboardButtons.Update(kb);
if (kb.Escape)
{
ExitSample();
}
}
#pragma endregion
#pragma region Frame Render
// Draws the scene.
void Sample::Render()
{
// Prepare the command list to render a new frame.
m_deviceResources->Prepare();
Clear();
DoRaytracing();
CopyRaytracingOutputToBackbuffer();
m_deviceResources->Present(D3D12_RESOURCE_STATE_PRESENT);
}
// Helper method to clear the back buffers.
void Sample::Clear()
{
auto commandList = m_deviceResources->GetCommandList();
// Clear the views.
auto rtvDescriptor = m_deviceResources->GetRenderTargetView();
auto dsvDescriptor = m_deviceResources->GetDepthStencilView();
commandList->OMSetRenderTargets(1, &rtvDescriptor, FALSE, &dsvDescriptor);
commandList->ClearDepthStencilView(dsvDescriptor, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr);
// Set the viewport and scissor rect.
auto viewport = m_deviceResources->GetScreenViewport();
auto scissorRect = m_deviceResources->GetScissorRect();
commandList->RSSetViewports(1, &viewport);
commandList->RSSetScissorRects(1, &scissorRect);
}
#pragma endregion
#pragma region Message Handlers
// Message handlers
void Sample::OnActivated()
{
}
void Sample::OnDeactivated()
{
}
void Sample::OnSuspending()
{
}
void Sample::OnResuming()
{
m_gamePadButtons.Reset();
m_keyboardButtons.Reset();
}
void Sample::OnWindowMoved()
{
auto r = m_deviceResources->GetOutputSize();
m_deviceResources->WindowSizeChanged(r.right, r.bottom);
}
void Sample::OnWindowSizeChanged(int width, int height)
{
if (!m_deviceResources->WindowSizeChanged(width, height))
return;
CreateWindowSizeDependentResources();
}
// Properties
void Sample::GetDefaultSize(int& width, int& height) const
{
width = 1280;
height = 720;
}
#pragma endregion
#pragma region Direct3D Resources
// These are the resources that depend on the device.
void Sample::CreateDeviceDependentResources()
{
// Create root signatures for the shaders.
CreateRaytracingRootSignatures();
// Create a raytracing pipeline state object which defines the binding of shaders, state and resources to be used during raytracing.
CreateRaytracingPipelineStateObject();
// Create a heap for descriptors.
CreateRaytracingDescriptorHeap();
// Build geometry to be used in the sample.
BuildSceneGeometry();
// Build raytracing acceleration structures from the generated geometry.
BuildRaytracingAccelerationStructures();
// Build shader tables, which define shaders and their local root arguments.
BuildRaytracingShaderTables();
// Create an output 2D texture to store the raytracing result to.
CreateRaytracingOutputResource();
}
// Allocate all memory resources that change on a window SizeChanged event.
void Sample::CreateWindowSizeDependentResources()
{
}
void Sample::OnDeviceLost()
{
m_indexBuffer.Reset();
m_vertexBuffer.Reset();
m_raytracingGlobalRootSignature.Reset();
m_raytracingDescriptorHeap.Reset();
m_bottomLevelAccelerationStructure.Reset();
m_topLevelAccelerationStructure.Reset();
m_raytracingOutput.Reset();
m_dxrStateObject.Reset();
m_missShaderTable.Reset();
m_hitGroupShaderTable.Reset();
m_rayGenShaderTable.Reset();
}
void Sample::OnDeviceRestored()
{
CreateDeviceDependentResources();
CreateWindowSizeDependentResources();
}
#pragma endregion
#pragma region Raytracing
void Sample::CreateRaytracingRootSignatures()
{
// Global Root Signature, This is a root signature that is shared across all raytracing shaders invoked during a DispatchRays() call.
CD3DX12_DESCRIPTOR_RANGE UAVDescriptor;
UAVDescriptor.Init(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 0);
CD3DX12_ROOT_PARAMETER rootParameters[GlobalRootSigIndex_Count];
rootParameters[GlobalRootSigIndex_OutputViewSlot].InitAsDescriptorTable(1, &UAVDescriptor);
rootParameters[GlobalRootSigIndex_AccelerationStructureSlot].InitAsShaderResourceView(0);
CD3DX12_ROOT_SIGNATURE_DESC globalRootSignatureDesc(GlobalRootSigIndex_Count, rootParameters);
SerializeAndCreateRaytracingRootSignature(globalRootSignatureDesc, &m_raytracingGlobalRootSignature);
}
void Sample::SerializeAndCreateRaytracingRootSignature(D3D12_ROOT_SIGNATURE_DESC& desc, ComPtr<ID3D12RootSignature>* rootSig)
{
auto device = m_deviceResources->GetD3DDevice();
ComPtr<ID3DBlob> blob;
ComPtr<ID3DBlob> error;
DX::ThrowIfFailed(D3D12SerializeRootSignature(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &blob, &error));
DX::ThrowIfFailed(device->CreateRootSignature(1, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(&(*rootSig))));
}
// Create a raytracing pipeline state object (RTPSO).
// An RTPSO represents a full set of shaders reachable by a DispatchRays() call,
// with all configuration options resolved, such as local signatures and other state.
void Sample::CreateRaytracingPipelineStateObject()
{
// Create 5 subobjects that combine into a RTPSO:
// Subobjects need to be associated with DXIL exports (i.e. shaders) either by way of default or explicit associations.
// Default association applies to every exported shader entrypoint that doesn't have any of the same type of subobject associated with it.
// This simple sample utilizes default shader association except for local root signature subobject
// which has an explicit association specified purely for demonstration purposes.
// 1 - DXIL library
// 1 - Triangle hit group
// 1 - Shader config
// 1 - Global root signature
// 1 - Pipeline config
CD3DX12_STATE_OBJECT_DESC raytracingPipeline{ D3D12_STATE_OBJECT_TYPE_RAYTRACING_PIPELINE };
// DXIL library
// This contains the shaders and their entrypoints for the state object.
// Since shaders are not considered a subobject, they need to be passed in via DXIL library subobjects.
auto lib = raytracingPipeline.CreateSubobject<CD3DX12_DXIL_LIBRARY_SUBOBJECT>();
D3D12_SHADER_BYTECODE libdxil = CD3DX12_SHADER_BYTECODE((void *)g_pSimpleRaytracing, ARRAYSIZE(g_pSimpleRaytracing));
lib->SetDXILLibrary(&libdxil);
// Define which shader exports to surface from the library.
// If no shader exports are defined for a DXIL library subobject, all shaders will be surfaced.
// In this sample, this could be omitted for convenience since the sample uses all shaders in the library.
{
lib->DefineExport(c_raygenShaderName);
lib->DefineExport(c_closestHitShaderName);
lib->DefineExport(c_missShaderName);
}
// Triangle hit group
// A hit group specifies closest hit, any hit and intersection shaders to be executed when a ray intersects the geometry's triangle/AABB.
// In this sample, we only use triangle geometry with a closest hit shader, so others are not set.
auto hitGroup = raytracingPipeline.CreateSubobject<CD3DX12_HIT_GROUP_SUBOBJECT>();
hitGroup->SetClosestHitShaderImport(c_closestHitShaderName);
hitGroup->SetHitGroupExport(c_hitGroupName);
hitGroup->SetHitGroupType(D3D12_HIT_GROUP_TYPE_TRIANGLES);
// Shader config
// Defines the maximum sizes in bytes for the ray payload and attribute structure.
auto shaderConfig = raytracingPipeline.CreateSubobject<CD3DX12_RAYTRACING_SHADER_CONFIG_SUBOBJECT>();
UINT payloadSize = 4 * sizeof(float); // float4 color
UINT attributeSize = 2 * sizeof(float); // float2 barycentrics
shaderConfig->Config(payloadSize, attributeSize);
// There is no local root signature for this sample.
// Global root signature
// This is a root signature that is shared across all raytracing shaders invoked during a DispatchRays() call.
auto globalRootSignature = raytracingPipeline.CreateSubobject<CD3DX12_GLOBAL_ROOT_SIGNATURE_SUBOBJECT>();
globalRootSignature->SetRootSignature(m_raytracingGlobalRootSignature.Get());
// Pipeline config
// Defines the maximum TraceRay() recursion depth.
auto pipelineConfig = raytracingPipeline.CreateSubobject<CD3DX12_RAYTRACING_PIPELINE_CONFIG_SUBOBJECT>();
// PERFOMANCE TIP: Set max recursion depth as low as needed
// as drivers may apply optimization strategies for low recursion depths.
UINT maxRecursionDepth = 1; // ~ primary rays only.
pipelineConfig->Config(maxRecursionDepth);
#ifdef _DEBUG
PrintStateObjectDesc(raytracingPipeline);
#endif
// Create the state object.
auto device = m_deviceResources->GetD3DDevice();
DX::ThrowIfFailed(device->CreateStateObject(raytracingPipeline, IID_PPV_ARGS(m_dxrStateObject.ReleaseAndGetAddressOf())));
}
void Sample::CreateRaytracingDescriptorHeap()
{
auto device = m_deviceResources->GetD3DDevice();
D3D12_DESCRIPTOR_HEAP_DESC descriptorHeapDesc = {};
// Allocate a heap for 3 descriptors:
// 2 - bottom and top level acceleration structure fallback wrapped pointers
// 1 - raytracing output texture SRV
descriptorHeapDesc.NumDescriptors = 3;
descriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
descriptorHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
descriptorHeapDesc.NodeMask = 0;
device->CreateDescriptorHeap(&descriptorHeapDesc, IID_PPV_ARGS(m_raytracingDescriptorHeap.ReleaseAndGetAddressOf()));
m_raytracingDescriptorHeap->SetName(L"DXR Heap");
m_raytracingDescriptorSize = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
}
// Build geometry used in the sample.
void Sample::BuildSceneGeometry()
{
auto device = m_deviceResources->GetD3DDevice();
Index indices[3] = { 0, 1, 2 };
AllocateUploadBuffer(device, indices, sizeof(indices), &m_indexBuffer);
const float depthValue = 1.0;
const float offset = 0.5f;
// The sample raytraces in screen space coordinates.
// Since DirectX screen space coordinates are right handed (i.e. Y axis points down).
// Define the vertices in counter clockwise order ~ clockwise in left handed.
Vertex vertices[3] = { { 0, -offset, depthValue }, { -offset, offset, depthValue }, { offset, offset, depthValue } };
AllocateUploadBuffer(device, vertices, sizeof(vertices), &m_vertexBuffer);
}
// Build acceleration structures needed for raytracing.
void Sample::BuildRaytracingAccelerationStructures()
{
auto device = m_deviceResources->GetD3DDevice();
auto commandList = m_deviceResources->GetCommandList();
auto commandAllocator = m_deviceResources->GetCommandAllocator();
// Reset the command list for the acceleration structure construction.
commandList->Reset(commandAllocator, nullptr);
D3D12_RAYTRACING_GEOMETRY_DESC geometryDesc = {};
geometryDesc.Type = D3D12_RAYTRACING_GEOMETRY_TYPE_TRIANGLES;
geometryDesc.Triangles.IndexBuffer = m_indexBuffer->GetGPUVirtualAddress();
geometryDesc.Triangles.IndexCount = static_cast<UINT>(m_indexBuffer->GetDesc().Width) / sizeof(Index);
geometryDesc.Triangles.IndexFormat = DXGI_FORMAT_R16_UINT;
geometryDesc.Triangles.Transform3x4 = 0;
geometryDesc.Triangles.VertexFormat = DXGI_FORMAT_R32G32B32_FLOAT;
geometryDesc.Triangles.VertexCount = static_cast<UINT>(m_vertexBuffer->GetDesc().Width) / sizeof(Vertex);
geometryDesc.Triangles.VertexBuffer.StartAddress = m_vertexBuffer->GetGPUVirtualAddress();
geometryDesc.Triangles.VertexBuffer.StrideInBytes = sizeof(Vertex);
// Mark the geometry as opaque.
// PERFORMANCE TIP: mark geometry as opaque whenever applicable as it can enable important ray processing optimizations.
// Note: When rays encounter opaque geometry an any hit shader will not be executed whether it is present or not.
geometryDesc.Flags = D3D12_RAYTRACING_GEOMETRY_FLAG_OPAQUE;
// Get required sizes for an acceleration structure.
D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAGS buildFlags = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_PREFER_FAST_TRACE;
D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS topLevelInputs = {};
topLevelInputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY;
topLevelInputs.Flags = buildFlags;
topLevelInputs.NumDescs = 1;
D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO topLevelPrebuildInfo = {};
topLevelInputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY;
topLevelInputs.Flags = buildFlags;
topLevelInputs.NumDescs = 1;
topLevelInputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL;
device->GetRaytracingAccelerationStructurePrebuildInfo(&topLevelInputs, &topLevelPrebuildInfo);
assert(topLevelPrebuildInfo.ResultDataMaxSizeInBytes > 0);
D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO bottomLevelPrebuildInfo = {};
D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS bottomLevelInputs = topLevelInputs;
bottomLevelInputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL;
bottomLevelInputs.pGeometryDescs = &geometryDesc;
device->GetRaytracingAccelerationStructurePrebuildInfo(&bottomLevelInputs, &bottomLevelPrebuildInfo);
assert(bottomLevelPrebuildInfo.ResultDataMaxSizeInBytes > 0);
ComPtr<ID3D12Resource> scratchResource;
AllocateUAVBuffer(device, std::max(topLevelPrebuildInfo.ScratchDataSizeInBytes, bottomLevelPrebuildInfo.ScratchDataSizeInBytes), &scratchResource, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, L"ScratchResource");
// Allocate resources for acceleration structures.
// Acceleration structures can only be placed in resources that are created in the default heap (or custom heap equivalent).
// Default heap is OK since the application doesn't need CPU read/write access to them.
// The resources that will contain acceleration structures must be created in the state D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE,
// and must have resource flag D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS. The ALLOW_UNORDERED_ACCESS requirement simply acknowledges both:
// - the system will be doing this type of access in its implementation of acceleration structure builds behind the scenes.
// - from the app point of view, synchronization of writes/reads to acceleration structures is accomplished using UAV barriers.
AllocateUAVBuffer(device, bottomLevelPrebuildInfo.ResultDataMaxSizeInBytes, &m_bottomLevelAccelerationStructure,
D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE, L"BottomLevelAccelerationStructure");
AllocateUAVBuffer(device, topLevelPrebuildInfo.ResultDataMaxSizeInBytes, &m_topLevelAccelerationStructure,
D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE, L"TopLevelAccelerationStructure");
// Create an instance desc for the bottom-level acceleration structure.
D3D12_RAYTRACING_INSTANCE_DESC instanceDesc = {};
instanceDesc.Transform[0][0] = instanceDesc.Transform[1][1] = instanceDesc.Transform[2][2] = 1;
instanceDesc.InstanceMask = 1;
instanceDesc.AccelerationStructure = m_bottomLevelAccelerationStructure->GetGPUVirtualAddress();
ComPtr<ID3D12Resource> instanceDescs;
AllocateUploadBuffer(device, &instanceDesc, sizeof(instanceDesc), &instanceDescs, L"InstanceDescs");
// Bottom Level Acceleration Structure desc
D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC bottomLevelBuildDesc = {};
{
bottomLevelBuildDesc.Inputs = bottomLevelInputs;
bottomLevelBuildDesc.ScratchAccelerationStructureData = scratchResource->GetGPUVirtualAddress();
bottomLevelBuildDesc.DestAccelerationStructureData = m_bottomLevelAccelerationStructure->GetGPUVirtualAddress();
}
// Top Level Acceleration Structure desc
D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC topLevelBuildDesc = bottomLevelBuildDesc;
{
topLevelInputs.InstanceDescs = instanceDescs->GetGPUVirtualAddress();
topLevelBuildDesc.Inputs = topLevelInputs;
topLevelBuildDesc.DestAccelerationStructureData = m_topLevelAccelerationStructure->GetGPUVirtualAddress();
topLevelBuildDesc.ScratchAccelerationStructureData = scratchResource->GetGPUVirtualAddress();
}
// Build acceleration structure.
commandList->BuildRaytracingAccelerationStructure(&bottomLevelBuildDesc, 0, nullptr);
CD3DX12_RESOURCE_BARRIER bottomBarrier = CD3DX12_RESOURCE_BARRIER::UAV(m_bottomLevelAccelerationStructure.Get());
commandList->ResourceBarrier(1, &bottomBarrier);
commandList->BuildRaytracingAccelerationStructure(&topLevelBuildDesc, 0, nullptr);
// Kick off acceleration structure construction.
DX::ThrowIfFailed(commandList->Close());
m_deviceResources->GetCommandQueue()->ExecuteCommandLists(1, CommandListCast(&commandList));
// Wait for GPU to finish as the locally created temporary GPU resources will get released once we go out of scope.
m_deviceResources->WaitForGpu();
}
// Build shader tables.
// This encapsulates all shader records - shaders and the arguments for their local root signatures.
void Sample::BuildRaytracingShaderTables()
{
auto device = m_deviceResources->GetD3DDevice();
void* rayGenShaderIdentifier;
void* missShaderIdentifier;
void* hitGroupShaderIdentifier;
auto GetShaderIdentifiers = [&](auto* stateObjectProperties)
{
rayGenShaderIdentifier = stateObjectProperties->GetShaderIdentifier(c_raygenShaderName);
missShaderIdentifier = stateObjectProperties->GetShaderIdentifier(c_missShaderName);
hitGroupShaderIdentifier = stateObjectProperties->GetShaderIdentifier(c_hitGroupName);
};
// Get shader identifiers.
ComPtr<ID3D12StateObjectProperties> stateObjectProperties;
DX::ThrowIfFailed(m_dxrStateObject.As(&stateObjectProperties));
GetShaderIdentifiers(stateObjectProperties.Get());
UINT shaderIdentifierSize = D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES;
// Ray gen shader table
{
UINT numShaderRecords = 1;
UINT shaderRecordSize = shaderIdentifierSize;
ShaderTable rayGenShaderTable(device, numShaderRecords, shaderRecordSize, L"RayGenShaderTable");
rayGenShaderTable.Add(ShaderRecord(rayGenShaderIdentifier, shaderIdentifierSize));
m_rayGenShaderTable = rayGenShaderTable.GetResource();
}
// Miss shader table
{
UINT numShaderRecords = 1;
UINT shaderRecordSize = shaderIdentifierSize;
ShaderTable missShaderTable(device, numShaderRecords, shaderRecordSize, L"MissShaderTable");
missShaderTable.Add(ShaderRecord(missShaderIdentifier, shaderIdentifierSize));
m_missShaderTable = missShaderTable.GetResource();
}
// Hit group shader table
{
UINT numShaderRecords = 1;
UINT shaderRecordSize = shaderIdentifierSize;
ShaderTable hitGroupShaderTable(device, numShaderRecords, shaderRecordSize, L"HitGroupShaderTable");
hitGroupShaderTable.Add(ShaderRecord(hitGroupShaderIdentifier, shaderIdentifierSize));
m_hitGroupShaderTable = hitGroupShaderTable.GetResource();
}
}
/// Create 2D output texture for raytracing.
void Sample::CreateRaytracingOutputResource()
{
auto device = m_deviceResources->GetD3DDevice();
auto backbufferFormat = m_deviceResources->GetBackBufferFormat();
// Create the output resource. The dimensions and format should match the swap-chain.
auto output = m_deviceResources->GetOutputSize();
auto uavDesc = CD3DX12_RESOURCE_DESC::Tex2D(backbufferFormat, output.right - output.left, output.bottom - output.top, 1, 1, 1, 0, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS);
auto defaultHeapProperties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);
DX::ThrowIfFailed(device->CreateCommittedResource(
&defaultHeapProperties, D3D12_HEAP_FLAG_NONE, &uavDesc, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, nullptr, IID_PPV_ARGS(&m_raytracingOutput)));
m_raytracingOutput->SetName(L"DXR Output");
D3D12_CPU_DESCRIPTOR_HANDLE uavDescriptorHandle;
m_raytracingOutputResourceUAVDescriptorHeapIndex = AllocateRaytracingDescriptor(&uavDescriptorHandle, m_raytracingOutputResourceUAVDescriptorHeapIndex);
D3D12_UNORDERED_ACCESS_VIEW_DESC UAVDesc = {};
UAVDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
device->CreateUnorderedAccessView(m_raytracingOutput.Get(), nullptr, &UAVDesc, uavDescriptorHandle);
m_raytracingOutputResourceUAVGpuDescriptor = CD3DX12_GPU_DESCRIPTOR_HANDLE(m_raytracingDescriptorHeap->GetGPUDescriptorHandleForHeapStart(), m_raytracingOutputResourceUAVDescriptorHeapIndex, m_raytracingDescriptorSize);
}
// Allocate a descriptor and return its index.
// If the passed descriptorIndexToUse is valid, it will be used instead of allocating a new one.
UINT Sample::AllocateRaytracingDescriptor(D3D12_CPU_DESCRIPTOR_HANDLE* cpuDescriptor, UINT descriptorIndexToUse)
{
auto descriptorHeapCpuBase = m_raytracingDescriptorHeap->GetCPUDescriptorHandleForHeapStart();
if (descriptorIndexToUse >= m_raytracingDescriptorHeap->GetDesc().NumDescriptors)
{
descriptorIndexToUse = m_raytracingDescriptorsAllocated++;
}
*cpuDescriptor = CD3DX12_CPU_DESCRIPTOR_HANDLE(descriptorHeapCpuBase, descriptorIndexToUse, m_raytracingDescriptorSize);
return descriptorIndexToUse;
}
void Sample::DoRaytracing()
{
auto output = m_deviceResources->GetOutputSize();
auto commandList = m_deviceResources->GetCommandList();
auto DispatchRays = [&](auto* commandList, auto* stateObject, auto* dispatchDesc)
{
// Since each shader table has only one shader record, the stride is same as the size.
dispatchDesc->Depth = 1;
dispatchDesc->Width = output.right - output.left;
dispatchDesc->Height = output.bottom - output.top;
dispatchDesc->HitGroupTable.StartAddress = m_hitGroupShaderTable->GetGPUVirtualAddress();
dispatchDesc->HitGroupTable.SizeInBytes = m_hitGroupShaderTable->GetDesc().Width;
dispatchDesc->HitGroupTable.StrideInBytes = dispatchDesc->HitGroupTable.SizeInBytes;
dispatchDesc->MissShaderTable.StartAddress = m_missShaderTable->GetGPUVirtualAddress();
dispatchDesc->MissShaderTable.SizeInBytes = m_missShaderTable->GetDesc().Width;
dispatchDesc->MissShaderTable.StrideInBytes = dispatchDesc->MissShaderTable.SizeInBytes;
dispatchDesc->RayGenerationShaderRecord.StartAddress = m_rayGenShaderTable->GetGPUVirtualAddress();
dispatchDesc->RayGenerationShaderRecord.SizeInBytes = m_rayGenShaderTable->GetDesc().Width;
commandList->SetPipelineState1(stateObject);
commandList->DispatchRays(dispatchDesc);
};
commandList->SetComputeRootSignature(m_raytracingGlobalRootSignature.Get());
// Bind the heaps, acceleration structure and dispatch rays.
D3D12_DISPATCH_RAYS_DESC dispatchDesc = {};
commandList->SetDescriptorHeaps(1, m_raytracingDescriptorHeap.GetAddressOf());
commandList->SetComputeRootDescriptorTable(GlobalRootSigIndex_OutputViewSlot, m_raytracingOutputResourceUAVGpuDescriptor);
commandList->SetComputeRootShaderResourceView(GlobalRootSigIndex_AccelerationStructureSlot, m_topLevelAccelerationStructure->GetGPUVirtualAddress());
DispatchRays(commandList, m_dxrStateObject.Get(), &dispatchDesc);
}
// Copy the raytracing output to the backbuffer.
void Sample::CopyRaytracingOutputToBackbuffer()
{
auto commandList = m_deviceResources->GetCommandList();
auto renderTarget = m_deviceResources->GetRenderTarget();
D3D12_RESOURCE_BARRIER preCopyBarriers[2];
preCopyBarriers[0] = CD3DX12_RESOURCE_BARRIER::Transition(renderTarget, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_COPY_DEST);
preCopyBarriers[1] = CD3DX12_RESOURCE_BARRIER::Transition(m_raytracingOutput.Get(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
commandList->ResourceBarrier(ARRAYSIZE(preCopyBarriers), preCopyBarriers);
commandList->CopyResource(renderTarget, m_raytracingOutput.Get());
D3D12_RESOURCE_BARRIER postCopyBarriers[2];
postCopyBarriers[0] = CD3DX12_RESOURCE_BARRIER::Transition(renderTarget, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PRESENT);
postCopyBarriers[1] = CD3DX12_RESOURCE_BARRIER::Transition(m_raytracingOutput.Get(), D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
commandList->ResourceBarrier(ARRAYSIZE(postCopyBarriers), postCopyBarriers);
}
#pragma endregion