diff --git a/Libraries/D3D12RaytracingFallback/readme.md b/Libraries/D3D12RaytracingFallback/readme.md index dc2145db6..20aef5ef3 100644 --- a/Libraries/D3D12RaytracingFallback/readme.md +++ b/Libraries/D3D12RaytracingFallback/readme.md @@ -20,7 +20,7 @@ In order to build and run the Fallback Layer you will need: After cloning the project, you can open up D3D12Raytracing.sln in Samples/Desktop/D3D12Raytracing/src. There are several projects that demonstrate basic usage of the Fallback Layer/DXR API including a larger demo integrated with the MiniEngine. More details in the [readme](../../Samples/Desktop/D3D12Raytracing/readme.md) ## Developing with the Fallback Layer -For details on devoloping an app using the Fallback Layer, please consult the [developer guide](developerguide.md) +For details on developing an app using the Fallback Layer, please consult the [developer guide](developerguide.md) ## Driver requirements * Retail support for DXIL @@ -35,5 +35,4 @@ We would love to hear how you're using the Fallback Layer! We're also interested * Are there functional issues blocking adoption of the Fallback Layer? Are the ways the interface could be less obstructive? * Bugs/Issues using the Fallback Layer? -Please feel free to reach out at: -http://www.directxtech.com/ +Please feel free to reach out at [DirectX Raytracing forums](http://forums.directxtech.com/index.php?PHPSESSID=394klvdd3683tt1fjkh2jteav1&board=248.0). diff --git a/README.md b/README.md index 8c86034e8..59c9b9e16 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,17 @@ # DirectX-Graphics-Samples -This repo contains the DirectX Graphics samples that demonstrate how to build graphics intensive applications for Windows 10. We also have a YouTube channel! Visit us here: https://www.youtube.com/MicrosoftDirectX12andGraphicsEducation +This repo contains the DirectX 12 Graphics samples that demonstrate how to build graphics intensive applications for Windows 10. We invite you to join us at our [forums](http://www.directxtech.com/) and a [YouTube channel](https://www.youtube.com/MicrosoftDirectX12andGraphicsEducation) for DirectX12 . ## API Samples In the Samples directory, you will find samples that attempt to break off specific features and specific usage scenarios into bite sized chunks. For example, the ExecuteIndirect sample will show you just enough about execute indirect to get started with that feature without diving too deep into multiengine whereas the nBodyGravity sample will delve into multiengine without touching on the execute indirect feature etc. By doing this, we hope to make it easier to get started with DirectX 12. Recent API Sample Updates: -1. D3D12 Depth Bounds Test: This sample demos how to use depth bounds test with DirectX 12. In addition, it introduces a more flexible way to create pipeline state, called Pipeline State Object Stream (PSO Stream). PSO Stream is required to use Depth Bounds Test feature. -2. Shader Model 6 Wave Intrinsics: This sample visualizes several new wave intrinsics in Shader Model 6. +1. [D3D12 Raytracing](Samples/Desktop/D3D12Raytracing/readme.md): This is a set of tutorial and advanced samples that demonstrate the use of DirectX Raytracing. + +![D3D12 Raytracing Hello World GUI](Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/Screenshot_small.png) +![D3D12 Raytracing Mini Engine](Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingMiniEngineSample/Screenshot_small.png) + +2. [Shader Model 6 Wave Intrinsics](Samples/Desktop/D3D12SM6WaveIntrinsics/readme.md): This sample visualizes several new wave intrinsics in Shader Model 6. ## MiniEngine: A DirectX 12 Engine Starter Kit In addition to the samples, we are announcing the first DirectX 12 preview release of the MiniEngine. diff --git a/Samples/Desktop/D3D12Raytracing/readme.md b/Samples/Desktop/D3D12Raytracing/readme.md index ff87e1997..7dfa4e101 100644 --- a/Samples/Desktop/D3D12Raytracing/readme.md +++ b/Samples/Desktop/D3D12Raytracing/readme.md @@ -1,16 +1,38 @@ - # D3D12 Raytracing Samples -This collection of samples act as an introduction to Direct3D 12 Raytracing. Each sample introduces a few new concepts. +This collection of samples act as an introduction to DirectX Raytracing (DXR). The samples are divided into tutorials and advanced samples. Each tutorial sample introduces a few new concepts. Advanced samples demonstrate more complex techniques and applications of raytracing. + +The samples implement both DXR and D3D12 Raytracing Fallback Layer APIs. This is purely for demonstration purposes to show API differences. Real-world applications will implement only one or the other. The Fallback Layer uses DXR if a driver and OS supports it. Otherwise, it falls back to the compute pipeline to emulate raytracing. Developers aiming for wider HW support should target the Fallback Layer. -The samples implement both D3D12 Raytracing and D3D12 Raytracing Fallback Layer APIs. This is purely for demonstration purposes to show where the API differences are. Real-world applications will implement only one or the other. The Fallback Layer uses D3D12 Raytracing if a driver and OS supports it. Otherwise, it falls back to compute pipeline to emulate raytracing. Developers aiming for a wider HW support should target the Fallback Layer. +### Getting Started +* DXR spec/documentation is available in the SDK package at [Getting Started with Raytracing](http://forums.directxtech.com/index.php?topic=5860.0) post. +* [Raytracing Fallback Layer documentation](../../../Libraries/D3D12RaytracingFallback/readme.md). -## 1. Hello World Sample -This sample demonstrates how to setup a raytracing pipeline and render a triangle in screenspace. +# Tutorial Samples +## 1. [Hello World Sample](src/D3D12RaytracingHelloWorld/readme.md) +This sample demonstrates how to setup a raytracing pipeline and render a triangle in screen space. ![D3D12 Raytracing Hello World GUI](src/D3D12RaytracingHelloWorld/Screenshot_small.png) -## MiniEngine Sample +## 2. [Simple Lighting Sample](src/D3D12RaytracingSimpleLighting/readme.md) +This sample demonstrates how to do ray generation for a dynamic perspective camera and calculate simple diffuse shading for a cube from a dynamic point light. + +![D3D12 Raytracing Hello World GUI](src/D3D12RaytracingSimpleLighting/Screenshot_small.png) + + +# Advanced Samples + +## [MiniEngine Sample](src/D3D12RaytracingMiniEngineSample/readme.md) This sample demonstrates integration of the Fallback Layer in the MiniEngine's Model Viewer and several sample uses of raytracing. ![D3D12 Raytracing Mini Engine](src/D3D12RaytracingMiniEngineSample/Screenshot_small.png) +## Requirements +* [Visual Studio 2017](https://www.visualstudio.com/) with the [Windows 10 Fall Creators Update SDK](https://developer.microsoft.com/en-US/windows/downloads/windows-10-sdk) +* Windows 10 with the Fall Creators Update +* A graphics card with retail DXIL capabilities. +* DXIL.dll should be pulled from the latest Windows SDK to accompany the compiler or enable Developer mode. +* Download DirectXRaytracingBinariesV1.0.zip from https://github.com/Microsoft/DirectX-Graphics-Samples/releases and copy all the contents to Samples/Desktop/D3D12Raytracing/tools/x64 +* That is all that is required for the Fallback Layer. To take advantage of DXR you will additionally need a raytracing compatible driver. Please contact the IHV you’re working with for drivers that will work with DXR. For Nvidia, you can review the following blog post for the DXR support requirements: https://devblogs.nvidia.com/introduction-nvidia-rtx-directx-raytracing/. + +## Feedback and Questions +We welcome all feedback, questions and discussions about DXR and the Fallback Layer at [DirectX Raytracing forums](http://forums.directxtech.com/index.php?PHPSESSID=394klvdd3683tt1fjkh2jteav1&board=248.0). diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12Raytracing.sln b/Samples/Desktop/D3D12Raytracing/src/D3D12Raytracing.sln index 098a1066e..d008faeaf 100644 --- a/Samples/Desktop/D3D12Raytracing/src/D3D12Raytracing.sln +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12Raytracing.sln @@ -1,15 +1,23 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.27130.2036 +VisualStudioVersion = 15.0.27130.2010 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{250B50F1-543D-4D9D-B4FC-A1EA4E61B9E4}" + ProjectSection(SolutionItems) = preProject + ..\readme.md = ..\readme.md + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "D3D12RaytracingHelloWorld", "D3D12RaytracingHelloWorld\D3D12RaytracingHelloWorld.vcxproj", "{5018F6A3-6533-4744-B1FD-727D199FD2E9}" ProjectSection(ProjectDependencies) = postProject {4BE280A6-1066-41CA-ACDD-6BB7E532508B} = {4BE280A6-1066-41CA-ACDD-6BB7E532508B} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "D3D12RaytracingSimpleLighting", "D3D12RaytracingSimpleLighting\D3D12RaytracingSimpleLighting.vcxproj", "{A0848C98-F5AA-431C-9E76-7F1E7EFA368C}" + ProjectSection(ProjectDependencies) = postProject + {4BE280A6-1066-41CA-ACDD-6BB7E532508B} = {4BE280A6-1066-41CA-ACDD-6BB7E532508B} + EndProjectSection +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MiniEngine", "MiniEngine", "{0B851798-A730-4A11-A717-668D40A26088}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Core", "..\..\..\..\MiniEngine\Core\Core_VS15.vcxproj", "{86A58508-0D6A-4786-A32F-01A301FDC6F3}" @@ -24,6 +32,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FallbackLayer", "..\..\..\. EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FallbackLayerUnitTests", "..\..\..\..\Libraries\D3D12RaytracingFallback\src\FallbackLayerUnitTests\FallbackLayerUnitTests.vcxproj", "{13F1830C-EA8D-4488-89C8-70AAB15972AA}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tutorials", "Tutorials", "{22B9FE19-4D5A-4F3F-ABEA-F9ACB1574331}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Advanced", "Advanced", "{024FAECC-CCE3-4B06-9F06-C83FB58877EF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -44,6 +56,16 @@ Global {5018F6A3-6533-4744-B1FD-727D199FD2E9}.Release|x64.ActiveCfg = Release|x64 {5018F6A3-6533-4744-B1FD-727D199FD2E9}.Release|x64.Build.0 = Release|x64 {5018F6A3-6533-4744-B1FD-727D199FD2E9}.Release|x86.ActiveCfg = Release|x64 + {A0848C98-F5AA-431C-9E76-7F1E7EFA368C}.Debug|x64.ActiveCfg = Debug|x64 + {A0848C98-F5AA-431C-9E76-7F1E7EFA368C}.Debug|x64.Build.0 = Debug|x64 + {A0848C98-F5AA-431C-9E76-7F1E7EFA368C}.Debug|x86.ActiveCfg = Debug|x64 + {A0848C98-F5AA-431C-9E76-7F1E7EFA368C}.Profile|x64.ActiveCfg = Release|x64 + {A0848C98-F5AA-431C-9E76-7F1E7EFA368C}.Profile|x64.Build.0 = Release|x64 + {A0848C98-F5AA-431C-9E76-7F1E7EFA368C}.Profile|x86.ActiveCfg = Release|x64 + {A0848C98-F5AA-431C-9E76-7F1E7EFA368C}.Profile|x86.Build.0 = Release|x64 + {A0848C98-F5AA-431C-9E76-7F1E7EFA368C}.Release|x64.ActiveCfg = Release|x64 + {A0848C98-F5AA-431C-9E76-7F1E7EFA368C}.Release|x64.Build.0 = Release|x64 + {A0848C98-F5AA-431C-9E76-7F1E7EFA368C}.Release|x86.ActiveCfg = Release|x64 {86A58508-0D6A-4786-A32F-01A301FDC6F3}.Debug|x64.ActiveCfg = Debug|x64 {86A58508-0D6A-4786-A32F-01A301FDC6F3}.Debug|x64.Build.0 = Debug|x64 {86A58508-0D6A-4786-A32F-01A301FDC6F3}.Debug|x86.ActiveCfg = Debug|x64 @@ -88,21 +110,27 @@ Global {13F1830C-EA8D-4488-89C8-70AAB15972AA}.Profile|x64.Build.0 = Release|x64 {13F1830C-EA8D-4488-89C8-70AAB15972AA}.Profile|x86.ActiveCfg = Release|Win32 {13F1830C-EA8D-4488-89C8-70AAB15972AA}.Profile|x86.Build.0 = Release|Win32 + {13F1830C-EA8D-4488-89C8-70AAB15972AA}.Profile|x86.Deploy.0 = Release|Win32 {13F1830C-EA8D-4488-89C8-70AAB15972AA}.Release|x64.ActiveCfg = Release|x64 {13F1830C-EA8D-4488-89C8-70AAB15972AA}.Release|x64.Build.0 = Release|x64 + {13F1830C-EA8D-4488-89C8-70AAB15972AA}.Release|x64.Deploy.0 = Release|x64 {13F1830C-EA8D-4488-89C8-70AAB15972AA}.Release|x86.ActiveCfg = Release|Win32 {13F1830C-EA8D-4488-89C8-70AAB15972AA}.Release|x86.Build.0 = Release|Win32 + {13F1830C-EA8D-4488-89C8-70AAB15972AA}.Release|x86.Deploy.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {5018F6A3-6533-4744-B1FD-727D199FD2E9} = {250B50F1-543D-4D9D-B4FC-A1EA4E61B9E4} + {5018F6A3-6533-4744-B1FD-727D199FD2E9} = {22B9FE19-4D5A-4F3F-ABEA-F9ACB1574331} + {A0848C98-F5AA-431C-9E76-7F1E7EFA368C} = {22B9FE19-4D5A-4F3F-ABEA-F9ACB1574331} {86A58508-0D6A-4786-A32F-01A301FDC6F3} = {0B851798-A730-4A11-A717-668D40A26088} {5D3AEEFB-8789-48E5-9BD9-09C667052D09} = {0B851798-A730-4A11-A717-668D40A26088} - {315A1E1B-3732-41FE-9B4A-6A1E103BA2F5} = {0B851798-A730-4A11-A717-668D40A26088} + {315A1E1B-3732-41FE-9B4A-6A1E103BA2F5} = {024FAECC-CCE3-4B06-9F06-C83FB58877EF} {4BE280A6-1066-41CA-ACDD-6BB7E532508B} = {4F686017-C76B-497E-8405-7F023968E8AF} {13F1830C-EA8D-4488-89C8-70AAB15972AA} = {4F686017-C76B-497E-8405-7F023968E8AF} + {22B9FE19-4D5A-4F3F-ABEA-F9ACB1574331} = {250B50F1-543D-4D9D-B4FC-A1EA4E61B9E4} + {024FAECC-CCE3-4B06-9F06-C83FB58877EF} = {250B50F1-543D-4D9D-B4FC-A1EA4E61B9E4} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F763A25B-2115-4E87-87C4-2C6AA75C1542} diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/D3D12RaytracingHelloWorld.cpp b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/D3D12RaytracingHelloWorld.cpp index 740fd6cc9..3db08bded 100644 --- a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/D3D12RaytracingHelloWorld.cpp +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/D3D12RaytracingHelloWorld.cpp @@ -24,7 +24,6 @@ const wchar_t* D3D12RaytracingHelloWorld::c_missShaderName = L"MyMissShader"; D3D12RaytracingHelloWorld::D3D12RaytracingHelloWorld(UINT width, UINT height, std::wstring name) : DXSample(width, height, name), - m_frameIndex(0), m_raytracingOutputResourceUAVDescriptorHeapIndex(UINT_MAX) { m_isDxrSupported = EnableRaytracing(); @@ -61,7 +60,7 @@ D3D12RaytracingHelloWorld::D3D12RaytracingHelloWorld(UINT width, UINT height, st exit(EXIT_FAILURE); } - m_rayGenCB.rayGenViewport = { -1.0f, -1.0f, 1.0f, 1.0f }; + m_rayGenCB.viewport = { -1.0f, -1.0f, 1.0f, 1.0f }; UpdateForSizeChange(width, height); } @@ -180,7 +179,7 @@ void D3D12RaytracingHelloWorld::CreateRaytracingPipelineStateObject() lib->SetDXILLibrary(&libdxil); // Define which shader exports to surface from the library. // If no shader exports are defined for a DXIL library subobject, all shaders will be surfaced. - // In this sample, this could be for convenience since the sample uses all shaders in the library. + // In this sample, this could be omitted for convenience since the sample uses all shaders in the library. { lib->DefineExport(c_raygenShaderName); lib->DefineExport(c_closestHitShaderName); @@ -197,8 +196,8 @@ void D3D12RaytracingHelloWorld::CreateRaytracingPipelineStateObject() // Shader config // Defines the maximum sizes in bytes for the ray payload and attribute structure. auto shaderConfig = raytracingPipeline.CreateSubobject(); - UINT payloadSize = 4; // UINT index - UINT attributeSize = 8; // float2 barycentrics + UINT payloadSize = 4 * sizeof(float); // float4 color + UINT attributeSize = 2 * sizeof(float); // float2 barycentrics shaderConfig->Config(payloadSize, attributeSize); // Local root signature and shader association @@ -206,7 +205,7 @@ void D3D12RaytracingHelloWorld::CreateRaytracingPipelineStateObject() auto localRootSignature = raytracingPipeline.CreateSubobject(); localRootSignature->SetRootSignature(m_raytracingLocalRootSignature.Get()); // Define explicit shader association for the local root signature. - // In this sample, this could have been ommited for convenience since it matches the default association. + // In this sample, this could be ommited for convenience since it matches the default association. { auto rootSignatureAssociation = raytracingPipeline.CreateSubobject(); rootSignatureAssociation->SetSubobjectToAssociate(*localRootSignature); @@ -270,12 +269,15 @@ void D3D12RaytracingHelloWorld::CreateDescriptorHeap() auto device = m_deviceResources->GetD3DDevice(); D3D12_DESCRIPTOR_HEAP_DESC descriptorHeapDesc = {}; - // Allocate a heap for plenty of descriptors. - descriptorHeapDesc.NumDescriptors = 10; + // Allocate a heap for 3 descriptors: + // 2 - bottom and top level acceleration structure fallback wrapped pointers + // 1 - raytracing output texture SRV + descriptorHeapDesc.NumDescriptors = 3; descriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; descriptorHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; descriptorHeapDesc.NodeMask = 0; device->CreateDescriptorHeap(&descriptorHeapDesc, IID_PPV_ARGS(&m_descriptorHeap)); + NAME_D3D12_OBJECT(m_descriptorHeap); m_descriptorSize = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); } @@ -284,14 +286,14 @@ void D3D12RaytracingHelloWorld::CreateDescriptorHeap() void D3D12RaytracingHelloWorld::BuildGeometry() { auto device = m_deviceResources->GetD3DDevice(); - Index indicies[] = + Index indices[] = { 0, 1, 2 }; float depthValue = 1.0; float offset = 0.7f; - Vertex triangleVerts[] = + Vertex vertices[] = { // The sample raytraces in screen space coordinates. // Since DirectX screen space coordinates are right handed (i.e. Y axis points down). @@ -301,15 +303,15 @@ void D3D12RaytracingHelloWorld::BuildGeometry() { offset, offset, depthValue } }; - AllocateUploadBuffer(device, triangleVerts, sizeof(triangleVerts), &m_vertexBuffer); - AllocateUploadBuffer(device, indicies, sizeof(indicies), &m_indexBuffer); + AllocateUploadBuffer(device, vertices, sizeof(vertices), &m_vertexBuffer); + AllocateUploadBuffer(device, indices, sizeof(indices), &m_indexBuffer); } // Build acceleration structures needed for raytracing. void D3D12RaytracingHelloWorld::BuildAccelerationStructures() { auto device = m_deviceResources->GetD3DDevice(); - auto commandList= m_deviceResources->GetCommandList(); + auto commandList = m_deviceResources->GetCommandList(); auto commandQueue = m_deviceResources->GetCommandQueue(); auto commandAllocator = m_deviceResources->GetCommandAllocator(); @@ -325,11 +327,13 @@ void D3D12RaytracingHelloWorld::BuildAccelerationStructures() geometryDesc.Triangles.VertexFormat = DXGI_FORMAT_R32G32B32_FLOAT; geometryDesc.Triangles.VertexCount = static_cast(m_vertexBuffer->GetDesc().Width) / sizeof(Vertex); geometryDesc.Triangles.VertexBuffer.StartAddress = m_vertexBuffer->GetGPUVirtualAddress(); - geometryDesc.Triangles.VertexBuffer.StrideInBytes = sizeof(float) * 3; + geometryDesc.Triangles.VertexBuffer.StrideInBytes = sizeof(Vertex); // Get required sizes for an acceleration structure. + D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAGS buildFlags = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_PREFER_FAST_TRACE; D3D12_GET_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO_DESC prebuildInfoDesc = {}; prebuildInfoDesc.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY; + prebuildInfoDesc.Flags = buildFlags; prebuildInfoDesc.NumDescs = 1; D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO topLevelPrebuildInfo = {}; @@ -408,7 +412,7 @@ void D3D12RaytracingHelloWorld::BuildAccelerationStructures() D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC bottomLevelBuildDesc = {}; { bottomLevelBuildDesc.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY; - bottomLevelBuildDesc.Flags = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_PREFER_FAST_TRACE; + bottomLevelBuildDesc.Flags = buildFlags; bottomLevelBuildDesc.ScratchAccelerationStructureData = { scratchResource->GetGPUVirtualAddress(), scratchResource->GetDesc().Width }; bottomLevelBuildDesc.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL; bottomLevelBuildDesc.DestAccelerationStructureData = { m_bottomLevelAccelerationStructure->GetGPUVirtualAddress(), bottomLevelPrebuildInfo.ResultDataMaxSizeInBytes }; @@ -458,7 +462,7 @@ void D3D12RaytracingHelloWorld::BuildAccelerationStructures() // This encapsulates all shader records - shaders and the arguments for their local root signatures. void D3D12RaytracingHelloWorld::BuildShaderTables() { - const auto &device = m_deviceResources->GetD3DDevice(); + auto device = m_deviceResources->GetD3DDevice(); void* rayGenShaderIdentifier; void* missShaderIdentifier; @@ -629,7 +633,7 @@ void D3D12RaytracingHelloWorld::UpdateForSizeChange(UINT width, UINT height) float border = 0.1f; if (m_width <= m_height) { - m_rayGenCB.rayGenStencil = + m_rayGenCB.stencil = { -1 + border, -1 + border * m_aspectRatio, 1.0f - border, 1 - border * m_aspectRatio @@ -637,7 +641,7 @@ void D3D12RaytracingHelloWorld::UpdateForSizeChange(UINT width, UINT height) } else { - m_rayGenCB.rayGenStencil = + m_rayGenCB.stencil = { -1 + border / m_aspectRatio, -1 + border, 1 - border / m_aspectRatio, 1.0f - border diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/D3D12RaytracingHelloWorld.h b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/D3D12RaytracingHelloWorld.h index b0fe587db..91a1de920 100644 --- a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/D3D12RaytracingHelloWorld.h +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/D3D12RaytracingHelloWorld.h @@ -13,6 +13,7 @@ #include "DXSample.h" #include "StepTimer.h" +#include "RaytracingHlslCompat.h" namespace GlobalRootSignatureParams { enum Value { @@ -29,7 +30,7 @@ namespace LocalRootSignatureParams { }; } -// The sample supports both Fallback Layer and DirectX Raytracing APIs. +// The sample supports both Raytracing Fallback Layer and DirectX Raytracing APIs. // This is purely for demonstration purposes to show where the API differences are. // Real-world applications will implement only one or the other. // Fallback Layer uses DirectX Raytracing if a driver and OS supports it. @@ -59,23 +60,10 @@ class D3D12RaytracingHelloWorld : public DXSample virtual IDXGISwapChain* GetSwapchain() { return m_deviceResources->GetSwapChain(); } private: - struct Viewport - { - float Left; - float Top; - float Right; - float Bottom; - }; - - struct RayGenConstantBuffer - { - Viewport rayGenViewport; - Viewport rayGenStencil; - }; - static const UINT FrameCount = 2; + static const UINT FrameCount = 3; - // Fallback Layer (FL) attributes + // Raytracing Fallback Layer (FL) attributes ComPtr m_fallbackDevice; ComPtr m_fallbackCommandList; ComPtr m_fallbackStateObject; @@ -123,18 +111,7 @@ class D3D12RaytracingHelloWorld : public DXSample ComPtr m_missShaderTable; ComPtr m_hitGroupShaderTable; ComPtr m_rayGenShaderTable; - UINT shaderRecordSize; - size_t raygenShaderRecordOffset; - size_t missShaderRecordOffset; - size_t hitGroupShaderRecordOffset; - - // Window state - bool m_windowVisible; - bool m_windowedMode; - - // Frame synchronization objects - UINT m_frameIndex; - + // Application state RaytracingAPI m_raytracingAPI; bool m_forceComputeFallback; @@ -162,15 +139,4 @@ class D3D12RaytracingHelloWorld : public DXSample void CalculateFrameStats(); UINT AllocateDescriptor(D3D12_CPU_DESCRIPTOR_HANDLE* cpuDescriptor, UINT descriptorIndexToUse = UINT_MAX); WRAPPED_GPU_POINTER CreateFallbackWrappedPointer(ID3D12Resource* resource, UINT bufferNumElements); - - bool LoadBinaryResource(std::wstring name, const void*& data, size_t& size) - { - HRSRC resourceInfo = FindResource(NULL, name.c_str(), RT_RCDATA); - if (resourceInfo == NULL) return false; - HGLOBAL resourceData = LoadResource(NULL, resourceInfo); - if (resourceData == NULL) return false; - data = LockResource(resourceData); - size = SizeofResource(NULL, resourceInfo); - return true; - } }; diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/D3D12RaytracingHelloWorld.vcxproj b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/D3D12RaytracingHelloWorld.vcxproj index eeee2a18e..221cd7f26 100644 --- a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/D3D12RaytracingHelloWorld.vcxproj +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/D3D12RaytracingHelloWorld.vcxproj @@ -94,7 +94,7 @@ cd $(SolutionDir)..\tools\x64\ PostbuildCopy.bat $(SolutionDir)D3D12RaytracingHelloWorld\bin\$(Platform)\$(Configuration)\ - Sideload dxilcompiler.dll + Sideload required DXR binaries. @@ -147,7 +147,7 @@ PrebuildCheck.bat cd $(SolutionDir)..\tools\x64\ PostbuildCopy.bat $(SolutionDir)D3D12RaytracingHelloWorld\bin\$(Platform)\$(Configuration)\ - Sideload dxilcompiler.dll + Sideload required DXR binaries. cd $(SolutionDir)..\tools\x64\ @@ -159,6 +159,8 @@ PrebuildCheck.bat + + @@ -181,8 +183,8 @@ PrebuildCheck.bat Document - $(SolutionDir)..\tools\x64\dxc.exe -nologo -Fh "$(IntDir)\CompiledShaders\%(Filename).hlsl.h" -Vn g_p%(Filename) -T lib_6_1 "%(Identity)" - $(SolutionDir)..\tools\x64\dxc.exe -nologo -Fh "$(IntDir)\CompiledShaders\%(Filename).hlsl.h" -Vn g_p%(Filename) -T lib_6_1 "%(Identity)" + $(SolutionDir)..\tools\x64\dxc.exe -nologo -Zpr -Fh "$(IntDir)\CompiledShaders\%(Filename).hlsl.h" -Vn g_p%(Filename) -T lib_6_1 "%(Identity)" + $(SolutionDir)..\tools\x64\dxc.exe -nologo -Zpr -Fh "$(IntDir)\CompiledShaders\%(Filename).hlsl.h" -Vn g_p%(Filename) -T lib_6_1 "%(Identity)" %(Identity) %(Identity) $(IntDir)\CompiledShaders\%(Filename).hlsl.h diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/D3D12RaytracingHelloWorld.vcxproj.filters b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/D3D12RaytracingHelloWorld.vcxproj.filters index b2a5390da..43afca636 100644 --- a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/D3D12RaytracingHelloWorld.vcxproj.filters +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/D3D12RaytracingHelloWorld.vcxproj.filters @@ -19,6 +19,9 @@ {93d5e482-943e-4182-8023-5676929aec55} + + {c15c4672-ead7-4924-ba50-264ec192fdd8} + @@ -48,6 +51,12 @@ Header Files + + Assets\Shaders\Util + + + Assets\Shaders + diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/DXSampleHelper.h b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/DXSampleHelper.h index 9e6d33b65..defcf96da 100644 --- a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/DXSampleHelper.h +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/DXSampleHelper.h @@ -17,7 +17,6 @@ // referenced by the GPU. using Microsoft::WRL::ComPtr; - class HrException : public std::runtime_error { inline std::string HrToString(HRESULT hr) diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/DeviceResources.cpp b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/DeviceResources.cpp index 4c89b9d68..561a8cd8e 100644 --- a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/DeviceResources.cpp +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/DeviceResources.cpp @@ -13,7 +13,6 @@ #include "DeviceResources.h" #include "Win32Application.h" -//using namespace DirectX; using namespace DX; using namespace std; diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/DeviceResources.h b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/DeviceResources.h index c4f930e24..2181f41fe 100644 --- a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/DeviceResources.h +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/DeviceResources.h @@ -85,6 +85,7 @@ namespace DX 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; } diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/DirectXRaytracingHelper.h b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/DirectXRaytracingHelper.h index 9142697ae..437f40651 100644 --- a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/DirectXRaytracingHelper.h +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/DirectXRaytracingHelper.h @@ -1,3 +1,14 @@ +//********************************************************* +// +// 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) @@ -196,11 +207,11 @@ inline void PrintStateObjectDesc(const D3D12_STATE_OBJECT_DESC* desc) // Enable experimental features and return if they are supported. // To test them being supported we need to check both their enablement as well as device creation afterwards. template -inline bool EnableD3D12ExperimentalFeatures(UUID (&experimentalFeatures)[N]) +inline bool EnableD3D12ExperimentalFeatures(UUID(&experimentalFeatures)[N]) { - ComPtr testDevice; - return SUCCEEDED(D3D12EnableExperimentalFeatures(N, experimentalFeatures, nullptr, nullptr)) - && SUCCEEDED(D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&testDevice))); + ComPtr testDevice; + return SUCCEEDED(D3D12EnableExperimentalFeatures(N, experimentalFeatures, nullptr, nullptr)) + && SUCCEEDED(D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&testDevice))); } // Enable experimental features required for compute-based raytracing fallback. diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/HlslCompat.h b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/HlslCompat.h new file mode 100644 index 000000000..ba11e750c --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/HlslCompat.h @@ -0,0 +1,16 @@ +//********************************************************* +// +// 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 + +typedef float3 XMFLOAT3; +typedef float4 XMVECTOR; +typedef float4x4 XMMATRIX; \ No newline at end of file diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/Raytracing.hlsl b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/Raytracing.hlsl index 6cecbee06..04e638ad4 100644 --- a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/Raytracing.hlsl +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/Raytracing.hlsl @@ -1,28 +1,30 @@ -RaytracingAccelerationStructure Scene : register(t0, space0); -RWTexture2D RenderTarget : register(u0); +//********************************************************* +// +// 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. +// +//********************************************************* -struct Viewport -{ - float2 topLeft; - float2 bottomRight; -}; +#include "RaytracingHlslCompat.h" -cbuffer RayGenConstantBuffer : register(b0) -{ - Viewport viewport; - Viewport stencil; -} +RaytracingAccelerationStructure Scene : register(t0, space0); +RWTexture2D RenderTarget : register(u0); +ConstantBuffer g_rayGenCB : register(b0); typedef BuiltInTriangleIntersectionAttributes MyAttributes; struct HitData { - uint index; + float4 color; }; bool IsInsideViewport(float2 p, Viewport viewport) { - return (p.x >= viewport.topLeft.x && p.x <= viewport.bottomRight.x) - && (p.y >= viewport.topLeft.y && p.y <= viewport.bottomRight.y); + return (p.x >= viewport.left && p.x <= viewport.right) + && (p.y >= viewport.top && p.y <= viewport.bottom); } [shader("raygeneration")] @@ -30,22 +32,22 @@ void MyRaygenShader() { float2 lerpValues = (float2)DispatchRaysIndex() / DispatchRaysDimensions(); - // Orthographic projection since we're raytracing in screen space + // Orthographic projection since we're raytracing in screen space. float3 rayDir = float3(0, 0, 1); float3 origin = float3( - lerp(viewport.topLeft.x, viewport.bottomRight.x, lerpValues.x), - lerp(viewport.topLeft.y, viewport.bottomRight.y, lerpValues.y), + lerp(g_rayGenCB.viewport.left, g_rayGenCB.viewport.right, lerpValues.x), + lerp(g_rayGenCB.viewport.top, g_rayGenCB.viewport.bottom, lerpValues.y), 0.0f); - if (IsInsideViewport(origin.xy, stencil)) + if (IsInsideViewport(origin.xy, g_rayGenCB.stencil)) { - // Cast rays - RayDesc myRay = { origin, - 0.01f, - rayDir, - 10000.0f }; - HitData payload = { 0 }; + // Trace the ray. + RayDesc myRay = { origin, 0.0f, rayDir, 10000.0f }; + HitData payload = { float4(0, 0, 0, 0) }; TraceRay(Scene, RAY_FLAG_CULL_BACK_FACING_TRIANGLES, ~0, 0, 1, 0, myRay, payload); + + // Write the raytraced color to the output texture. + RenderTarget[DispatchRaysIndex()] = payload.color; } else { @@ -58,11 +60,11 @@ void MyRaygenShader() void MyClosestHitShader(inout HitData payload : SV_RayPayload, in MyAttributes attr : SV_IntersectionAttributes) { float3 barycentrics = float3(1 - attr.barycentrics.x - attr.barycentrics.y, attr.barycentrics.x, attr.barycentrics.y); - RenderTarget[DispatchRaysIndex()] = float4(barycentrics, 1); + payload.color = float4(barycentrics, 1); } [shader("miss")] void MyMissShader(inout HitData payload : SV_RayPayload) { - RenderTarget[DispatchRaysIndex()] = float4(0, 0, 0, 1); + payload.color = float4(0, 0, 0, 1); } \ No newline at end of file diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/RaytracingHlslCompat.h b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/RaytracingHlslCompat.h new file mode 100644 index 000000000..a760a6a98 --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/RaytracingHlslCompat.h @@ -0,0 +1,26 @@ +//********************************************************* +// +// 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 + +struct Viewport +{ + float left; + float top; + float right; + float bottom; +}; + +struct RayGenConstantBuffer +{ + Viewport viewport; + Viewport stencil; +}; diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/readme.md b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/readme.md index 28d56776b..285902d49 100644 --- a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/readme.md +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingHelloWorld/readme.md @@ -28,9 +28,4 @@ The title bar of the sample provides runtime information: * 3 - select DirectX Raytracing API. ## Requirements -* [Visual Studio 2017](https://www.visualstudio.com/) with the [Windows 10 Fall Creators Update SDK](https://developer.microsoft.com/en-US/windows/downloads/windows-10-sdk) -* Compatible OS and SDK: - * Fallback Layer: Windows 10 with the Fall Creators Update. - * DirectX Raytracing: Windows 10 with spring 2018 Update and private D3D12 headers & dlls for DirectX Raytracing (available from directxtech site). -* A graphics card with retail DXIL capabilities. -* DXIL.dll should be pulled from the latest Windows SDK to accompany the compiler or enable Developer mode. \ No newline at end of file +* Consult the main [D3D12 Raytracing readme](../../readme.md) for the requirements. \ No newline at end of file diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingMiniEngineSample/ModelViewer_VS15.vcxproj b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingMiniEngineSample/ModelViewer_VS15.vcxproj index 91fc0ab79..c611a9281 100644 --- a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingMiniEngineSample/ModelViewer_VS15.vcxproj +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingMiniEngineSample/ModelViewer_VS15.vcxproj @@ -6,6 +6,13 @@ + true + + + true + + + true @@ -145,6 +152,7 @@ true + @@ -219,14 +227,17 @@ cd $(SolutionDir)..\tools\x64\ PostbuildCopy.bat $(SolutionDir)D3D12RaytracingMiniEngineSample\ + Sideload required DXR binaries. cd $(SolutionDir)..\tools\x64\ PostbuildCopy.bat $(SolutionDir)D3D12RaytracingMiniEngineSample\ + Sideload required DXR binaries. cd $(SolutionDir)..\tools\x64\ PostbuildCopy.bat $(SolutionDir)D3D12RaytracingMiniEngineSample\ + Sideload required DXR binaries. false diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingMiniEngineSample/ModelViewer_VS15.vcxproj.filters b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingMiniEngineSample/ModelViewer_VS15.vcxproj.filters index 2b19b9a33..1178a313d 100644 --- a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingMiniEngineSample/ModelViewer_VS15.vcxproj.filters +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingMiniEngineSample/ModelViewer_VS15.vcxproj.filters @@ -1,12 +1,6 @@  - - - - - - @@ -156,12 +150,6 @@ - - - - - - Shaders @@ -183,6 +171,18 @@ Shaders + + + + + + + + + + + + @@ -393,6 +393,7 @@ Shaders + diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingMiniEngineSample/readme.md b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingMiniEngineSample/readme.md index 0cd2abbcf..c211d23d2 100644 --- a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingMiniEngineSample/readme.md +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingMiniEngineSample/readme.md @@ -2,7 +2,7 @@ ![MiniEngine Screenshot](Screenshot.png) ## Raytracing Modifications -This is a modified version of MiniEngine that uses the D3D12 Raytracing Fallback Layer for a series of effects +This is a modified version of MiniEngine that uses the D3D12 Raytracing Fallback Layer for a series of effects. The keys '1'...'7' can also be used to cycle through different modes (or using Backspace to open up the MiniEngine and going to Application/Raytracing/RaytraceMode): * *Off* - [1] Full rasterization. @@ -27,3 +27,6 @@ The keys '1'...'7' can also be used to cycle through different modes (or using B * Currently only tested on the Compute-based Fallback Layer * Shadow pass is buggy due to incorrect ray generation * Mipmap calculation is incorrect and uses too low of an LOD for distant objects + +## Requirements +* Consult the main [D3D12 Raytracing readme](../../readme.md) for the requirements. diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/D3D12RaytracingSimpleLighting.cpp b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/D3D12RaytracingSimpleLighting.cpp new file mode 100644 index 000000000..64b503874 --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/D3D12RaytracingSimpleLighting.cpp @@ -0,0 +1,1036 @@ +//********************************************************* +// +// 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 "D3D12RaytracingSimpleLighting.h" +#include "DirectXRaytracingHelper.h" +#include "CompiledShaders\Raytracing.hlsl.h" + +using namespace std; +using namespace DX; + +const wchar_t* D3D12RaytracingSimpleLighting::c_hitGroupName = L"MyHitGroup"; +const wchar_t* D3D12RaytracingSimpleLighting::c_raygenShaderName = L"MyRaygenShader"; +const wchar_t* D3D12RaytracingSimpleLighting::c_closestHitShaderName = L"MyClosestHitShader"; +const wchar_t* D3D12RaytracingSimpleLighting::c_missShaderName = L"MyMissShader"; + +D3D12RaytracingSimpleLighting::D3D12RaytracingSimpleLighting(UINT width, UINT height, std::wstring name) : + DXSample(width, height, name), + m_raytracingOutputResourceUAVDescriptorHeapIndex(UINT_MAX), + m_curRotationAngleRad(0.0f) +{ + m_isDxrSupported = EnableRaytracing(); + if (!m_isDxrSupported) + { + OutputDebugString( + L"Could not enable raytracing driver (D3D12EnableExperimentalFeatures() failed).\n" \ + L"Possible reasons:\n" \ + L" 1) your OS is not in developer mode.\n" \ + L" 2) your GPU driver doesn't match the D3D12 runtime loaded by the app (d3d12.dll and friends).\n" \ + L" 3) your D3D12 runtime doesn't match the D3D12 headers used by your app (in particular, the GUID passed to D3D12EnableExperimentalFeatures).\n\n"); + + OutputDebugString(L"Enabling compute based fallback raytracing support.\n"); + ThrowIfFalse(EnableComputeRaytracingFallback(), L"Could not enable compute based fallback raytracing support (D3D12EnableExperimentalFeatures() failed).\n"); + } + + m_forceComputeFallback = false; + SelectRaytracingAPI(RaytracingAPI::FallbackLayer); + + m_deviceResources = std::make_unique( + DXGI_FORMAT_R8G8B8A8_UNORM, + DXGI_FORMAT_UNKNOWN, + FrameCount, + D3D_FEATURE_LEVEL_11_0, + DeviceResources::c_AllowTearing + ); + m_deviceResources->RegisterDeviceNotify(this); + + // Sample shows handling of use cases with tearing support, which is OS dependent and has been supported since Threshold II. + // Since the Fallback Layer requires Fall Creator's update (RS3), we don't need to handle non-tearing cases. + if (!m_deviceResources->IsTearingSupported()) + { + OutputDebugString(L"Sample must be run on an OS with tearing support.\n"); + exit(EXIT_FAILURE); + } + + UpdateForSizeChange(width, height); +} + +void D3D12RaytracingSimpleLighting::OnInit() +{ + m_deviceResources->SetWindow(Win32Application::GetHwnd(), m_width, m_height); + + m_deviceResources->CreateDeviceResources(); + m_deviceResources->CreateWindowSizeDependentResources(); + + InitializeScene(); + + CreateDeviceDependentResources(); + CreateWindowSizeDependentResources(); +} + +// Update camera matrices passed into the shader. +void D3D12RaytracingSimpleLighting::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 D3D12RaytracingSimpleLighting::InitializeScene() +{ + auto frameIndex = m_deviceResources->GetCurrentFrameIndex(); + + // Setup materials. + { + XMFLOAT4 cubeDiffuseColor = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f); + m_cubeCB.diffuseColor = XMLoadFloat4(&cubeDiffuseColor); + } + + // Setup camera. + { + // Initialize the view and projection inverse matrices. + m_eye = { 0.0f, 2.0f, -5.0f, 1.0f }; + m_at = { 0.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); + m_up = XMVector3Transform(m_up, rotate); + + UpdateCameraMatrices(); + } + + // Setup lights. + { + // Initialize the lighting parameters. + XMFLOAT4 lightPosition; + XMFLOAT4 lightAmbientColor; + XMFLOAT4 lightDiffuseColor; + + lightPosition = XMFLOAT4(0.0f, 1.8f, -3.0, 0.0f); + m_sceneCB[frameIndex].lightPosition = XMLoadFloat4(&lightPosition); + + lightAmbientColor = XMFLOAT4(0.5f, 0.5f, 0.5f, 1.0f); + m_sceneCB[frameIndex].lightAmbientColor = XMLoadFloat4(&lightAmbientColor); + + lightDiffuseColor = XMFLOAT4(0.5f, 0.0f, 0.0f, 1.0f); + 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 D3D12RaytracingSimpleLighting::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 D3D12RaytracingSimpleLighting::CreateDeviceDependentResources() +{ + CreateRaytracingDevice(); + CreateDescriptorHeap(); + BuildGeometry(); + CreateConstantBuffers(); + CreateRootSignatures(); + CreateRaytracingPipelineStateObject(); + CreateRaytracingOutputResource(); + BuildAccelerationStructures(); + BuildShaderTables(); +} + +void D3D12RaytracingSimpleLighting::SerializeAndCreateRaytracingRootSignature(D3D12_ROOT_SIGNATURE_DESC& desc, ComPtr* rootSig) +{ + auto device = m_deviceResources->GetD3DDevice(); + ComPtr blob; + ComPtr error; + + if (m_raytracingAPI == RaytracingAPI::FallbackLayer) + { + ThrowIfFailed(m_fallbackDevice->D3D12SerializeRootSignature(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &blob, &error), error ? static_cast(error->GetBufferPointer()) : nullptr); + ThrowIfFailed(m_fallbackDevice->CreateRootSignature(1, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(&(*rootSig)))); + } + else // DirectX Raytracing + { + 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 D3D12RaytracingSimpleLighting::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, 2, 1); // 2 static index and vertex buffers. + + 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]); + CD3DX12_ROOT_SIGNATURE_DESC globalRootSignatureDesc(ARRAYSIZE(rootParameters), rootParameters); + 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_DESCRIPTOR_RANGE UAVDescriptor; + CD3DX12_ROOT_PARAMETER rootParameters[LocalRootSignatureParams::Count]; + rootParameters[LocalRootSignatureParams::CubeConstantSlot].InitAsConstants(SizeOfInUint32(m_cubeCB), 1); + CD3DX12_ROOT_SIGNATURE_DESC localRootSignatureDesc(ARRAYSIZE(rootParameters), rootParameters); + localRootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_LOCAL_ROOT_SIGNATURE; + SerializeAndCreateRaytracingRootSignature(localRootSignatureDesc, &m_raytracingLocalRootSignature); + } +} + +void D3D12RaytracingSimpleLighting::CreateRaytracingDevice() +{ + auto device = m_deviceResources->GetD3DDevice(); + auto commandList = m_deviceResources->GetCommandList(); + + if (m_raytracingAPI == RaytracingAPI::FallbackLayer) + { + CreateRaytracingFallbackDeviceFlags createDeviceFlags = m_forceComputeFallback ? + CreateRaytracingFallbackDeviceFlags::ForceComputeFallback : + CreateRaytracingFallbackDeviceFlags::None; + ThrowIfFailed(D3D12CreateRaytracingFallbackDevice(device, createDeviceFlags, 0, IID_PPV_ARGS(&m_fallbackDevice))); + m_fallbackDevice->QueryRaytracingCommandList(commandList, IID_PPV_ARGS(&m_fallbackCommandList)); + } + else // DirectX Raytracing + { + ThrowIfFailed(device->QueryInterface(__uuidof(ID3D12DeviceRaytracingPrototype), &m_dxrDevice), L"Couldn't get DirectX Raytracing interface for the device.\n"); + ThrowIfFailed(commandList->QueryInterface(__uuidof(ID3D12CommandListRaytracingPrototype), &m_dxrCommandList), L"Couldn't get DirectX Raytracing interface for the command list.\n"); + } +} + +// 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 D3D12RaytracingSimpleLighting::CreateRaytracingPipelineStateObject() +{ + // 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 + CD3D12_STATE_OBJECT_DESC raytracingPipeline{ D3D12_STATE_OBJECT_TYPE_RAYTRACING_PIPELINE }; + + + // DXIL library + // This contains the shaders and their entrypoints for the state object. + // Since shaders are not considered a subobject, they need to be passed in via DXIL library subobjects. + auto lib = raytracingPipeline.CreateSubobject(); + D3D12_SHADER_BYTECODE libdxil = CD3DX12_SHADER_BYTECODE((void *)g_pRaytracing, ARRAYSIZE(g_pRaytracing)); + lib->SetDXILLibrary(&libdxil); + // Define which shader exports to surface from the library. + // If no shader exports are defined for a DXIL library subobject, all shaders will be surfaced. + // In this sample, this could be ommited for convenience since the sample uses all shaders in the library. + { + lib->DefineExport(c_raygenShaderName); + lib->DefineExport(c_closestHitShaderName); + lib->DefineExport(c_missShaderName); + } + + // Triangle hit group + // A hit group specifies closest hit, any hit and intersection shaders to be executed when a ray intersects the geometry's triangle/AABB. + // In this sample, we only use triangle geometry with a closest hit shader, so others are not set. + auto hitGroup = raytracingPipeline.CreateSubobject(); + hitGroup->SetClosestHitShaderImport(c_closestHitShaderName); + hitGroup->SetHitGroupExport(c_hitGroupName); + + // Shader config + // Defines the maximum sizes in bytes for the ray payload and attribute structure. + auto shaderConfig = raytracingPipeline.CreateSubobject(); + UINT payloadSize = sizeof(XMFLOAT4); // float4 pixelColor + 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. + auto localRootSignature = raytracingPipeline.CreateSubobject(); + localRootSignature->SetRootSignature(m_raytracingLocalRootSignature.Get()); + // Define explicit shader association for the local root signature. + // In this sample, this could be ommited for convenience since it matches the default association. + { + auto rootSignatureAssociation = raytracingPipeline.CreateSubobject(); + rootSignatureAssociation->SetSubobjectToAssociate(*localRootSignature); + rootSignatureAssociation->AddExport(c_raygenShaderName); + rootSignatureAssociation->AddExport(c_missShaderName); + rootSignatureAssociation->AddExport(c_hitGroupName); + } + + // 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(); + // Setting max recursion depth at 1 ~ primary rays only. + // Drivers may apply optimization strategies for low recursion depths, + // so it is recommended to set max recursion depth as low as needed. + pipelineConfig->Config(1); + +#if _DEBUG + PrintStateObjectDesc(raytracingPipeline); +#endif + + // Create the state object. + if (m_raytracingAPI == RaytracingAPI::FallbackLayer) + { + ThrowIfFailed(m_fallbackDevice->CreateStateObject(raytracingPipeline, IID_PPV_ARGS(&m_fallbackStateObject)), L"Couldn't create DirectX Raytracing state object.\n"); + } + else // DirectX Raytracing + { + 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 D3D12RaytracingSimpleLighting::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 D3D12RaytracingSimpleLighting::CreateDescriptorHeap() +{ + auto device = m_deviceResources->GetD3DDevice(); + + D3D12_DESCRIPTOR_HEAP_DESC descriptorHeapDesc = {}; + // Allocate a heap for 5 descriptors: + // 2 - vertex and index buffer SRVs + // 1 - raytracing output texture SRV + // 2 - bottom and top level acceleration structure fallback wrapped pointer UAVs + descriptorHeapDesc.NumDescriptors = 5; + 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 D3D12RaytracingSimpleLighting::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); + + CreateBufferSRV(&m_indexBuffer, sizeof(indices)/4, sizeof(UINT)); + CreateBufferSRV(&m_vertexBuffer, ARRAYSIZE(vertices), sizeof(vertices[0])); +} + +// Build acceleration structures needed for raytracing. +void D3D12RaytracingSimpleLighting::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); + + D3D12_RAYTRACING_GEOMETRY_DESC geometryDesc = {}; + geometryDesc.Type = D3D12_RAYTRACING_GEOMETRY_TYPE_TRIANGLES; + geometryDesc.Triangles.IndexBuffer = m_indexBuffer.resource->GetGPUVirtualAddress(); + geometryDesc.Triangles.IndexCount = static_cast(m_indexBuffer.resource->GetDesc().Width) / sizeof(Index); + geometryDesc.Triangles.IndexFormat = DXGI_FORMAT_R16_UINT; + geometryDesc.Triangles.Transform = 0; + geometryDesc.Triangles.VertexFormat = DXGI_FORMAT_R32G32B32_FLOAT; + geometryDesc.Triangles.VertexCount = static_cast(m_vertexBuffer.resource->GetDesc().Width) / sizeof(Vertex); + geometryDesc.Triangles.VertexBuffer.StartAddress = m_vertexBuffer.resource->GetGPUVirtualAddress(); + geometryDesc.Triangles.VertexBuffer.StrideInBytes = sizeof(Vertex); + + // Get required sizes for an acceleration structure. + D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAGS buildFlags = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_PREFER_FAST_TRACE; + D3D12_GET_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO_DESC prebuildInfoDesc = {}; + prebuildInfoDesc.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY; + prebuildInfoDesc.Flags = buildFlags; + prebuildInfoDesc.NumDescs = 1; + + D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO topLevelPrebuildInfo = {}; + prebuildInfoDesc.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL; + prebuildInfoDesc.pGeometryDescs = nullptr; + if (m_raytracingAPI == RaytracingAPI::FallbackLayer) + { + m_fallbackDevice->GetRaytracingAccelerationStructurePrebuildInfo(&prebuildInfoDesc, &topLevelPrebuildInfo); + } + else // DirectX Raytracing + { + m_dxrDevice->GetRaytracingAccelerationStructurePrebuildInfo(&prebuildInfoDesc, &topLevelPrebuildInfo); + } + ThrowIfFalse(topLevelPrebuildInfo.ResultDataMaxSizeInBytes > 0); + + D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO bottomLevelPrebuildInfo = {}; + prebuildInfoDesc.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL; + prebuildInfoDesc.pGeometryDescs = &geometryDesc; + if (m_raytracingAPI == RaytracingAPI::FallbackLayer) + { + m_fallbackDevice->GetRaytracingAccelerationStructurePrebuildInfo(&prebuildInfoDesc, &bottomLevelPrebuildInfo); + } + else // DirectX Raytracing + { + m_dxrDevice->GetRaytracingAccelerationStructurePrebuildInfo(&prebuildInfoDesc, &bottomLevelPrebuildInfo); + } + ThrowIfFalse(bottomLevelPrebuildInfo.ResultDataMaxSizeInBytes > 0); + + ComPtr scratchResource; + AllocateUAVBuffer(device, max(topLevelPrebuildInfo.ScratchDataSizeInBytes, bottomLevelPrebuildInfo.ScratchDataSizeInBytes), &scratchResource, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, L"ScratchResource"); + + // Create resources for acceleration structures. + { + D3D12_RESOURCE_STATES initialResourceState; + if (m_raytracingAPI == RaytracingAPI::FallbackLayer) + { + initialResourceState = m_fallbackDevice->GetAccelerationStructureResourceState(); + } + else // DirectX Raytracing + { + initialResourceState = D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE; + } + + AllocateUAVBuffer(device, bottomLevelPrebuildInfo.ResultDataMaxSizeInBytes, &m_bottomLevelAccelerationStructure, initialResourceState, L"BottomLevelAccelerationStructure"); + AllocateUAVBuffer(device, topLevelPrebuildInfo.ResultDataMaxSizeInBytes, &m_topLevelAccelerationStructure, initialResourceState, L"TopLevelAccelerationStructure"); + } + + // Create an instance desc for the bottom level acceleration structure. + ComPtr instanceDescs; + if (m_raytracingAPI == RaytracingAPI::FallbackLayer) + { + D3D12_RAYTRACING_FALLBACK_INSTANCE_DESC instanceDesc = {}; + instanceDesc.Transform[0] = instanceDesc.Transform[5] = instanceDesc.Transform[10] = 1; + instanceDesc.InstanceMask = 1; + UINT numBufferElements = static_cast(bottomLevelPrebuildInfo.ResultDataMaxSizeInBytes) / sizeof(UINT32); + instanceDesc.AccelerationStructure = CreateFallbackWrappedPointer(m_bottomLevelAccelerationStructure.Get(), numBufferElements); + AllocateUploadBuffer(device, &instanceDesc, sizeof(instanceDesc), &instanceDescs, L"InstanceDescs"); + } + else // DirectX Raytracing + { + D3D12_RAYTRACING_INSTANCE_DESC instanceDesc = {}; + instanceDesc.Transform[0] = instanceDesc.Transform[5] = instanceDesc.Transform[10] = 1; + instanceDesc.InstanceMask = 1; + instanceDesc.AccelerationStructure = m_bottomLevelAccelerationStructure->GetGPUVirtualAddress(); + AllocateUploadBuffer(device, &instanceDesc, sizeof(instanceDesc), &instanceDescs, L"InstanceDescs"); + } + + // Create a wrapped pointer to the acceleration structure. + if (m_raytracingAPI == RaytracingAPI::FallbackLayer) + { + UINT numBufferElements = static_cast(topLevelPrebuildInfo.ResultDataMaxSizeInBytes) / sizeof(UINT32); + m_fallbackTopLevelAccelerationStructurePointer = CreateFallbackWrappedPointer(m_topLevelAccelerationStructure.Get(), numBufferElements); + } + + // Bottom Level Acceleration Structure desc + D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC bottomLevelBuildDesc = {}; + { + bottomLevelBuildDesc.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY; + bottomLevelBuildDesc.Flags = buildFlags; + bottomLevelBuildDesc.ScratchAccelerationStructureData = { scratchResource->GetGPUVirtualAddress(), scratchResource->GetDesc().Width }; + bottomLevelBuildDesc.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL; + bottomLevelBuildDesc.DestAccelerationStructureData = { m_bottomLevelAccelerationStructure->GetGPUVirtualAddress(), bottomLevelPrebuildInfo.ResultDataMaxSizeInBytes }; + bottomLevelBuildDesc.NumDescs = 1; + bottomLevelBuildDesc.pGeometryDescs = &geometryDesc; + } + + // Top Level Acceleration Structure desc + D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC topLevelBuildDesc = bottomLevelBuildDesc; + { + topLevelBuildDesc.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL; + topLevelBuildDesc.DestAccelerationStructureData = { m_topLevelAccelerationStructure->GetGPUVirtualAddress(), topLevelPrebuildInfo.ResultDataMaxSizeInBytes }; + topLevelBuildDesc.NumDescs = 1; + topLevelBuildDesc.pGeometryDescs = nullptr; + topLevelBuildDesc.InstanceDescs = instanceDescs->GetGPUVirtualAddress(); + topLevelBuildDesc.ScratchAccelerationStructureData = { scratchResource->GetGPUVirtualAddress(), scratchResource->GetDesc().Width }; + } + + auto BuildAccelerationStructure = [&](auto* raytracingCommandList) + { + raytracingCommandList->BuildRaytracingAccelerationStructure(&bottomLevelBuildDesc); + commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::UAV(m_bottomLevelAccelerationStructure.Get())); + raytracingCommandList->BuildRaytracingAccelerationStructure(&topLevelBuildDesc); + }; + + // Build acceleration structure. + if (m_raytracingAPI == RaytracingAPI::FallbackLayer) + { + // Set the descriptor heaps to be used during acceleration structure build for the Fallback Layer. + ID3D12DescriptorHeap *pDescriptorHeaps[] = { m_descriptorHeap.Get() }; + m_fallbackCommandList->SetDescriptorHeaps(ARRAYSIZE(pDescriptorHeaps), pDescriptorHeaps); + BuildAccelerationStructure(m_fallbackCommandList.Get()); + } + else // DirectX Raytracing + { + 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 D3D12RaytracingSimpleLighting::BuildShaderTables() +{ + auto device = m_deviceResources->GetD3DDevice(); + + void* rayGenShaderIdentifier; + void* missShaderIdentifier; + void* hitGroupShaderIdentifier; + + auto GetShaderIdentifiers = [&](auto* stateObjectProperties) + { + rayGenShaderIdentifier = stateObjectProperties->GetShaderIdentifier(c_raygenShaderName); + missShaderIdentifier = stateObjectProperties->GetShaderIdentifier(c_missShaderName); + hitGroupShaderIdentifier = stateObjectProperties->GetShaderIdentifier(c_hitGroupName); + }; + + // Get shader identifiers. + UINT shaderIdentifierSize; + if (m_raytracingAPI == RaytracingAPI::FallbackLayer) + { + GetShaderIdentifiers(m_fallbackStateObject.Get()); + shaderIdentifierSize = m_fallbackDevice->GetShaderIdentifierSize(); + } + else // DirectX Raytracing + { + ComPtr stateObjectProperties; + ThrowIfFailed(m_dxrStateObject.As(&stateObjectProperties)); + GetShaderIdentifiers(stateObjectProperties.Get()); + shaderIdentifierSize = m_dxrDevice->GetShaderIdentifierSize(); + } + + // Initialize shader records. + assert(LocalRootSignatureParams::CubeConstantSlot == 0 && LocalRootSignatureParams::Count == 1); + struct RootArguments { + CubeConstantBuffer cb; + } rootArguments; + rootArguments.cb = m_cubeCB; + UINT rootArgumentsSize = sizeof(rootArguments); + + // Shader record = {{ Shader ID }, { RootArguments }} + UINT shaderRecordSize = shaderIdentifierSize + rootArgumentsSize; + + ShaderRecord rayGenShaderRecord(rayGenShaderIdentifier, shaderIdentifierSize, &rootArguments, rootArgumentsSize); + rayGenShaderRecord.AllocateAsUploadBuffer(device, &m_rayGenShaderTable, L"RayGenShaderTable"); + + ShaderRecord missShaderRecord(missShaderIdentifier, shaderIdentifierSize, &rootArguments, rootArgumentsSize); + missShaderRecord.AllocateAsUploadBuffer(device, &m_missShaderTable, L"MissShaderTable"); + + ShaderRecord hitGroupShaderRecord(hitGroupShaderIdentifier, shaderIdentifierSize, &rootArguments, rootArgumentsSize); + hitGroupShaderRecord.AllocateAsUploadBuffer(device, &m_hitGroupShaderTable, L"HitGroupShaderTable"); +} + +void D3D12RaytracingSimpleLighting::SelectRaytracingAPI(RaytracingAPI type) +{ + if (type == RaytracingAPI::FallbackLayer) + { + m_raytracingAPI = type; + } + else // DirectX Raytracing + { + if (m_isDxrSupported) + { + m_raytracingAPI = type; + } + else + { + OutputDebugString(L"Invalid selection - DXR is not available.\n"); + } + } +} + +void D3D12RaytracingSimpleLighting::OnKeyDown(UINT8 key) +{ + // Store previous values. + RaytracingAPI previousRaytracingAPI = m_raytracingAPI; + bool previousForceComputeFallback = m_forceComputeFallback; + + switch (key) + { + case '1': // Fallback Layer + m_forceComputeFallback = false; + SelectRaytracingAPI(RaytracingAPI::FallbackLayer); + break; + case '2': // Fallback Layer + force compute path + m_forceComputeFallback = true; + SelectRaytracingAPI(RaytracingAPI::FallbackLayer); + break; + case '3': // DirectX Raytracing + SelectRaytracingAPI(RaytracingAPI::DirectXRaytracing); + break; + default: + break; + } + + if (m_raytracingAPI != previousRaytracingAPI || + m_forceComputeFallback != previousForceComputeFallback) + { + // Raytracing API selection changed, recreate everything. + RecreateD3D(); + } +} + +// Update frame-based values. +void D3D12RaytracingSimpleLighting::OnUpdate() +{ + m_timer.Tick(); + CalculateFrameStats(); + float elapsedTime = static_cast(m_timer.GetElapsedSeconds()); + auto frameIndex = m_deviceResources->GetCurrentFrameIndex(); + auto prevFrameIndex = m_deviceResources->GetPreviousFrameIndex(); + + // Rotate the camera around Y axis. + { + float secondsToRotateAround = 24.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 the origin + { + 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); + } +} + + +// Parse supplied command line args. +void D3D12RaytracingSimpleLighting::ParseCommandLineArgs(WCHAR* argv[], int argc) +{ + if (argc > 1) + { + if (_wcsnicmp(argv[1], L"-FL", wcslen(argv[1])) == 0 ) + { + m_forceComputeFallback = true; + SelectRaytracingAPI(RaytracingAPI::FallbackLayer); + } + else if (_wcsnicmp(argv[1], L"-DXR", wcslen(argv[1])) == 0) + { + SelectRaytracingAPI(RaytracingAPI::DirectXRaytracing); + } + } +} + +void D3D12RaytracingSimpleLighting::DoRaytracing() +{ + auto commandList = m_deviceResources->GetCommandList(); + auto frameIndex = m_deviceResources->GetCurrentFrameIndex(); + + auto DispatchRays = [&](auto* commandList, auto* stateObject, auto* dispatchDesc) + { + dispatchDesc->HitGroupTable.StartAddress = m_hitGroupShaderTable->GetGPUVirtualAddress(); + dispatchDesc->HitGroupTable.SizeInBytes = m_hitGroupShaderTable->GetDesc().Width; + dispatchDesc->HitGroupTable.StrideInBytes = dispatchDesc->HitGroupTable.SizeInBytes; + dispatchDesc->MissShaderTable.StartAddress = m_missShaderTable->GetGPUVirtualAddress(); + dispatchDesc->MissShaderTable.SizeInBytes = m_missShaderTable->GetDesc().Width; + dispatchDesc->MissShaderTable.StrideInBytes = dispatchDesc->MissShaderTable.SizeInBytes; + dispatchDesc->RayGenerationShaderRecord.StartAddress = m_rayGenShaderTable->GetGPUVirtualAddress(); + dispatchDesc->RayGenerationShaderRecord.SizeInBytes = m_rayGenShaderTable->GetDesc().Width; + dispatchDesc->Width = m_width; + dispatchDesc->Height = m_height; + commandList->DispatchRays(stateObject, 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. + if (m_raytracingAPI == RaytracingAPI::FallbackLayer) + { + D3D12_FALLBACK_DISPATCH_RAYS_DESC dispatchDesc = {}; + SetCommonPipelineState(m_fallbackCommandList.Get()); + m_fallbackCommandList->SetTopLevelAccelerationStructure(GlobalRootSignatureParams::AccelerationStructureSlot, m_fallbackTopLevelAccelerationStructurePointer); + DispatchRays(m_fallbackCommandList.Get(), m_fallbackStateObject.Get(), &dispatchDesc); + } + else // DirectX Raytracing + { + 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 D3D12RaytracingSimpleLighting::UpdateForSizeChange(UINT width, UINT height) +{ + DXSample::UpdateForSizeChange(width, height); +} + +// Copy the raytracing output to the backbuffer. +void D3D12RaytracingSimpleLighting::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 D3D12RaytracingSimpleLighting::CreateWindowSizeDependentResources() +{ + CreateRaytracingOutputResource(); + UpdateCameraMatrices(); +} + +// Release resources that are dependent on the size of the main window. +void D3D12RaytracingSimpleLighting::ReleaseWindowSizeDependentResources() +{ + m_raytracingOutput.Reset(); +} + +// Release all resources that depend on the device. +void D3D12RaytracingSimpleLighting::ReleaseDeviceDependentResources() +{ + m_fallbackDevice.Reset(); + m_fallbackCommandList.Reset(); + m_fallbackStateObject.Reset(); + 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_bottomLevelAccelerationStructure.Reset(); + m_topLevelAccelerationStructure.Reset(); + +} + +void D3D12RaytracingSimpleLighting::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 D3D12RaytracingSimpleLighting::OnRender() +{ + if (!m_deviceResources->IsWindowVisible()) + { + return; + } + + m_deviceResources->Prepare(); + + DoRaytracing(); + + CopyRaytracingOutputToBackbuffer(); + + m_deviceResources->Present(D3D12_RESOURCE_STATE_PRESENT); +} + +void D3D12RaytracingSimpleLighting::OnDestroy() +{ + OnDeviceLost(); +} + +// Release all device dependent resouces when a device is lost. +void D3D12RaytracingSimpleLighting::OnDeviceLost() +{ + ReleaseWindowSizeDependentResources(); + ReleaseDeviceDependentResources(); +} + +// Create all device dependent resources when a device is restored. +void D3D12RaytracingSimpleLighting::OnDeviceRestored() +{ + CreateDeviceDependentResources(); + CreateWindowSizeDependentResources(); +} + +// Compute the average frames per second and million rays per second. +void D3D12RaytracingSimpleLighting::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; + + if (m_raytracingAPI == RaytracingAPI::FallbackLayer) + { + if (m_fallbackDevice->UsingRaytracingDriver()) + { + windowText << L"(FL-DXR)"; + } + else + { + windowText << L"(FL)"; + } + } + else + { + windowText << L"(DXR)"; + } + windowText << setprecision(2) << fixed + << L" fps: " << fps << L" ~Million Primary Rays/s: " << MRaysPerSecond; + SetCustomWindowText(windowText.str().c_str()); + } +} + +// Handle OnSizeChanged message event. +void D3D12RaytracingSimpleLighting::OnSizeChanged(UINT width, UINT height, bool minimized) +{ + if (!m_deviceResources->WindowSizeChanged(width, height, minimized)) + { + return; + } + + UpdateForSizeChange(width, height); + + ReleaseWindowSizeDependentResources(); + CreateWindowSizeDependentResources(); +} + +// Create a wrapped pointer for the Fallback Layer path. +WRAPPED_GPU_POINTER D3D12RaytracingSimpleLighting::CreateFallbackWrappedPointer(ID3D12Resource* resource, UINT bufferNumElements) +{ + auto device = m_deviceResources->GetD3DDevice(); + + D3D12_UNORDERED_ACCESS_VIEW_DESC rawBufferUavDesc = {}; + rawBufferUavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER; + rawBufferUavDesc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_RAW; + rawBufferUavDesc.Format = DXGI_FORMAT_R32_TYPELESS; + rawBufferUavDesc.Buffer.NumElements = bufferNumElements; + + D3D12_CPU_DESCRIPTOR_HANDLE bottomLevelDescriptor; + + // Only compute fallback requires a valid descriptor index when creating a wrapped pointer. + UINT descriptorHeapIndex = 0; + if (!m_fallbackDevice->UsingRaytracingDriver()) + { + descriptorHeapIndex = AllocateDescriptor(&bottomLevelDescriptor); + device->CreateUnorderedAccessView(resource, nullptr, &rawBufferUavDesc, bottomLevelDescriptor); + } + return m_fallbackDevice->GetWrappedPointerSimple(descriptorHeapIndex, resource->GetGPUVirtualAddress()); +} + +// Allocate a descriptor and return its index. +// If the passed descriptorIndexToUse is valid, it will be used instead of allocating a new one. +UINT D3D12RaytracingSimpleLighting::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. +void D3D12RaytracingSimpleLighting::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.Format = DXGI_FORMAT_UNKNOWN; + srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE; + srvDesc.Buffer.NumElements = numElements; + 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); +}; \ No newline at end of file diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/D3D12RaytracingSimpleLighting.h b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/D3D12RaytracingSimpleLighting.h new file mode 100644 index 000000000..cf8fb386e --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/D3D12RaytracingSimpleLighting.h @@ -0,0 +1,166 @@ +//********************************************************* +// +// 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" + +namespace GlobalRootSignatureParams { + enum Value { + OutputViewSlot = 0, + AccelerationStructureSlot, + SceneConstantSlot, + VertexBuffersSlot, + Count + }; +} + +namespace LocalRootSignatureParams { + enum Value { + CubeConstantSlot = 0, + Count + }; +} + +// The sample supports both Raytracing Fallback Layer and DirectX Raytracing APIs. +// This is purely for demonstration purposes to show where the API differences are. +// Real-world applications will implement only one or the other. +// Fallback Layer uses DirectX Raytracing if a driver and OS supports it. +// Otherwise, it falls back to compute pipeline to emulate raytracing. +// Developers aiming for a wider HW support should target Fallback Layer. +class D3D12RaytracingSimpleLighting : public DXSample +{ + enum class RaytracingAPI { + FallbackLayer, + DirectXRaytracing, + }; + +public: + D3D12RaytracingSimpleLighting(UINT width, UINT height, std::wstring name); + + // IDeviceNotify + virtual void OnDeviceLost() override; + virtual void OnDeviceRestored() override; + + // Messages + virtual void OnInit(); + virtual void OnKeyDown(UINT8 key); + 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; + + // 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; + + // Raytracing Fallback Layer (FL) attributes + ComPtr m_fallbackDevice; + ComPtr m_fallbackCommandList; + ComPtr m_fallbackStateObject; + WRAPPED_GPU_POINTER m_fallbackTopLevelAccelerationStructurePointer; + + // DirectX Raytracing (DXR) attributes + ComPtr m_dxrDevice; + ComPtr m_dxrCommandList; + ComPtr m_dxrStateObject; + bool m_isDxrSupported; + + // Root signatures + ComPtr m_raytracingGlobalRootSignature; + ComPtr m_raytracingLocalRootSignature; + + // Descriptors + ComPtr m_descriptorHeap; + UINT m_descriptorsAllocated; + UINT m_descriptorSize; + + // Raytracing scene + SceneConstantBuffer m_sceneCB[FrameCount]; + CubeConstantBuffer m_cubeCB; + + // Geometry + struct D3DBuffer + { + ComPtr resource; + D3D12_CPU_DESCRIPTOR_HANDLE cpuDescriptorHandle; + D3D12_GPU_DESCRIPTOR_HANDLE gpuDescriptorHandle; + }; + D3DBuffer m_indexBuffer; + D3DBuffer m_vertexBuffer; + + // Acceleration structure + ComPtr m_bottomLevelAccelerationStructure; + 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_raygenShaderName; + static const wchar_t* c_closestHitShaderName; + static const wchar_t* c_missShaderName; + ComPtr m_missShaderTable; + ComPtr m_hitGroupShaderTable; + ComPtr m_rayGenShaderTable; + + // Application state + RaytracingAPI m_raytracingAPI; + bool m_forceComputeFallback; + StepTimer m_timer; + float m_curRotationAngleRad; + XMVECTOR m_eye; + XMVECTOR m_at; + XMVECTOR m_up; + + void ParseCommandLineArgs(WCHAR* argv[], int argc); + void UpdateCameraMatrices(); + void InitializeScene(); + void RecreateD3D(); + void DoRaytracing(); + void CreateConstantBuffers(); + void CreateDeviceDependentResources(); + void CreateWindowSizeDependentResources(); + void ReleaseDeviceDependentResources(); + void ReleaseWindowSizeDependentResources(); + void CreateRaytracingDevice(); + void SerializeAndCreateRaytracingRootSignature(D3D12_ROOT_SIGNATURE_DESC& desc, ComPtr* rootSig); + void CreateRootSignatures(); + void CreateRaytracingPipelineStateObject(); + void CreateDescriptorHeap(); + void CreateRaytracingOutputResource(); + void BuildGeometry(); + void BuildAccelerationStructures(); + void BuildShaderTables(); + void SelectRaytracingAPI(RaytracingAPI type); + void UpdateForSizeChange(UINT clientWidth, UINT clientHeight); + void CopyRaytracingOutputToBackbuffer(); + void CalculateFrameStats(); + UINT AllocateDescriptor(D3D12_CPU_DESCRIPTOR_HANDLE* cpuDescriptor, UINT descriptorIndexToUse = UINT_MAX); + void CreateBufferSRV(D3DBuffer* buffer, UINT numElements, UINT elementSize); + WRAPPED_GPU_POINTER CreateFallbackWrappedPointer(ID3D12Resource* resource, UINT bufferNumElements); +}; diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/D3D12RaytracingSimpleLighting.vcxproj b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/D3D12RaytracingSimpleLighting.vcxproj new file mode 100644 index 000000000..e84fcbe98 --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/D3D12RaytracingSimpleLighting.vcxproj @@ -0,0 +1,214 @@ + + + + $(SolutionDir)..\DxilCompiler\x64 + + + + Debug + x64 + + + Release + x64 + + + + {A0848C98-F5AA-431C-9E76-7F1E7EFA368C} + Win32Proj + D3D12Raytracing + D3D12RaytracingSimpleLighting + 10.0.15063.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + true + bin\$(Platform)\$(Configuration)\ + obj\$(Platform)\$(Configuration)\ + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(SolutionDir)..\Build_VS15\$(Platform)\$(Configuration)\Output\$(ProjectName) + true + + + true + + + false + bin\$(Platform)\$(Configuration)\ + obj\$(Platform)\$(Configuration)\ + $(VC_IncludePath);$(WindowsSDK_IncludePath);$(SolutionDir)..\Build_VS15\$(Platform)\$(Configuration)\Output\$(ProjectName) + true + true + + + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + ..\..\..\..\..\Libraries\D3D12RaytracingFallback\Include;$(IntDir);%(AdditionalIncludeDirectories) + false + + + + + Windows + true + d3d12.lib;dxgi.lib;dxguid.lib;$(SolutionDir)..\Build_VS15\$(Platform)\$(Configuration)\Output\fallbacklayer\fallbacklayer.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 + + + + + + cd $(SolutionDir)..\tools\x64\ +PostbuildCopy.bat $(SolutionDir)D3D12RaytracingSimpleLighting\bin\$(Platform)\$(Configuration)\ + Sideload required DXR binaries. + + + + + + + + + + + cd $(SolutionDir)..\tools\x64\ +PrebuildCheck.bat + + + + + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + ..\..\..\..\..\Libraries\D3D12RaytracingFallback\Include;$(IntDir);%(AdditionalIncludeDirectories) + false + + + Windows + true + true + true + d3d12.lib;dxgi.lib;$(SolutionDir)..\Build_VS15\$(Platform)\$(Configuration)\Output\fallbacklayer\fallbacklayer.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 + + + + + + cd $(SolutionDir)..\tools\x64\ +PostbuildCopy.bat $(SolutionDir)D3D12RaytracingSimpleLighting\bin\$(Platform)\$(Configuration)\ + Sideload required DXR binaries. + + + cd $(SolutionDir)..\tools\x64\ +PrebuildCheck.bat + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + + + + + Document + $(SolutionDir)..\tools\x64\dxc.exe -nologo -Zpr -Fh "$(IntDir)\CompiledShaders\%(Filename).hlsl.h" -Vn g_p%(Filename) -T lib_6_1 "%(Identity)" + $(SolutionDir)..\tools\x64\dxc.exe -nologo -Zpr -Fh "$(IntDir)\CompiledShaders\%(Filename).hlsl.h" -Vn g_p%(Filename) -T lib_6_1 "%(Identity)" + %(Identity) + %(Identity) + $(IntDir)\CompiledShaders\%(Filename).hlsl.h + false + $(IntDir)\CompiledShaders\%(Filename).hlsl.h + false + false + false + + + + + + + + + \ No newline at end of file diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/D3D12RaytracingSimpleLighting.vcxproj.filters b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/D3D12RaytracingSimpleLighting.vcxproj.filters new file mode 100644 index 000000000..774b78db3 --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/D3D12RaytracingSimpleLighting.vcxproj.filters @@ -0,0 +1,89 @@ + + + + + {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 + + + + + Assets\Shaders + + + + + + \ No newline at end of file diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/DXSample.cpp b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/DXSample.cpp new file mode 100644 index 000000000..20d7b9ede --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/DXSample.cpp @@ -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. +// +//********************************************************* + +#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) +{ + 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) + { + if (_wcsnicmp(argv[i], L"-disableUI", wcslen(argv[i])) == 0 || + _wcsnicmp(argv[i], L"/disableUI", wcslen(argv[i])) == 0) + { + m_enableUI = false; + } + } +} + +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/D3D12RaytracingSimpleLighting/DXSample.h b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/DXSample.h new file mode 100644 index 000000000..3e76fdef0 --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/DXSample.h @@ -0,0 +1,77 @@ +//********************************************************* +// +// 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 + 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/D3D12RaytracingSimpleLighting/DXSampleHelper.h b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/DXSampleHelper.h new file mode 100644 index 000000000..defcf96da --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/DXSampleHelper.h @@ -0,0 +1,214 @@ +//********************************************************* +// +// 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(); + } +} diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/DeviceResources.cpp b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/DeviceResources.cpp new file mode 100644 index 000000000..f637172e5 --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/DeviceResources.cpp @@ -0,0 +1,635 @@ +//********************************************************* +// +// 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, unsigned int flags) : + 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) +{ + 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"); + } +} + +// Destructor for DeviceResources. +DeviceResources::~DeviceResources() +{ + // Ensure that the GPU is no longer referencing resources that are about to be destroyed. + WaitForGpu(); +} + +// Configures the Direct3D device, and stores handles to it and the device context. +void DeviceResources::CreateDeviceResources() +{ + 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) + { + 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) + { + m_options &= ~c_AllowTearing; + OutputDebugStringA("WARNING: Variable refresh rate displays are not supported.\n"); + } + } + + ComPtr adapter; + GetAdapter(&adapter); + + // Create the DX12 API device object. + ThrowIfFailed(D3D12CreateDevice(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(); + +#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 + + 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 available hardware adapter that supports Direct3D 12. +// If no such adapter can be found, try WARP. Otherwise throw an exception. +void DeviceResources::GetAdapter(IDXGIAdapter1** ppAdapter) +{ + *ppAdapter = nullptr; + + ComPtr adapter; + for (UINT adapterIndex = 0; DXGI_ERROR_NOT_FOUND != m_dxgiFactory->EnumAdapters1(adapterIndex, &adapter); ++adapterIndex) + { + 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))) + { +#ifdef _DEBUG + wchar_t buff[256] = {}; + swprintf_s(buff, L"Direct3D Adapter (%u): VID:%04X, PID:%04X - %ls\n", adapterIndex, desc.VendorId, desc.DeviceId, desc.Description); + OutputDebugStringW(buff); +#endif + break; + } + } + +#if !defined(NDEBUG) + if (!adapter) + { + // 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) + { + throw exception("No Direct3D 12 device found"); + } + + *ppAdapter = adapter.Detach(); +} \ No newline at end of file diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/DeviceResources.h b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/DeviceResources.h new file mode 100644 index 000000000..2181f41fe --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/DeviceResources.h @@ -0,0 +1,151 @@ +//********************************************************* +// +// 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 + { + public: + static const unsigned int c_AllowTearing = 0x1; + + 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, + unsigned int flags = 0); + ~DeviceResources(); + + 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. + 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; } + + 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 GetAdapter(IDXGIAdapter1** ppAdapter); + + const static size_t MAX_BACK_BUFFER_COUNT = 3; + + UINT m_backBufferIndex; + + // 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/D3D12RaytracingSimpleLighting/DirectXRaytracingHelper.h b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/DirectXRaytracingHelper.h new file mode 100644 index 000000000..437f40651 --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/DirectXRaytracingHelper.h @@ -0,0 +1,233 @@ +//********************************************************* +// +// 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) + +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) + { + } + UINT Size() { return shaderIdentifier.size + localRootArguments.size; } + + void AllocateAsUploadBuffer(ID3D12Device* pDevice, ID3D12Resource **ppResource, const wchar_t* resourceName = nullptr) + { + auto uploadHeapProperties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD); + UINT size = Align(Size(), D3D12_RAYTRACING_SHADER_RECORD_BYTE_ALIGNMENT); + auto bufferDesc = CD3DX12_RESOURCE_DESC::Buffer(size); + ThrowIfFailed(pDevice->CreateCommittedResource( + &uploadHeapProperties, + D3D12_HEAP_FLAG_NONE, + &bufferDesc, + D3D12_RESOURCE_STATE_GENERIC_READ, + nullptr, + IID_PPV_ARGS(ppResource))); + if (resourceName) + { + (*ppResource)->SetName(resourceName); + } + uint8_t *pMappedData; + (*ppResource)->Map(0, nullptr, reinterpret_cast(&pMappedData)); + memcpy(pMappedData, shaderIdentifier.ptr, shaderIdentifier.size); + memcpy(pMappedData + shaderIdentifier.size, localRootArguments.ptr, localRootArguments.size); + (*ppResource)->Unmap(0, nullptr); + } + + struct PointerWithSize { + void *ptr; + UINT size; + + PointerWithSize() : ptr(nullptr), size(0) {} + PointerWithSize(void* _ptr, UINT _size) : ptr(_ptr), size(_size) {}; + }; + PointerWithSize shaderIdentifier; + PointerWithSize localRootArguments; + +}; + +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); + } +} + +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"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++) + { + if (depth > 0) + { + for (UINT j = 0; j < 2 * depth - 1; j++) woss << L" "; + woss << (i == numExports - 1 ? L"\xC0" : L"\xC3"); + } + 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"]: "; + if (desc->pSubobjects[i].Type == D3D12_STATE_SUBOBJECT_TYPE_FLAGS) + { + wstr << L"Flags (not yet defined)\n"; + } + if (desc->pSubobjects[i].Type == D3D12_STATE_SUBOBJECT_TYPE_ROOT_SIGNATURE) + { + wstr << L"Root Signature 0x" << desc->pSubobjects[i].pDesc << L"\n"; + } + if (desc->pSubobjects[i].Type == D3D12_STATE_SUBOBJECT_TYPE_LOCAL_ROOT_SIGNATURE) + { + wstr << L"Local Root Signature 0x" << desc->pSubobjects[i].pDesc << L"\n"; + } + if (desc->pSubobjects[i].Type == 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"; + } + if (desc->pSubobjects[i].Type == D3D12_STATE_SUBOBJECT_TYPE_CACHED_STATE_OBJECT) + { + wstr << L"Cached State Object (not yet defined)\n"; + } + if (desc->pSubobjects[i].Type == 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); + } + if (desc->pSubobjects[i].Type == 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); + } + if (desc->pSubobjects[i].Type == 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 << (j == association->NumExports - 1 ? L" \xC0" : L" \xC3") << L"[" << j << L"]: " << association->pExports[j] << L"\n"; + } + if (desc->pSubobjects[i].Type == 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 << (j == association->NumExports - 1 ? L" \xC0" : L" \xC3") << L"[" << j << L"]: " << association->pExports[j] << L"\n"; + } + if (desc->pSubobjects[i].Type == D3D12_STATE_SUBOBJECT_TYPE_RAYTRACING_SHADER_CONFIG) + { + wstr << L"Raytracing Shader Config\n"; + auto config = static_cast(desc->pSubobjects[i].pDesc); + wstr << L" \xC3" << L"[0]: Max Payload Size: " << config->MaxPayloadSizeInBytes << L" bytes\n"; + wstr << L" \xC0" << L"[1]: Max Attribute Size: " << config->MaxAttributeSizeInBytes << L" bytes\n"; + } + if (desc->pSubobjects[i].Type == D3D12_STATE_SUBOBJECT_TYPE_RAYTRACING_PIPELINE_CONFIG) + { + wstr << L"Raytracing Pipeline Config\n"; + auto config = static_cast(desc->pSubobjects[i].pDesc); + wstr << L" \xC0" << L"[0]: Max Recursion Depth: " << config->MaxTraceRecursionDepth << L"\n"; + } + if (desc->pSubobjects[i].Type == 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" \xC3" << L"[0]: Any Hit Import: " << (hitGroup->AnyHitShaderImport ? hitGroup->AnyHitShaderImport : L"[none]") << L"\n"; + wstr << L" \xC3" << L"[1]: Closest Hit Import: " << (hitGroup->ClosestHitShaderImport ? hitGroup->ClosestHitShaderImport : L"[none]") << L"\n"; + wstr << L" \xC0" << L"[2]: Intersection Import: " << (hitGroup->IntersectionShaderImport ? hitGroup->IntersectionShaderImport : L"[none]") << L"\n"; + } + } + OutputDebugStringW(wstr.str().c_str()); +} + +// Enable experimental features and return if they are supported. +// To test them being supported we need to check both their enablement as well as device creation afterwards. +template +inline bool EnableD3D12ExperimentalFeatures(UUID(&experimentalFeatures)[N]) +{ + ComPtr testDevice; + return SUCCEEDED(D3D12EnableExperimentalFeatures(N, experimentalFeatures, nullptr, nullptr)) + && SUCCEEDED(D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&testDevice))); +} + +// Enable experimental features required for compute-based raytracing fallback. +// This will set active D3D12 devices to DEVICE_REMOVED state. +// Returns bool whether the call succeeded and the device supports the feature. +inline bool EnableComputeRaytracingFallback() +{ + UUID experimentalFeatures[] = { D3D12ExperimentalShaderModels }; + return EnableD3D12ExperimentalFeatures(experimentalFeatures); +} + +// Enable experimental features required for driver and compute-based fallback raytracing. +// This will set active D3D12 devices to DEVICE_REMOVED state. +// Returns bool whether the call succeeded and the device supports the feature. +inline bool EnableRaytracing() +{ + UUID experimentalFeatures[] = { D3D12ExperimentalShaderModels, D3D12RaytracingPrototype }; + return EnableD3D12ExperimentalFeatures(experimentalFeatures); +} \ No newline at end of file diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/HlslCompat.h b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/HlslCompat.h new file mode 100644 index 000000000..ba11e750c --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/HlslCompat.h @@ -0,0 +1,16 @@ +//********************************************************* +// +// 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 + +typedef float3 XMFLOAT3; +typedef float4 XMVECTOR; +typedef float4x4 XMMATRIX; \ No newline at end of file diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/Main.cpp b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/Main.cpp new file mode 100644 index 000000000..582c5099b --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/Main.cpp @@ -0,0 +1,20 @@ +//********************************************************* +// +// 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 "D3D12RaytracingSimpleLighting.h" + +_Use_decl_annotations_ +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow) +{ + D3D12RaytracingSimpleLighting sample(1280, 720, L"D3D12 Raytracing - Simple Lighting"); + return Win32Application::Run(&sample, hInstance, nCmdShow); +} diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/RayTracingHlslCompat.h b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/RayTracingHlslCompat.h new file mode 100644 index 000000000..7bf8f657b --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/RayTracingHlslCompat.h @@ -0,0 +1,41 @@ +//********************************************************* +// +// 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 + +#ifdef HLSL +#include "HlslCompat.h" +#else +using namespace DirectX; + +// Shader will use byte encoding to access indices. +typedef UINT16 Index; +#endif + +struct SceneConstantBuffer +{ + XMMATRIX projectionToWorld; + XMVECTOR cameraPosition; + XMVECTOR lightPosition; + XMVECTOR lightAmbientColor; + XMVECTOR lightDiffuseColor; +}; + +struct CubeConstantBuffer +{ + XMVECTOR diffuseColor; +}; + +struct Vertex +{ + XMFLOAT3 position; + XMFLOAT3 normal; +}; diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/Raytracing.hlsl b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/Raytracing.hlsl new file mode 100644 index 000000000..a1dbe70f4 --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/Raytracing.hlsl @@ -0,0 +1,158 @@ +//********************************************************* +// +// 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. +// +//********************************************************* + +#define HLSL +#include "RaytracingHlslCompat.h" + +RaytracingAccelerationStructure Scene : register(t0, space0); +RWTexture2D RenderTarget : register(u0); +ByteAddressBuffer Indices : register(t1, space0); +StructuredBuffer Vertices : register(t2, space0); + +ConstantBuffer g_sceneCB : register(b0); +ConstantBuffer g_cubeCB : register(b1); + +// Load three 16 bit indices from a byte addressed buffer. +uint3 Load3x16BitIndices(uint offsetBytes) +{ + uint3 indices; + + // ByteAdressBuffer loads must be aligned at a 4 byte boundary. + // Since we need to read three 16 bit indices: { 0, 1, 2 } + // aligned at a 4 byte boundary as: { 0 1 } { 2 0 } { 1 2 } { 0 1 } ... + // we will load 8 bytes (~ 4 indices { a b | c d }) to handle two possible index triplet layouts, + // based on first index's offsetBytes being aligned at the 4 byte boundary or not: + // Aligned: { 0 1 | 2 - } + // Not aligned: { - 0 | 1 2 } + const uint dwordAlignedOffset = offsetBytes & ~3; + const uint2 four16BitIndices = Indices.Load2(dwordAlignedOffset); + + // Aligned: { 0 1 | 2 - } => retrieve first three 16bit indices + if (dwordAlignedOffset == offsetBytes) + { + indices.x = four16BitIndices.x & 0xffff; + indices.y = (four16BitIndices.x >> 16) & 0xffff; + indices.z = four16BitIndices.y & 0xffff; + } + else // Not aligned: { - 0 | 1 2 } => retrieve last three 16bit indices + { + indices.x = (four16BitIndices.x >> 16) & 0xffff; + indices.y = four16BitIndices.y & 0xffff; + indices.z = (four16BitIndices.y >> 16) & 0xffff; + } + + return indices; +} + +typedef BuiltInTriangleIntersectionAttributes MyAttributes; +struct HitData +{ + float4 color; +}; + +// Retrieve hit world position. +float3 HitWorldPosition() +{ + return WorldRayOrigin() + RayTCurrent() * WorldRayDirection(); +} + +// Retrieve attribute at a hit position interpolated from vertex attributes using the hit's barycentrics. +float3 HitAttribute(float3 vertexAttribute[3], BuiltInTriangleIntersectionAttributes attr) +{ + return vertexAttribute[0] + + attr.barycentrics.x * (vertexAttribute[1] - vertexAttribute[0]) + + attr.barycentrics.y * (vertexAttribute[2] - vertexAttribute[0]); +} + +// Generate a ray in world space for a camera pixel corresponding to an index from the dispatched 2D grid. +inline void GenerateCameraRay(uint2 index, out float3 origin, out float3 direction) +{ + float2 xy = index + 0.5f; // center in the middle of the pixel. + float2 screenPos = xy / DispatchRaysDimensions() * 2.0 - 1.0; + + // Invert Y for DirectX-style coordinates. + screenPos.y = -screenPos.y; + + // Unproject the pixel coordinate into a ray. + float4 world = mul(float4(screenPos, 0, 1), g_sceneCB.projectionToWorld); + + world.xyz /= world.w; + origin = g_sceneCB.cameraPosition.xyz; + direction = normalize(world - origin); +} + +// Diffuse lighting calculation. +float4 CalculateDiffuseLighting(float3 hitPosition, float3 normal) +{ + float3 pixelToLight = normalize(g_sceneCB.lightPosition - hitPosition); + + // Diffuse contribution. + float fNDotL = max(0.0f, dot(pixelToLight, normal)); + + return g_cubeCB.diffuseColor * g_sceneCB.lightDiffuseColor * fNDotL; +} + +[shader("raygeneration")] +void MyRaygenShader() +{ + float3 rayDir; + float3 origin; + + // Generate a ray for a camera pixel corresponding to an index from the dispatched 2D grid. + GenerateCameraRay(DispatchRaysIndex(), origin, rayDir); + + // Trace the ray. + RayDesc myRay = { origin, 0.0f, rayDir, 10000.0f }; + HitData payload = { float4(0, 0, 0, 0) }; + TraceRay(Scene, RAY_FLAG_CULL_BACK_FACING_TRIANGLES, ~0, 0, 1, 0, myRay, payload); + + // Write the raytraced color to the output texture. + RenderTarget[DispatchRaysIndex()] = payload.color; +} + +[shader("closesthit")] +void MyClosestHitShader(inout HitData payload : SV_RayPayload, in MyAttributes attr : SV_IntersectionAttributes) +{ + float3 hitPosition = HitWorldPosition(); + + // Get the base index of the triangle's first 16 bit index. + uint indexSizeInBytes = 2; + uint indicesPerTriangle = 3; + uint triangleIndexStride = indicesPerTriangle * indexSizeInBytes; + uint baseIndex = PrimitiveIndex() * triangleIndexStride; + + // Load up 3 16 bit indices for the triangle. + const uint3 indices = Load3x16BitIndices(baseIndex); + + // Retrieve corresponding vertex normals for the triangle vertices. + float3 vertexNormals[3] = { + Vertices[indices[0]].normal, + Vertices[indices[1]].normal, + Vertices[indices[2]].normal + }; + + // Compute the triangle's normal. + // This is redundant and done for illustration purposes + // as all the per-vertex normals are the same and match triangle's normal in this sample. + float3 triangleNormal = HitAttribute(vertexNormals, attr); + + float4 diffuseColor = CalculateDiffuseLighting(hitPosition, triangleNormal); + float4 color = g_sceneCB.lightAmbientColor + diffuseColor; + + payload.color = color; +} + +[shader("miss")] +void MyMissShader(inout HitData payload : SV_RayPayload) +{ + float4 background = float4(0.0f, 0.2f, 0.4f, 1.0f); + payload.color = background; +} \ No newline at end of file diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/Screenshot.png b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/Screenshot.png new file mode 100644 index 000000000..db3fa0f2b Binary files /dev/null and b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/Screenshot.png differ diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/Screenshot_small.png b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/Screenshot_small.png new file mode 100644 index 000000000..83e4a304f Binary files /dev/null and b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/Screenshot_small.png differ diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/StepTimer.h b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/StepTimer.h new file mode 100644 index 000000000..aa50c422a --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/StepTimer.h @@ -0,0 +1,183 @@ +//********************************************************* +// +// 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 + +// Helper class for animation and simulation timing. +class StepTimer +{ +public: + StepTimer() : + m_elapsedTicks(0), + m_totalTicks(0), + m_leftOverTicks(0), + m_frameCount(0), + m_framesPerSecond(0), + m_framesThisSecond(0), + m_qpcSecondCounter(0), + m_isFixedTimeStep(false), + m_targetElapsedTicks(TicksPerSecond / 60) + { + QueryPerformanceFrequency(&m_qpcFrequency); + QueryPerformanceCounter(&m_qpcLastTime); + + // Initialize max delta to 1/10 of a second. + m_qpcMaxDelta = m_qpcFrequency.QuadPart / 10; + } + + // Get elapsed time since the previous Update call. + UINT64 GetElapsedTicks() const { return m_elapsedTicks; } + double GetElapsedSeconds() const { return TicksToSeconds(m_elapsedTicks); } + + // Get total time since the start of the program. + UINT64 GetTotalTicks() const { return m_totalTicks; } + double GetTotalSeconds() const { return TicksToSeconds(m_totalTicks); } + + // Get total number of updates since start of the program. + UINT32 GetFrameCount() const { return m_frameCount; } + + // Get the current framerate. + UINT32 GetFramesPerSecond() const { return m_framesPerSecond; } + + // Set whether to use fixed or variable timestep mode. + void SetFixedTimeStep(bool isFixedTimestep) { m_isFixedTimeStep = isFixedTimestep; } + + // Set how often to call Update when in fixed timestep mode. + void SetTargetElapsedTicks(UINT64 targetElapsed) { m_targetElapsedTicks = targetElapsed; } + void SetTargetElapsedSeconds(double targetElapsed) { m_targetElapsedTicks = SecondsToTicks(targetElapsed); } + + // Integer format represents time using 10,000,000 ticks per second. + static const UINT64 TicksPerSecond = 10000000; + + static double TicksToSeconds(UINT64 ticks) { return static_cast(ticks) / TicksPerSecond; } + static UINT64 SecondsToTicks(double seconds) { return static_cast(seconds * TicksPerSecond); } + + // After an intentional timing discontinuity (for instance a blocking IO operation) + // call this to avoid having the fixed timestep logic attempt a set of catch-up + // Update calls. + + void ResetElapsedTime() + { + QueryPerformanceCounter(&m_qpcLastTime); + + m_leftOverTicks = 0; + m_framesPerSecond = 0; + m_framesThisSecond = 0; + m_qpcSecondCounter = 0; + } + + typedef void(*LPUPDATEFUNC) (void); + + // Update timer state, calling the specified Update function the appropriate number of times. + void Tick(LPUPDATEFUNC update = nullptr) + { + // Query the current time. + LARGE_INTEGER currentTime; + + QueryPerformanceCounter(¤tTime); + + UINT64 timeDelta = currentTime.QuadPart - m_qpcLastTime.QuadPart; + + m_qpcLastTime = currentTime; + m_qpcSecondCounter += timeDelta; + + // Clamp excessively large time deltas (e.g. after paused in the debugger). + if (timeDelta > m_qpcMaxDelta) + { + timeDelta = m_qpcMaxDelta; + } + + // Convert QPC units into a canonical tick format. This cannot overflow due to the previous clamp. + timeDelta *= TicksPerSecond; + timeDelta /= m_qpcFrequency.QuadPart; + + UINT32 lastFrameCount = m_frameCount; + + if (m_isFixedTimeStep) + { + // Fixed timestep update logic + + // If the app is running very close to the target elapsed time (within 1/4 of a millisecond) just clamp + // the clock to exactly match the target value. This prevents tiny and irrelevant errors + // from accumulating over time. Without this clamping, a game that requested a 60 fps + // fixed update, running with vsync enabled on a 59.94 NTSC display, would eventually + // accumulate enough tiny errors that it would drop a frame. It is better to just round + // small deviations down to zero to leave things running smoothly. + + if (abs(static_cast(timeDelta - m_targetElapsedTicks)) < TicksPerSecond / 4000) + { + timeDelta = m_targetElapsedTicks; + } + + m_leftOverTicks += timeDelta; + + while (m_leftOverTicks >= m_targetElapsedTicks) + { + m_elapsedTicks = m_targetElapsedTicks; + m_totalTicks += m_targetElapsedTicks; + m_leftOverTicks -= m_targetElapsedTicks; + m_frameCount++; + + if (update) + { + update(); + } + } + } + else + { + // Variable timestep update logic. + m_elapsedTicks = timeDelta; + m_totalTicks += timeDelta; + m_leftOverTicks = 0; + m_frameCount++; + + if (update) + { + update(); + } + } + + // Track the current framerate. + if (m_frameCount != lastFrameCount) + { + m_framesThisSecond++; + } + + if (m_qpcSecondCounter >= static_cast(m_qpcFrequency.QuadPart)) + { + m_framesPerSecond = m_framesThisSecond; + m_framesThisSecond = 0; + m_qpcSecondCounter %= m_qpcFrequency.QuadPart; + } + } + +private: + // Source timing data uses QPC units. + LARGE_INTEGER m_qpcFrequency; + LARGE_INTEGER m_qpcLastTime; + UINT64 m_qpcMaxDelta; + + // Derived timing data uses a canonical tick format. + UINT64 m_elapsedTicks; + UINT64 m_totalTicks; + UINT64 m_leftOverTicks; + + // Members for tracking the framerate. + UINT32 m_frameCount; + UINT32 m_framesPerSecond; + UINT32 m_framesThisSecond; + UINT64 m_qpcSecondCounter; + + // Members for configuring fixed timestep mode. + bool m_isFixedTimeStep; + UINT64 m_targetElapsedTicks; +}; diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/Win32Application.cpp b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/Win32Application.cpp new file mode 100644 index 000000000..b4b62407d --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/Win32Application.cpp @@ -0,0 +1,299 @@ +//********************************************************* +// +// 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 "Win32Application.h" +#include "DXSampleHelper.h" + +HWND Win32Application::m_hwnd = nullptr; +bool Win32Application::m_fullscreenMode = false; +RECT Win32Application::m_windowRect; +using Microsoft::WRL::ComPtr; + +int Win32Application::Run(DXSample* pSample, HINSTANCE hInstance, int nCmdShow) +{ + try + { + // Parse the command line parameters + int argc; + LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc); + pSample->ParseCommandLineArgs(argv, argc); + LocalFree(argv); + + // Initialize the window class. + WNDCLASSEX windowClass = { 0 }; + windowClass.cbSize = sizeof(WNDCLASSEX); + windowClass.style = CS_HREDRAW | CS_VREDRAW; + windowClass.lpfnWndProc = WindowProc; + windowClass.hInstance = hInstance; + windowClass.hCursor = LoadCursor(NULL, IDC_ARROW); + windowClass.lpszClassName = L"DXSampleClass"; + RegisterClassEx(&windowClass); + + RECT windowRect = { 0, 0, static_cast(pSample->GetWidth()), static_cast(pSample->GetHeight()) }; + AdjustWindowRect(&windowRect, WS_OVERLAPPEDWINDOW, FALSE); + + // Create the window and store a handle to it. + m_hwnd = CreateWindow( + windowClass.lpszClassName, + pSample->GetTitle(), + m_windowStyle, + CW_USEDEFAULT, + CW_USEDEFAULT, + windowRect.right - windowRect.left, + windowRect.bottom - windowRect.top, + nullptr, // We have no parent window. + nullptr, // We aren't using menus. + hInstance, + pSample); + + // Initialize the sample. OnInit is defined in each child-implementation of DXSample. + pSample->OnInit(); + + ShowWindow(m_hwnd, nCmdShow); + + // Main sample loop. + MSG msg = {}; + while (msg.message != WM_QUIT) + { + // Process any messages in the queue. + if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + pSample->OnDestroy(); + + // Return this part of the WM_QUIT message to Windows. + return static_cast(msg.wParam); + } + catch (std::exception& e) + { + OutputDebugString(L"Application hit a problem: "); + OutputDebugStringA(e.what()); + OutputDebugString(L"\nTerminating.\n"); + + pSample->OnDestroy(); + return EXIT_FAILURE; + } +} + +// Convert a styled window into a fullscreen borderless window and back again. +void Win32Application::ToggleFullscreenWindow(IDXGISwapChain* pSwapChain) +{ + if (m_fullscreenMode) + { + // Restore the window's attributes and size. + SetWindowLong(m_hwnd, GWL_STYLE, m_windowStyle); + + SetWindowPos( + m_hwnd, + HWND_NOTOPMOST, + m_windowRect.left, + m_windowRect.top, + m_windowRect.right - m_windowRect.left, + m_windowRect.bottom - m_windowRect.top, + SWP_FRAMECHANGED | SWP_NOACTIVATE); + + ShowWindow(m_hwnd, SW_NORMAL); + } + else + { + // Save the old window rect so we can restore it when exiting fullscreen mode. + GetWindowRect(m_hwnd, &m_windowRect); + + // Make the window borderless so that the client area can fill the screen. + SetWindowLong(m_hwnd, GWL_STYLE, m_windowStyle & ~(WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SYSMENU | WS_THICKFRAME)); + + RECT fullscreenWindowRect; + try + { + if (pSwapChain) + { + // Get the settings of the display on which the app's window is currently displayed + ComPtr pOutput; + ThrowIfFailed(pSwapChain->GetContainingOutput(&pOutput)); + DXGI_OUTPUT_DESC Desc; + ThrowIfFailed(pOutput->GetDesc(&Desc)); + fullscreenWindowRect = Desc.DesktopCoordinates; + } + else + { + // Fallback to EnumDisplaySettings implementation + throw HrException(S_FALSE); + } + } + catch (HrException& e) + { + UNREFERENCED_PARAMETER(e); + + // Get the settings of the primary display + DEVMODE devMode = {}; + devMode.dmSize = sizeof(DEVMODE); + EnumDisplaySettings(nullptr, ENUM_CURRENT_SETTINGS, &devMode); + + fullscreenWindowRect = { + devMode.dmPosition.x, + devMode.dmPosition.y, + devMode.dmPosition.x + static_cast(devMode.dmPelsWidth), + devMode.dmPosition.y + static_cast(devMode.dmPelsHeight) + }; + } + + SetWindowPos( + m_hwnd, + HWND_TOPMOST, + fullscreenWindowRect.left, + fullscreenWindowRect.top, + fullscreenWindowRect.right, + fullscreenWindowRect.bottom, + SWP_FRAMECHANGED | SWP_NOACTIVATE); + + + ShowWindow(m_hwnd, SW_MAXIMIZE); + } + + m_fullscreenMode = !m_fullscreenMode; +} + +void Win32Application::SetWindowZorderToTopMost(bool setToTopMost) +{ + RECT windowRect; + GetWindowRect(m_hwnd, &windowRect); + + SetWindowPos( + m_hwnd, + (setToTopMost) ? HWND_TOPMOST : HWND_NOTOPMOST, + windowRect.left, + windowRect.top, + windowRect.right - windowRect.left, + windowRect.bottom - windowRect.top, + SWP_FRAMECHANGED | SWP_NOACTIVATE); +} + +// Main message handler for the sample. +LRESULT CALLBACK Win32Application::WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + DXSample* pSample = reinterpret_cast(GetWindowLongPtr(hWnd, GWLP_USERDATA)); + + switch (message) + { + case WM_CREATE: + { + // Save the DXSample* passed in to CreateWindow. + LPCREATESTRUCT pCreateStruct = reinterpret_cast(lParam); + SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast(pCreateStruct->lpCreateParams)); + } + return 0; + + case WM_KEYDOWN: + if (pSample) + { + pSample->OnKeyDown(static_cast(wParam)); + } + return 0; + + case WM_KEYUP: + if (pSample) + { + pSample->OnKeyUp(static_cast(wParam)); + } + return 0; + + case WM_SYSKEYDOWN: + // Handle ALT+ENTER: + if ((wParam == VK_RETURN) && (lParam & (1 << 29))) + { + if (pSample && pSample->GetDeviceResources()->IsTearingSupported()) + { + ToggleFullscreenWindow(pSample->GetSwapchain()); + return 0; + } + } + // Send all other WM_SYSKEYDOWN messages to the default WndProc. + break; + + case WM_PAINT: + if (pSample) + { + pSample->OnUpdate(); + pSample->OnRender(); + } + return 0; + + case WM_SIZE: + if (pSample) + { + RECT windowRect = {}; + GetWindowRect(hWnd, &windowRect); + pSample->SetWindowBounds(windowRect.left, windowRect.top, windowRect.right, windowRect.bottom); + + RECT clientRect = {}; + GetClientRect(hWnd, &clientRect); + pSample->OnSizeChanged(clientRect.right - clientRect.left, clientRect.bottom - clientRect.top, wParam == SIZE_MINIMIZED); + } + return 0; + + case WM_MOVE: + if (pSample) + { + RECT windowRect = {}; + GetWindowRect(hWnd, &windowRect); + pSample->SetWindowBounds(windowRect.left, windowRect.top, windowRect.right, windowRect.bottom); + + int xPos = (int)(short)LOWORD(lParam); + int yPos = (int)(short)HIWORD(lParam); + pSample->OnWindowMoved(xPos, yPos); + } + return 0; + + case WM_DISPLAYCHANGE: + if (pSample) + { + pSample->OnDisplayChanged(); + } + return 0; + + case WM_MOUSEMOVE: + if (pSample && static_cast(wParam) == MK_LBUTTON) + { + UINT x = LOWORD(lParam); + UINT y = HIWORD(lParam); + pSample->OnMouseMove(x, y); + } + return 0; + + case WM_LBUTTONDOWN: + { + UINT x = LOWORD(lParam); + UINT y = HIWORD(lParam); + pSample->OnLeftButtonDown(x, y); + } + return 0; + + case WM_LBUTTONUP: + { + UINT x = LOWORD(lParam); + UINT y = HIWORD(lParam); + pSample->OnLeftButtonUp(x, y); + } + return 0; + + case WM_DESTROY: + PostQuitMessage(0); + return 0; + } + + // Handle any messages the switch statement didn't. + return DefWindowProc(hWnd, message, wParam, lParam); +} \ No newline at end of file diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/Win32Application.h b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/Win32Application.h new file mode 100644 index 000000000..163cbdfa7 --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/Win32Application.h @@ -0,0 +1,35 @@ +//********************************************************* +// +// 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" + +class DXSample; + +class Win32Application +{ +public: + static int Run(DXSample* pSample, HINSTANCE hInstance, int nCmdShow); + static void ToggleFullscreenWindow(IDXGISwapChain* pOutput = nullptr); + static void SetWindowZorderToTopMost(bool setToTopMost); + static HWND GetHwnd() { return m_hwnd; } + static bool IsFullscreen() { return m_fullscreenMode; } + +protected: + static LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + +private: + static HWND m_hwnd; + static bool m_fullscreenMode; + static const UINT m_windowStyle = WS_OVERLAPPEDWINDOW; + static RECT m_windowRect; +}; \ No newline at end of file diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/readme.md b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/readme.md new file mode 100644 index 000000000..08a9b74a1 --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/readme.md @@ -0,0 +1,31 @@ +# D3D12 Raytracing Simple Lighting sample +![D3D12 Raytracing Simple Lighting GUI](Screenshot.png) + +This sample demonstrates how to do ray generation for a dynamic perspective camera and calculate simple diffuse shading for a cube from a dynamic point light. + + +### Usage +The sample starts with Fallback Layer implementation being used by default. The Fallback Layer will use raytracing driver if available, otherwise it will default to the compute fallback. This default behavior can be overriden via UI controls or input arguments. + +D3D12RaytracingSimpleLighting.exe [ -FL | -DXR ] +* [-FL] - select Fallback Layer API with forced compute fallback path. +* [-DXR] - select DirectX Raytracing API. + +### UI +The title bar of the sample provides runtime information: +* Name of the sample +* Raytracing API being active: + * FL - Fallback Layer with compute fallback being used + * FL-DXR - Fallback Layer with raytracing driver being used + * DXR - DirectX Raytracing being used +* Frames per second +* Million Primary Rays/s: a number of dispatched rays per second calculated based of FPS. + +### Controls +* ALT+ENTER - toggles between windowed and fullscreen modes. +* 1 - select Fallback Layer API. +* 2 - select Fallback Layer API with forced compute fallback path. +* 3 - select DirectX Raytracing API. + +## Requirements +* Consult the main [D3D12 Raytracing readme](../../readme.md) for requirements. \ No newline at end of file diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/stdafx.cpp b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/stdafx.cpp new file mode 100644 index 000000000..a6533dc11 --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/stdafx.cpp @@ -0,0 +1,12 @@ +//********************************************************* +// +// 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" diff --git a/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/stdafx.h b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/stdafx.h new file mode 100644 index 000000000..e64fc3f26 --- /dev/null +++ b/Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingSimpleLighting/stdafx.h @@ -0,0 +1,54 @@ +//********************************************************* +// +// 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. +// +//********************************************************* + +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently. + +#pragma once + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers. +#endif + +#include + +// C RunTime Header Files +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "d3d12_1.h" +#include +#include "D3D12RaytracingFallback.h" +#include "D3D12RaytracingPrototypeHelpers.hpp" +#include "d3dx12.h" + +#include + +#ifdef _DEBUG +#include +#endif + +#include "DXSampleHelper.h" +#include "DeviceResources.h" + diff --git a/Samples/Desktop/D3D12Raytracing/tools/x64/PostbuildCopy.bat b/Samples/Desktop/D3D12Raytracing/tools/x64/PostbuildCopy.bat index d77a7eb2a..f6d6fc44a 100644 --- a/Samples/Desktop/D3D12Raytracing/tools/x64/PostbuildCopy.bat +++ b/Samples/Desktop/D3D12Raytracing/tools/x64/PostbuildCopy.bat @@ -9,7 +9,7 @@ set UseRS4Binaries=0 rem If the user is running on any "recent" RS4 build, copy DXR-enabled D3D12 binaries rem RS3 and older can only use the Fallback layer and RS5 and newer already has DXR built-in -if %BuildVersion% GEQ 17093 if %BuildVersion% LEQ 17600 set UseRS4Binaries=1 +if %BuildVersion% GEQ 17093 if %BuildVersion% LEQ 17623 set UseRS4Binaries=1 rem Copy Dxcompiler, needed regardless of whether it's using native DXR or the Fallback layer set outputDirectory=%1