788 строки
34 KiB
C++
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
|