diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/.gitignore b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/.gitignore new file mode 100644 index 000000000..9e0c3cad1 --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/.gitignore @@ -0,0 +1,5 @@ +# Ignore binaries and debug files +dx*.dll +dx*.pdb +dx*.exe + diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/D3D12RaytracingSakuraScene.cpp b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/D3D12RaytracingSakuraScene.cpp new file mode 100644 index 000000000..eb78b9c4d --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/D3D12RaytracingSakuraScene.cpp @@ -0,0 +1,1963 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#include "stdafx.h" +#include "D3D12RaytracingSakuraScene.h" +#include "DirectXRaytracingHelper.h" +#include "dxcapi.h" +#include +#include +#include + +#include +#include +#include "SharedCode.h" +#include + +extern "C" { __declspec(dllexport) extern const UINT D3D12SDKVersion = 717; } +extern "C" { __declspec(dllexport) extern const char* D3D12SDKPath = u8".\\D3D12\\"; } + +using namespace std; +using namespace DX; + +const wchar_t* D3D12RaytracingSakuraScene::c_hitGroupName = L"MyHitGroup"; +const wchar_t* D3D12RaytracingSakuraScene::c_trunkHitGroupName = L"TrunkHitGroup"; +const wchar_t* D3D12RaytracingSakuraScene::c_leavesHitGroupName = L"LeavesHitGroup"; +const wchar_t* D3D12RaytracingSakuraScene::c_leavesLightHitGroupName = L"LeavesHitGroupLight"; +const wchar_t* D3D12RaytracingSakuraScene::c_leavesDarkHitGroupName = L"LeavesHitGroupDark"; +const wchar_t* D3D12RaytracingSakuraScene::c_leavesExtraDarkHitGroupName = L"LeavesHitGroupExtraDark"; +const wchar_t* D3D12RaytracingSakuraScene::c_bushHitGroupName = L"BushHitGroup"; +const wchar_t* D3D12RaytracingSakuraScene::c_transparentCubeHitGroupName = L"TCubeHitGroup"; +const wchar_t* D3D12RaytracingSakuraScene::c_raygenShaderName = L"MyRaygenShader"; +const wchar_t* D3D12RaytracingSakuraScene::c_closestHitShaderName = L"MyClosestHitShader"; +const wchar_t* D3D12RaytracingSakuraScene::c_trunkClosestHitShaderName = L"TrunkClosestHitShader"; +const wchar_t* D3D12RaytracingSakuraScene::c_leavesClosestHitShaderName = L"LeavesClosestHitShader"; +const wchar_t* D3D12RaytracingSakuraScene::c_leavesLightClosestHitShaderName = L"LeavesLightClosestHitShader"; +const wchar_t* D3D12RaytracingSakuraScene::c_leavesDarkClosestHitShaderName = L"LeavesDarkClosestHitShader"; +const wchar_t* D3D12RaytracingSakuraScene::c_leavesExtraDarkClosestHitShaderName = L"LeavesExtraDarkClosestHitShader"; +const wchar_t* D3D12RaytracingSakuraScene::c_bushClosestHitShaderName = L"BushClosestHitShader"; +const wchar_t* D3D12RaytracingSakuraScene::c_tcubeClosestHitShaderName = L"TCubeClosestHitShader"; +const wchar_t* D3D12RaytracingSakuraScene::c_missShaderName = L"MyMissShader"; + +#define PRINT(text) OutputDebugStringA(text); + +//================================================================================================================================= +HRESULT CompileDxilLibraryFromFile( + _In_ LPCWSTR pFile, + _In_ LPCWSTR pTarget, + _In_reads_(cDefines) DxcDefine* pDefines, + _In_ UINT cDefines, + _Out_ ID3DBlob** ppCode) +{ + HRESULT hr = S_OK; + *ppCode = nullptr; + + static HMODULE s_hmod = 0; + static HMODULE s_hmodDxil = 0; + static DxcCreateInstanceProc s_pDxcCreateInstanceProc = nullptr; + if (s_hmodDxil == 0) + { + s_hmodDxil = LoadLibrary(L"dxil.dll"); + if (s_hmodDxil == 0) + { + PRINT("dxil.dll missing or wrong architecture"); + return E_FAIL; + } + } + if (s_hmod == 0) + { + s_hmod = LoadLibrary(L"dxcompiler.dll"); + if (s_hmod == 0) + { + PRINT("dxcompiler.dll missing or wrong architecture"); + return E_FAIL; + } + + if (s_pDxcCreateInstanceProc == nullptr) + { + s_pDxcCreateInstanceProc = (DxcCreateInstanceProc)GetProcAddress(s_hmod, "DxcCreateInstance"); + if (s_pDxcCreateInstanceProc == nullptr) + { + PRINT("Unable to find dxcompiler!DxcCreateInstance"); + return E_FAIL; + } + } + } + + CComPtr compiler; + CComPtr library; + CComPtr source; + CComPtr operationResult; + CComPtr includeHandler; + hr = s_pDxcCreateInstanceProc(CLSID_DxcLibrary, __uuidof(IDxcLibrary), reinterpret_cast(&library)); + if (FAILED(hr)) + { + PRINT("Failed to instantiate compiler."); + return hr; + } + + HRESULT createBlobHr = library->CreateBlobFromFile(pFile, nullptr, &source); + if (createBlobHr != S_OK) + { + PRINT("Create Blob From File Failed - perhaps file is missing?"); + return E_FAIL; + } + + hr = library->CreateIncludeHandler(&includeHandler); + if (FAILED(hr)) + { + PRINT("Failed to create include handler."); + return hr; + } + hr = s_pDxcCreateInstanceProc(CLSID_DxcCompiler, __uuidof(IDxcCompiler), reinterpret_cast(&compiler)); + if (FAILED(hr)) + { + PRINT("Failed to instantiate compiler."); + return hr; + } + + LPCWSTR args[] = { L"" }; + UINT cArgs = 0; + hr = compiler->Compile( + source, + nullptr, + nullptr, + pTarget, + args, cArgs, + pDefines, cDefines, + includeHandler, + &operationResult); + if (FAILED(hr)) + { + PRINT("Failed to compile."); + return hr; + } + + operationResult->GetStatus(&hr); + if (SUCCEEDED(hr)) + { + hr = operationResult->GetResult((IDxcBlob**)ppCode); + if (FAILED(hr)) + { + PRINT("Failed to retrieve compiled code."); + } + } + CComPtr pErrors; + if (SUCCEEDED(operationResult->GetErrorBuffer(&pErrors))) + { + auto pText = pErrors->GetBufferPointer(); + if (pText) + { + PRINT((char*)pText); + } + } + + return hr; +} + +D3D12RaytracingSakuraScene::D3D12RaytracingSakuraScene(UINT width, UINT height, std::wstring name) : + DXSample(width, height, name), + m_raytracingOutputResourceUAVDescriptorHeapIndex(UINT_MAX), + m_curRotationAngleRad(0.0f), + m_serEnabled(true), + m_sortByHit(true), + m_sortByMaterial(false), + m_sortByBoth(false), + rotateCamera(true) +{ + UpdateForSizeChange(width, height); +} + +void D3D12RaytracingSakuraScene::OnInit() +{ + UUID Features[] = { D3D12ExperimentalShaderModels }; + ThrowIfFailed(D3D12EnableExperimentalFeatures(_countof(Features), Features, nullptr, nullptr)); + + m_deviceResources = std::make_unique( + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_UNKNOWN, + FrameCount, + D3D_FEATURE_LEVEL_11_0, + // Sample shows handling of use cases with tearing support, which is OS dependent and has been supported since TH2. + // Since the sample requires build 1809 (RS5) or higher, we don't need to handle non-tearing cases. + DeviceResources::c_RequireTearingSupport, + m_adapterIDoverride + ); + m_deviceResources->RegisterDeviceNotify(this); + m_deviceResources->SetWindow(Win32Application::GetHwnd(), m_width, m_height); + m_deviceResources->InitializeDXGIAdapter(); + + ThrowIfFalse(IsDirectXRaytracingSupported(m_deviceResources->GetAdapter()), + L"ERROR: DirectX Raytracing is not supported by your OS, GPU and/or driver.\n\n"); + + m_deviceResources->CreateDeviceResources(); + m_deviceResources->CreateWindowSizeDependentResources(); + + InitializeScene(); + + CreateDeviceDependentResources(); + + CreateWindowSizeDependentResources(); + + m_keyboard = std::make_unique(); +} + +// Update camera matrices passed into the shader. +void D3D12RaytracingSakuraScene::UpdateCameraMatrices() +{ + auto frameIndex = m_deviceResources->GetCurrentFrameIndex(); + + m_sceneCB[frameIndex].cameraPosition = m_eye; + float fovAngleY = 45.0f; + XMMATRIX view = XMMatrixLookAtLH(m_eye, m_at, m_up); + XMMATRIX proj = XMMatrixPerspectiveFovLH(XMConvertToRadians(fovAngleY), m_aspectRatio, 1.0f, 125.0f); + XMMATRIX viewProj = view * proj; + + m_sceneCB[frameIndex].projectionToWorld = XMMatrixInverse(nullptr, viewProj); +} + +// Initialize scene rendering parameters. +void D3D12RaytracingSakuraScene::InitializeScene() +{ + auto frameIndex = m_deviceResources->GetCurrentFrameIndex(); + // Setup camera. + { + // Initialize the view and projection inverse matrices. + m_eye = { 8.5f, 5.0f, -6.0f, 1.0f }; + // m_at = { 0.5f, 0.5f, -6.0f, 1.0f }; + m_at = { 1.0f, 0.0f, 0.0f, 1.0f }; + XMVECTOR right = { 1.0f, 0.0f, 0.0f, 0.0f }; + + XMVECTOR direction = XMVector4Normalize(m_at - m_eye); + m_up = XMVector3Normalize(XMVector3Cross(direction, right)); + + // Rotate camera around Y axis. + XMMATRIX rotate = XMMatrixRotationY(XMConvertToRadians(45.0f)); + m_eye = XMVector3Transform(m_eye, rotate); + + // Keep the camera upright + m_up = { 0.0f, 1.0f, 0.0f, 0.0f }; + + + UpdateCameraMatrices(); + } + + // Setup lights. + { + // Initialize the lighting parameters. + XMFLOAT4 lightPosition; + XMFLOAT4 lightAmbientColor; + XMFLOAT4 lightDiffuseColor; + + lightPosition = XMFLOAT4(0.0f, 15.8f,233.0f, 0.0f); + m_sceneCB[frameIndex].lightPosition = XMLoadFloat4(&lightPosition); + + lightAmbientColor = XMFLOAT4(0.8f, 0.8f, 0.8f, 1.0f); // Brighter ambient light + m_sceneCB[frameIndex].lightAmbientColor = XMLoadFloat4(&lightAmbientColor); + + lightDiffuseColor = XMFLOAT4(1.2f, 1.0f, 1.0f, 1.0f); // Stronger diffuse light + m_sceneCB[frameIndex].lightDiffuseColor = XMLoadFloat4(&lightDiffuseColor); + } + + // Apply the initial values to all frames' buffer instances. + for (auto& sceneCB : m_sceneCB) + { + sceneCB = m_sceneCB[frameIndex]; + } +} + +// Create constant buffers. +void D3D12RaytracingSakuraScene::CreateConstantBuffers() +{ + auto device = m_deviceResources->GetD3DDevice(); + auto frameCount = m_deviceResources->GetBackBufferCount(); + + // Create the constant buffer memory and map the CPU and GPU addresses + const D3D12_HEAP_PROPERTIES uploadHeapProperties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD); + + // Allocate one constant buffer per frame, since it gets updated every frame. + size_t cbSize = frameCount * sizeof(AlignedSceneConstantBuffer); + const D3D12_RESOURCE_DESC constantBufferDesc = CD3DX12_RESOURCE_DESC::Buffer(cbSize); + + ThrowIfFailed(device->CreateCommittedResource( + &uploadHeapProperties, + D3D12_HEAP_FLAG_NONE, + &constantBufferDesc, + D3D12_RESOURCE_STATE_GENERIC_READ, + nullptr, + IID_PPV_ARGS(&m_perFrameConstants))); + + // Map the constant buffer and cache its heap pointers. + // We don't unmap this until the app closes. Keeping buffer mapped for the lifetime of the resource is okay. + CD3DX12_RANGE readRange(0, 0); // We do not intend to read from this resource on the CPU. + ThrowIfFailed(m_perFrameConstants->Map(0, nullptr, reinterpret_cast(&m_mappedConstantData))); +} + +// Create resources that depend on the device. +void D3D12RaytracingSakuraScene::CreateDeviceDependentResources() +{ + // Initialize raytracing pipeline. + + // Create raytracing interfaces: raytracing device and commandlist. + CreateRaytracingInterfaces(); + + // Create root signatures for the shaders. + CreateRootSignatures(); + + // 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. + CreateDescriptorHeap(); + + // Load gometry from OBJ files. + m_ObjModelLoader.Load(L"trunk.obj"); + m_ObjModelLoader.Load(L"leaves.obj"); + m_ObjModelLoader.Load(L"bush.obj"); + + // Build geometry to be used in the sample. + BuildGeometry(); + + // Build trunk geometry (the torus knot to illustrate the use of multiple geometries in the scene) + BuildTreeGeometry(); + + // Create texture + CreateTexture(); + + // Build raytracing acceleration structures from the generated geometry. + BuildAccelerationStructures(); + + // Create constant buffers for the geometry and the scene. + CreateConstantBuffers(); + + // Build shader tables, which define shaders and their local root arguments. + BuildShaderTables(); + + // Create an output 2D texture to store the raytracing result to. + CreateRaytracingOutputResource(); + + // Load fonts + CreateUIFont(); +} + +void D3D12RaytracingSakuraScene::CreateTexture() +{ + auto device = m_deviceResources->GetD3DDevice(); + auto commandQueue = m_deviceResources->GetCommandQueue(); + + // Begin a resource upload batch + DirectX::ResourceUploadBatch resourceUpload(device); + resourceUpload.Begin(); + + // Load texture from file using DirectXTK + ComPtr texture1; + + HRESULT hr = DirectX::CreateWICTextureFromFile( + device, + resourceUpload, + L"tree-trunk.png", + texture1.GetAddressOf() + ); + + if (FAILED(hr)) + { + OutputDebugStringA("Failed to load texture using CreateWICTextureFromFile.\n"); + return; + } + + // Load texture from file using DirectXTK + ComPtr texture2; + + hr = DirectX::CreateWICTextureFromFile( + device, + resourceUpload, + L"sakura.jpg", + texture2.GetAddressOf() + ); + + if (FAILED(hr)) + { + OutputDebugStringA("Failed to load texture using CreateWICTextureFromFile.\n"); + return; + } + + // Load texture from file using DirectXTK + ComPtr texture3; + + hr = DirectX::CreateWICTextureFromFile( + device, + resourceUpload, + L"bush-texture.jpg", + texture3.GetAddressOf() + ); + + if (FAILED(hr)) + { + OutputDebugStringA("Failed to load texture using CreateWICTextureFromFile.\n"); + return; + } + + // Schedule the upload and wait for it to complete + auto uploadResourcesFinished = resourceUpload.End(commandQueue); + uploadResourcesFinished.wait(); + + m_texture1 = texture1; + m_texture2 = texture2; + m_texture3 = texture3; + + // Create SRV for the texture + D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc1 = {}; + srvDesc1.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + srvDesc1.Format = m_texture1->GetDesc().Format; + srvDesc1.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + srvDesc1.Texture2D.MipLevels = 1; + + D3D12_CPU_DESCRIPTOR_HANDLE srvHandle1; + UINT descriptorIndex1 = AllocateDescriptor(&srvHandle1); + device->CreateShaderResourceView(m_texture1.Get(), &srvDesc1, srvHandle1); + + m_textureSrvGpuDescriptor1 = CD3DX12_GPU_DESCRIPTOR_HANDLE( + m_descriptorHeap->GetGPUDescriptorHandleForHeapStart(), + descriptorIndex1, + m_descriptorSize); + + // Create SRV for the texture + D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc2 = {}; + srvDesc2.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + srvDesc2.Format = m_texture2->GetDesc().Format; + srvDesc2.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + srvDesc2.Texture2D.MipLevels = 1; + + D3D12_CPU_DESCRIPTOR_HANDLE srvHandle2; + UINT descriptorIndex2 = AllocateDescriptor(&srvHandle2); + device->CreateShaderResourceView(m_texture2.Get(), &srvDesc2, srvHandle2); + + m_textureSrvGpuDescriptor2 = CD3DX12_GPU_DESCRIPTOR_HANDLE( + m_descriptorHeap->GetGPUDescriptorHandleForHeapStart(), + descriptorIndex2, + m_descriptorSize); + + // Create SRV for the texture + D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc3 = {}; + srvDesc3.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + srvDesc3.Format = m_texture3->GetDesc().Format; + srvDesc3.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + srvDesc3.Texture2D.MipLevels = 1; + + D3D12_CPU_DESCRIPTOR_HANDLE srvHandle3; + UINT descriptorIndex3 = AllocateDescriptor(&srvHandle3); + device->CreateShaderResourceView(m_texture3.Get(), &srvDesc3, srvHandle3); + + m_textureSrvGpuDescriptor3 = CD3DX12_GPU_DESCRIPTOR_HANDLE( + m_descriptorHeap->GetGPUDescriptorHandleForHeapStart(), + descriptorIndex3, + m_descriptorSize); +} + +void D3D12RaytracingSakuraScene::CreateUIFont() +{ + auto device = m_deviceResources->GetD3DDevice(); + auto size = m_deviceResources->GetOutputSize(); + + m_graphicsMemory = std::make_unique(device); + + ResourceUploadBatch resourceUpload(device); + + resourceUpload.Begin(); + + { + RenderTargetState rtState(m_deviceResources->GetBackBufferFormat(), m_deviceResources->GetDepthBufferFormat()); + SpriteBatchPipelineStateDescription pd(rtState); + m_spriteBatch = std::make_unique(device, resourceUpload, pd); + } + + auto uploadResourcesFinished = resourceUpload.End(m_deviceResources->GetCommandQueue()); + uploadResourcesFinished.wait(); + + D3D12_CPU_DESCRIPTOR_HANDLE fontHandle = m_descriptorHeap->GetCPUDescriptorHandleForHeapStart(); + fontHandle.ptr += (Descriptors::FONT * m_descriptorSize); + + D3D12_GPU_DESCRIPTOR_HANDLE fontGpuHandle = m_descriptorHeap->GetGPUDescriptorHandleForHeapStart(); + fontGpuHandle.ptr += (Descriptors::FONT * m_descriptorSize); + + + // Begin uploading texture resources + { + ResourceUploadBatch resourceUpload(device); + resourceUpload.Begin(); + + m_smallFont = std::make_unique(device, resourceUpload, + L"SegoeUI_18.spritefont", + fontHandle, + fontGpuHandle); + + auto finished = resourceUpload.End(m_deviceResources->GetCommandQueue()); + finished.wait(); + } +} + +void D3D12RaytracingSakuraScene::SerializeAndCreateRaytracingRootSignature(D3D12_ROOT_SIGNATURE_DESC& desc, ComPtr* rootSig) +{ + auto device = m_deviceResources->GetD3DDevice(); + ComPtr blob; + ComPtr error; + + ThrowIfFailed(D3D12SerializeRootSignature(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &blob, &error), error ? static_cast(error->GetBufferPointer()) : nullptr); + ThrowIfFailed(device->CreateRootSignature(1, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(&(*rootSig)))); +} + +void D3D12RaytracingSakuraScene::CreateRootSignatures() +{ + auto device = m_deviceResources->GetD3DDevice(); + + // Global Root Signature + // This is a root signature that is shared across all raytracing shaders invoked during a DispatchRays() call. + { + CD3DX12_DESCRIPTOR_RANGE ranges[2]; // Perfomance TIP: Order from most frequent to least frequent. + ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 0); // 1 output texture + ranges[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 11, 1); // 4 static index and 4 vertex buffers + 3 texture + + CD3DX12_ROOT_PARAMETER rootParameters[GlobalRootSignatureParams::Count]; + rootParameters[GlobalRootSignatureParams::OutputViewSlot].InitAsDescriptorTable(1, &ranges[0]); + rootParameters[GlobalRootSignatureParams::AccelerationStructureSlot].InitAsShaderResourceView(0); + rootParameters[GlobalRootSignatureParams::SceneConstantSlot].InitAsConstantBufferView(0); + rootParameters[GlobalRootSignatureParams::VertexBuffersSlot].InitAsDescriptorTable(1, &ranges[1]); + + // Static sampler + D3D12_STATIC_SAMPLER_DESC samplers[3] = {}; + + // Sampler for s0 + samplers[0].Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; + samplers[0].AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + samplers[0].AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + samplers[0].AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + samplers[0].MipLODBias = 0; + samplers[0].MaxAnisotropy = 0; + samplers[0].ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER; + samplers[0].BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK; + samplers[0].MinLOD = 0.0f; + samplers[0].MaxLOD = D3D12_FLOAT32_MAX; + samplers[0].ShaderRegister = 0; + samplers[0].RegisterSpace = 0; + samplers[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + + // Sampler for s1 + samplers[1] = samplers[0]; // Copy settings + samplers[1].ShaderRegister = 1; + + // Sampler for s1 + samplers[2] = samplers[0]; // Copy settings + samplers[2].ShaderRegister = 2; + + CD3DX12_ROOT_SIGNATURE_DESC globalRootSignatureDesc( + ARRAYSIZE(rootParameters), rootParameters, + ARRAYSIZE(samplers), samplers); + + SerializeAndCreateRaytracingRootSignature(globalRootSignatureDesc, &m_raytracingGlobalRootSignature); + } + + // Local Root Signature + // This is a root signature that enables a shader to have unique arguments that come from shader tables. + { + CD3DX12_ROOT_PARAMETER rootParameters[LocalRootSignatureParams::Count]; + rootParameters[LocalRootSignatureParams::CubeConstantSlot].InitAsConstants(SizeOfInUint32(m_objectCB), 1); + CD3DX12_ROOT_SIGNATURE_DESC localRootSignatureDesc(ARRAYSIZE(rootParameters), rootParameters); + localRootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_LOCAL_ROOT_SIGNATURE; + SerializeAndCreateRaytracingRootSignature(localRootSignatureDesc, &m_raytracingLocalRootSignature); + } +} + +// Create raytracing device and command list. +void D3D12RaytracingSakuraScene::CreateRaytracingInterfaces() +{ + auto device = m_deviceResources->GetD3DDevice(); + auto commandList = m_deviceResources->GetCommandList(); + + ThrowIfFailed(device->QueryInterface(IID_PPV_ARGS(&m_dxrDevice)), L"Couldn't get DirectX Raytracing interface for the device.\n"); + ThrowIfFailed(commandList->QueryInterface(IID_PPV_ARGS(&m_dxrCommandList)), L"Couldn't get DirectX Raytracing interface for the command list.\n"); +} + +// Local root signature and shader association +// This is a root signature that enables a shader to have unique arguments that come from shader tables. +void D3D12RaytracingSakuraScene::CreateLocalRootSignatureSubobjects(CD3DX12_STATE_OBJECT_DESC* raytracingPipeline) +{ + // Ray gen and miss shaders in this sample are not using a local root signature and thus one is not associated with them. + + // Local root signature to be used in a hit group. + auto localRootSignature = raytracingPipeline->CreateSubobject(); + localRootSignature->SetRootSignature(m_raytracingLocalRootSignature.Get()); + // Define explicit shader association for the local root signature. + { + auto rootSignatureAssociation = raytracingPipeline->CreateSubobject(); + rootSignatureAssociation->SetSubobjectToAssociate(*localRootSignature); + rootSignatureAssociation->AddExport(c_hitGroupName); + rootSignatureAssociation->AddExport(c_trunkHitGroupName); + rootSignatureAssociation->AddExport(c_leavesHitGroupName); + rootSignatureAssociation->AddExport(c_leavesLightHitGroupName); + rootSignatureAssociation->AddExport(c_leavesDarkHitGroupName); + rootSignatureAssociation->AddExport(c_leavesExtraDarkHitGroupName); + rootSignatureAssociation->AddExport(c_bushHitGroupName); + rootSignatureAssociation->AddExport(c_transparentCubeHitGroupName); + rootSignatureAssociation->AddExport(c_raygenShaderName); + } +} + +// 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 D3D12RaytracingSakuraScene::CreateRaytracingPipelineStateObject() +{ + D3D12_FEATURE_DATA_SHADER_MODEL SM; + SM.HighestShaderModel = D3D_SHADER_MODEL_6_9; + m_dxrDevice->CheckFeatureSupport(D3D12_FEATURE_SHADER_MODEL, &SM, sizeof(SM)); + ThrowIfFalse(SM.HighestShaderModel >= D3D_SHADER_MODEL_6_9, + L"ERROR: Device doesn't support Shader Model 6.9.\n\n"); + + // Create 7 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 + // 2 - Local root signature and association + // 1 - Global root signature + // 1 - Pipeline config + CD3DX12_STATE_OBJECT_DESC raytracingPipeline{ D3D12_STATE_OBJECT_TYPE_RAYTRACING_PIPELINE }; + + CComPtr library; + ThrowIfFailed(CompileDxilLibraryFromFile(L"Raytracing.hlsl", L"lib_6_9", nullptr, 0, &library)); + + auto lib = raytracingPipeline.CreateSubobject(); + CD3DX12_SHADER_BYTECODE libCode(library); + lib->SetDXILLibrary(&libCode); + { + lib->DefineExport(c_raygenShaderName); + lib->DefineExport(c_closestHitShaderName); + lib->DefineExport(c_trunkClosestHitShaderName); + lib->DefineExport(c_leavesClosestHitShaderName); + lib->DefineExport(c_leavesLightClosestHitShaderName); + lib->DefineExport(c_leavesDarkClosestHitShaderName); + lib->DefineExport(c_leavesExtraDarkClosestHitShaderName); + lib->DefineExport(c_bushClosestHitShaderName); + lib->DefineExport(c_tcubeClosestHitShaderName); + 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(); + hitGroup->SetClosestHitShaderImport(c_closestHitShaderName); + hitGroup->SetHitGroupExport(c_hitGroupName); + hitGroup->SetHitGroupType(D3D12_HIT_GROUP_TYPE_TRIANGLES); + + + auto trunkHitGroup = raytracingPipeline.CreateSubobject(); + trunkHitGroup->SetClosestHitShaderImport(c_trunkClosestHitShaderName); + trunkHitGroup->SetHitGroupExport(c_trunkHitGroupName); + trunkHitGroup->SetHitGroupType(D3D12_HIT_GROUP_TYPE_TRIANGLES); + + auto leavesHitGroup = raytracingPipeline.CreateSubobject(); + leavesHitGroup->SetClosestHitShaderImport(c_leavesClosestHitShaderName); + leavesHitGroup->SetHitGroupExport(c_leavesHitGroupName); + leavesHitGroup->SetHitGroupType(D3D12_HIT_GROUP_TYPE_TRIANGLES); + + auto leavesLightHitGroup = raytracingPipeline.CreateSubobject(); + leavesLightHitGroup->SetClosestHitShaderImport(c_leavesLightClosestHitShaderName); + leavesLightHitGroup->SetHitGroupExport(c_leavesLightHitGroupName); + leavesLightHitGroup->SetHitGroupType(D3D12_HIT_GROUP_TYPE_TRIANGLES); + + auto leavesDarkHitGroup = raytracingPipeline.CreateSubobject(); + leavesDarkHitGroup->SetClosestHitShaderImport(c_leavesDarkClosestHitShaderName); + leavesDarkHitGroup->SetHitGroupExport(c_leavesDarkHitGroupName); + leavesDarkHitGroup->SetHitGroupType(D3D12_HIT_GROUP_TYPE_TRIANGLES); + + + auto leavesExtraDarkHitGroup = raytracingPipeline.CreateSubobject(); + leavesExtraDarkHitGroup->SetClosestHitShaderImport(c_leavesExtraDarkClosestHitShaderName); + leavesExtraDarkHitGroup->SetHitGroupExport(c_leavesExtraDarkHitGroupName); + leavesExtraDarkHitGroup->SetHitGroupType(D3D12_HIT_GROUP_TYPE_TRIANGLES); + + + auto bushHitGroup = raytracingPipeline.CreateSubobject(); + bushHitGroup->SetClosestHitShaderImport(c_bushClosestHitShaderName); + bushHitGroup->SetHitGroupExport(c_bushHitGroupName); + bushHitGroup->SetHitGroupType(D3D12_HIT_GROUP_TYPE_TRIANGLES); + + auto tcubeHitGroup = raytracingPipeline.CreateSubobject(); + tcubeHitGroup->SetClosestHitShaderImport(c_tcubeClosestHitShaderName); + tcubeHitGroup->SetHitGroupExport(c_transparentCubeHitGroupName); + tcubeHitGroup->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(); + UINT payloadSize = sizeof(XMFLOAT4) + sizeof(UINT); + UINT attributeSize = sizeof(XMFLOAT2); // float2 barycentrics + shaderConfig->Config(payloadSize, attributeSize); + + // Local root signature and shader association + // This is a root signature that enables a shader to have unique arguments that come from shader tables. + CreateLocalRootSignatureSubobjects(&raytracingPipeline); + + // Global root signature + // This is a root signature that is shared across all raytracing shaders invoked during a DispatchRays() call. + auto globalRootSignature = raytracingPipeline.CreateSubobject(); + globalRootSignature->SetRootSignature(m_raytracingGlobalRootSignature.Get()); + + // Pipeline config + // Defines the maximum TraceRay() recursion depth. + auto pipelineConfig = raytracingPipeline.CreateSubobject(); + // PERFOMANCE TIP: Set max recursion depth as low as needed + // as drivers may apply optimization strategies for low recursion depths. + UINT maxRecursionDepth = 4;// ~ primary rays only. + pipelineConfig->Config(maxRecursionDepth); + +#if _DEBUG + PrintStateObjectDesc(raytracingPipeline); +#endif + + // Create the state object. + ThrowIfFailed(m_dxrDevice->CreateStateObject(raytracingPipeline, IID_PPV_ARGS(&m_dxrStateObject)), L"Couldn't create DirectX Raytracing state object.\n"); +} + +// Create 2D output texture for raytracing. +void D3D12RaytracingSakuraScene::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 uavDesc = CD3DX12_RESOURCE_DESC::Tex2D(backbufferFormat, m_width, m_height, 1, 1, 1, 0, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS); + + auto defaultHeapProperties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT); + ThrowIfFailed(device->CreateCommittedResource( + &defaultHeapProperties, D3D12_HEAP_FLAG_NONE, &uavDesc, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, nullptr, IID_PPV_ARGS(&m_raytracingOutput))); + NAME_D3D12_OBJECT(m_raytracingOutput); + + D3D12_CPU_DESCRIPTOR_HANDLE uavDescriptorHandle; + m_raytracingOutputResourceUAVDescriptorHeapIndex = AllocateDescriptor(&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_descriptorHeap->GetGPUDescriptorHandleForHeapStart(), m_raytracingOutputResourceUAVDescriptorHeapIndex, m_descriptorSize); +} + +void D3D12RaytracingSakuraScene::CreateDescriptorHeap() +{ + auto device = m_deviceResources->GetD3DDevice(); + + D3D12_DESCRIPTOR_HEAP_DESC descriptorHeapDesc = {}; + // Allocate a heap for 6 descriptors: + // 2 - vertex and index buffer SRVs for cube + // 2 - vertex and index buffer SRVs for trunk + // 1 - raytracing output texture UAV + // 1 - texture SRV + + descriptorHeapDesc.NumDescriptors = 12; + 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_descriptorHeap)); + NAME_D3D12_OBJECT(m_descriptorHeap); + + m_descriptorSize = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); +} + +// Build geometry used in the sample. +void D3D12RaytracingSakuraScene::BuildGeometry() +{ + auto device = m_deviceResources->GetD3DDevice(); + + // Cube indices. + Index indices[] = + { + 3,1,0, + 2,1,3, + + 6,4,5, + 7,4,6, + + 11,9,8, + 10,9,11, + + 14,12,13, + 15,12,14, + + 19,17,16, + 18,17,19, + + 22,20,21, + 23,20,22 + }; + + // Cube vertices positions and corresponding triangle normals. + Vertex vertices[] = + { + { XMFLOAT3(-1.0f, 1.0f, -1.0f), XMFLOAT3(0.0f, 1.0f, 0.0f) }, + { XMFLOAT3(1.0f, 1.0f, -1.0f), XMFLOAT3(0.0f, 1.0f, 0.0f) }, + { XMFLOAT3(1.0f, 1.0f, 1.0f), XMFLOAT3(0.0f, 1.0f, 0.0f) }, + { XMFLOAT3(-1.0f, 1.0f, 1.0f), XMFLOAT3(0.0f, 1.0f, 0.0f) }, + + { XMFLOAT3(-1.0f, -1.0f, -1.0f), XMFLOAT3(0.0f, -1.0f, 0.0f) }, + { XMFLOAT3(1.0f, -1.0f, -1.0f), XMFLOAT3(0.0f, -1.0f, 0.0f) }, + { XMFLOAT3(1.0f, -1.0f, 1.0f), XMFLOAT3(0.0f, -1.0f, 0.0f) }, + { XMFLOAT3(-1.0f, -1.0f, 1.0f), XMFLOAT3(0.0f, -1.0f, 0.0f) }, + + { XMFLOAT3(-1.0f, -1.0f, 1.0f), XMFLOAT3(-1.0f, 0.0f, 0.0f) }, + { XMFLOAT3(-1.0f, -1.0f, -1.0f), XMFLOAT3(-1.0f, 0.0f, 0.0f) }, + { XMFLOAT3(-1.0f, 1.0f, -1.0f), XMFLOAT3(-1.0f, 0.0f, 0.0f) }, + { XMFLOAT3(-1.0f, 1.0f, 1.0f), XMFLOAT3(-1.0f, 0.0f, 0.0f) }, + + { XMFLOAT3(1.0f, -1.0f, 1.0f), XMFLOAT3(1.0f, 0.0f, 0.0f) }, + { XMFLOAT3(1.0f, -1.0f, -1.0f), XMFLOAT3(1.0f, 0.0f, 0.0f) }, + { XMFLOAT3(1.0f, 1.0f, -1.0f), XMFLOAT3(1.0f, 0.0f, 0.0f) }, + { XMFLOAT3(1.0f, 1.0f, 1.0f), XMFLOAT3(1.0f, 0.0f, 0.0f) }, + + { XMFLOAT3(-1.0f, -1.0f, -1.0f), XMFLOAT3(0.0f, 0.0f, -1.0f) }, + { XMFLOAT3(1.0f, -1.0f, -1.0f), XMFLOAT3(0.0f, 0.0f, -1.0f) }, + { XMFLOAT3(1.0f, 1.0f, -1.0f), XMFLOAT3(0.0f, 0.0f, -1.0f) }, + { XMFLOAT3(-1.0f, 1.0f, -1.0f), XMFLOAT3(0.0f, 0.0f, -1.0f) }, + + { XMFLOAT3(-1.0f, -1.0f, 1.0f), XMFLOAT3(0.0f, 0.0f, 1.0f) }, + { XMFLOAT3(1.0f, -1.0f, 1.0f), XMFLOAT3(0.0f, 0.0f, 1.0f) }, + { XMFLOAT3(1.0f, 1.0f, 1.0f), XMFLOAT3(0.0f, 0.0f, 1.0f) }, + { XMFLOAT3(-1.0f, 1.0f, 1.0f), XMFLOAT3(0.0f, 0.0f, 1.0f) }, + }; + + AllocateUploadBuffer(device, indices, sizeof(indices), &m_indexBuffer.resource); + AllocateUploadBuffer(device, vertices, sizeof(vertices), &m_vertexBuffer.resource); + + // Vertex buffer is passed to the shader along with index buffer as a descriptor table. + // Vertex buffer descriptor must follow index buffer descriptor in the descriptor heap. + UINT descriptorIndexIB = CreateBufferSRV(&m_indexBuffer, sizeof(indices) / 4, 0); + UINT descriptorIndexVB = CreateBufferSRV(&m_vertexBuffer, ARRAYSIZE(vertices), sizeof(vertices[0])); + ThrowIfFalse(descriptorIndexVB == descriptorIndexIB + 1, L"Vertex Buffer descriptor index must follow that of Index Buffer descriptor index!"); +} + + +// Build geometry for a trunk torus knot shape +void D3D12RaytracingSakuraScene::BuildTreeGeometry() +{ + auto device = m_deviceResources->GetD3DDevice(); + std::vector trunkVertices; + std::vector trunkIndices; + std::vector leavesVertices; + std::vector leavesIndices; + std::vector bushVertices; + std::vector bushIndices; + + { + XMVECTOR xAxis = { 1, 0, 0 }; + XMVECTOR yAxis = { 0, 1, 0 }; + XMVECTOR zAxis = { 0, 0, 1 }; + XMMATRIX transform = XMMatrixRotationAxis(xAxis, 3.14159f / 2.0f) * XMMatrixRotationAxis(yAxis, 3.14159f / 12.0f) * XMMatrixRotationAxis(zAxis, 3.14159f) * XMMatrixTranslation(-1.5f, 0, 0); + m_ObjModelLoader.GetObjectVerticesAndIndices( + "tree", + 0.007f, + &trunkVertices, + &trunkIndices); + m_ObjModelLoader.GetObjectVerticesAndIndices( + "leaves", + 0.007f, + &leavesVertices, + &leavesIndices); + m_ObjModelLoader.GetObjectVerticesAndIndices( + "bush", + 0.007f, + &bushVertices, + &bushIndices); + } + + size_t trunkIndexBufferSize = trunkIndices.size() * sizeof(Index); + AllocateUploadBuffer(device, trunkIndices.data(), trunkIndexBufferSize, &m_trunkIndexBuffer.resource); + AllocateUploadBuffer(device, trunkVertices.data(), trunkVertices.size() * sizeof(trunkVertices[0]), &m_trunkVertexBuffer.resource); + + size_t leavesIndexBufferSize = leavesIndices.size() * sizeof(Index); + AllocateUploadBuffer(device, leavesIndices.data(), leavesIndexBufferSize, &m_leavesIndexBuffer.resource); + AllocateUploadBuffer(device, leavesVertices.data(), leavesVertices.size() * sizeof(leavesVertices[0]), &m_leavesVertexBuffer.resource); + + size_t bushIndexBufferSize = bushIndices.size() * sizeof(Index); + AllocateUploadBuffer(device, bushIndices.data(), bushIndexBufferSize, &m_bushIndexBuffer.resource); + AllocateUploadBuffer(device, bushVertices.data(), bushVertices.size() * sizeof(bushVertices[0]), &m_bushVertexBuffer.resource); + + + m_totalTrunkVertexCount = trunkVertices.size(); + // Vertex buffer is passed to the shader along with index buffer as a descriptor table. + // Vertex buffer descriptor must follow index buffer descriptor in the descriptor heap. + UINT trunkDescriptorIndexIB = CreateBufferSRV( + &m_trunkIndexBuffer, + static_cast(trunkIndices.size() * sizeof(Index) / 4), + 0 + ); + UINT trunkDescriptorIndexVB = CreateBufferSRV(&m_trunkVertexBuffer, trunkVertices.size(), sizeof(trunkVertices[0])); + ThrowIfFalse(trunkDescriptorIndexVB == trunkDescriptorIndexIB + 1, L"Vertex Buffer descriptor index must follow that of Index Buffer descriptor index!"); + + m_totalLeavesVertexCount = leavesVertices.size(); + UINT leavesDescriptorIndexIB = CreateBufferSRV( + &m_leavesIndexBuffer, + static_cast(leavesIndices.size() * sizeof(Index) / 4), + 0 + ); + UINT leavesDescriptorIndexVB = CreateBufferSRV(&m_leavesVertexBuffer, leavesVertices.size(), sizeof(leavesVertices[0])); + ThrowIfFalse(leavesDescriptorIndexVB == leavesDescriptorIndexIB + 1, L"Vertex Buffer descriptor index must follow that of Index Buffer descriptor index!"); + + m_totalBushVertexCount = bushVertices.size(); + UINT bushDescriptorIndexIB = CreateBufferSRV( + &m_bushIndexBuffer, + static_cast(bushIndices.size() * sizeof(Index) / 4), + 0 + ); + UINT bushDescriptorIndexVB = CreateBufferSRV(&m_bushVertexBuffer, bushVertices.size(), sizeof(bushVertices[0])); + ThrowIfFalse(bushDescriptorIndexVB == bushDescriptorIndexIB + 1, L"Vertex Buffer descriptor index must follow that of Index Buffer descriptor index!"); +} + + +// Build acceleration structures needed for raytracing. +void D3D12RaytracingSakuraScene::BuildAccelerationStructures() +{ + auto device = m_deviceResources->GetD3DDevice(); + auto commandList = m_deviceResources->GetCommandList(); + auto commandQueue = m_deviceResources->GetCommandQueue(); + auto commandAllocator = m_deviceResources->GetCommandAllocator(); + + // Reset the command list for the acceleration structure construction. + commandList->Reset(commandAllocator, nullptr); + + // Setup cube geometry desc + D3D12_RAYTRACING_GEOMETRY_DESC cubeGeometryDesc = {}; + cubeGeometryDesc.Type = D3D12_RAYTRACING_GEOMETRY_TYPE_TRIANGLES; + cubeGeometryDesc.Triangles.IndexBuffer = m_indexBuffer.resource->GetGPUVirtualAddress(); + cubeGeometryDesc.Triangles.IndexCount = static_cast(m_indexBuffer.resource->GetDesc().Width) / sizeof(Index); + cubeGeometryDesc.Triangles.IndexFormat = DXGI_FORMAT_R32_UINT; + cubeGeometryDesc.Triangles.Transform3x4 = 0; + cubeGeometryDesc.Triangles.VertexFormat = DXGI_FORMAT_R32G32B32_FLOAT; + cubeGeometryDesc.Triangles.VertexCount = static_cast(m_vertexBuffer.resource->GetDesc().Width) / sizeof(Vertex); + cubeGeometryDesc.Triangles.VertexBuffer.StartAddress = m_vertexBuffer.resource->GetGPUVirtualAddress(); + cubeGeometryDesc.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. + cubeGeometryDesc.Flags = D3D12_RAYTRACING_GEOMETRY_FLAG_OPAQUE; + + // Setup trunk geometry desc + D3D12_RAYTRACING_GEOMETRY_DESC trunkGeometryDesc = {}; + trunkGeometryDesc.Type = D3D12_RAYTRACING_GEOMETRY_TYPE_TRIANGLES; + trunkGeometryDesc.Triangles.IndexBuffer = m_trunkIndexBuffer.resource->GetGPUVirtualAddress(); + trunkGeometryDesc.Triangles.IndexCount = static_cast(m_trunkIndexBuffer.resource->GetDesc().Width) / sizeof(Index); + trunkGeometryDesc.Triangles.IndexFormat = DXGI_FORMAT_R32_UINT; + trunkGeometryDesc.Triangles.Transform3x4 = 0; + trunkGeometryDesc.Triangles.VertexFormat = DXGI_FORMAT_R32G32B32_FLOAT; + trunkGeometryDesc.Triangles.VertexCount = static_cast(m_trunkVertexBuffer.resource->GetDesc().Width) / sizeof(Vertex); + trunkGeometryDesc.Triangles.VertexBuffer.StartAddress = m_trunkVertexBuffer.resource->GetGPUVirtualAddress(); + trunkGeometryDesc.Triangles.VertexBuffer.StrideInBytes = sizeof(Vertex); + trunkGeometryDesc.Flags = D3D12_RAYTRACING_GEOMETRY_FLAG_OPAQUE; + + // Setup leaves shape geometry desc + D3D12_RAYTRACING_GEOMETRY_DESC leavesGeometryDesc = {}; + leavesGeometryDesc.Type = D3D12_RAYTRACING_GEOMETRY_TYPE_TRIANGLES; + leavesGeometryDesc.Triangles.IndexBuffer = m_leavesIndexBuffer.resource->GetGPUVirtualAddress(); + leavesGeometryDesc.Triangles.IndexCount = static_cast(m_leavesIndexBuffer.resource->GetDesc().Width) / sizeof(Index); + leavesGeometryDesc.Triangles.IndexFormat = DXGI_FORMAT_R32_UINT; + leavesGeometryDesc.Triangles.Transform3x4 = 0; + leavesGeometryDesc.Triangles.VertexFormat = DXGI_FORMAT_R32G32B32_FLOAT; + leavesGeometryDesc.Triangles.VertexCount = static_cast(m_leavesVertexBuffer.resource->GetDesc().Width) / sizeof(Vertex); + leavesGeometryDesc.Triangles.VertexBuffer.StartAddress = m_leavesVertexBuffer.resource->GetGPUVirtualAddress(); + leavesGeometryDesc.Triangles.VertexBuffer.StrideInBytes = sizeof(Vertex); + leavesGeometryDesc.Flags = D3D12_RAYTRACING_GEOMETRY_FLAG_OPAQUE; + + // Setup bush shape geometry desc + D3D12_RAYTRACING_GEOMETRY_DESC bushGeometryDesc = {}; + bushGeometryDesc.Type = D3D12_RAYTRACING_GEOMETRY_TYPE_TRIANGLES; + bushGeometryDesc.Triangles.IndexBuffer = m_bushIndexBuffer.resource->GetGPUVirtualAddress(); + bushGeometryDesc.Triangles.IndexCount = static_cast(m_bushIndexBuffer.resource->GetDesc().Width) / sizeof(Index); + bushGeometryDesc.Triangles.IndexFormat = DXGI_FORMAT_R32_UINT; + bushGeometryDesc.Triangles.Transform3x4 = 0; + bushGeometryDesc.Triangles.VertexFormat = DXGI_FORMAT_R32G32B32_FLOAT; + bushGeometryDesc.Triangles.VertexCount = static_cast(m_bushVertexBuffer.resource->GetDesc().Width) / sizeof(Vertex); + bushGeometryDesc.Triangles.VertexBuffer.StartAddress = m_bushVertexBuffer.resource->GetGPUVirtualAddress(); + bushGeometryDesc.Triangles.VertexBuffer.StrideInBytes = sizeof(Vertex); + bushGeometryDesc.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; + + // Get prebuild info for the bottom-level acceleration structure. + D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS cubeBLASInputs = {}; + cubeBLASInputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY; + cubeBLASInputs.Flags = buildFlags; + cubeBLASInputs.NumDescs = 1; // 1 geometry desc + cubeBLASInputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL; + cubeBLASInputs.pGeometryDescs = &cubeGeometryDesc; + + D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS trunkBLASInputs = {}; + trunkBLASInputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY; + trunkBLASInputs.Flags = buildFlags; + trunkBLASInputs.NumDescs = 1; // 1 geometry desc + trunkBLASInputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL; + trunkBLASInputs.pGeometryDescs = &trunkGeometryDesc; + + D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS leavesBLASInputs = {}; + leavesBLASInputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY; + leavesBLASInputs.Flags = buildFlags; + leavesBLASInputs.NumDescs = 1; // 1 geometry desc + leavesBLASInputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL; + leavesBLASInputs.pGeometryDescs = &leavesGeometryDesc; + + D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS bushBLASInputs = {}; + bushBLASInputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY; + bushBLASInputs.Flags = buildFlags; + bushBLASInputs.NumDescs = 1; // 1 geometry desc + bushBLASInputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL; + bushBLASInputs.pGeometryDescs = &bushGeometryDesc; + + D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO cubeBLASPrebuildInfo = {}; + D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO trunkBLASPrebuildInfo = {}; + D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO leavesBLASPrebuildInfo = {}; + D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO bushBLASPrebuildInfo = {}; + + m_dxrDevice->GetRaytracingAccelerationStructurePrebuildInfo(&cubeBLASInputs, &cubeBLASPrebuildInfo); + m_dxrDevice->GetRaytracingAccelerationStructurePrebuildInfo(&trunkBLASInputs, &trunkBLASPrebuildInfo); + m_dxrDevice->GetRaytracingAccelerationStructurePrebuildInfo(&leavesBLASInputs, &leavesBLASPrebuildInfo); + m_dxrDevice->GetRaytracingAccelerationStructurePrebuildInfo(&bushBLASInputs, &bushBLASPrebuildInfo); + + // Tp-level acceleration structure - 3 instances (two cubes, one trunk). + D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS topLevelInputs = {}; + topLevelInputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY; + topLevelInputs.Flags = buildFlags; + topLevelInputs.NumDescs = 2205; + topLevelInputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL; + + D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO topLevelPrebuildInfo = {}; + m_dxrDevice->GetRaytracingAccelerationStructurePrebuildInfo(&topLevelInputs, &topLevelPrebuildInfo); + ThrowIfFalse(topLevelPrebuildInfo.ResultDataMaxSizeInBytes > 0); + + ComPtr scratchResource; + SIZE_T scratchSize = max( + leavesBLASPrebuildInfo.ScratchDataSizeInBytes, + max( + cubeBLASPrebuildInfo.ScratchDataSizeInBytes, + max( + trunkBLASPrebuildInfo.ScratchDataSizeInBytes, + max( + topLevelPrebuildInfo.ScratchDataSizeInBytes, + bushBLASPrebuildInfo.ScratchDataSizeInBytes + ) + ) + ) + ); + + + AllocateUAVBuffer(device, scratchSize, &scratchResource, D3D12_RESOURCE_STATE_COMMON, 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. + { + D3D12_RESOURCE_STATES initialResourceState = D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE; + + AllocateUAVBuffer(device, cubeBLASPrebuildInfo.ResultDataMaxSizeInBytes, &m_bottomLevelAccelerationStructureCube, initialResourceState, L"BottomLevelAccelerationStructureCube"); + AllocateUAVBuffer(device, trunkBLASPrebuildInfo.ResultDataMaxSizeInBytes, &m_bottomLevelAccelerationStructureTrunk, initialResourceState, L"BottomLevelAccelerationStructureTrunk"); + AllocateUAVBuffer(device, leavesBLASPrebuildInfo.ResultDataMaxSizeInBytes, &m_bottomLevelAccelerationStructureLeaves, initialResourceState, L"BottomLevelAccelerationStructureLeaves"); + AllocateUAVBuffer(device, bushBLASPrebuildInfo.ResultDataMaxSizeInBytes, &m_bottomLevelAccelerationStructureBushes, initialResourceState, L"BottomLevelAccelerationStructureBush"); + AllocateUAVBuffer(device, topLevelPrebuildInfo.ResultDataMaxSizeInBytes, &m_topLevelAccelerationStructure, initialResourceState, L"TopLevelAccelerationStructure"); + } + + // Create an instance desc for the bottom-level acceleration structure. + ComPtr instanceDescsResource; + std::vector instanceDesc; + int objectsPerRow = 20; // Object per row along Z and X axis + float largerCubeSpacing = 4.0f; // Spacing between larger cubes + float randomCubeSpacing = 0.7f; // Spacing between random smaller cubes + float spacingGap = 5.0f; + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_real_distribution randomOffset(0.2f, 1.7f); + + // Larger cubes for the floor + for (int x = -objectsPerRow / 2; x <= objectsPerRow / 2; ++x) + { + for (int z = -objectsPerRow / 2; z <= objectsPerRow / 2; ++z) + { + float posX = x * largerCubeSpacing; + float posY = 0.0f; + float posZ = z * largerCubeSpacing; + + D3D12_RAYTRACING_INSTANCE_DESC desc = {}; + float scale = 2.0f; // Larger cube scale + desc.Transform[0][0] = scale; + desc.Transform[1][1] = scale; + desc.Transform[2][2] = scale; + + desc.Transform[0][3] = posX; + desc.Transform[1][3] = posY; + desc.Transform[2][3] = posZ; + + desc.InstanceMask = 1; + desc.AccelerationStructure = m_bottomLevelAccelerationStructureCube->GetGPUVirtualAddress(); + desc.InstanceID = instanceDesc.size(); + desc.InstanceContributionToHitGroupIndex = static_cast(instanceDesc.size()); + instanceDesc.push_back(desc); + } + } + + // Smaller transparent cubes that are placed randomly on the floor + for (int x = -objectsPerRow / 2; x <= objectsPerRow / 2; ++x) + { + for (int z = -objectsPerRow / 2; z <= objectsPerRow / 2; ++z) + { + float randomYOffset = randomOffset(gen); + + float posX = x * randomCubeSpacing; + if (x >= 0) posX += spacingGap; // Shift the second group to the right + float posY = 2.0f + randomYOffset; + float posZ = z * randomCubeSpacing; + + D3D12_RAYTRACING_INSTANCE_DESC desc = {}; + float scale = 0.05f; // Smaller cube scale + desc.Transform[0][0] = scale; + desc.Transform[1][1] = scale; + desc.Transform[2][2] = scale; + + desc.Transform[0][3] = 0.5f + posX; // X position with offset + desc.Transform[1][3] = posY; // Y position with offset + desc.Transform[2][3] = posZ; // Z position + + desc.InstanceMask = 1; + desc.AccelerationStructure = m_bottomLevelAccelerationStructureCube->GetGPUVirtualAddress(); + desc.InstanceID = static_cast(instanceDesc.size()); + desc.InstanceContributionToHitGroupIndex = static_cast(instanceDesc.size()); + instanceDesc.push_back(desc); + } + } + + + // Trunk and leaves + // Store random positions for trunks + std::vector> trunkPositions; + + // First loop: Initialize trunks + for (int x = -objectsPerRow / 2; x <= objectsPerRow / 2; ++x) + { + for (int z = -objectsPerRow / 2; z <= objectsPerRow / 2; ++z) + { + float spacingBetweenTrees = (x < 0) ? 1.3f : 1.7f; + float randomXOffset = randomOffset(gen); + float randomYOffset = randomOffset(gen); + float randomZOffset = randomOffset(gen); + + float posX = x * spacingBetweenTrees + randomXOffset; + if (x >= 0) posX += spacingGap; // Shift the second group to the right + float posY = 2.0f - (randomYOffset / 8); + float posZ = z * spacingBetweenTrees + randomZOffset; + trunkPositions.emplace_back(posX, posY, posZ); + + // Trunk instance + D3D12_RAYTRACING_INSTANCE_DESC desc = {}; + float scale = 35.0f; + desc.Transform[0][0] = scale; + desc.Transform[1][1] = scale; + desc.Transform[2][2] = scale; + + desc.Transform[0][3] = posX; + desc.Transform[1][3] = posY; + desc.Transform[2][3] = posZ; + desc.InstanceMask = 1; + desc.AccelerationStructure = m_bottomLevelAccelerationStructureTrunk->GetGPUVirtualAddress(); + desc.InstanceID = static_cast(instanceDesc.size()); + desc.InstanceContributionToHitGroupIndex = static_cast(instanceDesc.size()); + instanceDesc.push_back(desc); + } + } + + + // Second loop: Initialize leaves + for (size_t i = 0; i < trunkPositions.size(); ++i) + { + // Leaves instance + D3D12_RAYTRACING_INSTANCE_DESC desc = {}; + float scale = 35.0f; + desc.Transform[0][0] = scale; + desc.Transform[1][1] = scale; + desc.Transform[2][2] = scale; + + // Use the same position as the corresponding trunk + desc.Transform[0][3] = std::get<0>(trunkPositions[i]); // X position + desc.Transform[1][3] = std::get<1>(trunkPositions[i]); // Y position + desc.Transform[2][3] = std::get<2>(trunkPositions[i]); // Z position + + desc.InstanceMask = 1; + desc.Flags = D3D12_RAYTRACING_INSTANCE_FLAG_TRIANGLE_CULL_DISABLE; + desc.AccelerationStructure = m_bottomLevelAccelerationStructureLeaves->GetGPUVirtualAddress(); + desc.InstanceID = instanceDesc.size(); + desc.InstanceContributionToHitGroupIndex = static_cast(instanceDesc.size()); + instanceDesc.push_back(desc); + } + + // Third loop: Initialize bushes + for (size_t i = 0; i < trunkPositions.size(); ++i) + { + float randomXOffset = randomOffset(gen); + + // Leaves instance + D3D12_RAYTRACING_INSTANCE_DESC desc = {}; + float scale = 17.0f; + desc.Transform[0][0] = scale; + desc.Transform[1][1] = scale; + desc.Transform[2][2] = scale; + + // Use the same position as the corresponding trunk + desc.Transform[0][3] = std::get<0>(trunkPositions[i]) + randomXOffset; // X position with random offset + desc.Transform[1][3] = std::get<1>(trunkPositions[i]) - 0.5f; + desc.Transform[2][3] = std::get<2>(trunkPositions[i]); // Z position + + desc.InstanceMask = 1; + desc.Flags = D3D12_RAYTRACING_INSTANCE_FLAG_TRIANGLE_CULL_DISABLE; + desc.AccelerationStructure = m_bottomLevelAccelerationStructureBushes->GetGPUVirtualAddress(); + desc.InstanceID = instanceDesc.size(); + desc.InstanceContributionToHitGroupIndex = static_cast(instanceDesc.size()); + instanceDesc.push_back(desc); + } + + AllocateUploadBuffer(device, instanceDesc.data(), instanceDesc.size() * sizeof(D3D12_RAYTRACING_INSTANCE_DESC), &instanceDescsResource, L"InstanceDesc"); + + // Build bottom-level acceleration structures for cube + D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC cubeBLASDesc = {}; + cubeBLASDesc.Inputs = cubeBLASInputs; + cubeBLASDesc.ScratchAccelerationStructureData = scratchResource->GetGPUVirtualAddress(); + cubeBLASDesc.DestAccelerationStructureData = m_bottomLevelAccelerationStructureCube->GetGPUVirtualAddress(); + + // Build bottom-level acceleration structures for trunk + D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC trunkBLASDesc = {}; + trunkBLASDesc.Inputs = trunkBLASInputs; + trunkBLASDesc.ScratchAccelerationStructureData = scratchResource->GetGPUVirtualAddress(); + trunkBLASDesc.DestAccelerationStructureData = m_bottomLevelAccelerationStructureTrunk->GetGPUVirtualAddress(); + + // Build bottom-level acceleration structures for leaves + D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC leavesBLASDesc = {}; + leavesBLASDesc.Inputs = leavesBLASInputs; + leavesBLASDesc.ScratchAccelerationStructureData = scratchResource->GetGPUVirtualAddress(); + leavesBLASDesc.DestAccelerationStructureData = m_bottomLevelAccelerationStructureLeaves->GetGPUVirtualAddress(); + + // Build bottom-level acceleration structures for bushes + D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC bushBLASDesc = {}; + bushBLASDesc.Inputs = bushBLASInputs; + bushBLASDesc.ScratchAccelerationStructureData = scratchResource->GetGPUVirtualAddress(); + bushBLASDesc.DestAccelerationStructureData = m_bottomLevelAccelerationStructureBushes->GetGPUVirtualAddress(); + + D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC topLevelBuildDesc = {}; + topLevelBuildDesc.Inputs = topLevelInputs; + topLevelBuildDesc.Inputs.InstanceDescs = instanceDescsResource->GetGPUVirtualAddress(); + topLevelBuildDesc.DestAccelerationStructureData = m_topLevelAccelerationStructure->GetGPUVirtualAddress(); + topLevelBuildDesc.ScratchAccelerationStructureData = scratchResource->GetGPUVirtualAddress(); + + auto BuildAccelerationStructure = [&](auto* raytracingCommandList) + { + raytracingCommandList->BuildRaytracingAccelerationStructure(&cubeBLASDesc, 0, nullptr); + commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::UAV(m_bottomLevelAccelerationStructureCube.Get())); + raytracingCommandList->BuildRaytracingAccelerationStructure(&trunkBLASDesc, 0, nullptr); + commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::UAV(m_bottomLevelAccelerationStructureTrunk.Get())); + raytracingCommandList->BuildRaytracingAccelerationStructure(&leavesBLASDesc, 0, nullptr); + commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::UAV(m_bottomLevelAccelerationStructureLeaves.Get())); + raytracingCommandList->BuildRaytracingAccelerationStructure(&bushBLASDesc, 0, nullptr); + commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::UAV(m_bottomLevelAccelerationStructureBushes.Get())); + raytracingCommandList->BuildRaytracingAccelerationStructure(&topLevelBuildDesc, 0, nullptr); + }; + + // Build acceleration structure. + BuildAccelerationStructure(m_dxrCommandList.Get()); + + // Kick off acceleration structure construction. + m_deviceResources->ExecuteCommandList(); + + // 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 D3D12RaytracingSakuraScene::BuildShaderTables() +{ + auto device = m_deviceResources->GetD3DDevice(); + + void* rayGenShaderIdentifier; + void* missShaderIdentifier; + void* hitGroupShaderIdentifier; + void* trunkHitGroupShaderIdentifier; + void* leavesHitGroupShaderIdentifier; + void* leavesLightHitGroupShaderIdentifier; + void* leavesDarkHitGroupShaderIdentifier; + void* leavesExtraDarkHitGroupShaderIdentifier; + void* bushHitGroupShaderIdentifier; + void* tcubeHitGroupShaderIdentifier; + + auto GetShaderIdentifiers = [&](auto* stateObjectProperties) + { + rayGenShaderIdentifier = stateObjectProperties->GetShaderIdentifier(c_raygenShaderName); + missShaderIdentifier = stateObjectProperties->GetShaderIdentifier(c_missShaderName); + hitGroupShaderIdentifier = stateObjectProperties->GetShaderIdentifier(c_hitGroupName); + trunkHitGroupShaderIdentifier = stateObjectProperties->GetShaderIdentifier(c_trunkHitGroupName); + leavesHitGroupShaderIdentifier = stateObjectProperties->GetShaderIdentifier(c_leavesHitGroupName); + leavesLightHitGroupShaderIdentifier = stateObjectProperties->GetShaderIdentifier(c_leavesLightHitGroupName); + leavesDarkHitGroupShaderIdentifier = stateObjectProperties->GetShaderIdentifier(c_leavesDarkHitGroupName); + leavesExtraDarkHitGroupShaderIdentifier = stateObjectProperties->GetShaderIdentifier(c_leavesExtraDarkHitGroupName); + bushHitGroupShaderIdentifier = stateObjectProperties->GetShaderIdentifier(c_bushHitGroupName); + tcubeHitGroupShaderIdentifier = stateObjectProperties->GetShaderIdentifier(c_transparentCubeHitGroupName); + }; + + // Get shader identifiers. + UINT shaderIdentifierSize; + { + ComPtr stateObjectProperties; + ThrowIfFailed(m_dxrStateObject.As(&stateObjectProperties)); + GetShaderIdentifiers(stateObjectProperties.Get()); + shaderIdentifierSize = D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES; + } + + // Ray gen shader table + { + struct RootArguments { + ObjectConstantBuffer cb; + } rootArguments; + rootArguments.cb = m_objectCB; + + UINT numShaderRecords = 1; + UINT shaderRecordSize = shaderIdentifierSize + sizeof(rootArguments); + ShaderTable rayGenShaderTable(device, numShaderRecords, shaderRecordSize, L"RayGenShaderTable"); + rayGenShaderTable.push_back(ShaderRecord(rayGenShaderIdentifier, shaderIdentifierSize, &rootArguments, sizeof(rootArguments))); + m_rayGenShaderTable = rayGenShaderTable.GetResource(); + } + + // Miss shader table + { + UINT numShaderRecords = 1; + UINT shaderRecordSize = shaderIdentifierSize; + ShaderTable missShaderTable(device, numShaderRecords, shaderRecordSize, L"MissShaderTable"); + missShaderTable.push_back(ShaderRecord(missShaderIdentifier, shaderIdentifierSize)); + m_missShaderTable = missShaderTable.GetResource(); + } + + // Hit group shader table + { + struct RootArguments { + ObjectConstantBuffer cb; + }; + + UINT numShaderRecords = 2205; + UINT shaderRecordSize = shaderIdentifierSize + sizeof(RootArguments); + ShaderTable hitGroupShaderTable(device, numShaderRecords, shaderRecordSize, L"HitGroupShaderTable"); + + //// Larger cube shader records + for (int i = 0; i < 441; ++i) { + RootArguments argument; + argument.cb = m_cubeCB; + argument.cb.albedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f); // White color + argument.cb.materialID = 0; + hitGroupShaderTable.push_back(ShaderRecord(hitGroupShaderIdentifier, shaderIdentifierSize, &argument, sizeof(argument))); + } + + // Transparant cube shader records + for (int i = 0; i < 441; ++i) { + RootArguments argument; + argument.cb = m_transparentCubeCB; + argument.cb.albedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f); // White color + argument.cb.materialID = 1; + hitGroupShaderTable.push_back(ShaderRecord(tcubeHitGroupShaderIdentifier, shaderIdentifierSize, &argument, sizeof(argument))); + } + + // Create a random number generator for trunks + std::random_device rdTrunk; + std::mt19937 genTrunk(rdTrunk()); + std::uniform_int_distribution<> distribTrunk(0, 1); // 2 hit groups: 0 and 1 + + // Tree trunk shader records + for (int i = 0; i < 441; ++i) { + RootArguments argument; + const void* shaderIdentifier = nullptr; + int randomIndex = distribTrunk(genTrunk); // Random number between 0 and 1 + switch (randomIndex) { + + case 0: + argument.cb = m_trunkCB; + argument.cb.albedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f); // 16 bytes + argument.cb.materialID = 2; + hitGroupShaderTable.push_back(ShaderRecord(trunkHitGroupShaderIdentifier, shaderIdentifierSize, &argument, sizeof(argument))); + break; + case 1: + argument.cb = m_trunkTransparentCB; + argument.cb.albedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f); // 16 bytes + argument.cb.materialID = 8; + hitGroupShaderTable.push_back(ShaderRecord(tcubeHitGroupShaderIdentifier, shaderIdentifierSize, &argument, sizeof(argument))); + break; + } + } + + // Create a random number generator for leaves + std::random_device rdLeaves; + std::mt19937 genLeaves(rdLeaves()); + std::uniform_int_distribution<> distribLeaves(0, 4); // 5 hit groups: 0, 1, 2, 3, and 4 + + // Tree leaves shader records + for (int i = 0; i < 441; ++i) { + RootArguments argument; + const void* shaderIdentifier = nullptr; + int randomIndex = distribLeaves(genLeaves); // Random number between 0 and 3 + switch (randomIndex) { + + case 0: + argument.cb = m_leavesCB; + argument.cb.albedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f); // 16 bytes + argument.cb.materialID = 3; + hitGroupShaderTable.push_back(ShaderRecord(leavesHitGroupShaderIdentifier, shaderIdentifierSize, &argument, sizeof(argument))); + break; + case 1: + argument.cb = m_leavesLightCB; + argument.cb.albedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f); // 16 bytes + argument.cb.materialID = 5; + hitGroupShaderTable.push_back(ShaderRecord(leavesLightHitGroupShaderIdentifier, shaderIdentifierSize, &argument, sizeof(argument))); + break; + case 2: + argument.cb = m_leavesDarkCB; + argument.cb.albedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f); // 16 bytes + argument.cb.materialID = 6; + hitGroupShaderTable.push_back(ShaderRecord(leavesDarkHitGroupShaderIdentifier, shaderIdentifierSize, &argument, sizeof(argument))); + break; + case 3: + argument.cb = m_leavesExtraDarkCB; + argument.cb.albedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f); // 16 bytes + argument.cb.materialID = 7; + hitGroupShaderTable.push_back(ShaderRecord(leavesExtraDarkHitGroupShaderIdentifier, shaderIdentifierSize, &argument, sizeof(argument))); + break; + case 4: + argument.cb = m_transparentLeavesCB; + argument.cb.albedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f); // 16 bytes + argument.cb.materialID = 9; + hitGroupShaderTable.push_back(ShaderRecord(leavesDarkHitGroupShaderIdentifier, shaderIdentifierSize, &argument, sizeof(argument))); + break; + } + } + + + // Bush shader records + for (int i = 0; i < 441; ++i) { + RootArguments argument; + argument.cb = m_bushCB; + argument.cb.albedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f); // 16 bytes + argument.cb.materialID = 4; + const void* shaderIdentifier = nullptr; + switch (i % 2) { + case 0: + hitGroupShaderTable.push_back(ShaderRecord(tcubeHitGroupShaderIdentifier, shaderIdentifierSize, &argument, sizeof(argument))); + break; + case 1: + hitGroupShaderTable.push_back(ShaderRecord(bushHitGroupShaderIdentifier, shaderIdentifierSize, &argument, sizeof(argument))); + break; + } + } + + // Add this line to fix the null pointer issue: + m_hitGroupShaderTable = hitGroupShaderTable.GetResource(); + } +} + +// Update frame-based values. +void D3D12RaytracingSakuraScene::OnUpdate() +{ + m_timer.Tick(); + CalculateFrameStats(); + float elapsedTime = static_cast(m_timer.GetElapsedSeconds()); + auto frameIndex = m_deviceResources->GetCurrentFrameIndex(); + auto prevFrameIndex = m_deviceResources->GetPreviousFrameIndex(); + + auto kb = m_keyboard->GetState(); + m_keyboardButtons.Update(kb); + + if (m_keyboardButtons.IsKeyPressed(Keyboard::Keys::R)) + { + rotateCamera = !rotateCamera; + } + + if (m_keyboardButtons.IsKeyPressed(Keyboard::Keys::P)) + { + OutputDebugStringA("P key pressed!\n"); + m_serEnabled = !m_serEnabled; + + + if (m_serEnabled) + { + // Default to sorting by HitObject when SER is enabled + m_sortByHit = true; + m_sortByMaterial = false; + m_sortByBoth = false; + } + else + { + // Disable all sorting when SER is off + m_sortByHit = false; + m_sortByMaterial = false; + m_sortByBoth = false; + } + + } + + if (m_keyboardButtons.IsKeyPressed(Keyboard::Keys::H)) + { + OutputDebugStringA("H key pressed!\n"); + m_serEnabled = true; + m_sortByHit = true; + m_sortByMaterial = false; + m_sortByBoth = false; + } + + if (m_keyboardButtons.IsKeyPressed(Keyboard::Keys::M)) + { + OutputDebugStringA("M key pressed!\n"); + m_serEnabled = true; + m_sortByHit = false; + m_sortByMaterial = true; + m_sortByBoth = false; + } + + if (m_keyboardButtons.IsKeyPressed(Keyboard::Keys::B)) + { + OutputDebugStringA("B key pressed!\n"); + m_serEnabled = true; + m_sortByHit = false; + m_sortByMaterial = false; + m_sortByBoth = true; + } + + // Camera movement speed + float movementSpeed = 5.0f * elapsedTime; + + // Forward and backward movement (W/S) + if (kb.W) + { + XMVECTOR forward = XMVector3Normalize(m_at - m_eye); + m_eye += forward * movementSpeed; + m_at += forward * movementSpeed; + } + if (kb.S) + { + XMVECTOR backward = XMVector3Normalize(m_eye - m_at); + m_eye += backward * movementSpeed; + m_at += backward * movementSpeed; + } + + // Left and right strafing (A/D) + if (kb.A || kb.Left) + { + XMVECTOR forward = XMVector3Normalize(m_at - m_eye); + XMVECTOR left = XMVector3Normalize(XMVector3Cross(forward, m_up)); + m_eye += left * movementSpeed; + m_at += left * movementSpeed; + } + if (kb.D || kb.Right) + { + XMVECTOR forward = XMVector3Normalize(m_at - m_eye); + XMVECTOR right = XMVector3Normalize(XMVector3Cross(m_up, forward)); + m_eye += right * movementSpeed; + m_at += right * movementSpeed; + } + + // Up and down movement + if (kb.Up) + { + m_eye += m_up * movementSpeed; + m_at += m_up * movementSpeed; + } + if (kb.Down) + { + m_eye -= m_up * movementSpeed; + m_at -= m_up * movementSpeed; + } + + if (kb.Q) // Look down + { + XMVECTOR forward = XMVector3Normalize(m_at - m_eye); + XMVECTOR right = XMVector3Normalize(XMVector3Cross(m_up, forward)); + + float pitchSpeed = XMConvertToRadians(30.0f) * elapsedTime; // degrees/sec + + XMMATRIX pitchMatrix = XMMatrixRotationAxis(right, pitchSpeed); + XMVECTOR newForward = XMVector3TransformNormal(forward, pitchMatrix); + + m_at = m_eye + newForward; + } + + if (kb.E) // Look up + { + XMVECTOR forward = XMVector3Normalize(m_at - m_eye); + XMVECTOR right = XMVector3Normalize(XMVector3Cross(m_up, forward)); + + float pitchSpeed = XMConvertToRadians(-30.0f) * elapsedTime; + + XMMATRIX pitchMatrix = XMMatrixRotationAxis(right, pitchSpeed); + XMVECTOR newForward = XMVector3TransformNormal(forward, pitchMatrix); + + m_at = m_eye + newForward; + } + + + + + + // Rotate the camera around Y axis. + // if (rotateCamera) + //{ + // float secondsToRotateAround = 52.0f; + // float angleToRotateBy = 360.0f * (elapsedTime / secondsToRotateAround); + // XMMATRIX rotate = XMMatrixRotationY(XMConvertToRadians(angleToRotateBy)); + // m_eye = XMVector3Transform(m_eye, rotate); + // m_up = XMVector3Transform(m_up, rotate); + // m_at = XMVector3Transform(m_at, rotate); + //} + UpdateCameraMatrices(); + + // Rotate the second light around Y axis. + //{ + // float secondsToRotateAround = 8.0f; + // float angleToRotateBy = -360.0f * (elapsedTime / secondsToRotateAround); + // XMMATRIX rotate = XMMatrixRotationY(XMConvertToRadians(angleToRotateBy)); + // const XMVECTOR& prevLightPosition = m_sceneCB[prevFrameIndex].lightPosition; + // m_sceneCB[frameIndex].lightPosition = XMVector3Transform(prevLightPosition, rotate); + //} + m_sceneCB[frameIndex].enableSER = m_serEnabled ? 1 : 0; + m_sceneCB[frameIndex].enableSortByHit = m_sortByHit ? 1 : 0; + m_sceneCB[frameIndex].enableSortByMaterial = m_sortByMaterial ? 1 : 0; + m_sceneCB[frameIndex].enableSortByBoth = m_sortByBoth ? 1 : 0; +} + +void D3D12RaytracingSakuraScene::DoRaytracing() +{ + auto commandList = m_deviceResources->GetCommandList(); + auto frameIndex = m_deviceResources->GetCurrentFrameIndex(); + + 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->HitGroupTable.StartAddress = m_hitGroupShaderTable->GetGPUVirtualAddress(); + dispatchDesc->HitGroupTable.SizeInBytes = m_hitGroupShaderTable->GetDesc().Width; + dispatchDesc->HitGroupTable.StrideInBytes = m_hitGroupShaderTable->GetDesc().Width / 2205; + 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; + dispatchDesc->Width = m_width; + dispatchDesc->Height = m_height; + dispatchDesc->Depth = 1; + commandList->SetPipelineState1(stateObject); + commandList->DispatchRays(dispatchDesc); + }; + + auto SetCommonPipelineState = [&](auto* descriptorSetCommandList) + { + descriptorSetCommandList->SetDescriptorHeaps(1, m_descriptorHeap.GetAddressOf()); + // Set index and successive vertex buffer decriptor tables + commandList->SetComputeRootDescriptorTable(GlobalRootSignatureParams::VertexBuffersSlot, m_indexBuffer.gpuDescriptorHandle); + commandList->SetComputeRootDescriptorTable(GlobalRootSignatureParams::OutputViewSlot, m_raytracingOutputResourceUAVGpuDescriptor); + }; + + commandList->SetComputeRootSignature(m_raytracingGlobalRootSignature.Get()); + + // Copy the updated scene constant buffer to GPU. + memcpy(&m_mappedConstantData[frameIndex].constants, &m_sceneCB[frameIndex], sizeof(m_sceneCB[frameIndex])); + auto cbGpuAddress = m_perFrameConstants->GetGPUVirtualAddress() + frameIndex * sizeof(m_mappedConstantData[0]); + commandList->SetComputeRootConstantBufferView(GlobalRootSignatureParams::SceneConstantSlot, cbGpuAddress); + + // Bind the heaps, acceleration structure and dispatch rays. + D3D12_DISPATCH_RAYS_DESC dispatchDesc = {}; + SetCommonPipelineState(commandList); + commandList->SetComputeRootShaderResourceView(GlobalRootSignatureParams::AccelerationStructureSlot, m_topLevelAccelerationStructure->GetGPUVirtualAddress()); + DispatchRays(m_dxrCommandList.Get(), m_dxrStateObject.Get(), &dispatchDesc); +} + +// Update the application state with the new resolution. +void D3D12RaytracingSakuraScene::UpdateForSizeChange(UINT width, UINT height) +{ + DXSample::UpdateForSizeChange(width, height); +} + +// Render the UI +void D3D12RaytracingSakuraScene::RenderUI() +{ + auto commandList = m_deviceResources->GetCommandList(); + auto renderTarget = m_deviceResources->GetRenderTarget(); + auto viewport = m_deviceResources->GetScreenViewport(); + auto scissorRect = m_deviceResources->GetScissorRect(); + auto rtvDescriptor = m_deviceResources->GetRenderTargetView(); + + + // Transition render target to RENDER_TARGET state + D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( + renderTarget, + D3D12_RESOURCE_STATE_PRESENT, + D3D12_RESOURCE_STATE_RENDER_TARGET + ); + commandList->ResourceBarrier(1, &barrier); + + + // Set the render target + commandList->OMSetRenderTargets(1, &rtvDescriptor, FALSE, nullptr); + commandList->RSSetViewports(1, &viewport); + commandList->RSSetScissorRects(1, &scissorRect); + + m_spriteBatch->SetViewport(viewport); + m_spriteBatch->Begin(commandList); + + XMFLOAT2 textPos = XMFLOAT2(30, 30); + XMVECTOR textColor = XMVectorSet(1, 1, 1, 1); + + wchar_t buffer[256]; + + + m_smallFont->DrawString(m_spriteBatch.get(), L"D3D12: Shader Execution Reordering", textPos, textColor); + textPos.y += m_smallFont->GetLineSpacing() * 2; + + //swprintf_s(buffer, ARRAYSIZE(buffer), L"Rotate Camera: %s - Press 'R'", rotateCamera ? L"Rotating" : L"Disabled"); + //m_smallFont->DrawString(m_spriteBatch.get(), buffer, textPos, textColor); + //textPos.y += m_smallFont->GetLineSpacing(); + + swprintf_s(buffer, ARRAYSIZE(buffer), L"Toggle SER: %s - Press 'P'", m_serEnabled ? L"Enabled" : L"Disabled"); + m_smallFont->DrawString(m_spriteBatch.get(), buffer, textPos, textColor); + textPos.y += m_smallFont->GetLineSpacing(); + + swprintf_s(buffer, ARRAYSIZE(buffer), L"Sort by HitObject: %s - Press 'H'", m_sortByHit ? L"Enabled" : L"Disabled"); + m_smallFont->DrawString(m_spriteBatch.get(), buffer, textPos, textColor); + textPos.y += m_smallFont->GetLineSpacing(); + + swprintf_s(buffer, ARRAYSIZE(buffer), L"Sort by MaterialID: %s - Press 'M'", m_sortByMaterial ? L"Enabled" : L"Disabled"); + m_smallFont->DrawString(m_spriteBatch.get(), buffer, textPos, textColor); + textPos.y += m_smallFont->GetLineSpacing(); + + swprintf_s(buffer, ARRAYSIZE(buffer), L"Sort by both: %s - Press 'B'", m_sortByBoth ? L"Enabled" : L"Disabled"); + m_smallFont->DrawString(m_spriteBatch.get(), buffer, textPos, textColor); + textPos.y += m_smallFont->GetLineSpacing(); + + swprintf_s(buffer, ARRAYSIZE(buffer), L"Use WASD to move around the scene."); + m_smallFont->DrawString(m_spriteBatch.get(), buffer, textPos, textColor); + textPos.y += m_smallFont->GetLineSpacing(); + + m_spriteBatch->End(); + + + // Transition render target back to PRESENT state + D3D12_RESOURCE_BARRIER barrierBack = CD3DX12_RESOURCE_BARRIER::Transition( + renderTarget, + D3D12_RESOURCE_STATE_RENDER_TARGET, + D3D12_RESOURCE_STATE_PRESENT + ); + commandList->ResourceBarrier(1, &barrierBack); +} + + +// Copy the raytracing output to the backbuffer. +void D3D12RaytracingSakuraScene::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); +} + +// Create resources that are dependent on the size of the main window. +void D3D12RaytracingSakuraScene::CreateWindowSizeDependentResources() +{ + CreateRaytracingOutputResource(); + UpdateCameraMatrices(); +} + +// Release resources that are dependent on the size of the main window. +void D3D12RaytracingSakuraScene::ReleaseWindowSizeDependentResources() +{ + m_raytracingOutput.Reset(); +} + +// Release all resources that depend on the device. +void D3D12RaytracingSakuraScene::ReleaseDeviceDependentResources() +{ + m_raytracingGlobalRootSignature.Reset(); + m_raytracingLocalRootSignature.Reset(); + + m_dxrDevice.Reset(); + m_dxrCommandList.Reset(); + m_dxrStateObject.Reset(); + + m_descriptorHeap.Reset(); + m_descriptorsAllocated = 0; + m_raytracingOutputResourceUAVDescriptorHeapIndex = UINT_MAX; + m_indexBuffer.resource.Reset(); + m_vertexBuffer.resource.Reset(); + m_perFrameConstants.Reset(); + m_rayGenShaderTable.Reset(); + m_missShaderTable.Reset(); + m_hitGroupShaderTable.Reset(); + m_bottomLevelAccelerationStructureTrunk.Reset(); + m_bottomLevelAccelerationStructureLeaves.Reset(); + m_bottomLevelAccelerationStructureCube.Reset(); + m_topLevelAccelerationStructure.Reset(); + +} + +void D3D12RaytracingSakuraScene::RecreateD3D() +{ + // Give GPU a chance to finish its execution in progress. + try + { + m_deviceResources->WaitForGpu(); + } + catch (HrException&) + { + // Do nothing, currently attached adapter is unresponsive. + } + m_deviceResources->HandleDeviceLost(); +} + +// Render the scene. +void D3D12RaytracingSakuraScene::OnRender() +{ + if (!m_deviceResources->IsWindowVisible()) + { + return; + } + + m_deviceResources->Prepare(); + DoRaytracing(); + CopyRaytracingOutputToBackbuffer(); + RenderUI(); + + m_deviceResources->Present(D3D12_RESOURCE_STATE_PRESENT); + m_graphicsMemory->Commit(m_deviceResources->GetCommandQueue()); +} + +void D3D12RaytracingSakuraScene::OnDestroy() +{ + // Let GPU finish before releasing D3D resources. + m_deviceResources->WaitForGpu(); + OnDeviceLost(); +} + +// Release all device dependent resouces when a device is lost. +void D3D12RaytracingSakuraScene::OnDeviceLost() +{ + ReleaseWindowSizeDependentResources(); + ReleaseDeviceDependentResources(); +} + +// Create all device dependent resources when a device is restored. +void D3D12RaytracingSakuraScene::OnDeviceRestored() +{ + CreateDeviceDependentResources(); + CreateWindowSizeDependentResources(); +} + +// Compute the average frames per second and million rays per second. +void D3D12RaytracingSakuraScene::CalculateFrameStats() +{ + static int frameCnt = 0; + static double elapsedTime = 0.0f; + double totalTime = m_timer.GetTotalSeconds(); + frameCnt++; + + // Compute averages over one second period. + if ((totalTime - elapsedTime) >= 1.0f) + { + float diff = static_cast(totalTime - elapsedTime); + float fps = static_cast(frameCnt) / diff; // Normalize to an exact second. + + frameCnt = 0; + elapsedTime = totalTime; + + float MRaysPerSecond = (m_width * m_height * fps) / static_cast(1e6); + + wstringstream windowText; + windowText << setprecision(2) << fixed + << L" fps: " << fps << L" ~Million Primary Rays/s: " << MRaysPerSecond + << L" GPU[" << m_deviceResources->GetAdapterID() << L"]: " << m_deviceResources->GetAdapterDescription() + << L" SER: " << (m_serEnabled ? L"ON" : L"OFF"); + SetCustomWindowText(windowText.str().c_str()); + } +} + +// Handle OnSizeChanged message event. +void D3D12RaytracingSakuraScene::OnSizeChanged(UINT width, UINT height, bool minimized) +{ + if (!m_deviceResources->WindowSizeChanged(width, height, minimized)) + { + return; + } + + UpdateForSizeChange(width, height); + + ReleaseWindowSizeDependentResources(); + CreateWindowSizeDependentResources(); +} + +// Allocate a descriptor and return its index. +// If the passed descriptorIndexToUse is valid, it will be used instead of allocating a new one. +UINT D3D12RaytracingSakuraScene::AllocateDescriptor(D3D12_CPU_DESCRIPTOR_HANDLE* cpuDescriptor, UINT descriptorIndexToUse) +{ + auto descriptorHeapCpuBase = m_descriptorHeap->GetCPUDescriptorHandleForHeapStart(); + if (descriptorIndexToUse >= m_descriptorHeap->GetDesc().NumDescriptors) + { + descriptorIndexToUse = m_descriptorsAllocated++; + } + *cpuDescriptor = CD3DX12_CPU_DESCRIPTOR_HANDLE(descriptorHeapCpuBase, descriptorIndexToUse, m_descriptorSize); + return descriptorIndexToUse; +} + +// Create SRV for a buffer. +UINT D3D12RaytracingSakuraScene::CreateBufferSRV(D3DBuffer* buffer, UINT numElements, UINT elementSize) +{ + auto device = m_deviceResources->GetD3DDevice(); + + // SRV + D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; + srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + srvDesc.Buffer.NumElements = numElements; + if (elementSize == 0) + { + srvDesc.Format = DXGI_FORMAT_R32_TYPELESS; + srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_RAW; + srvDesc.Buffer.StructureByteStride = 0; + } + else + { + srvDesc.Format = DXGI_FORMAT_UNKNOWN; + srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE; + srvDesc.Buffer.StructureByteStride = elementSize; + } + UINT descriptorIndex = AllocateDescriptor(&buffer->cpuDescriptorHandle); + device->CreateShaderResourceView(buffer->resource.Get(), &srvDesc, buffer->cpuDescriptorHandle); + buffer->gpuDescriptorHandle = CD3DX12_GPU_DESCRIPTOR_HANDLE(m_descriptorHeap->GetGPUDescriptorHandleForHeapStart(), descriptorIndex, m_descriptorSize); + return descriptorIndex; +} \ No newline at end of file diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/D3D12RaytracingSakuraScene.h b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/D3D12RaytracingSakuraScene.h new file mode 100644 index 000000000..a5b7f0d44 --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/D3D12RaytracingSakuraScene.h @@ -0,0 +1,223 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once + +#include "DXSample.h" +#include "StepTimer.h" +#include "RaytracingHlslCompat.h" +#include "ObjModelLoader.h" +//#include "ObjectConstantBuffer.h" + +namespace GlobalRootSignatureParams { + enum Value { + OutputViewSlot = 0, + AccelerationStructureSlot, + SceneConstantSlot, + VertexBuffersSlot, + Count + }; +} + +namespace LocalRootSignatureParams { + enum Value { + CubeConstantSlot = 0, + Count + }; +} + +class D3D12RaytracingSakuraScene : public DXSample +{ +public: + D3D12RaytracingSakuraScene(UINT width, UINT height, std::wstring name); + + // IDeviceNotify + virtual void OnDeviceLost() override; + virtual void OnDeviceRestored() override; + + // Messages + virtual void OnInit(); + virtual void OnUpdate(); + virtual void OnRender(); + virtual void OnSizeChanged(UINT width, UINT height, bool minimized); + virtual void OnDestroy(); + virtual IDXGISwapChain* GetSwapchain() { return m_deviceResources->GetSwapChain(); } + +private: + static const UINT FrameCount = 3; + static const UINT TextureWidth = 256; + static const UINT TextureHeight = 256; + static const UINT TexturePixelSize = 4; + + // We'll allocate space for several of these and they will need to be padded for alignment. + static_assert(sizeof(SceneConstantBuffer) < D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT, "Checking the size here."); + + union AlignedSceneConstantBuffer + { + SceneConstantBuffer constants; + uint8_t alignmentPadding[D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT]; + }; + AlignedSceneConstantBuffer* m_mappedConstantData; + ComPtr m_perFrameConstants; + + // DirectX Raytracing (DXR) attributes + ComPtr m_dxrDevice; + ComPtr m_dxrCommandList; + ComPtr m_dxrStateObject; + + // Root signatures + ComPtr m_raytracingGlobalRootSignature; + ComPtr m_raytracingLocalRootSignature; + + // Descriptors + ComPtr m_descriptorHeap; + UINT m_descriptorsAllocated; + UINT m_descriptorSize; + + // UI + std::unique_ptr m_spriteBatch; + std::unique_ptr m_smallFont; + + std::unique_ptr m_keyboard; + DirectX::Keyboard::KeyboardStateTracker m_keyboardButtons; + + // User toggle + bool m_serEnabled; + bool m_sortByHit; + bool m_sortByMaterial; + bool m_sortByBoth; + bool m_rebuildASNextFrame; + bool rotateCamera; + + // Texture resources + ComPtr m_texture1; + ComPtr m_texture2; + ComPtr m_texture3; + CD3DX12_GPU_DESCRIPTOR_HANDLE m_textureSrvGpuDescriptor1; + CD3DX12_GPU_DESCRIPTOR_HANDLE m_textureSrvGpuDescriptor2; + CD3DX12_GPU_DESCRIPTOR_HANDLE m_textureSrvGpuDescriptor3; + + // Raytracing scene + SceneConstantBuffer m_sceneCB[FrameCount]; + ObjectConstantBuffer m_objectCB; + ObjectConstantBuffer m_cubeCB; + ObjectConstantBuffer m_tcubeCB; + ObjectConstantBuffer m_transparentCubeCB; + ObjectConstantBuffer m_trunkCB; + ObjectConstantBuffer m_trunkTransparentCB; + ObjectConstantBuffer m_leavesCB; + ObjectConstantBuffer m_leavesLightCB; + ObjectConstantBuffer m_leavesDarkCB; + ObjectConstantBuffer m_leavesExtraDarkCB; + ObjectConstantBuffer m_transparentLeavesCB; + ObjectConstantBuffer m_bushCB; + + // Asset loader + ObjModelLoader m_ObjModelLoader; + + // Geometry + struct D3DBuffer + { + ComPtr resource; + D3D12_CPU_DESCRIPTOR_HANDLE cpuDescriptorHandle; + D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptorHandle; + }; + D3DBuffer m_indexBuffer; + D3DBuffer m_vertexBuffer; + D3DBuffer m_trunkIndexBuffer; + D3DBuffer m_trunkVertexBuffer; + D3DBuffer m_leavesIndexBuffer; + D3DBuffer m_leavesVertexBuffer; + D3DBuffer m_bushIndexBuffer; + D3DBuffer m_bushVertexBuffer; + int m_totalTrunkVertexCount; + int m_totalLeavesVertexCount; + int m_totalBushVertexCount; + + // Acceleration structure + ComPtr m_bottomLevelAccelerationStructure; + ComPtr m_bottomLevelAccelerationStructureCube; + ComPtr m_bottomLevelAccelerationStructureTrunk; + ComPtr m_bottomLevelAccelerationStructureLeaves; + ComPtr m_bottomLevelAccelerationStructureBushes; + + ComPtr m_topLevelAccelerationStructuretrunk; + ComPtr m_topLevelAccelerationStructureLeaves; + ComPtr m_topLevelAccelerationStructure; + + // Raytracing output + ComPtr m_raytracingOutput; + D3D12_GPU_DESCRIPTOR_HANDLE m_raytracingOutputResourceUAVGpuDescriptor; + UINT m_raytracingOutputResourceUAVDescriptorHeapIndex; + + // Shader tables + static const wchar_t* c_hitGroupName; + static const wchar_t* c_trunkHitGroupName; + static const wchar_t* c_leavesHitGroupName; + static const wchar_t* c_leavesLightHitGroupName; + static const wchar_t* c_leavesDarkHitGroupName; + static const wchar_t* c_leavesExtraDarkHitGroupName; + static const wchar_t* c_bushHitGroupName; + static const wchar_t* c_transparentCubeHitGroupName; + static const wchar_t* c_raygenShaderName; + static const wchar_t* c_closestHitShaderName; + static const wchar_t* c_trunkClosestHitShaderName; + static const wchar_t* c_leavesClosestHitShaderName; + static const wchar_t* c_leavesLightClosestHitShaderName; + static const wchar_t* c_leavesDarkClosestHitShaderName; + static const wchar_t* c_leavesExtraDarkClosestHitShaderName; + static const wchar_t* c_tcubeClosestHitShaderName; + static const wchar_t* c_missShaderName; + static const wchar_t* c_bushClosestHitShaderName; + ComPtr m_missShaderTable; + ComPtr m_hitGroupShaderTable; + ComPtr m_rayGenShaderTable; + + // Application state + StepTimer m_timer; + float m_curRotationAngleRad; + XMVECTOR m_eye; + XMVECTOR m_at; + XMVECTOR m_up; + + // DirectXTK objects. + std::unique_ptr m_graphicsMemory; + + void UpdateCameraMatrices(); + void InitializeScene(); + void RecreateD3D(); + void DoRaytracing(); + void CreateConstantBuffers(); + std::vector GenerateTextureData(); + void CreateDeviceDependentResources(); + void CreateUIFont(); + void CreateWindowSizeDependentResources(); + void ReleaseDeviceDependentResources(); + void ReleaseWindowSizeDependentResources(); + void CreateRaytracingInterfaces(); + void SerializeAndCreateRaytracingRootSignature(D3D12_ROOT_SIGNATURE_DESC& desc, ComPtr* rootSig); + void CreateRootSignatures(); + void CreateLocalRootSignatureSubobjects(CD3DX12_STATE_OBJECT_DESC* raytracingPipeline); + void CreateRaytracingPipelineStateObject(); + void CreateDescriptorHeap(); + void CreateRaytracingOutputResource(); + void BuildGeometry(); + void CreateTexture(); + void BuildTreeGeometry(); + void BuildAccelerationStructures(); + void BuildShaderTables(); + void UpdateForSizeChange(UINT clientWidth, UINT clientHeight); + void RenderUI(); + void CopyRaytracingOutputToBackbuffer(); + void CalculateFrameStats(); + UINT AllocateDescriptor(D3D12_CPU_DESCRIPTOR_HANDLE* cpuDescriptor, UINT descriptorIndexToUse = UINT_MAX); + UINT CreateBufferSRV(D3DBuffer* buffer, UINT numElements, UINT elementSize); +}; \ No newline at end of file diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/D3D12RaytracingSakuraScene.vcxproj b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/D3D12RaytracingSakuraScene.vcxproj new file mode 100644 index 000000000..580c77867 --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/D3D12RaytracingSakuraScene.vcxproj @@ -0,0 +1,228 @@ + + + + + + + Debug + x64 + + + Release + x64 + + + + {B102F64F-EC2F-4F01-97A8-72B6D3CA7017} + Win32Proj + D3D12Raytracing + D3D12RaytracingSakuraScene + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + true + bin\$(Platform)\$(Configuration)\ + obj\$(Platform)\$(Configuration)\ + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(SolutionDir)..\Build_VS15\$(Platform)\$(Configuration)\Output\$(ProjectName) + + + + + + + + + false + bin\$(Platform)\$(Configuration)\ + obj\$(Platform)\$(Configuration)\ + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(SolutionDir)..\Build_VS15\$(Platform)\$(Configuration)\Output\$(ProjectName) + + + + + + + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + ..\;$(IntDir);%(AdditionalIncludeDirectories) + false + + + stdcpp17 + + + Windows + true + d3d12.lib;dxgi.lib;dxguid.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + d3d12.dll + + + true + + + copy %(Identity) "$(OutDir)" > NUL + $(OutDir)\%(Identity) + true + + + + 4.0_level_9_3 + + + + + + + + + + + + + + + + + + + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + ..\;$(IntDir);%(AdditionalIncludeDirectories) + false + + + Windows + true + true + true + d3d12.lib;dxgi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + d3d12.dll + + + true + + + copy %(Identity) "$(OutDir)" > NUL + $(OutDir)\%(Identity) + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + + + + + true + -Qembed_debug %(AdditionalOptions) + Library + 6.3 + g_p%(Filename) + $(IntDir)CompiledShaders\%(Filename).hlsl.h + /Zpr %(AdditionalOptions) + 6.7 + true + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + + + + \ No newline at end of file diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/D3D12RaytracingSakuraScene.vcxproj.filters b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/D3D12RaytracingSakuraScene.vcxproj.filters new file mode 100644 index 000000000..6698db842 --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/D3D12RaytracingSakuraScene.vcxproj.filters @@ -0,0 +1,92 @@ + + + + + {67bebf86-9539-4f7f-b9e9-a376519ec1f1} + + + {954a8f61-1c3e-419b-baca-c5e2b83c689a} + + + {09095d68-bcd7-45c4-b007-5c8ddb5d60a4} + + + {8b0cb0c9-e0d8-4107-91c8-11654fac88e7} + + + {500a71f9-a0df-431e-9f3b-5d4ab5e3942b} + + + {93d5e482-943e-4182-8023-5676929aec55} + + + {87b45108-4083-4b05-a52d-914447b5fc90} + + + + + Header Files + + + Header Files\Util + + + Header Files\Util + + + Header Files\Util + + + Header Files\Util + + + Header Files\Util + + + Header Files\Util + + + Header Files + + + Header Files + + + Assets\Shaders\Util + + + Assets\Shaders + + + + + + + + Source Files + + + Source Files + + + Source Files\Util + + + Source Files\Util + + + Source Files\Util + + + Source Files + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/DXSample.cpp b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/DXSample.cpp new file mode 100644 index 000000000..8840cd8f7 --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/DXSample.cpp @@ -0,0 +1,90 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#include "stdafx.h" +#include "DXSample.h" + +using namespace Microsoft::WRL; +using namespace std; + +DXSample::DXSample(UINT width, UINT height, std::wstring name) : + m_width(width), + m_height(height), + m_windowBounds{ 0,0,0,0 }, + m_title(name), + m_aspectRatio(0.0f), + m_enableUI(true), + m_adapterIDoverride(UINT_MAX) +{ + WCHAR assetsPath[512]; + GetAssetsPath(assetsPath, _countof(assetsPath)); + m_assetsPath = assetsPath; + + UpdateForSizeChange(width, height); +} + +DXSample::~DXSample() +{ +} + +void DXSample::UpdateForSizeChange(UINT clientWidth, UINT clientHeight) +{ + m_width = clientWidth; + m_height = clientHeight; + m_aspectRatio = static_cast(clientWidth) / static_cast(clientHeight); +} + +// Helper function for resolving the full path of assets. +std::wstring DXSample::GetAssetFullPath(LPCWSTR assetName) +{ + return m_assetsPath + assetName; +} + + +// Helper function for setting the window's title text. +void DXSample::SetCustomWindowText(LPCWSTR text) +{ + std::wstring windowText = m_title + L": " + text; + SetWindowText(Win32Application::GetHwnd(), windowText.c_str()); +} + +// Helper function for parsing any supplied command line args. +_Use_decl_annotations_ +void DXSample::ParseCommandLineArgs(WCHAR* argv[], int argc) +{ + for (int i = 1; i < argc; ++i) + { + // -disableUI + if (_wcsnicmp(argv[i], L"-disableUI", wcslen(argv[i])) == 0 || + _wcsnicmp(argv[i], L"/disableUI", wcslen(argv[i])) == 0) + { + m_enableUI = false; + } + // -forceAdapter [id] + else if (_wcsnicmp(argv[i], L"-forceAdapter", wcslen(argv[i])) == 0 || + _wcsnicmp(argv[i], L"/forceAdapter", wcslen(argv[i])) == 0) + { + ThrowIfFalse(i + 1 < argc, L"Incorrect argument format passed in."); + + m_adapterIDoverride = _wtoi(argv[i + 1]); + i++; + } + } + +} + +void DXSample::SetWindowBounds(int left, int top, int right, int bottom) +{ + m_windowBounds.left = static_cast(left); + m_windowBounds.top = static_cast(top); + m_windowBounds.right = static_cast(right); + m_windowBounds.bottom = static_cast(bottom); +} \ No newline at end of file diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/DXSample.h b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/DXSample.h new file mode 100644 index 000000000..3b5f4ed8a --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/DXSample.h @@ -0,0 +1,78 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once + +#include "DXSampleHelper.h" +#include "Win32Application.h" +#include "DeviceResources.h" + +class DXSample : public DX::IDeviceNotify +{ +public: + DXSample(UINT width, UINT height, std::wstring name); + virtual ~DXSample(); + + virtual void OnInit() = 0; + virtual void OnUpdate() = 0; + virtual void OnRender() = 0; + virtual void OnSizeChanged(UINT width, UINT height, bool minimized) = 0; + virtual void OnDestroy() = 0; + + // Samples override the event handlers to handle specific messages. + virtual void OnKeyDown(UINT8 /*key*/) {} + virtual void OnKeyUp(UINT8 /*key*/) {} + virtual void OnWindowMoved(int /*x*/, int /*y*/) {} + virtual void OnMouseMove(UINT /*x*/, UINT /*y*/) {} + virtual void OnLeftButtonDown(UINT /*x*/, UINT /*y*/) {} + virtual void OnLeftButtonUp(UINT /*x*/, UINT /*y*/) {} + virtual void OnDisplayChanged() {} + + // Overridable members. + virtual void ParseCommandLineArgs(_In_reads_(argc) WCHAR* argv[], int argc); + + // Accessors. + UINT GetWidth() const { return m_width; } + UINT GetHeight() const { return m_height; } + const WCHAR* GetTitle() const { return m_title.c_str(); } + RECT GetWindowsBounds() const { return m_windowBounds; } + virtual IDXGISwapChain* GetSwapchain() { return nullptr; } + DX::DeviceResources* GetDeviceResources() const { return m_deviceResources.get(); } + + void UpdateForSizeChange(UINT clientWidth, UINT clientHeight); + void SetWindowBounds(int left, int top, int right, int bottom); + std::wstring GetAssetFullPath(LPCWSTR assetName); + +protected: + void SetCustomWindowText(LPCWSTR text); + + // Viewport dimensions. + UINT m_width; + UINT m_height; + float m_aspectRatio; + + // Window bounds + RECT m_windowBounds; + + // Override to be able to start without Dx11on12 UI for PIX. PIX doesn't support 11 on 12. + bool m_enableUI; + + // D3D device resources + UINT m_adapterIDoverride; + std::unique_ptr m_deviceResources; + +private: + // Root assets path. + std::wstring m_assetsPath; + + // Window title. + std::wstring m_title; +}; diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/DXSampleHelper.h b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/DXSampleHelper.h new file mode 100644 index 000000000..90e58c1b1 --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/DXSampleHelper.h @@ -0,0 +1,348 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once + +// Note that while ComPtr is used to manage the lifetime of resources on the CPU, +// it has no understanding of the lifetime of resources on the GPU. Apps must account +// for the GPU lifetime of resources to avoid destroying objects that may still be +// referenced by the GPU. +using Microsoft::WRL::ComPtr; + +class HrException : public std::runtime_error +{ + inline std::string HrToString(HRESULT hr) + { + char s_str[64] = {}; + sprintf_s(s_str, "HRESULT of 0x%08X", static_cast(hr)); + return std::string(s_str); + } +public: + HrException(HRESULT hr) : std::runtime_error(HrToString(hr)), m_hr(hr) {} + HRESULT Error() const { return m_hr; } +private: + const HRESULT m_hr; +}; + +#define SAFE_RELEASE(p) if (p) (p)->Release() + +inline void ThrowIfFailed(HRESULT hr) +{ + if (FAILED(hr)) + { + throw HrException(hr); + } +} + +inline void ThrowIfFailed(HRESULT hr, const wchar_t* msg) +{ + if (FAILED(hr)) + { + OutputDebugString(msg); + throw HrException(hr); + } +} + +inline void ThrowIfFalse(bool value) +{ + ThrowIfFailed(value ? S_OK : E_FAIL); +} + +inline void ThrowIfFalse(bool value, const wchar_t* msg) +{ + ThrowIfFailed(value ? S_OK : E_FAIL, msg); +} + + +inline void GetAssetsPath(_Out_writes_(pathSize) WCHAR* path, UINT pathSize) +{ + if (path == nullptr) + { + throw std::exception(); + } + + DWORD size = GetModuleFileName(nullptr, path, pathSize); + if (size == 0 || size == pathSize) + { + // Method failed or path was truncated. + throw std::exception(); + } + + WCHAR* lastSlash = wcsrchr(path, L'\\'); + if (lastSlash) + { + *(lastSlash + 1) = L'\0'; + } +} + +inline HRESULT ReadDataFromFile(LPCWSTR filename, byte** data, UINT* size) +{ + using namespace Microsoft::WRL; + + CREATEFILE2_EXTENDED_PARAMETERS extendedParams = {}; + extendedParams.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS); + extendedParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL; + extendedParams.dwFileFlags = FILE_FLAG_SEQUENTIAL_SCAN; + extendedParams.dwSecurityQosFlags = SECURITY_ANONYMOUS; + extendedParams.lpSecurityAttributes = nullptr; + extendedParams.hTemplateFile = nullptr; + + Wrappers::FileHandle file(CreateFile2(filename, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, &extendedParams)); + if (file.Get() == INVALID_HANDLE_VALUE) + { + throw std::exception(); + } + + FILE_STANDARD_INFO fileInfo = {}; + if (!GetFileInformationByHandleEx(file.Get(), FileStandardInfo, &fileInfo, sizeof(fileInfo))) + { + throw std::exception(); + } + + if (fileInfo.EndOfFile.HighPart != 0) + { + throw std::exception(); + } + + *data = reinterpret_cast(malloc(fileInfo.EndOfFile.LowPart)); + *size = fileInfo.EndOfFile.LowPart; + + if (!ReadFile(file.Get(), *data, fileInfo.EndOfFile.LowPart, nullptr, nullptr)) + { + throw std::exception(); + } + + return S_OK; +} + +// Assign a name to the object to aid with debugging. +#if defined(_DEBUG) || defined(DBG) +inline void SetName(ID3D12Object* pObject, LPCWSTR name) +{ + pObject->SetName(name); +} +inline void SetNameIndexed(ID3D12Object* pObject, LPCWSTR name, UINT index) +{ + WCHAR fullName[50]; + if (swprintf_s(fullName, L"%s[%u]", name, index) > 0) + { + pObject->SetName(fullName); + } +} +#else +inline void SetName(ID3D12Object*, LPCWSTR) +{ +} +inline void SetNameIndexed(ID3D12Object*, LPCWSTR, UINT) +{ +} +#endif + +// Naming helper for ComPtr. +// Assigns the name of the variable as the name of the object. +// The indexed variant will include the index in the name of the object. +#define NAME_D3D12_OBJECT(x) SetName((x).Get(), L#x) +#define NAME_D3D12_OBJECT_INDEXED(x, n) SetNameIndexed((x)[n].Get(), L#x, n) + +inline UINT Align(UINT size, UINT alignment) +{ + return (size + (alignment - 1)) & ~(alignment - 1); +} + +inline UINT CalculateConstantBufferByteSize(UINT byteSize) +{ + // Constant buffer size is required to be aligned. + return Align(byteSize, D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT); +} + +#ifdef D3D_COMPILE_STANDARD_FILE_INCLUDE +inline Microsoft::WRL::ComPtr CompileShader( + const std::wstring& filename, + const D3D_SHADER_MACRO* defines, + const std::string& entrypoint, + const std::string& target) +{ + UINT compileFlags = 0; +#if defined(_DEBUG) || defined(DBG) + compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; +#endif + + HRESULT hr; + + Microsoft::WRL::ComPtr byteCode = nullptr; + Microsoft::WRL::ComPtr errors; + hr = D3DCompileFromFile(filename.c_str(), defines, D3D_COMPILE_STANDARD_FILE_INCLUDE, + entrypoint.c_str(), target.c_str(), compileFlags, 0, &byteCode, &errors); + + if (errors != nullptr) + { + OutputDebugStringA((char*)errors->GetBufferPointer()); + } + ThrowIfFailed(hr); + + return byteCode; +} +#endif + +// Resets all elements in a ComPtr array. +template +void ResetComPtrArray(T* comPtrArray) +{ + for (auto &i : *comPtrArray) + { + i.Reset(); + } +} + + +// Resets all elements in a unique_ptr array. +template +void ResetUniquePtrArray(T* uniquePtrArray) +{ + for (auto &i : *uniquePtrArray) + { + i.reset(); + } +} + +class GpuUploadBuffer +{ +public: + ComPtr GetResource() { return m_resource; } + +protected: + ComPtr m_resource; + + GpuUploadBuffer() {} + ~GpuUploadBuffer() + { + if (m_resource.Get()) + { + m_resource->Unmap(0, nullptr); + } + } + + void Allocate(ID3D12Device* device, UINT bufferSize, LPCWSTR resourceName = nullptr) + { + auto uploadHeapProperties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD); + + auto bufferDesc = CD3DX12_RESOURCE_DESC::Buffer(bufferSize); + ThrowIfFailed(device->CreateCommittedResource( + &uploadHeapProperties, + D3D12_HEAP_FLAG_NONE, + &bufferDesc, + D3D12_RESOURCE_STATE_GENERIC_READ, + nullptr, + IID_PPV_ARGS(&m_resource))); + m_resource->SetName(resourceName); + } + + uint8_t* MapCpuWriteOnly() + { + uint8_t* mappedData; + // We don't unmap this until the app closes. Keeping buffer mapped for the lifetime of the resource is okay. + CD3DX12_RANGE readRange(0, 0); // We do not intend to read from this resource on the CPU. + ThrowIfFailed(m_resource->Map(0, &readRange, reinterpret_cast(&mappedData))); + return mappedData; + } +}; + +struct D3DBuffer +{ + ComPtr resource; + D3D12_CPU_DESCRIPTOR_HANDLE cpuDescriptorHandle; + D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptorHandle; +}; + +// Helper class to create and update a constant buffer with proper constant buffer alignments. +// Usage: ToDo +// ConstantBuffer<...> cb; +// cb.Create(...); +// cb.staging.var = ...; | cb->var = ... ; +// cb.CopyStagingToGPU(...); +template +class ConstantBuffer : public GpuUploadBuffer +{ + uint8_t* m_mappedConstantData; + UINT m_alignedInstanceSize; + UINT m_numInstances; + +public: + ConstantBuffer() : m_alignedInstanceSize(0), m_numInstances(0), m_mappedConstantData(nullptr) {} + + void Create(ID3D12Device* device, UINT numInstances = 1, LPCWSTR resourceName = nullptr) + { + m_numInstances = numInstances; + UINT alignedSize = Align(sizeof(T), D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT); + UINT bufferSize = numInstances * alignedSize; + Allocate(device, bufferSize, resourceName); + m_mappedConstantData = MapCpuWriteOnly(); + } + + void CopyStagingToGpu(UINT instanceIndex = 0) + { + memcpy(m_mappedConstantData + instanceIndex * m_alignedInstanceSize, &staging, sizeof(T)); + } + + // Accessors + T staging; + T* operator->() { return &staging; } + UINT NumInstances() { return m_numInstances; } + D3D12_GPU_VIRTUAL_ADDRESS GpuVirtualAddress(UINT instanceIndex = 0) + { + return m_resource->GetGPUVirtualAddress() + instanceIndex * m_alignedInstanceSize; + } +}; + + +// Helper class to create and update a structured buffer. +// Usage: ToDo +// ConstantBuffer<...> cb; +// cb.Create(...); +// cb.staging.var = ...; | cb->var = ... ; +// cb.CopyStagingToGPU(...); +template +class StructuredBuffer : public GpuUploadBuffer +{ + T* m_mappedBuffers; + std::vector m_staging; + UINT m_numInstances; + +public: + // Performance tip: Align structures on sizeof(float4) boundary. + // Ref: https://developer.nvidia.com/content/understanding-structured-buffer-performance + static_assert(sizeof(T) % 16 == 0, L"Align structure buffers on 16 byte boundary for performance reasons."); + + StructuredBuffer() : m_mappedBuffers(nullptr), m_numInstances(0) {} + + void Create(ID3D12Device* device, UINT numElements, UINT numInstances = 1, LPCWSTR resourceName = nullptr) + { + m_staging.resize(numElements); + UINT bufferSize = numInstances * numElements * sizeof(T); + Allocate(device, bufferSize, resourceName); + m_mappedBuffers = reinterpret_cast(MapCpuWriteOnly()); + } + + void CopyStagingToGpu(UINT instanceIndex = 0) + { + memcpy(m_mappedBuffers + instanceIndex, &m_staging[0], InstanceSize()); + } + + // Accessors + T& operator[](UINT elementIndex) { return m_staging[elementIndex]; } + size_t NumElementsPerInstance() { return m_staging.size(); } + UINT NumInstances() { return m_staging.size(); } + size_t InstanceSize() { return NumElementsPerInstance() * sizeof(T); } + D3D12_GPU_VIRTUAL_ADDRESS GpuVirtualAddress(UINT instanceIndex = 0) + { + return m_resource->GetGPUVirtualAddress() + instanceIndex * InstanceSize(); + } +}; \ No newline at end of file diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/DeviceResources.cpp b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/DeviceResources.cpp new file mode 100644 index 000000000..6ec18c4b2 --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/DeviceResources.cpp @@ -0,0 +1,669 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#include "stdafx.h" +#include "DeviceResources.h" +#include "Win32Application.h" + +using namespace DX; +using namespace std; + +using Microsoft::WRL::ComPtr; + +namespace +{ + inline DXGI_FORMAT NoSRGB(DXGI_FORMAT fmt) + { + switch (fmt) + { + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: return DXGI_FORMAT_R8G8B8A8_UNORM; + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: return DXGI_FORMAT_B8G8R8A8_UNORM; + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: return DXGI_FORMAT_B8G8R8X8_UNORM; + default: return fmt; + } + } +}; + +// Constructor for DeviceResources. +DeviceResources::DeviceResources(DXGI_FORMAT backBufferFormat, DXGI_FORMAT depthBufferFormat, UINT backBufferCount, D3D_FEATURE_LEVEL minFeatureLevel, UINT flags, UINT adapterIDoverride) : + m_backBufferIndex(0), + m_fenceValues{}, + m_rtvDescriptorSize(0), + m_screenViewport{}, + m_scissorRect{}, + m_backBufferFormat(backBufferFormat), + m_depthBufferFormat(depthBufferFormat), + m_backBufferCount(backBufferCount), + m_d3dMinFeatureLevel(minFeatureLevel), + m_window(nullptr), + m_d3dFeatureLevel(D3D_FEATURE_LEVEL_11_0), + m_outputSize{ 0, 0, 1, 1 }, + m_options(flags), + m_deviceNotify(nullptr), + m_isWindowVisible(true), + m_adapterIDoverride(adapterIDoverride), + m_adapterID(UINT_MAX) +{ + if (backBufferCount > MAX_BACK_BUFFER_COUNT) + { + throw out_of_range("backBufferCount too large"); + } + + if (minFeatureLevel < D3D_FEATURE_LEVEL_11_0) + { + throw out_of_range("minFeatureLevel too low"); + } + if (m_options & c_RequireTearingSupport) + { + m_options |= c_AllowTearing; + } +} + +// Destructor for DeviceResources. +DeviceResources::~DeviceResources() +{ + // Ensure that the GPU is no longer referencing resources that are about to be destroyed. + WaitForGpu(); +} + +// Configures DXGI Factory and retrieve an adapter. +void DeviceResources::InitializeDXGIAdapter() +{ + bool debugDXGI = false; + +#if defined(_DEBUG) + // Enable the debug layer (requires the Graphics Tools "optional feature"). + // NOTE: Enabling the debug layer after device creation will invalidate the active device. + { + ComPtr debugController; + if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)))) + { + debugController->EnableDebugLayer(); + } + else + { + OutputDebugStringA("WARNING: Direct3D Debug Device is not available\n"); + } + + ComPtr dxgiInfoQueue; + if (SUCCEEDED(DXGIGetDebugInterface1(0, IID_PPV_ARGS(&dxgiInfoQueue)))) + { + debugDXGI = true; + + ThrowIfFailed(CreateDXGIFactory2(DXGI_CREATE_FACTORY_DEBUG, IID_PPV_ARGS(&m_dxgiFactory))); + + dxgiInfoQueue->SetBreakOnSeverity(DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR, true); + dxgiInfoQueue->SetBreakOnSeverity(DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION, true); + } + } +#endif + + if (!debugDXGI) + { + ThrowIfFailed(CreateDXGIFactory1(IID_PPV_ARGS(&m_dxgiFactory))); + } + + // Determines whether tearing support is available for fullscreen borderless windows. + if (m_options & (c_AllowTearing | c_RequireTearingSupport)) + { + BOOL allowTearing = FALSE; + + ComPtr factory5; + HRESULT hr = m_dxgiFactory.As(&factory5); + if (SUCCEEDED(hr)) + { + hr = factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allowTearing, sizeof(allowTearing)); + } + + if (FAILED(hr) || !allowTearing) + { + OutputDebugStringA("WARNING: Variable refresh rate displays are not supported.\n"); + if (m_options & c_RequireTearingSupport) + { + ThrowIfFailed(false, L"Error: Sample must be run on an OS with tearing support.\n"); + } + m_options &= ~c_AllowTearing; + } + } + + InitializeAdapter(&m_adapter); +} + +// Configures the Direct3D device, and stores handles to it and the device context. +void DeviceResources::CreateDeviceResources() +{ + // Create the DX12 API device object. + ThrowIfFailed(D3D12CreateDevice(m_adapter.Get(), m_d3dMinFeatureLevel, IID_PPV_ARGS(&m_d3dDevice))); + +#ifndef NDEBUG + // Configure debug device (if active). + ComPtr d3dInfoQueue; + if (SUCCEEDED(m_d3dDevice.As(&d3dInfoQueue))) + { +#ifdef _DEBUG + d3dInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true); + d3dInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true); +#endif + D3D12_MESSAGE_ID hide[] = + { + D3D12_MESSAGE_ID_MAP_INVALID_NULLRANGE, + D3D12_MESSAGE_ID_UNMAP_INVALID_NULLRANGE + }; + D3D12_INFO_QUEUE_FILTER filter = {}; + filter.DenyList.NumIDs = _countof(hide); + filter.DenyList.pIDList = hide; + d3dInfoQueue->AddStorageFilterEntries(&filter); + } +#endif + + // Determine maximum supported feature level for this device + static const D3D_FEATURE_LEVEL s_featureLevels[] = + { + D3D_FEATURE_LEVEL_12_1, + D3D_FEATURE_LEVEL_12_0, + D3D_FEATURE_LEVEL_11_1, + D3D_FEATURE_LEVEL_11_0, + }; + + D3D12_FEATURE_DATA_FEATURE_LEVELS featLevels = + { + _countof(s_featureLevels), s_featureLevels, D3D_FEATURE_LEVEL_11_0 + }; + + HRESULT hr = m_d3dDevice->CheckFeatureSupport(D3D12_FEATURE_FEATURE_LEVELS, &featLevels, sizeof(featLevels)); + if (SUCCEEDED(hr)) + { + m_d3dFeatureLevel = featLevels.MaxSupportedFeatureLevel; + } + else + { + m_d3dFeatureLevel = m_d3dMinFeatureLevel; + } + + // Create the command queue. + D3D12_COMMAND_QUEUE_DESC queueDesc = {}; + queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + + ThrowIfFailed(m_d3dDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&m_commandQueue))); + + // Create descriptor heaps for render target views and depth stencil views. + D3D12_DESCRIPTOR_HEAP_DESC rtvDescriptorHeapDesc = {}; + rtvDescriptorHeapDesc.NumDescriptors = m_backBufferCount; + rtvDescriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; + + ThrowIfFailed(m_d3dDevice->CreateDescriptorHeap(&rtvDescriptorHeapDesc, IID_PPV_ARGS(&m_rtvDescriptorHeap))); + + m_rtvDescriptorSize = m_d3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); + + if (m_depthBufferFormat != DXGI_FORMAT_UNKNOWN) + { + D3D12_DESCRIPTOR_HEAP_DESC dsvDescriptorHeapDesc = {}; + dsvDescriptorHeapDesc.NumDescriptors = 1; + dsvDescriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV; + + ThrowIfFailed(m_d3dDevice->CreateDescriptorHeap(&dsvDescriptorHeapDesc, IID_PPV_ARGS(&m_dsvDescriptorHeap))); + } + + // Create a command allocator for each back buffer that will be rendered to. + for (UINT n = 0; n < m_backBufferCount; n++) + { + ThrowIfFailed(m_d3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&m_commandAllocators[n]))); + } + + // Create a command list for recording graphics commands. + ThrowIfFailed(m_d3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_commandAllocators[0].Get(), nullptr, IID_PPV_ARGS(&m_commandList))); + ThrowIfFailed(m_commandList->Close()); + + // Create a fence for tracking GPU execution progress. + ThrowIfFailed(m_d3dDevice->CreateFence(m_fenceValues[m_backBufferIndex], D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence))); + m_fenceValues[m_backBufferIndex]++; + + m_fenceEvent.Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr)); + if (!m_fenceEvent.IsValid()) + { + ThrowIfFailed(E_FAIL, L"CreateEvent failed.\n"); + } +} + +// These resources need to be recreated every time the window size is changed. +void DeviceResources::CreateWindowSizeDependentResources() +{ + if (!m_window) + { + ThrowIfFailed(E_HANDLE, L"Call SetWindow with a valid Win32 window handle.\n"); + } + + // Wait until all previous GPU work is complete. + WaitForGpu(); + + // Release resources that are tied to the swap chain and update fence values. + for (UINT n = 0; n < m_backBufferCount; n++) + { + m_renderTargets[n].Reset(); + m_fenceValues[n] = m_fenceValues[m_backBufferIndex]; + } + + // Determine the render target size in pixels. + UINT backBufferWidth = max(m_outputSize.right - m_outputSize.left, 1); + UINT backBufferHeight = max(m_outputSize.bottom - m_outputSize.top, 1); + DXGI_FORMAT backBufferFormat = NoSRGB(m_backBufferFormat); + + // If the swap chain already exists, resize it, otherwise create one. + if (m_swapChain) + { + // If the swap chain already exists, resize it. + HRESULT hr = m_swapChain->ResizeBuffers( + m_backBufferCount, + backBufferWidth, + backBufferHeight, + backBufferFormat, + (m_options & c_AllowTearing) ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0 + ); + + if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) + { +#ifdef _DEBUG + char buff[64] = {}; + sprintf_s(buff, "Device Lost on ResizeBuffers: Reason code 0x%08X\n", (hr == DXGI_ERROR_DEVICE_REMOVED) ? m_d3dDevice->GetDeviceRemovedReason() : hr); + OutputDebugStringA(buff); +#endif + // If the device was removed for any reason, a new device and swap chain will need to be created. + HandleDeviceLost(); + + // Everything is set up now. Do not continue execution of this method. HandleDeviceLost will reenter this method + // and correctly set up the new device. + return; + } + else + { + ThrowIfFailed(hr); + } + } + else + { + // Create a descriptor for the swap chain. + DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; + swapChainDesc.Width = backBufferWidth; + swapChainDesc.Height = backBufferHeight; + swapChainDesc.Format = backBufferFormat; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.BufferCount = m_backBufferCount; + swapChainDesc.SampleDesc.Count = 1; + swapChainDesc.SampleDesc.Quality = 0; + swapChainDesc.Scaling = DXGI_SCALING_STRETCH; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; + swapChainDesc.Flags = (m_options & c_AllowTearing) ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0; + + DXGI_SWAP_CHAIN_FULLSCREEN_DESC fsSwapChainDesc = { 0 }; + fsSwapChainDesc.Windowed = TRUE; + + // Create a swap chain for the window. + ComPtr swapChain; + + // DXGI does not allow creating a swapchain targeting a window which has fullscreen styles(no border + topmost). + // Temporarily remove the topmost property for creating the swapchain. + bool prevIsFullscreen = Win32Application::IsFullscreen(); + if (prevIsFullscreen) + { + Win32Application::SetWindowZorderToTopMost(false); + } + + ThrowIfFailed(m_dxgiFactory->CreateSwapChainForHwnd(m_commandQueue.Get(), m_window, &swapChainDesc, &fsSwapChainDesc, nullptr, &swapChain)); + + if (prevIsFullscreen) + { + Win32Application::SetWindowZorderToTopMost(true); + } + + ThrowIfFailed(swapChain.As(&m_swapChain)); + + // With tearing support enabled we will handle ALT+Enter key presses in the + // window message loop rather than let DXGI handle it by calling SetFullscreenState. + if (IsTearingSupported()) + { + m_dxgiFactory->MakeWindowAssociation(m_window, DXGI_MWA_NO_ALT_ENTER); + } + } + + // Obtain the back buffers for this window which will be the final render targets + // and create render target views for each of them. + for (UINT n = 0; n < m_backBufferCount; n++) + { + ThrowIfFailed(m_swapChain->GetBuffer(n, IID_PPV_ARGS(&m_renderTargets[n]))); + + wchar_t name[25] = {}; + swprintf_s(name, L"Render target %u", n); + m_renderTargets[n]->SetName(name); + + D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {}; + rtvDesc.Format = m_backBufferFormat; + rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; + + CD3DX12_CPU_DESCRIPTOR_HANDLE rtvDescriptor(m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), n, m_rtvDescriptorSize); + m_d3dDevice->CreateRenderTargetView(m_renderTargets[n].Get(), &rtvDesc, rtvDescriptor); + } + + // Reset the index to the current back buffer. + m_backBufferIndex = m_swapChain->GetCurrentBackBufferIndex(); + + if (m_depthBufferFormat != DXGI_FORMAT_UNKNOWN) + { + // Allocate a 2-D surface as the depth/stencil buffer and create a depth/stencil view + // on this surface. + CD3DX12_HEAP_PROPERTIES depthHeapProperties(D3D12_HEAP_TYPE_DEFAULT); + + D3D12_RESOURCE_DESC depthStencilDesc = CD3DX12_RESOURCE_DESC::Tex2D( + m_depthBufferFormat, + backBufferWidth, + backBufferHeight, + 1, // This depth stencil view has only one texture. + 1 // Use a single mipmap level. + ); + depthStencilDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; + + D3D12_CLEAR_VALUE depthOptimizedClearValue = {}; + depthOptimizedClearValue.Format = m_depthBufferFormat; + depthOptimizedClearValue.DepthStencil.Depth = 1.0f; + depthOptimizedClearValue.DepthStencil.Stencil = 0; + + ThrowIfFailed(m_d3dDevice->CreateCommittedResource(&depthHeapProperties, + D3D12_HEAP_FLAG_NONE, + &depthStencilDesc, + D3D12_RESOURCE_STATE_DEPTH_WRITE, + &depthOptimizedClearValue, + IID_PPV_ARGS(&m_depthStencil) + )); + + m_depthStencil->SetName(L"Depth stencil"); + + D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = {}; + dsvDesc.Format = m_depthBufferFormat; + dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; + + m_d3dDevice->CreateDepthStencilView(m_depthStencil.Get(), &dsvDesc, m_dsvDescriptorHeap->GetCPUDescriptorHandleForHeapStart()); + } + + // Set the 3D rendering viewport and scissor rectangle to target the entire window. + m_screenViewport.TopLeftX = m_screenViewport.TopLeftY = 0.f; + m_screenViewport.Width = static_cast(backBufferWidth); + m_screenViewport.Height = static_cast(backBufferHeight); + m_screenViewport.MinDepth = D3D12_MIN_DEPTH; + m_screenViewport.MaxDepth = D3D12_MAX_DEPTH; + + m_scissorRect.left = m_scissorRect.top = 0; + m_scissorRect.right = backBufferWidth; + m_scissorRect.bottom = backBufferHeight; +} + +// This method is called when the Win32 window is created (or re-created). +void DeviceResources::SetWindow(HWND window, int width, int height) +{ + m_window = window; + + m_outputSize.left = m_outputSize.top = 0; + m_outputSize.right = width; + m_outputSize.bottom = height; +} + +// This method is called when the Win32 window changes size. +// It returns true if window size change was applied. +bool DeviceResources::WindowSizeChanged(int width, int height, bool minimized) +{ + m_isWindowVisible = !minimized; + + if (minimized || width == 0 || height == 0) + { + return false; + } + + RECT newRc; + newRc.left = newRc.top = 0; + newRc.right = width; + newRc.bottom = height; + if (newRc.left == m_outputSize.left + && newRc.top == m_outputSize.top + && newRc.right == m_outputSize.right + && newRc.bottom == m_outputSize.bottom) + { + return false; + } + + m_outputSize = newRc; + CreateWindowSizeDependentResources(); + return true; +} + +// Recreate all device resources and set them back to the current state. +void DeviceResources::HandleDeviceLost() +{ + if (m_deviceNotify) + { + m_deviceNotify->OnDeviceLost(); + } + + for (UINT n = 0; n < m_backBufferCount; n++) + { + m_commandAllocators[n].Reset(); + m_renderTargets[n].Reset(); + } + + m_depthStencil.Reset(); + m_commandQueue.Reset(); + m_commandList.Reset(); + m_fence.Reset(); + m_rtvDescriptorHeap.Reset(); + m_dsvDescriptorHeap.Reset(); + m_swapChain.Reset(); + m_d3dDevice.Reset(); + m_dxgiFactory.Reset(); + m_adapter.Reset(); + +#ifdef _DEBUG + { + ComPtr dxgiDebug; + if (SUCCEEDED(DXGIGetDebugInterface1(0, IID_PPV_ARGS(&dxgiDebug)))) + { + dxgiDebug->ReportLiveObjects(DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_FLAGS(DXGI_DEBUG_RLO_SUMMARY | DXGI_DEBUG_RLO_IGNORE_INTERNAL)); + } + } +#endif + InitializeDXGIAdapter(); + CreateDeviceResources(); + CreateWindowSizeDependentResources(); + + if (m_deviceNotify) + { + m_deviceNotify->OnDeviceRestored(); + } +} + +// Prepare the command list and render target for rendering. +void DeviceResources::Prepare(D3D12_RESOURCE_STATES beforeState) +{ + // Reset command list and allocator. + ThrowIfFailed(m_commandAllocators[m_backBufferIndex]->Reset()); + ThrowIfFailed(m_commandList->Reset(m_commandAllocators[m_backBufferIndex].Get(), nullptr)); + + if (beforeState != D3D12_RESOURCE_STATE_RENDER_TARGET) + { + // Transition the render target into the correct state to allow for drawing into it. + D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_RENDER_TARGET); + m_commandList->ResourceBarrier(1, &barrier); + } +} + +// Present the contents of the swap chain to the screen. +void DeviceResources::Present(D3D12_RESOURCE_STATES beforeState) +{ + if (beforeState != D3D12_RESOURCE_STATE_PRESENT) + { + // Transition the render target to the state that allows it to be presented to the display. + D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_renderTargets[m_backBufferIndex].Get(), beforeState, D3D12_RESOURCE_STATE_PRESENT); + m_commandList->ResourceBarrier(1, &barrier); + } + + ExecuteCommandList(); + + HRESULT hr; + if (m_options & c_AllowTearing) + { + // Recommended to always use tearing if supported when using a sync interval of 0. + // Note this will fail if in true 'fullscreen' mode. + hr = m_swapChain->Present(0, DXGI_PRESENT_ALLOW_TEARING); + } + else + { + // The first argument instructs DXGI to block until VSync, putting the application + // to sleep until the next VSync. This ensures we don't waste any cycles rendering + // frames that will never be displayed to the screen. + hr = m_swapChain->Present(1, 0); + } + + // If the device was reset we must completely reinitialize the renderer. + if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) + { +#ifdef _DEBUG + char buff[64] = {}; + sprintf_s(buff, "Device Lost on Present: Reason code 0x%08X\n", (hr == DXGI_ERROR_DEVICE_REMOVED) ? m_d3dDevice->GetDeviceRemovedReason() : hr); + OutputDebugStringA(buff); +#endif + HandleDeviceLost(); + } + else + { + ThrowIfFailed(hr); + + MoveToNextFrame(); + } +} + +// Send the command list off to the GPU for processing. +void DeviceResources::ExecuteCommandList() +{ + ThrowIfFailed(m_commandList->Close()); + ID3D12CommandList *commandLists[] = { m_commandList.Get() }; + m_commandQueue->ExecuteCommandLists(ARRAYSIZE(commandLists), commandLists); +} + +// Wait for pending GPU work to complete. +void DeviceResources::WaitForGpu() noexcept +{ + if (m_commandQueue && m_fence && m_fenceEvent.IsValid()) + { + // Schedule a Signal command in the GPU queue. + UINT64 fenceValue = m_fenceValues[m_backBufferIndex]; + if (SUCCEEDED(m_commandQueue->Signal(m_fence.Get(), fenceValue))) + { + // Wait until the Signal has been processed. + if (SUCCEEDED(m_fence->SetEventOnCompletion(fenceValue, m_fenceEvent.Get()))) + { + WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); + + // Increment the fence value for the current frame. + m_fenceValues[m_backBufferIndex]++; + } + } + } +} + +// Prepare to render the next frame. +void DeviceResources::MoveToNextFrame() +{ + // Schedule a Signal command in the queue. + const UINT64 currentFenceValue = m_fenceValues[m_backBufferIndex]; + ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue)); + + // Update the back buffer index. + m_backBufferIndex = m_swapChain->GetCurrentBackBufferIndex(); + + // If the next frame is not ready to be rendered yet, wait until it is ready. + if (m_fence->GetCompletedValue() < m_fenceValues[m_backBufferIndex]) + { + ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_backBufferIndex], m_fenceEvent.Get())); + WaitForSingleObjectEx(m_fenceEvent.Get(), INFINITE, FALSE); + } + + // Set the fence value for the next frame. + m_fenceValues[m_backBufferIndex] = currentFenceValue + 1; +} + +// This method acquires the first high-performance hardware adapter that supports Direct3D 12. +// If no such adapter can be found, try WARP. Otherwise throw an exception. +void DeviceResources::InitializeAdapter(IDXGIAdapter1** ppAdapter) +{ + *ppAdapter = nullptr; + + ComPtr adapter; + ComPtr factory6; + HRESULT hr = m_dxgiFactory.As(&factory6); + if (FAILED(hr)) + { + throw exception("DXGI 1.6 not supported"); + } + for (UINT adapterID = 0; DXGI_ERROR_NOT_FOUND != factory6->EnumAdapterByGpuPreference(adapterID, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, IID_PPV_ARGS(&adapter)); ++adapterID) + { + if (m_adapterIDoverride != UINT_MAX && adapterID != m_adapterIDoverride) + { + continue; + } + + DXGI_ADAPTER_DESC1 desc; + ThrowIfFailed(adapter->GetDesc1(&desc)); + + if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) + { + // Don't select the Basic Render Driver adapter. + continue; + } + + // Check to see if the adapter supports Direct3D 12, but don't create the actual device yet. + if (SUCCEEDED(D3D12CreateDevice(adapter.Get(), m_d3dMinFeatureLevel, _uuidof(ID3D12Device), nullptr))) + { + m_adapterID = adapterID; + m_adapterDescription = desc.Description; +#ifdef _DEBUG + wchar_t buff[256] = {}; + swprintf_s(buff, L"Direct3D Adapter (%u): VID:%04X, PID:%04X - %ls\n", adapterID, desc.VendorId, desc.DeviceId, desc.Description); + OutputDebugStringW(buff); +#endif + break; + } + } + +#if !defined(NDEBUG) + if (!adapter && m_adapterIDoverride == UINT_MAX) + { + // Try WARP12 instead + if (FAILED(m_dxgiFactory->EnumWarpAdapter(IID_PPV_ARGS(&adapter)))) + { + throw exception("WARP12 not available. Enable the 'Graphics Tools' optional feature"); + } + + OutputDebugStringA("Direct3D Adapter - WARP12\n"); + } +#endif + + if (!adapter) + { + if (m_adapterIDoverride != UINT_MAX) + { + throw exception("Unavailable adapter requested."); + } + else + { + throw exception("Unavailable adapter."); + } + } + + *ppAdapter = adapter.Detach(); +} \ No newline at end of file diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/DeviceResources.h b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/DeviceResources.h new file mode 100644 index 000000000..a5177d64f --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/DeviceResources.h @@ -0,0 +1,163 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +// +// DeviceResources.h - A wrapper for the Direct3D 12 device and swapchain +// + +#pragma once + +namespace DX +{ + // Provides an interface for an application that owns DeviceResources to be notified of the device being lost or created. + interface IDeviceNotify + { + virtual void OnDeviceLost() = 0; + virtual void OnDeviceRestored() = 0; + }; + + // Controls all the DirectX device resources. + class DeviceResources + { + DeviceResources() {} + public: + static const unsigned int c_AllowTearing = 0x1; + static const unsigned int c_RequireTearingSupport = 0x2; + + DeviceResources(DXGI_FORMAT backBufferFormat = DXGI_FORMAT_B8G8R8A8_UNORM, + DXGI_FORMAT depthBufferFormat = DXGI_FORMAT_D32_FLOAT, + UINT backBufferCount = 2, + D3D_FEATURE_LEVEL minFeatureLevel = D3D_FEATURE_LEVEL_11_0, + UINT flags = 0, + UINT adapterIDoverride = UINT_MAX); + ~DeviceResources(); + + void InitializeDXGIAdapter(); + void SetAdapterOverride(UINT adapterID) { m_adapterIDoverride = adapterID; } + void CreateDeviceResources(); + void CreateWindowSizeDependentResources(); + void SetWindow(HWND window, int width, int height); + bool WindowSizeChanged(int width, int height, bool minimized); + void HandleDeviceLost(); + void RegisterDeviceNotify(IDeviceNotify* deviceNotify) + { + m_deviceNotify = deviceNotify; + + // On RS4 and higher, applications that handle device removal + // should declare themselves as being able to do so + __if_exists(DXGIDeclareAdapterRemovalSupport) + { + if (deviceNotify) + { + if (FAILED(DXGIDeclareAdapterRemovalSupport())) + { + OutputDebugString(L"Warning: application failed to declare adapter removal support.\n"); + } + } + } + } + + void Prepare(D3D12_RESOURCE_STATES beforeState = D3D12_RESOURCE_STATE_PRESENT); + void Present(D3D12_RESOURCE_STATES beforeState = D3D12_RESOURCE_STATE_RENDER_TARGET); + void ExecuteCommandList(); + void WaitForGpu() noexcept; + + // Device Accessors. + RECT GetOutputSize() const { return m_outputSize; } + bool IsWindowVisible() const { return m_isWindowVisible; } + bool IsTearingSupported() const { return m_options & c_AllowTearing; } + + // Direct3D Accessors. + IDXGIAdapter1* GetAdapter() const { return m_adapter.Get(); } + ID3D12Device* GetD3DDevice() const { return m_d3dDevice.Get(); } + IDXGIFactory4* GetDXGIFactory() const { return m_dxgiFactory.Get(); } + IDXGISwapChain3* GetSwapChain() const { return m_swapChain.Get(); } + D3D_FEATURE_LEVEL GetDeviceFeatureLevel() const { return m_d3dFeatureLevel; } + ID3D12Resource* GetRenderTarget() const { return m_renderTargets[m_backBufferIndex].Get(); } + ID3D12Resource* GetDepthStencil() const { return m_depthStencil.Get(); } + ID3D12CommandQueue* GetCommandQueue() const { return m_commandQueue.Get(); } + ID3D12CommandAllocator* GetCommandAllocator() const { return m_commandAllocators[m_backBufferIndex].Get(); } + ID3D12GraphicsCommandList* GetCommandList() const { return m_commandList.Get(); } + DXGI_FORMAT GetBackBufferFormat() const { return m_backBufferFormat; } + DXGI_FORMAT GetDepthBufferFormat() const { return m_depthBufferFormat; } + D3D12_VIEWPORT GetScreenViewport() const { return m_screenViewport; } + D3D12_RECT GetScissorRect() const { return m_scissorRect; } + UINT GetCurrentFrameIndex() const { return m_backBufferIndex; } + UINT GetPreviousFrameIndex() const { return m_backBufferIndex == 0 ? m_backBufferCount - 1 : m_backBufferIndex - 1; } + UINT GetBackBufferCount() const { return m_backBufferCount; } + unsigned int GetDeviceOptions() const { return m_options; } + LPCWSTR GetAdapterDescription() const { return m_adapterDescription.c_str(); } + UINT GetAdapterID() const { return m_adapterID; } + + CD3DX12_CPU_DESCRIPTOR_HANDLE GetRenderTargetView() const + { + return CD3DX12_CPU_DESCRIPTOR_HANDLE(m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), m_backBufferIndex, m_rtvDescriptorSize); + } + CD3DX12_CPU_DESCRIPTOR_HANDLE GetDepthStencilView() const + { + return CD3DX12_CPU_DESCRIPTOR_HANDLE(m_dsvDescriptorHeap->GetCPUDescriptorHandleForHeapStart()); + } + + private: + void MoveToNextFrame(); + void InitializeAdapter(IDXGIAdapter1** ppAdapter); + + const static size_t MAX_BACK_BUFFER_COUNT = 3; + + UINT m_adapterIDoverride; + UINT m_backBufferIndex; + ComPtr m_adapter; + UINT m_adapterID; + std::wstring m_adapterDescription; + + // Direct3D objects. + Microsoft::WRL::ComPtr m_d3dDevice; + Microsoft::WRL::ComPtr m_commandQueue; + Microsoft::WRL::ComPtr m_commandList; + Microsoft::WRL::ComPtr m_commandAllocators[MAX_BACK_BUFFER_COUNT]; + + // Swap chain objects. + Microsoft::WRL::ComPtr m_dxgiFactory; + Microsoft::WRL::ComPtr m_swapChain; + Microsoft::WRL::ComPtr m_renderTargets[MAX_BACK_BUFFER_COUNT]; + Microsoft::WRL::ComPtr m_depthStencil; + + // Presentation fence objects. + Microsoft::WRL::ComPtr m_fence; + UINT64 m_fenceValues[MAX_BACK_BUFFER_COUNT]; + Microsoft::WRL::Wrappers::Event m_fenceEvent; + + // Direct3D rendering objects. + Microsoft::WRL::ComPtr m_rtvDescriptorHeap; + Microsoft::WRL::ComPtr m_dsvDescriptorHeap; + UINT m_rtvDescriptorSize; + D3D12_VIEWPORT m_screenViewport; + D3D12_RECT m_scissorRect; + + // Direct3D properties. + DXGI_FORMAT m_backBufferFormat; + DXGI_FORMAT m_depthBufferFormat; + UINT m_backBufferCount; + D3D_FEATURE_LEVEL m_d3dMinFeatureLevel; + + // Cached device properties. + HWND m_window; + D3D_FEATURE_LEVEL m_d3dFeatureLevel; + RECT m_outputSize; + bool m_isWindowVisible; + + // DeviceResources options (see flags above) + unsigned int m_options; + + // The IDeviceNotify can be held directly as it owns the DeviceResources. + IDeviceNotify* m_deviceNotify; + }; +} diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/DirectXRaytracingHelper.h b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/DirectXRaytracingHelper.h new file mode 100644 index 000000000..fe3921fcd --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/DirectXRaytracingHelper.h @@ -0,0 +1,292 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#pragma once + +#define SizeOfInUint32(obj) ((sizeof(obj) - 1) / sizeof(UINT32) + 1) + +struct AccelerationStructureBuffers +{ + ComPtr scratch; + ComPtr accelerationStructure; + ComPtr instanceDesc; // Used only for top-level AS + UINT64 ResultDataMaxSizeInBytes; +}; + +// Shader record = {{Shader ID}, {RootArguments}} +class ShaderRecord +{ +public: + ShaderRecord(void* pShaderIdentifier, UINT shaderIdentifierSize) : + shaderIdentifier(pShaderIdentifier, shaderIdentifierSize) + { + } + + ShaderRecord(void* pShaderIdentifier, UINT shaderIdentifierSize, void* pLocalRootArguments, UINT localRootArgumentsSize) : + shaderIdentifier(pShaderIdentifier, shaderIdentifierSize), + localRootArguments(pLocalRootArguments, localRootArgumentsSize) + { + } + + void CopyTo(void* dest) const + { + uint8_t* byteDest = static_cast(dest); + memcpy(byteDest, shaderIdentifier.ptr, shaderIdentifier.size); + if (localRootArguments.ptr) + { + memcpy(byteDest + shaderIdentifier.size, localRootArguments.ptr, localRootArguments.size); + } + } + + struct PointerWithSize { + void *ptr; + UINT size; + + PointerWithSize() : ptr(nullptr), size(0) {} + PointerWithSize(void* _ptr, UINT _size) : ptr(_ptr), size(_size) {}; + }; + PointerWithSize shaderIdentifier; + PointerWithSize localRootArguments; +}; + +// Shader table = {{ ShaderRecord 1}, {ShaderRecord 2}, ...} +class ShaderTable : public GpuUploadBuffer +{ + uint8_t* m_mappedShaderRecords; + UINT m_shaderRecordSize; + + // Debug support + std::wstring m_name; + std::vector m_shaderRecords; + + ShaderTable() {} +public: + ShaderTable(ID3D12Device* device, UINT numShaderRecords, UINT shaderRecordSize, LPCWSTR resourceName = nullptr) + : m_name(resourceName) + { + m_shaderRecordSize = Align(shaderRecordSize, D3D12_RAYTRACING_SHADER_RECORD_BYTE_ALIGNMENT); + m_shaderRecords.reserve(numShaderRecords); + UINT bufferSize = numShaderRecords * m_shaderRecordSize; + Allocate(device, bufferSize, resourceName); + m_mappedShaderRecords = MapCpuWriteOnly(); + } + + void push_back(const ShaderRecord& shaderRecord) + { + ThrowIfFalse(m_shaderRecords.size() < m_shaderRecords.capacity()); + m_shaderRecords.push_back(shaderRecord); + shaderRecord.CopyTo(m_mappedShaderRecords); + m_mappedShaderRecords += m_shaderRecordSize; + } + + UINT GetShaderRecordSize() { return m_shaderRecordSize; } + + // Pretty-print the shader records. + void DebugPrint(std::unordered_map shaderIdToStringMap) + { + std::wstringstream wstr; + wstr << L"|--------------------------------------------------------------------\n"; + wstr << L"|Shader table - " << m_name.c_str() << L": " + << m_shaderRecordSize << L" | " + << m_shaderRecords.size() * m_shaderRecordSize << L" bytes\n"; + + for (UINT i = 0; i < m_shaderRecords.size(); i++) + { + wstr << L"| [" << i << L"]: "; + wstr << shaderIdToStringMap[m_shaderRecords[i].shaderIdentifier.ptr] << L", "; + wstr << m_shaderRecords[i].shaderIdentifier.size << L" + " << m_shaderRecords[i].localRootArguments.size << L" bytes \n"; + } + wstr << L"|--------------------------------------------------------------------\n"; + wstr << L"\n"; + OutputDebugStringW(wstr.str().c_str()); + } +}; + +inline void AllocateUAVBuffer(ID3D12Device* pDevice, UINT64 bufferSize, ID3D12Resource **ppResource, D3D12_RESOURCE_STATES initialResourceState = D3D12_RESOURCE_STATE_COMMON, const wchar_t* resourceName = nullptr) +{ + auto uploadHeapProperties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT); + auto bufferDesc = CD3DX12_RESOURCE_DESC::Buffer(bufferSize, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS); + ThrowIfFailed(pDevice->CreateCommittedResource( + &uploadHeapProperties, + D3D12_HEAP_FLAG_NONE, + &bufferDesc, + initialResourceState, + nullptr, + IID_PPV_ARGS(ppResource))); + if (resourceName) + { + (*ppResource)->SetName(resourceName); + } +} + +template +void DefineExports(T* obj, LPCWSTR(&Exports)[N]) +{ + for (UINT i = 0; i < N; i++) + { + obj->DefineExport(Exports[i]); + } +} + +template +void DefineExports(T* obj, LPCWSTR(&Exports)[N][M]) +{ + for (UINT i = 0; i < N; i++) + for (UINT j = 0; j < M; j++) + { + obj->DefineExport(Exports[i][j]); + } +} + + +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); + ThrowIfFailed(pDevice->CreateCommittedResource( + &uploadHeapProperties, + D3D12_HEAP_FLAG_NONE, + &bufferDesc, + D3D12_RESOURCE_STATE_GENERIC_READ, + nullptr, + IID_PPV_ARGS(ppResource))); + if (resourceName) + { + (*ppResource)->SetName(resourceName); + } + void *pMappedData; + (*ppResource)->Map(0, nullptr, &pMappedData); + memcpy(pMappedData, pData, datasize); + (*ppResource)->Unmap(0, nullptr); +} + +// 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(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(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(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(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(desc->pSubobjects[i].pDesc); + UINT index = static_cast(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(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(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(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(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()); +} + +// Returns bool whether the device supports DirectX Raytracing tier. +inline bool IsDirectXRaytracingSupported(IDXGIAdapter1* adapter) +{ + ComPtr testDevice; + D3D12_FEATURE_DATA_D3D12_OPTIONS5 featureSupportData = {}; + + return SUCCEEDED(D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&testDevice))) + && SUCCEEDED(testDevice->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS5, &featureSupportData, sizeof(featureSupportData))) + && featureSupportData.RaytracingTier != D3D12_RAYTRACING_TIER_NOT_SUPPORTED; +} \ No newline at end of file diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/HlslCompat.h b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/HlslCompat.h new file mode 100644 index 000000000..2ac225d34 --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/HlslCompat.h @@ -0,0 +1,21 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#ifndef HLSLCOMPAT_H +#define HLSLCOMPAT_H + +typedef float3 XMFLOAT3; +typedef float4 XMFLOAT4; +typedef float4 XMVECTOR; +typedef float4x4 XMMATRIX; +typedef uint UINT; + +#endif // HLSLCOMPAT_H \ No newline at end of file diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/Main.cpp b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/Main.cpp new file mode 100644 index 000000000..53a24cd76 --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/Main.cpp @@ -0,0 +1,37 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#include "stdafx.h" +#include "D3D12RaytracingSakuraScene.h" +#pragma comment(lib, "runtimeobject.lib") + +_Use_decl_annotations_ +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow) +{ +#if (_WIN32_WINNT >= 0x0A00 /*_WIN32_WINNT_WIN10*/) + Microsoft::WRL::Wrappers::RoInitializeWrapper initialize(RO_INIT_MULTITHREADED); + if (FAILED(initialize)) + { + OutputDebugStringA("Failed to initialize WinRT.\n"); + return -1; + } +#else + HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED); + if (FAILED(hr)) + { + OutputDebugStringA("Failed to initialize COM.\n"); + return -1; + } +#endif + + D3D12RaytracingSakuraScene sample(1280, 720, L"D3D12 Raytracing - Simple Lighting"); + return Win32Application::Run(&sample, hInstance, nCmdShow); +} diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/ObjModelLoader.cpp b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/ObjModelLoader.cpp new file mode 100644 index 000000000..cb81e8e2e --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/ObjModelLoader.cpp @@ -0,0 +1,208 @@ +#include "stdafx.h" +#include "ObjModelLoader.h" +#include + + +void ObjModelLoader::Load(wchar_t const* fileName) +{ + std::ifstream openedFile(fileName); + std::string line; + Object* currentObject = nullptr; + + while (std::getline(openedFile, line)) + { + if (line.empty()) continue; + + if (line._Starts_with("o ")) { + std::string objName = line.substr(2); + std::cout << "Loaded object: " << objName << std::endl; + currentObject = FindOrCreateObject(line.substr(2)); + + continue; + } + + if (line._Starts_with("vt ")) { + XMFLOAT2 texCoord; + std::stringstream ss(line.substr(3)); + ss >> texCoord.x >> texCoord.y; + + if (currentObject != nullptr) { + currentObject->m_texCoords.push_back(texCoord); + } else { + std::cerr << "Error: currentObject is null." << std::endl; + } + continue; + } + + if (line._Starts_with("vn ")) { + XMFLOAT3 normal; + std::stringstream ss(line.substr(2)); + ss >> normal.x >> normal.y >> normal.z; + if (currentObject != nullptr) { + currentObject->m_normals.push_back(normal); + } + else { + std::cerr << "Error: currentObject is null." << std::endl; + } + continue; + } + + if (line._Starts_with("v ")) { + XMFLOAT3 vertex; + std::stringstream ss(line.substr(1)); + ss >> vertex.x >> vertex.y >> vertex.z; + if (currentObject != nullptr) { + currentObject->m_vertices.push_back(vertex); + } + else { + std::cerr << "Error: currentObject is null." << std::endl; + } + continue; + } + + if (line._Starts_with("f ")) + { + std::string tokens[3]; + std::stringstream stringStream(line.substr(2)); + stringStream >> tokens[0] >> tokens[1] >> tokens[2]; + + Object::FaceElementIndices v{}, n{}, t{}; + bool hasNormals = false, hasTexCoords = false; + + for (int i = 0; i < 3; ++i) + { + std::string& token = tokens[i]; + size_t s1 = token.find('/'); + size_t s2 = token.find('/', s1 + 1); + + + v.Values[i] = std::stoi(token.substr(0, s1)); + + if (s1 != std::string::npos && s2 != std::string::npos) + { + std::string texCoordStr = token.substr(s1 + 1, s2 - s1 - 1); + std::string normalStr = token.substr(s2 + 1); + + if (!texCoordStr.empty()) + { + t.Values[i] = std::stoi(texCoordStr); + hasTexCoords = true; + } + + if (!normalStr.empty()) + { + n.Values[i] = std::stoi(normalStr); + hasNormals = true; + } + } + else if (s1 != std::string::npos) + { + std::string texCoordStr = token.substr(s1 + 1); + if (!texCoordStr.empty()) + { + t.Values[i] = std::stoi(texCoordStr); + hasTexCoords = true; + } + } + } + + if (hasNormals && hasTexCoords) + { + currentObject->AddObjFace(v, n, t); + } + else if (hasNormals) + { + currentObject->AddObjFace(v, n); + } + else + { + currentObject->AddObjFace(v); + } + } + + + } +} + +ObjModelLoader::Object* ObjModelLoader::GetObject(std::string const& name) +{ + for (auto& obj : m_objects) + { + if (obj.GetName() == name) + return &obj; + } + + return nullptr; +} + +ObjModelLoader::Object* ObjModelLoader::FindOrCreateObject(std::string const& name) +{ + if (auto* obj = GetObject(name)) + return obj; + else + { + m_objects.push_back(Object(name)); + } + return &m_objects.back(); +} + + +void ObjModelLoader::GetObjectVerticesAndIndices( + std::string const& name, + float scale, + std::vector* vertices, + std::vector* indices) +{ + size_t baseIndex = vertices->size(); + Object* obj = GetObject(name); + if (!obj) { + throw std::runtime_error("Object '" + name + "' not found in OBJ file."); + } + + + for (const auto& face : obj->m_faces) + { + for (int vertexIndex = 0; vertexIndex < 3; ++vertexIndex) + { + Vertex v{}; + + // Get vertex position + Index index = face.VertexIndices.Values[vertexIndex] - 1; // 1-indexed + if (index >= obj->m_vertices.size()) { + throw std::runtime_error("Vertex index out of bounds."); + } + + v.position = obj->m_vertices.at(index); + v.position.x *= scale; + v.position.y *= scale; + v.position.z *= scale; + + // Get normal if available + if (face.UseNormals) + { + uint32_t normalIndex = face.NormalIndices.Values[vertexIndex] - 1; + v.normal = obj->m_normals[normalIndex]; + } + + // Get texture coordinate if available + if (face.UseTexCoords) + { + uint32_t texIndex = face.TexCoordIndices.Values[vertexIndex] - 1; + XMFLOAT2 texCoord = obj->m_texCoords[texIndex]; + v.uv.x = texCoord.x; + v.uv.y = texCoord.y; + + } + else + { + // Default UV + v.uv.x = 0.5f; + v.uv.y = 0.5f; + } + + indices->push_back(static_cast(vertices->size())); + vertices->push_back(v); + } + } +} + diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/ObjModelLoader.h b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/ObjModelLoader.h new file mode 100644 index 000000000..c0d70632f --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/ObjModelLoader.h @@ -0,0 +1,82 @@ +#pragma once +#include "RaytracingHlslCompat.h" +#include + + +class ObjModelLoader +{ +public: + void Load(wchar_t const* fileName); + + void GetObjectVerticesAndIndices( + std::string const& name, + float scale, + std::vector* vertices, + std::vector* indices); + +private: + class Object + { + public: + Object(std::string const& name) + : m_name(name) + { + } + + std::string const& GetName() const { return m_name; } + + struct FaceElementIndices + { + unsigned int Values[3]; + }; + + struct Face + { + FaceElementIndices VertexIndices; + + bool UseNormals; + bool UseTexCoords; + FaceElementIndices NormalIndices; + FaceElementIndices TexCoordIndices; + }; + + void AddObjFace(const FaceElementIndices& vertexIndices, + const std::optional& normalIndices = std::nullopt, + const std::optional& texCoordIndices = std::nullopt) + { + Face face; + face.VertexIndices = vertexIndices; + + if (normalIndices) { + face.UseNormals = true; + face.NormalIndices = *normalIndices; + } + else { + face.UseNormals = false; + } + + if (texCoordIndices) { + face.UseTexCoords = true; + face.TexCoordIndices = *texCoordIndices; + } + else { + face.UseTexCoords = false; + } + + m_faces.push_back(face); + } + + + std::vector m_vertices; + std::vector m_normals; + std::vector m_texCoords; + std::vector m_faces; + + private: + std::string m_name; + }; + + std::vector m_objects; + Object* GetObject(std::string const& name); + Object* FindOrCreateObject(std::string const& name); +}; \ No newline at end of file diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/RayTracingHlslCompat.h b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/RayTracingHlslCompat.h new file mode 100644 index 000000000..4031d7a2b --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/RayTracingHlslCompat.h @@ -0,0 +1,63 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#ifndef RAYTRACINGHLSLCOMPAT_H +#define RAYTRACINGHLSLCOMPAT_H + +#ifdef HLSL +#include "HlslCompat.h" +#else +using namespace DirectX; + +// Shader will use byte encoding to access indices. +typedef UINT32 Index; +#endif +class ObjModelLoader; +struct SceneConstantBuffer +{ + XMMATRIX projectionToWorld; + XMVECTOR cameraPosition; + XMVECTOR lightPosition; + XMVECTOR lightAmbientColor; + XMVECTOR lightDiffuseColor; + uint32_t enableSER; + uint32_t enableSortByHit; + uint32_t enableSortByMaterial; + uint32_t enableSortByBoth; +}; + +struct Vertex +{ + XMFLOAT3 position; + XMFLOAT3 normal; + XMFLOAT3 uv; +}; + +struct ObjectConstantBuffer +{ + XMFLOAT4 albedo; + uint32_t materialID; +}; + + +#define CHECKERBOARD_FLOOR_MATERIAL 1 +#define TREE_MATERIAL 2 + +enum TextureIdentifier +{ + TextureID_None = -1, + + // During raytracing + TextureID_Checkerboard = 0, + + TextureCount +}; +#endif // RAYTRACINGHLSLCOMPAT_H \ No newline at end of file diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/Raytracing-output.h b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/Raytracing-output.h new file mode 100644 index 000000000..a0af8e02c --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSakuraScene/Raytracing-output.h @@ -0,0 +1,1335 @@ +#if 0 +; shader hash: bab87ba55693cfe19db7ca502f6e26fb +; +; Buffer Definitions: +; +; cbuffer g_sceneCB +; { +; +; struct hostlayout.g_sceneCB +; { +; +; struct hostlayout.struct.SceneConstantBuffer +; { +; +; column_major float4x4 projectionToWorld; ; Offset: 0 +; float4 cameraPosition; ; Offset: 64 +; float4 lightPosition; ; Offset: 80 +; float4 lightAmbientColor; ; Offset: 96 +; float4 lightDiffuseColor; ; Offset: 112 +; +; } g_sceneCB; ; Offset: 0 +; +; +; } g_sceneCB; ; Offset: 0 Size: 128 +; +; } +; +; cbuffer g_cubeCB +; { +; +; struct g_cubeCB +; { +; +; struct struct.ObjectConstantBuffer +; { +; +; float4 albedo; ; Offset: 0 +; uint materialID; ; Offset: 16 +; +; } g_cubeCB; ; Offset: 0 +; +; +; } g_cubeCB; ; Offset: 0 Size: 20 +; +; } +; +; Resource bind info for Vertices +; { +; +; struct struct.Vertex +; { +; +; float3 position; ; Offset: 0 +; float3 normal; ; Offset: 12 +; +; } $Element; ; Offset: 0 Size: 24 +; +; } +; +; +; Resource Bindings: +; +; Name Type Format Dim ID HLSL Bind Count +; ------------------------------ ---------- ------- ----------- ------- -------------- ------ +; g_sceneCB cbuffer NA NA CB0 cb0 1 +; g_cubeCB cbuffer NA NA CB1 cb1 1 +; Scene texture i32 ras T0 t0 1 +; Indices texture byte r/o T1 t1 1 +; Vertices texture struct r/o T2 t2 1 +; RenderTarget UAV f32 2d U0 u0 1 +; +target datalayout = "e-m:e-p:32:32-i1:32-i8:32-i16:32-i32:32-i64:64-f16:32-f32:32-f64:64-n8:16:32:64" +target triple = "dxil-ms-dx" + +%dx.types.Handle = type { i8* } +%hostlayout.g_sceneCB = type { %hostlayout.struct.SceneConstantBuffer } +%hostlayout.struct.SceneConstantBuffer = type { [4 x <4 x float>], <4 x float>, <4 x float>, <4 x float>, <4 x float> } +%struct.RayPayload = type { <4 x float> } +%dx.types.ResourceProperties = type { i32, i32 } +%dx.types.CBufRet.f32 = type { float, float, float, float } +%dx.types.HitObject = type { i8* } +%struct.BuiltInTriangleIntersectionAttributes = type { <2 x float> } +%dx.types.ResRet.v2i32 = type { <2 x i32>, i32 } +%dx.types.ResRet.v3f32 = type { <3 x float>, i32 } +%dx.types.CBufRet.i32 = type { i32, i32, i32, i32 } +%struct.RaytracingAccelerationStructure = type { i32 } +%struct.ByteAddressBuffer = type { i32 } +%"class.StructuredBuffer" = type { %struct.Vertex } +%struct.Vertex = type { <3 x float>, <3 x float> } +%"class.RWTexture2D >" = type { <4 x float> } +%g_cubeCB = type { %struct.ObjectConstantBuffer } +%struct.ObjectConstantBuffer = type { <4 x float>, i32 } + +@"\01?Scene@@3URaytracingAccelerationStructure@@A" = external constant %dx.types.Handle, align 4 +@"\01?RenderTarget@@3V?$RWTexture2D@V?$vector@M$03@@@@A" = external constant %dx.types.Handle, align 4 +@"\01?Indices@@3UByteAddressBuffer@@A" = external constant %dx.types.Handle, align 4 +@"\01?Vertices@@3V?$StructuredBuffer@UVertex@@@@A" = external constant %dx.types.Handle, align 4 +@g_cubeCB = external constant %dx.types.Handle +@g_sceneCB_legacy = external global %hostlayout.g_sceneCB + +; Function Attrs: nounwind +define void @"\01?MyRaygenShader@@YAXXZ"() #0 { + %1 = load %dx.types.Handle, %dx.types.Handle* @"\01?Scene@@3URaytracingAccelerationStructure@@A", align 4 + %2 = load %dx.types.Handle, %dx.types.Handle* @"\01?RenderTarget@@3V?$RWTexture2D@V?$vector@M$03@@@@A", align 4 + %3 = load %hostlayout.g_sceneCB, %hostlayout.g_sceneCB* @g_sceneCB_legacy + %4 = alloca %struct.RayPayload, align 8 + %5 = alloca %struct.RayPayload, align 8 + %6 = call %dx.types.Handle @dx.op.createHandleForLib.hostlayout.g_sceneCB(i32 160, %hostlayout.g_sceneCB %3) ; CreateHandleForLib(Resource) + %7 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %6, %dx.types.ResourceProperties { i32 13, i32 128 }) ; AnnotateHandle(res,props) resource: CBuffer + %8 = call i32 @dx.op.dispatchRaysIndex.i32(i32 145, i8 0) ; DispatchRaysIndex(col) + %9 = insertelement <2 x i32> undef, i32 %8, i32 0 + %10 = call i32 @dx.op.dispatchRaysIndex.i32(i32 145, i8 1) ; DispatchRaysIndex(col) + %11 = insertelement <2 x i32> %9, i32 %10, i32 1 + %12 = uitofp <2 x i32> %11 to <2 x float> + %13 = fadd fast <2 x float> %12, + %14 = call i32 @dx.op.dispatchRaysDimensions.i32(i32 146, i8 0) ; DispatchRaysDimensions(col) + %15 = insertelement <2 x i32> undef, i32 %14, i32 0 + %16 = call i32 @dx.op.dispatchRaysDimensions.i32(i32 146, i8 1) ; DispatchRaysDimensions(col) + %17 = insertelement <2 x i32> %15, i32 %16, i32 1 + %18 = uitofp <2 x i32> %17 to <2 x float> + %19 = fdiv fast <2 x float> %13, %18 + %20 = fmul fast <2 x float> %19, + %21 = fadd fast <2 x float> %20, + %22 = extractelement <2 x float> %21, i32 1 + %23 = fsub fast float -0.000000e+00, %22 + %24 = extractelement <2 x float> %21, i64 0 + %25 = call %dx.types.CBufRet.f32 @dx.op.cbufferLoadLegacy.f32(i32 59, %dx.types.Handle %7, i32 0) ; CBufferLoadLegacy(handle,regIndex) + %26 = extractvalue %dx.types.CBufRet.f32 %25, 0 + %27 = extractvalue %dx.types.CBufRet.f32 %25, 1 + %28 = extractvalue %dx.types.CBufRet.f32 %25, 2 + %29 = extractvalue %dx.types.CBufRet.f32 %25, 3 + %30 = call %dx.types.CBufRet.f32 @dx.op.cbufferLoadLegacy.f32(i32 59, %dx.types.Handle %7, i32 1) ; CBufferLoadLegacy(handle,regIndex) + %31 = extractvalue %dx.types.CBufRet.f32 %30, 0 + %32 = extractvalue %dx.types.CBufRet.f32 %30, 1 + %33 = extractvalue %dx.types.CBufRet.f32 %30, 2 + %34 = extractvalue %dx.types.CBufRet.f32 %30, 3 + %35 = call %dx.types.CBufRet.f32 @dx.op.cbufferLoadLegacy.f32(i32 59, %dx.types.Handle %7, i32 3) ; CBufferLoadLegacy(handle,regIndex) + %36 = extractvalue %dx.types.CBufRet.f32 %35, 0 + %37 = extractvalue %dx.types.CBufRet.f32 %35, 1 + %38 = extractvalue %dx.types.CBufRet.f32 %35, 2 + %39 = extractvalue %dx.types.CBufRet.f32 %35, 3 + %40 = fmul fast float %24, %26 + %41 = call float @dx.op.tertiary.f32(i32 46, float %31, float %23, float %40) ; FMad(a,b,c) + %42 = fadd fast float %36, %41 + %43 = insertelement <3 x float> undef, float %42, i32 0 + %44 = fmul fast float %24, %27 + %45 = call float @dx.op.tertiary.f32(i32 46, float %32, float %23, float %44) ; FMad(a,b,c) + %46 = fadd fast float %45, %37 + %47 = insertelement <3 x float> %43, float %46, i32 1 + %48 = fmul fast float %24, %28 + %49 = call float @dx.op.tertiary.f32(i32 46, float %33, float %23, float %48) ; FMad(a,b,c) + %50 = fadd fast float %49, %38 + %51 = insertelement <3 x float> %47, float %50, i32 2 + %52 = fmul fast float %24, %29 + %53 = call float @dx.op.tertiary.f32(i32 46, float %34, float %23, float %52) ; FMad(a,b,c) + %54 = fadd fast float %53, %39 + %55 = insertelement <3 x float> undef, float %54, i32 0 + %56 = shufflevector <3 x float> %55, <3 x float> undef, <3 x i32> zeroinitializer + %57 = fdiv fast <3 x float> %51, %56 + %58 = call %dx.types.CBufRet.f32 @dx.op.cbufferLoadLegacy.f32(i32 59, %dx.types.Handle %7, i32 4) ; CBufferLoadLegacy(handle,regIndex) + %59 = extractvalue %dx.types.CBufRet.f32 %58, 0 + %60 = insertelement <3 x float> undef, float %59, i32 0 + %61 = extractvalue %dx.types.CBufRet.f32 %58, 1 + %62 = insertelement <3 x float> %60, float %61, i32 1 + %63 = extractvalue %dx.types.CBufRet.f32 %58, 2 + %64 = insertelement <3 x float> %62, float %63, i32 2 + %65 = fsub fast <3 x float> %57, %64 + %66 = extractelement <3 x float> %65, i64 0 + %67 = extractelement <3 x float> %65, i64 1 + %68 = extractelement <3 x float> %65, i64 2 + %69 = call float @dx.op.dot3.f32(i32 55, float %66, float %67, float %68, float %66, float %67, float %68) ; Dot3(ax,ay,az,bx,by,bz) + %70 = call float @dx.op.unary.f32(i32 25, float %69) ; Rsqrt(value) + %71 = insertelement <3 x float> undef, float %70, i64 0 + %72 = insertelement <3 x float> %71, float %70, i64 1 + %73 = insertelement <3 x float> %72, float %70, i64 2 + %74 = fmul fast <3 x float> %65, %73 + %75 = call %dx.types.Handle @dx.op.createHandleForLib.dx.types.Handle(i32 160, %dx.types.Handle %1) ; CreateHandleForLib(Resource) + %76 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %75, %dx.types.ResourceProperties { i32 16, i32 0 }) ; AnnotateHandle(res,props) resource: RTAccelerationStructure + %77 = getelementptr inbounds %struct.RayPayload, %struct.RayPayload* %5, i32 0, i32 0 + store <4 x float> zeroinitializer, <4 x float>* %77, align 8 + %78 = extractelement <3 x float> %74, i64 0 + %79 = extractelement <3 x float> %74, i64 1 + %80 = extractelement <3 x float> %74, i64 2 + %81 = call %dx.types.HitObject @dx.op.hitObject_TraceRay.struct.RayPayload(i32 262, %dx.types.Handle %76, i32 16, i32 -1, i32 0, i32 1, i32 0, float %59, float %61, float %63, float 0x3F50624DE0000000, float %78, float %79, float %80, float 1.000000e+04, %struct.RayPayload* nonnull %5) ; HitObject_TraceRay(accelerationStructure,rayFlags,instanceInclusionMask,rayContributionToHitGroupIndex,multiplierForGeometryContributionToHitGroupIndex,missShaderIndex,Origin_X,Origin_Y,Origin_Z,TMin,Direction_X,Direction_Y,Direction_Z,TMax,payload) + %82 = load <4 x float>, <4 x float>* %77, align 8 + %83 = call i32 @dx.op.hitObject_LoadLocalRootTableConstant(i32 288, %dx.types.HitObject %81, i32 16) ; HitObject_LoadLocalRootTableConstant(hitObject,offset) + %84 = call %dx.types.HitObject @dx.op.hitObject_MakeNop(i32 266) ; HitObject_MakeNop() + call void @dx.op.maybeReorderThread(i32 268, %dx.types.HitObject %84, i32 %83, i32 1) ; MaybeReorderThread(hitObject,coherenceHint,numCoherenceHintBitsFromLSB) + %85 = getelementptr inbounds %struct.RayPayload, %struct.RayPayload* %4, i32 0, i32 0 + store <4 x float> %82, <4 x float>* %85, align 8 + call void @dx.op.hitObject_Invoke.struct.RayPayload(i32 267, %dx.types.HitObject %81, %struct.RayPayload* nonnull %4) ; HitObject_Invoke(hitObject,payload) + %86 = load <4 x float>, <4 x float>* %85, align 8 + %87 = call %dx.types.Handle @dx.op.createHandleForLib.dx.types.Handle(i32 160, %dx.types.Handle %2) ; CreateHandleForLib(Resource) + %88 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %87, %dx.types.ResourceProperties { i32 4098, i32 1033 }) ; AnnotateHandle(res,props) resource: RWTexture2D<4xF32> + %89 = extractelement <4 x float> %86, i64 0 + %90 = extractelement <4 x float> %86, i64 1 + %91 = extractelement <4 x float> %86, i64 2 + %92 = extractelement <4 x float> %86, i64 3 + call void @dx.op.textureStore.f32(i32 67, %dx.types.Handle %88, i32 %8, i32 %10, i32 undef, float %89, float %90, float %91, float %92, i8 15) ; TextureStore(srv,coord0,coord1,coord2,value0,value1,value2,value3,mask) + ret void +} + +; Function Attrs: nounwind +define void @"\01?MyClosestHitShader@@YAXURayPayload@@UBuiltInTriangleIntersectionAttributes@@@Z"(%struct.RayPayload* noalias nocapture %payload, %struct.BuiltInTriangleIntersectionAttributes* nocapture readonly %attr) #0 { + %1 = load %dx.types.Handle, %dx.types.Handle* @"\01?Vertices@@3V?$StructuredBuffer@UVertex@@@@A", align 4 + %2 = load %dx.types.Handle, %dx.types.Handle* @"\01?Indices@@3UByteAddressBuffer@@A", align 4 + %3 = load %dx.types.Handle, %dx.types.Handle* @g_cubeCB, align 4 + %4 = load %hostlayout.g_sceneCB, %hostlayout.g_sceneCB* @g_sceneCB_legacy + %5 = call %dx.types.Handle @dx.op.createHandleForLib.dx.types.Handle(i32 160, %dx.types.Handle %3) ; CreateHandleForLib(Resource) + %6 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %5, %dx.types.ResourceProperties { i32 13, i32 20 }) ; AnnotateHandle(res,props) resource: CBuffer + %7 = call %dx.types.Handle @dx.op.createHandleForLib.hostlayout.g_sceneCB(i32 160, %hostlayout.g_sceneCB %4) ; CreateHandleForLib(Resource) + %8 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %7, %dx.types.ResourceProperties { i32 13, i32 128 }) ; AnnotateHandle(res,props) resource: CBuffer + %9 = call float @dx.op.worldRayOrigin.f32(i32 147, i8 0) ; WorldRayOrigin(col) + %10 = insertelement <3 x float> undef, float %9, i64 0 + %11 = call float @dx.op.worldRayOrigin.f32(i32 147, i8 1) ; WorldRayOrigin(col) + %12 = insertelement <3 x float> %10, float %11, i64 1 + %13 = call float @dx.op.worldRayOrigin.f32(i32 147, i8 2) ; WorldRayOrigin(col) + %14 = insertelement <3 x float> %12, float %13, i64 2 + %15 = call float @dx.op.rayTCurrent.f32(i32 154) ; RayTCurrent() + %16 = insertelement <3 x float> undef, float %15, i32 0 + %17 = shufflevector <3 x float> %16, <3 x float> undef, <3 x i32> zeroinitializer + %18 = call float @dx.op.worldRayDirection.f32(i32 148, i8 0) ; WorldRayDirection(col) + %19 = insertelement <3 x float> undef, float %18, i64 0 + %20 = call float @dx.op.worldRayDirection.f32(i32 148, i8 1) ; WorldRayDirection(col) + %21 = insertelement <3 x float> %19, float %20, i64 1 + %22 = call float @dx.op.worldRayDirection.f32(i32 148, i8 2) ; WorldRayDirection(col) + %23 = insertelement <3 x float> %21, float %22, i64 2 + %24 = fmul fast <3 x float> %23, %17 + %25 = call i32 @dx.op.primitiveIndex.i32(i32 161) ; PrimitiveIndex() + %26 = mul i32 %25, 6 + %27 = and i32 %26, -4 + %28 = call %dx.types.Handle @dx.op.createHandleForLib.dx.types.Handle(i32 160, %dx.types.Handle %2) ; CreateHandleForLib(Resource) + %29 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %28, %dx.types.ResourceProperties { i32 11, i32 0 }) ; AnnotateHandle(res,props) resource: ByteAddressBuffer + %30 = call %dx.types.ResRet.v2i32 @dx.op.rawBufferVectorLoad.v2i32(i32 303, %dx.types.Handle %29, i32 %27, i32 undef, i32 4) ; RawBufferVectorLoad(buf,index,elementOffset,alignment) + %31 = extractvalue %dx.types.ResRet.v2i32 %30, 0 + %32 = icmp eq i32 %27, %26 + %33 = extractelement <2 x i32> %31, i32 0 + %34 = lshr i32 %33, 16 + %35 = extractelement <2 x i32> %31, i32 1 + %36 = and i32 %35, 65535 + br i1 %32, label %37, label %42 + +;