├── .gitignore ├── .gitmodules ├── CLA.txt ├── CMakeLists.txt ├── LICENSE.txt ├── README.md └── adaptive_shading ├── AdaptiveShading.cpp ├── CMakeLists.txt ├── ComputeNASData.hlsl ├── ComputeShadingRate.hlsl ├── Compute_cb.h ├── ShadingRateVis.hlsl ├── SmoothShadingRate.hlsl └── shaders.cfg /.gitignore: -------------------------------------------------------------------------------- 1 | build*/ 2 | media*/ 3 | media 4 | .vs 5 | nvapi/ 6 | bin/ 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "donut_examples"] 2 | path = donut_examples 3 | url = https://github.com/NVIDIAGameWorks/donut_examples.git 4 | branch = main 5 | -------------------------------------------------------------------------------- /CLA.txt: -------------------------------------------------------------------------------- 1 | Contribution License Agreement 2 | 3 | This Contribution License Agreement ("Agreement") is agreed to by the party 4 | signing below ("You"), and conveys certain license rights to NVIDIA Corporation 5 | and its affiliates ("NVIDIA") for Your contributions to NVIDIA open source 6 | projects. This Agreement is effective as of the latest signature date below. 7 | 8 | 1. Definitions. 9 | 10 | "Code" means the computer software code, whether in human-readable or 11 | machine-executable form, that is delivered by You to NVIDIA under this 12 | Agreement. 13 | 14 | "Project" means any of the projects owned or managed by NVIDIA in which 15 | software is offered under a license approved by the Open Source Initiative 16 | (OSI) (www.opensource.org) and documentation offered under an OSI or a 17 | Creative Commons license (https://creativecommons.org/licenses). 18 | 19 | "Submit" is the act of uploading, submitting, transmitting, or distributing 20 | code or other content to any Project, including but not limited to 21 | communication on electronic mailing lists, source code control systems, 22 | and issue tracking systems that are managed by, or on behalf of, the 23 | Project for the purpose of discussing and improving that Project, but 24 | excluding communication that is conspicuously marked or otherwise 25 | designated in writing by You as "Not a Submission." 26 | 27 | "Submission" means the Code and any other copyrightable material Submitted 28 | by You, including any associated comments and documentation. 29 | 30 | 2. Your Submission. You must agree to the terms of this Agreement before 31 | making a Submission to any Project. This Agreement covers any and all 32 | Submissions that You, now or in the future (except as described in Section 33 | 4 below), Submit to any Project. 34 | 35 | 3. Originality of Work. You represent that each of Your Submissions is 36 | entirely Your original work. Should You wish to Submit materials that are 37 | not Your original work, You may Submit them separately to the Project if 38 | You (a) retain all copyright and license information that was in the 39 | materials as You received them, (b) in the description accompanying Your 40 | Submission, include the phrase "Submission containing materials of a 41 | third party:" followed by the names of the third party and any licenses 42 | or other restrictions of which You are aware, and (c) follow any other 43 | instructions in the Project’s written guidelines concerning Submissions. 44 | 45 | 4. Your Employer. References to "employer" in this Agreement include Your 46 | employer or anyone else for whom You are acting in making Your Submission, 47 | e.g. as a contractor, vendor, or agent. If Your Submission is made in the 48 | course of Your work for an employer or Your employer has intellectual 49 | property rights in Your Submission by contract or applicable law, You must 50 | secure permission from Your employer to make the Submission before signing 51 | this Agreement. In that case, the term "You" in this Agreement will refer 52 | to You and the employer collectively. If You change employers in the 53 | future and desire to Submit additional Submissions for the new employer, 54 | then You agree to sign a new Agreement and secure permission from the 55 | new employer before Submitting those Submissions. 56 | 57 | 5. Licenses. 58 | 59 | a. Copyright License. You grant NVIDIA, and those who receive the Submission 60 | directly or indirectly from NVIDIA, a perpetual, worldwide, non-exclusive, 61 | royalty-free, irrevocable license in the Submission to reproduce, prepare 62 | derivative works of, publicly display, publicly perform, and distribute the 63 | Submission and such derivative works, and to sublicense any or all of the 64 | foregoing rights to third parties. 65 | 66 | b. Patent License. You grant NVIDIA, and those who receive the Submission 67 | directly or indirectly from NVIDIA, a perpetual, worldwide, non-exclusive, 68 | royalty-free, irrevocable license under Your patent claims that are 69 | necessarily infringed by the Submission or the combination of the Submission 70 | with the Project to which it was Submitted to make, have made, use, offer to 71 | sell, sell and import or otherwise dispose of the Submission alone or with 72 | the Project. 73 | 74 | c. Other Rights Reserved. Each party reserves all rights not expressly 75 | granted in this Agreement. No additional licenses or rights whatsoever 76 | (including, without limitation, any implied licenses) are granted by 77 | implication, exhaustion, estoppel or otherwise. 78 | 79 | 6. Representations and Warranties. You represent that You are legally 80 | entitled to grant the above licenses. You represent that each of Your 81 | Submissions is entirely Your original work (except as You may have 82 | disclosed under Section 3). You represent that You have secured permission 83 | from Your employer to make the Submission in cases where Your Submission 84 | is made in the course of Your work for Your employer or Your employer has 85 | intellectual property rights in Your Submission by contract or applicable 86 | law. If You are signing this Agreement on behalf of Your employer, You 87 | represent and warrant that You have the necessary authority to bind the 88 | listed employer to the obligations contained in this Agreement. You are 89 | not expected to provide support for Your Submission, unless You choose to 90 | do so. UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING, AND 91 | EXCEPT FOR THE WARRANTIES EXPRESSLY STATED IN SECTIONS 3, 4, AND 6, THE 92 | SUBMISSION PROVIDED UNDER THIS AGREEMENT IS PROVIDED WITHOUT WARRANTY OF 93 | ANY KIND, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY OF NONINFRINGEMENT, 94 | MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. 95 | 96 | 7. Notice to NVIDIA. You agree to notify NVIDIA in writing of any facts 97 | or circumstances of which You later become aware that would make Your 98 | representations in this Agreement inaccurate in any respect. 99 | 100 | 8. Information about Submissions. You agree that contributions to Projects 101 | and information about contributions may be maintained indefinitely and 102 | disclosed publicly, including Your name and other information that You 103 | submit with Your Submission. 104 | 105 | 9. Governing Law/Jurisdiction. Claims arising under this Agreement shall 106 | be governed by the laws of Delaware, excluding its principles of conflict 107 | of laws and the United Nations Convention on Contracts for the Sale of 108 | Goods. The state and/or federal courts residing in Santa Clara County, 109 | California shall have exclusive jurisdiction over any dispute or claim 110 | arising out of this Agreement. You may not export the Software in 111 | violation of applicable export laws and regulations. 112 | 113 | 10. Entire Agreement/Assignment. This Agreement is the entire agreement 114 | between the parties, and supersedes any and all prior agreements, 115 | understandings or communications, written or oral, between the parties 116 | relating to the subject matter hereof. This Agreement may be assigned by 117 | NVIDIA. 118 | 119 | 120 | 121 | Please select one of the options below and sign as indicated. By signing, 122 | You accept and agree to the terms of this Contribution License Agreement 123 | for Your present and future Submissions to NVIDIA. 124 | 125 | ___ I have sole ownership of intellectual property rights to my Submissions 126 | and I am not making Submissions in the course of work for my employer. 127 | 128 | Name ("You"): _________________________________________ 129 | Signature: _________________________________________ 130 | Date: _________________________________________ 131 | GitHub Login: _________________________________________ 132 | Email: _________________________________________ 133 | Address: _________________________________________ 134 | 135 | 136 | ___ I am making Submissions in the course of work for my employer (or my 137 | employer has intellectual property rights in my Submissions by contract or 138 | applicable law). I have permission from my employer to make Submissions and 139 | enter into this Agreement on behalf of my employer. By signing below, the 140 | defined term "You" includes me and my employer. 141 | 142 | Company Name: _________________________________________ 143 | Signature: _________________________________________ 144 | By: _________________________________________ 145 | Title: _________________________________________ 146 | Date: _________________________________________ 147 | GitHub Login: _________________________________________ 148 | Email: _________________________________________ 149 | Address: _________________________________________ 150 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | project(adaptive_shading) 4 | 5 | set(CMAKE_CXX_STANDARD 17) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | set(CMAKE_CXX_EXTENSIONS ON) 8 | 9 | if (MSVC) 10 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_ITERATOR_DEBUG_LEVEL=1") 11 | endif() 12 | 13 | add_subdirectory(donut_examples) 14 | add_subdirectory(adaptive_shading) 15 | 16 | file(CREATE_LINK "${CMAKE_CURRENT_SOURCE_DIR}/donut_examples/media" "${CMAKE_SOURCE_DIR}/media" SYMBOLIC) 17 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a 4 | copy of this software and associated documentation files (the "Software"), 5 | to deal in the Software without restriction, including without limitation 6 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the 8 | Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NVIDIA Variable-Rate Shading (VRS) and NVIDIA Adaptive Shading (NAS) Samples 2 | 3 | ## Sample Contents 4 | 5 | This repository contains two samples. The Variable-Rate Shading (VRS) sample shows how to use the D3D12 API to enable the feature as well as a simple example shader for initializing the shading rate surface. The NVIDIA Adaptive Shading (NAS) sample implements the fully-featured NAS algorithm described in the paper "Visually Lossless Content and Motion Adaptive Shading in Games" by Yang et al. 6 | 7 | ## VRS Sample 8 | 9 | located in `donut_examples/examples/variable_shading 10 | 11 | This sample intends to show the necessary steps to enable VRS in a D3D12 application. All D3D12-specific code is in `variable_shading.cpp`. Within the file, the VRS-specific runtime calls are marked are located in the code section under the comment `// VRS-specific code starts here`. The compute shader used to fill the shading rate surface is written in `shaders.hlsl`. This shader averages the color in the VRS tile and outputs a 4x4 shading rate if the green channel is bigger than the red channel, or 1x1 rate otherwise. This calcuation is not an intelligent algorithm of any kind and is only used to put some kind of variable data into the shading rate surface. The sample also supports VRS via a more abstract rendering framework (NVRHI), which is more similar to the design used by most game engines. This abstraction layer supports both D3D12 and Vulkan. There is no "raw API" sample path for Vulkan, due to the API requiring that VRS state be attached to framebuffers, renderpasses, and graphics pipeline state, all of which are not currently accessible through the NVRHI abstraction layer. To learn more about how to use the Vulkan VRS API, we recommend looking at the Vulkan backend of NVRHI. 12 | 13 | ## NAS Sample 14 | 15 | located in `adaptive_shading` 16 | 17 | This sample implements the algorithm described in the "Visually Lossless Content and Motion Adaptive Shading in Games" paper by Yang et al. Inside the `AdaptiveShading.cpp` file, NAS-specific initialization and runtime calls are located under the comment `// NAS-related functions begin here`. Those functions are then called from the main loop to compute and apply the NAS algorithm. Most of the algorithm itself is located in shader files. `ComputeNASData.hlsl` computes a partial derivative-based luminance error for a pixel tile. Then, `ComputeShadingRate.hlsl` uses that error along with the additional motion-adaptive terms to compute the minimum acceptable shading rate for the tile. Finally, `SmoothShadingRate.hlsl` fills in sharp transitions between high and low shading rates with intermediate rate values for a smoother boundary. This output is the VRS surface which will set the shading rates for subsequent draw calls. 18 | 19 | ## Requirements 20 | 21 | * Windows or Linux 22 | * CMake 3.12 23 | * C++ 17 24 | 25 | ## Build 26 | 27 | 1. Clone a source tree with all sub-module dependencies: 28 | 29 | ```bash 30 | git clone --recursive 31 | ``` 32 | 33 | 2. Create a build folder 34 | * any name works, but git is configured to ignore folders named 'build' 35 | * this folder should be placed under directory created by 'git clone' in step #1 36 | 37 | 3. Use CMake to configure the build and generate the project files. 38 | 39 | * Linux: `cmake ..` 40 | * Windows: use of CMake GUI is recommended. Make sure to select the x64 platform for the generator. 41 | 42 | 4. Build the solution generated by CMake in the build folder. 43 | 44 | * Linux: `make -j8` (example for an 8-core CPU) 45 | * Windows: Open the generated solution with Visual Studio and build it. 46 | 47 | 5. Run the examples. They should be built in the `bin` folder. 48 | 49 | ## Command Line 50 | 51 | Both the VRS and NAS samples support D3D12 and Vulkan on Windows. They are built with all APIs supported in the same executable, 52 | and the API can be switched from the command line. Use these command line arguments: 53 | 54 | - `-dx12` for D3D12 (default) 55 | - `-vk` for Vulkan 56 | 57 | ## License 58 | 59 | Donut Examples are licensed under the [MIT License](LICENSE.txt). 60 | -------------------------------------------------------------------------------- /adaptive_shading/AdaptiveShading.cpp: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------------------------------- 2 | // File: AdaptiveShading.cpp 3 | // Site: http://developer.nvidia.com/ 4 | // 5 | // Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. 6 | // 7 | // Redistribution and use in source and binary forms, with or without 8 | // modification, are permitted provided that the following conditions 9 | // are met: 10 | // * Redistributions of source code must retain the above copyright 11 | // notice, this list of conditions and the following disclaimer. 12 | // * Redistributions in binary form must reproduce the above copyright 13 | // notice, this list of conditions and the following disclaimer in the 14 | // documentation and/or other materials provided with the distribution. 15 | // * Neither the name of NVIDIA CORPORATION nor the names of its 16 | // contributors may be used to endorse or promote products derived 17 | // from this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 20 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 | // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 23 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 27 | // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | // 31 | //---------------------------------------------------------------------------------- 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | 72 | #ifdef DONUT_WITH_TASKFLOW 73 | #include 74 | #endif 75 | 76 | using namespace donut; 77 | using namespace donut::math; 78 | using namespace donut::app; 79 | using namespace donut::vfs; 80 | using namespace donut::engine; 81 | using namespace donut::render; 82 | 83 | static bool g_PrintSceneGraph = false; 84 | 85 | #include "Compute_cb.h" // requires donut::math 86 | 87 | // NVIDIA Adaptive Shading (NAS) feature and algorithm demo 88 | // NAS/VRS-related functions should be identifiable by function name 89 | 90 | class RenderTargets : public GBufferRenderTargets 91 | { 92 | public: 93 | nvrhi::TextureHandle HdrColor; 94 | nvrhi::TextureHandle LdrColor; 95 | nvrhi::TextureHandle MaterialIDs; 96 | nvrhi::TextureHandle ResolvedColor; 97 | nvrhi::TextureHandle TemporalFeedback1; 98 | nvrhi::TextureHandle TemporalFeedback2; 99 | nvrhi::TextureHandle AmbientOcclusion; 100 | nvrhi::TextureHandle m_VRSRateSurface; 101 | nvrhi::TextureHandle m_NASDataSurface; 102 | 103 | nvrhi::HeapHandle Heap; 104 | 105 | std::shared_ptr ForwardFramebuffer; 106 | std::shared_ptr HdrFramebuffer; 107 | std::shared_ptr LdrFramebuffer; 108 | std::shared_ptr ResolvedFramebuffer; 109 | std::shared_ptr MaterialIDFramebuffer; 110 | std::shared_ptr DepthPrePassFramebuffer; 111 | 112 | uint2 m_VRSSurfaceSize; 113 | uint m_VRSTileSize; 114 | 115 | void Init( 116 | nvrhi::IDevice* device, 117 | dm::uint2 size, 118 | dm::uint sampleCount, 119 | bool enableMotionVectors, 120 | bool useReverseProjection) override 121 | { 122 | GBufferRenderTargets::Init(device, size, sampleCount, enableMotionVectors, useReverseProjection); 123 | 124 | nvrhi::TextureDesc desc; 125 | desc.width = size.x; 126 | desc.height = size.y; 127 | desc.isRenderTarget = true; 128 | desc.useClearValue = true; 129 | desc.clearValue = nvrhi::Color(1.f); 130 | desc.sampleCount = sampleCount; 131 | desc.dimension = sampleCount > 1 ? nvrhi::TextureDimension::Texture2DMS : nvrhi::TextureDimension::Texture2D; 132 | desc.keepInitialState = true; 133 | desc.isVirtual = device->queryFeatureSupport(nvrhi::Feature::VirtualResources); 134 | 135 | desc.clearValue = nvrhi::Color(0.f); 136 | desc.isTypeless = false; 137 | desc.isUAV = sampleCount == 1; 138 | desc.format = nvrhi::Format::RGBA16_FLOAT; 139 | desc.initialState = nvrhi::ResourceStates::RenderTarget; 140 | desc.debugName = "HdrColor"; 141 | HdrColor = device->createTexture(desc); 142 | 143 | desc.format = nvrhi::Format::RG16_UINT; 144 | desc.isUAV = false; 145 | desc.debugName = "MaterialIDs"; 146 | MaterialIDs = device->createTexture(desc); 147 | 148 | // The render targets below this point are non-MSAA 149 | desc.sampleCount = 1; 150 | desc.dimension = nvrhi::TextureDimension::Texture2D; 151 | 152 | desc.format = nvrhi::Format::RGBA16_FLOAT; 153 | desc.isUAV = true; 154 | desc.debugName = "ResolvedColor"; 155 | ResolvedColor = device->createTexture(desc); 156 | 157 | desc.format = nvrhi::Format::RGBA16_SNORM; 158 | desc.debugName = "TemporalFeedback1"; 159 | TemporalFeedback1 = device->createTexture(desc); 160 | desc.debugName = "TemporalFeedback2"; 161 | TemporalFeedback2 = device->createTexture(desc); 162 | 163 | desc.format = nvrhi::Format::SRGBA8_UNORM; 164 | desc.isUAV = false; 165 | desc.debugName = "LdrColor"; 166 | LdrColor = device->createTexture(desc); 167 | 168 | desc.format = nvrhi::Format::R8_UNORM; 169 | desc.isUAV = true; 170 | desc.debugName = "AmbientOcclusion"; 171 | AmbientOcclusion = device->createTexture(desc); 172 | 173 | // NAS/VRS surfaces 174 | { 175 | nvrhi::VariableRateShadingFeatureInfo info = {}; 176 | bool vrsSupported = device->queryFeatureSupport(nvrhi::Feature::VariableRateShading, &info, sizeof(info)); 177 | if (!vrsSupported) 178 | { 179 | log::fatal("VRS is not supported by the device."); 180 | } 181 | 182 | m_VRSTileSize = info.shadingRateImageTileSize; 183 | m_VRSSurfaceSize = uint2((size.x + m_VRSTileSize - 1) / m_VRSTileSize, (size.y + m_VRSTileSize - 1) / m_VRSTileSize); 184 | 185 | nvrhi::TextureDesc desc; 186 | desc.width = m_VRSSurfaceSize.x; 187 | desc.height = m_VRSSurfaceSize.y; 188 | desc.isRenderTarget = false; 189 | desc.useClearValue = false; 190 | desc.sampleCount = 1; 191 | desc.dimension = nvrhi::TextureDimension::Texture2D; 192 | desc.keepInitialState = true; 193 | desc.initialState = nvrhi::ResourceStates::UnorderedAccess; 194 | desc.arraySize = 1; 195 | desc.isUAV = true; 196 | desc.isShadingRateSurface = true; 197 | desc.format = nvrhi::Format::R8_UINT; 198 | 199 | m_VRSRateSurface = device->createTexture(desc); 200 | 201 | desc.isShadingRateSurface = false; 202 | desc.format = nvrhi::Format::RG16_FLOAT; 203 | m_NASDataSurface = device->createTexture(desc); 204 | } 205 | 206 | if (desc.isVirtual) 207 | { 208 | uint64_t heapSize = 0; 209 | nvrhi::ITexture* const textures[] = { 210 | HdrColor, 211 | MaterialIDs, 212 | ResolvedColor, 213 | TemporalFeedback1, 214 | TemporalFeedback2, 215 | LdrColor, 216 | AmbientOcclusion, 217 | m_VRSRateSurface, 218 | m_NASDataSurface 219 | }; 220 | 221 | for (auto texture : textures) 222 | { 223 | nvrhi::MemoryRequirements memReq = device->getTextureMemoryRequirements(texture); 224 | heapSize = nvrhi::align(heapSize, memReq.alignment); 225 | heapSize += memReq.size; 226 | } 227 | 228 | nvrhi::HeapDesc heapDesc; 229 | heapDesc.type = nvrhi::HeapType::DeviceLocal; 230 | heapDesc.capacity = heapSize; 231 | heapDesc.debugName = "RenderTargetHeap"; 232 | 233 | Heap = device->createHeap(heapDesc); 234 | 235 | uint64_t offset = 0; 236 | for (auto texture : textures) 237 | { 238 | nvrhi::MemoryRequirements memReq = device->getTextureMemoryRequirements(texture); 239 | offset = nvrhi::align(offset, memReq.alignment); 240 | 241 | device->bindTextureMemory(texture, Heap, offset); 242 | 243 | offset += memReq.size; 244 | } 245 | } 246 | 247 | ForwardFramebuffer = std::make_shared(device); 248 | ForwardFramebuffer->RenderTargets = { HdrColor }; 249 | ForwardFramebuffer->DepthTarget = Depth; 250 | 251 | HdrFramebuffer = std::make_shared(device); 252 | HdrFramebuffer->RenderTargets = { HdrColor }; 253 | 254 | LdrFramebuffer = std::make_shared(device); 255 | LdrFramebuffer->RenderTargets = { LdrColor }; 256 | 257 | ResolvedFramebuffer = std::make_shared(device); 258 | ResolvedFramebuffer->RenderTargets = { ResolvedColor }; 259 | 260 | MaterialIDFramebuffer = std::make_shared(device); 261 | MaterialIDFramebuffer->RenderTargets = { MaterialIDs }; 262 | MaterialIDFramebuffer->DepthTarget = Depth; 263 | 264 | DepthPrePassFramebuffer = std::make_shared(device); 265 | DepthPrePassFramebuffer->DepthTarget = Depth; 266 | } 267 | 268 | [[nodiscard]] bool IsUpdateRequired(uint2 size, uint sampleCount) const 269 | { 270 | if (any(m_Size != size) || m_SampleCount != sampleCount) 271 | return true; 272 | 273 | return false; 274 | } 275 | 276 | void Clear(nvrhi::ICommandList* commandList) override 277 | { 278 | GBufferRenderTargets::Clear(commandList); 279 | 280 | commandList->clearTextureFloat(HdrColor, nvrhi::AllSubresources, nvrhi::Color(0.f)); 281 | } 282 | }; 283 | 284 | enum class AntiAliasingMode 285 | { 286 | NONE, 287 | TEMPORAL, 288 | MSAA_2X, 289 | MSAA_4X, 290 | MSAA_8X 291 | }; 292 | 293 | struct UIData 294 | { 295 | bool ShowUI = true; 296 | bool ShowConsole = false; 297 | bool UseDeferredShading = false; 298 | bool Stereo = false; 299 | bool EnableSsao = true; 300 | SsaoParameters SsaoParameters; 301 | ToneMappingParameters ToneMappingParams = {0.8f, 0.95f, 4.f, 4.f, 0.02f, 0.5f, -0.5f, 3.f, true}; 302 | TemporalAntiAliasingParameters TemporalAntiAliasingParams; 303 | SkyParameters SkyParams; 304 | enum AntiAliasingMode AntiAliasingMode = AntiAliasingMode::TEMPORAL; 305 | enum TemporalAntiAliasingJitter TemporalAntiAliasingJitter = TemporalAntiAliasingJitter::MSAA; 306 | bool EnableVsync = true; 307 | bool ShaderReloadRequested = false; 308 | bool EnableProceduralSky = true; 309 | bool EnableBloom = true; 310 | float BloomSigma = 32.f; 311 | float BloomAlpha = 0.05f; 312 | bool EnableTranslucency = true; 313 | bool EnableMaterialEvents = false; 314 | bool EnableShadows = true; 315 | float AmbientIntensity = 1.0f; 316 | bool EnableLightProbe = true; 317 | float LightProbeDiffuseScale = 1.f; 318 | float LightProbeSpecularScale = 1.f; 319 | float CsmExponent = 4.f; 320 | bool EnableNAS = true; 321 | bool EnableShadingRateVis = false; 322 | float NASErrorSensitivity = 0.07f; 323 | float NASMotionSensitivity = 0.5f; 324 | float NASBrightnessSensitivity = 0.1f; 325 | bool EnableShadingRateSurfaceSmoothing = true; 326 | bool DisplayShadowMap = false; 327 | bool UseThirdPersonCamera = false; 328 | bool EnableAnimations = false; 329 | std::shared_ptr SelectedMaterial; 330 | std::shared_ptr SelectedNode; 331 | std::string ScreenshotFileName; 332 | std::shared_ptr ActiveSceneCamera; 333 | }; 334 | 335 | struct ComputePass 336 | { 337 | nvrhi::ShaderHandle Shader; 338 | nvrhi::BindingLayoutHandle BindingLayout; 339 | nvrhi::BindingSetHandle BindingSet; 340 | nvrhi::ComputePipelineHandle Pipeline; 341 | nvrhi::BufferHandle ConstantBuffer; 342 | }; 343 | 344 | struct FullscreenPass 345 | { 346 | nvrhi::ShaderHandle VS; 347 | nvrhi::ShaderHandle PS; 348 | nvrhi::BindingLayoutHandle BindingLayout; 349 | nvrhi::BindingSetHandle BindingSet; 350 | nvrhi::GraphicsPipelineHandle Pipeline; 351 | nvrhi::FramebufferHandle Framebuffer; 352 | }; 353 | 354 | class FeatureDemo : public ApplicationBase 355 | { 356 | friend class UIRenderer; 357 | private: 358 | typedef ApplicationBase Super; 359 | 360 | std::shared_ptr m_RootFs; 361 | std::vector m_SceneFilesAvailable; 362 | std::string m_CurrentSceneName; 363 | std::shared_ptr m_Scene; 364 | std::shared_ptr m_ShaderFactory; 365 | std::shared_ptr m_SunLight; 366 | std::shared_ptr m_ShadowMap; 367 | std::shared_ptr m_ShadowFramebuffer; 368 | std::shared_ptr m_ShadowDepthPass; 369 | std::shared_ptr m_OpaqueDrawStrategy; 370 | std::shared_ptr m_TransparentDrawStrategy; 371 | std::unique_ptr m_RenderTargets; 372 | std::shared_ptr m_ForwardPass; 373 | std::unique_ptr m_GBufferPass; 374 | std::unique_ptr m_DeferredLightingPass; 375 | std::unique_ptr m_DepthPrePass; 376 | std::unique_ptr m_SkyPass; 377 | std::unique_ptr m_EnvironmentMapPass; 378 | std::unique_ptr m_TemporalAntiAliasingPass; 379 | std::unique_ptr m_BloomPass; 380 | std::unique_ptr m_ToneMappingPass; 381 | std::unique_ptr m_SsaoPass; 382 | std::shared_ptr m_LightProbePass; 383 | std::unique_ptr m_MaterialIDPass; 384 | std::unique_ptr m_PixelReadbackPass; 385 | 386 | std::shared_ptr m_View; 387 | std::shared_ptr m_ViewPrevious; 388 | 389 | nvrhi::CommandListHandle m_CommandList; 390 | bool m_PreviousViewsValid = false; 391 | FirstPersonCamera m_FirstPersonCamera; 392 | ThirdPersonCamera m_ThirdPersonCamera; 393 | BindingCache m_BindingCache; 394 | 395 | float m_CameraVerticalFov = 60.f; 396 | float3 m_AmbientTop = 0.f; 397 | float3 m_AmbientBottom = 0.f; 398 | uint2 m_PickPosition = 0u; 399 | bool m_Pick = false; 400 | 401 | std::shared_ptr m_EnvironmentMap; 402 | std::vector> m_LightProbes; 403 | nvrhi::TextureHandle m_LightProbeDiffuseTexture; 404 | nvrhi::TextureHandle m_LightProbeSpecularTexture; 405 | 406 | float m_WallclockTime = 0.f; 407 | 408 | UIData& m_ui; 409 | 410 | nvrhi::TimerQueryHandle m_tqDepthPrePass; 411 | nvrhi::TimerQueryHandle m_tqForwardOpaque; 412 | nvrhi::TimerQueryHandle m_tqForwardSky; 413 | nvrhi::TimerQueryHandle m_tqForwardTransparent; 414 | nvrhi::TimerQueryHandle m_tqMotionVector; 415 | 416 | ComputePass m_NASDataPass; 417 | ComputePass m_ShadingRatePass; 418 | ComputePass m_ShadingRateSmoothPass; 419 | FullscreenPass m_VRSRateVisPass; 420 | 421 | nvrhi::SamplerHandle m_BilinearSampler; 422 | 423 | public: 424 | 425 | FeatureDemo(DeviceManager* deviceManager, UIData& ui, const std::string& sceneName) 426 | : Super(deviceManager) 427 | , m_ui(ui) 428 | , m_BindingCache(deviceManager->GetDevice()) 429 | { 430 | std::shared_ptr nativeFS = std::make_shared(); 431 | 432 | std::filesystem::path mediaPath = app::GetDirectoryWithExecutable().parent_path() / "media"; 433 | std::filesystem::path frameworkShaderPath = app::GetDirectoryWithExecutable() / "shaders/framework" / app::GetShaderTypeName(GetDevice()->getGraphicsAPI()); 434 | std::filesystem::path appShaderPath = app::GetDirectoryWithExecutable() / "shaders/adaptive_shading" / app::GetShaderTypeName(GetDevice()->getGraphicsAPI()); 435 | 436 | m_RootFs = std::make_shared(); 437 | m_RootFs->mount("/media", mediaPath); 438 | m_RootFs->mount("/shaders/donut", frameworkShaderPath); 439 | m_RootFs->mount("/shaders/app", appShaderPath); 440 | m_RootFs->mount("/native", nativeFS); 441 | 442 | std::filesystem::path scenePath = "/media/glTF-Sample-Models/2.0"; 443 | m_SceneFilesAvailable = FindScenes(*m_RootFs, scenePath); 444 | 445 | if (sceneName.empty() && m_SceneFilesAvailable.empty()) 446 | { 447 | log::fatal("No scene file found in media folder '%s'\n" 448 | "Please make sure that folder contains valid scene files.", scenePath.generic_string().c_str()); 449 | } 450 | 451 | m_TextureCache = std::make_shared(GetDevice(), m_RootFs, nullptr); 452 | 453 | m_ShaderFactory = std::make_shared(GetDevice(), m_RootFs, "/shaders"); 454 | m_CommonPasses = std::make_shared(GetDevice(), m_ShaderFactory); 455 | 456 | m_OpaqueDrawStrategy = std::make_shared(); 457 | m_TransparentDrawStrategy = std::make_shared(); 458 | 459 | m_ShadowMap = std::make_shared(GetDevice(), 2048, 4, 0, nvrhi::Format::D24S8); 460 | m_ShadowMap->SetupProxyViews(); 461 | 462 | m_ShadowFramebuffer = std::make_shared(GetDevice()); 463 | m_ShadowFramebuffer->DepthTarget = m_ShadowMap->GetTexture(); 464 | 465 | DepthPass::CreateParameters shadowDepthParams; 466 | shadowDepthParams.slopeScaledDepthBias = 4.f; 467 | shadowDepthParams.depthBias = 100; 468 | m_ShadowDepthPass = std::make_shared(GetDevice(), m_CommonPasses); 469 | m_ShadowDepthPass->Init(*m_ShaderFactory, shadowDepthParams); 470 | 471 | m_CommandList = GetDevice()->createCommandList(); 472 | 473 | m_FirstPersonCamera.SetMoveSpeed(3.0f); 474 | m_ThirdPersonCamera.SetMoveSpeed(3.0f); 475 | 476 | SetAsynchronousLoadingEnabled(true); 477 | 478 | if (sceneName.empty()) 479 | SetCurrentSceneName(app::FindPreferredScene(m_SceneFilesAvailable, "Sponza.gltf")); 480 | else 481 | SetCurrentSceneName("/native/" + sceneName); 482 | 483 | // Load the environment map 484 | m_EnvironmentMap = m_TextureCache->LoadTextureFromFileDeferred(mediaPath / "environment/space.dds", true); 485 | 486 | CreateLightProbes(4); 487 | 488 | m_tqDepthPrePass = GetDevice()->createTimerQuery(); 489 | m_tqForwardOpaque = GetDevice()->createTimerQuery(); 490 | m_tqForwardSky = GetDevice()->createTimerQuery(); 491 | m_tqForwardTransparent = GetDevice()->createTimerQuery(); 492 | m_tqMotionVector = GetDevice()->createTimerQuery(); 493 | } 494 | 495 | std::shared_ptr GetRootFs() const 496 | { 497 | return m_RootFs; 498 | } 499 | 500 | BaseCamera& GetActiveCamera() const 501 | { 502 | return m_ui.UseThirdPersonCamera ? (BaseCamera&)m_ThirdPersonCamera : (BaseCamera&)m_FirstPersonCamera; 503 | } 504 | 505 | std::vector const& GetAvailableScenes() const 506 | { 507 | return m_SceneFilesAvailable; 508 | } 509 | 510 | std::string GetCurrentSceneName() const 511 | { 512 | return m_CurrentSceneName; 513 | } 514 | 515 | void SetCurrentSceneName(const std::string& sceneName) 516 | { 517 | if (m_CurrentSceneName == sceneName) 518 | return; 519 | 520 | m_CurrentSceneName = sceneName; 521 | 522 | BeginLoadingScene(m_RootFs, m_CurrentSceneName); 523 | } 524 | 525 | void CopyActiveCameraToFirstPerson() 526 | { 527 | if (m_ui.ActiveSceneCamera) 528 | { 529 | dm::affine3 viewToWorld = m_ui.ActiveSceneCamera->GetViewToWorldMatrix(); 530 | dm::float3 cameraPos = viewToWorld.m_translation; 531 | m_FirstPersonCamera.LookAt(cameraPos, cameraPos + viewToWorld.m_linear.row2, viewToWorld.m_linear.row1); 532 | } 533 | else if (m_ui.UseThirdPersonCamera) 534 | { 535 | m_FirstPersonCamera.LookAt(m_ThirdPersonCamera.GetPosition(), m_ThirdPersonCamera.GetPosition() + m_ThirdPersonCamera.GetDir(), m_ThirdPersonCamera.GetUp()); 536 | } 537 | } 538 | 539 | virtual bool KeyboardUpdate(int key, int scancode, int action, int mods) override 540 | { 541 | if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) 542 | { 543 | m_ui.ShowUI = !m_ui.ShowUI; 544 | return true; 545 | } 546 | 547 | if (key == GLFW_KEY_GRAVE_ACCENT && action == GLFW_PRESS) 548 | { 549 | m_ui.ShowConsole = !m_ui.ShowConsole; 550 | return true; 551 | } 552 | 553 | if (key == GLFW_KEY_SPACE && action == GLFW_PRESS) 554 | { 555 | m_ui.EnableAnimations = !m_ui.EnableAnimations; 556 | return true; 557 | } 558 | 559 | if (key == GLFW_KEY_T && action == GLFW_PRESS) 560 | { 561 | CopyActiveCameraToFirstPerson(); 562 | if (m_ui.ActiveSceneCamera) 563 | { 564 | m_ui.UseThirdPersonCamera = false; 565 | m_ui.ActiveSceneCamera = nullptr; 566 | } 567 | else 568 | { 569 | m_ui.UseThirdPersonCamera = !m_ui.UseThirdPersonCamera; 570 | } 571 | return true; 572 | } 573 | 574 | if (!m_ui.ActiveSceneCamera) 575 | GetActiveCamera().KeyboardUpdate(key, scancode, action, mods); 576 | return true; 577 | } 578 | 579 | virtual bool MousePosUpdate(double xpos, double ypos) override 580 | { 581 | if (!m_ui.ActiveSceneCamera) 582 | GetActiveCamera().MousePosUpdate(xpos, ypos); 583 | 584 | m_PickPosition = uint2(static_cast(xpos), static_cast(ypos)); 585 | 586 | return true; 587 | } 588 | 589 | virtual bool MouseButtonUpdate(int button, int action, int mods) override 590 | { 591 | if (!m_ui.ActiveSceneCamera) 592 | GetActiveCamera().MouseButtonUpdate(button, action, mods); 593 | 594 | if (action == GLFW_PRESS && button == GLFW_MOUSE_BUTTON_2) 595 | m_Pick = true; 596 | 597 | return true; 598 | } 599 | 600 | virtual bool MouseScrollUpdate(double xoffset, double yoffset) override 601 | { 602 | if (!m_ui.ActiveSceneCamera) 603 | GetActiveCamera().MouseScrollUpdate(xoffset, yoffset); 604 | 605 | return true; 606 | } 607 | 608 | virtual void Animate(float fElapsedTimeSeconds) override 609 | { 610 | if (!m_ui.ActiveSceneCamera) 611 | { 612 | if (m_ui.UseThirdPersonCamera) 613 | GetActiveCamera().Animate(fElapsedTimeSeconds); 614 | else 615 | m_FirstPersonCamera.AnimateSmooth(fElapsedTimeSeconds); 616 | } 617 | 618 | if(m_ToneMappingPass) 619 | m_ToneMappingPass->AdvanceFrame(fElapsedTimeSeconds); 620 | 621 | if (IsSceneLoaded() && m_ui.EnableAnimations) 622 | { 623 | m_WallclockTime += fElapsedTimeSeconds; 624 | 625 | for (const auto& anim : m_Scene->GetSceneGraph()->GetAnimations()) 626 | { 627 | float duration = anim->GetDuration(); 628 | float integral; 629 | float animationTime = std::modf(m_WallclockTime / duration, &integral) * duration; 630 | (void)anim->Apply(animationTime); 631 | } 632 | } 633 | } 634 | 635 | 636 | virtual void SceneUnloading() override 637 | { 638 | if (m_ForwardPass) m_ForwardPass->ResetBindingCache(); 639 | if (m_DeferredLightingPass) m_DeferredLightingPass->ResetBindingCache(); 640 | if (m_GBufferPass) m_GBufferPass->ResetBindingCache(); 641 | if (m_LightProbePass) m_LightProbePass->ResetCaches(); 642 | if (m_ShadowDepthPass) m_ShadowDepthPass->ResetBindingCache(); 643 | if (m_DepthPrePass) m_DepthPrePass->ResetBindingCache(); 644 | m_BindingCache.Clear(); 645 | m_SunLight.reset(); 646 | m_ui.SelectedMaterial = nullptr; 647 | m_ui.SelectedNode = nullptr; 648 | 649 | for (auto probe : m_LightProbes) 650 | { 651 | probe->enabled = false; 652 | } 653 | } 654 | 655 | virtual bool LoadScene(std::shared_ptr fs, const std::filesystem::path& fileName) override 656 | { 657 | using namespace std::chrono; 658 | 659 | Scene* scene = new Scene(GetDevice(), *m_ShaderFactory, fs, m_TextureCache, nullptr, nullptr); 660 | 661 | auto startTime = high_resolution_clock::now(); 662 | 663 | if (scene->Load(fileName)) 664 | { 665 | m_Scene = std::unique_ptr(scene); 666 | 667 | auto endTime = high_resolution_clock::now(); 668 | auto duration = duration_cast(endTime - startTime).count(); 669 | log::info("Scene loading time: %llu ms", duration); 670 | 671 | return true; 672 | } 673 | 674 | return false; 675 | } 676 | 677 | virtual void SceneLoaded() override 678 | { 679 | Super::SceneLoaded(); 680 | 681 | m_Scene->FinishedLoading(GetFrameIndex()); 682 | 683 | m_WallclockTime = 0.f; 684 | m_PreviousViewsValid = false; 685 | 686 | for (auto light : m_Scene->GetSceneGraph()->GetLights()) 687 | { 688 | if (light->GetLightType() == LightType_Directional) 689 | { 690 | m_SunLight = std::static_pointer_cast(light); 691 | break; 692 | } 693 | } 694 | 695 | if (!m_SunLight) 696 | { 697 | m_SunLight = std::make_shared(); 698 | m_SunLight->angularSize = 0.53f; 699 | m_SunLight->irradiance = 1.f; 700 | 701 | auto node = std::make_shared(); 702 | node->SetLeaf(m_SunLight); 703 | m_SunLight->SetDirection(dm::double3(0.1, -0.9, 0.1)); 704 | m_SunLight->SetName("Sun"); 705 | m_Scene->GetSceneGraph()->Attach(m_Scene->GetSceneGraph()->GetRootNode(), node); 706 | } 707 | 708 | auto cameras = m_Scene->GetSceneGraph()->GetCameras(); 709 | if (!cameras.empty()) 710 | { 711 | m_ui.ActiveSceneCamera = cameras[0]; 712 | } 713 | else 714 | { 715 | m_ui.ActiveSceneCamera.reset(); 716 | 717 | m_FirstPersonCamera.LookAt( 718 | float3(0.f, 1.8f, 0.f), 719 | float3(1.f, 1.8f, 0.f)); 720 | m_CameraVerticalFov = 60.f; 721 | } 722 | 723 | m_ThirdPersonCamera.SetRotation(dm::radians(135.f), dm::radians(20.f)); 724 | PointThirdPersonCameraAt(m_Scene->GetSceneGraph()->GetRootNode()); 725 | 726 | m_ui.UseThirdPersonCamera = false; 727 | 728 | CopyActiveCameraToFirstPerson(); 729 | 730 | if (g_PrintSceneGraph) 731 | PrintSceneGraph(m_Scene->GetSceneGraph()->GetRootNode()); 732 | } 733 | 734 | void PointThirdPersonCameraAt(const std::shared_ptr& node) 735 | { 736 | dm::box3 bounds = node->GetGlobalBoundingBox(); 737 | m_ThirdPersonCamera.SetTargetPosition(bounds.center()); 738 | float radius = length(bounds.diagonal()) * 0.5f; 739 | float distance = radius / sinf(dm::radians(m_CameraVerticalFov * 0.5f)); 740 | m_ThirdPersonCamera.SetDistance(distance); 741 | m_ThirdPersonCamera.Animate(0.f); 742 | } 743 | 744 | bool IsStereo() 745 | { 746 | return m_ui.Stereo; 747 | } 748 | 749 | std::shared_ptr GetTextureCache() 750 | { 751 | return m_TextureCache; 752 | } 753 | 754 | std::shared_ptr GetScene() 755 | { 756 | return m_Scene; 757 | } 758 | 759 | bool SetupView() 760 | { 761 | float2 renderTargetSize = float2(m_RenderTargets->GetSize()); 762 | 763 | if (m_TemporalAntiAliasingPass) 764 | m_TemporalAntiAliasingPass->SetJitter(m_ui.TemporalAntiAliasingJitter); 765 | 766 | float2 pixelOffset = m_ui.AntiAliasingMode == AntiAliasingMode::TEMPORAL && m_TemporalAntiAliasingPass 767 | ? m_TemporalAntiAliasingPass->GetCurrentPixelOffset() 768 | : float2(0.f); 769 | 770 | std::shared_ptr stereoView = std::dynamic_pointer_cast(m_View); 771 | std::shared_ptr planarView = std::dynamic_pointer_cast(m_View); 772 | 773 | dm::affine3 viewMatrix; 774 | float verticalFov = dm::radians(m_CameraVerticalFov); 775 | float zNear = 0.01f; 776 | if (m_ui.ActiveSceneCamera) 777 | { 778 | auto perspectiveCamera = std::dynamic_pointer_cast(m_ui.ActiveSceneCamera); 779 | if (perspectiveCamera) 780 | { 781 | zNear = perspectiveCamera->zNear; 782 | verticalFov = perspectiveCamera->verticalFov; 783 | } 784 | 785 | viewMatrix = m_ui.ActiveSceneCamera->GetWorldToViewMatrix(); 786 | } 787 | else 788 | { 789 | viewMatrix = GetActiveCamera().GetWorldToViewMatrix(); 790 | } 791 | 792 | bool topologyChanged = false; 793 | 794 | if (IsStereo()) 795 | { 796 | if (!stereoView) 797 | { 798 | m_View = stereoView = std::make_shared(); 799 | m_ViewPrevious = std::make_shared(); 800 | topologyChanged = true; 801 | } 802 | 803 | stereoView->LeftView.SetViewport(nvrhi::Viewport(renderTargetSize.x * 0.5f, renderTargetSize.y)); 804 | stereoView->LeftView.SetPixelOffset(pixelOffset); 805 | 806 | stereoView->RightView.SetViewport(nvrhi::Viewport(renderTargetSize.x * 0.5f, renderTargetSize.x, 0.f, renderTargetSize.y, 0.f, 1.f)); 807 | stereoView->RightView.SetPixelOffset(pixelOffset); 808 | 809 | { 810 | float4x4 projection = perspProjD3DStyleReverse(verticalFov, renderTargetSize.x / renderTargetSize.y * 0.5f, zNear); 811 | 812 | affine3 leftView = viewMatrix; 813 | stereoView->LeftView.SetMatrices(leftView, projection); 814 | 815 | affine3 rightView = leftView; 816 | rightView.m_translation -= float3(0.2f, 0, 0); 817 | stereoView->RightView.SetMatrices(rightView, projection); 818 | } 819 | 820 | stereoView->LeftView.UpdateCache(); 821 | stereoView->RightView.UpdateCache(); 822 | 823 | m_ThirdPersonCamera.SetView(stereoView->LeftView); 824 | 825 | if (topologyChanged) 826 | { 827 | *std::static_pointer_cast(m_ViewPrevious) = *std::static_pointer_cast(m_View); 828 | } 829 | } 830 | else 831 | { 832 | if (!planarView) 833 | { 834 | m_View = planarView = std::make_shared(); 835 | m_ViewPrevious = std::make_shared(); 836 | topologyChanged = true; 837 | } 838 | 839 | float4x4 projection = perspProjD3DStyleReverse(verticalFov, renderTargetSize.x / renderTargetSize.y, zNear); 840 | 841 | planarView->SetViewport(nvrhi::Viewport(renderTargetSize.x, renderTargetSize.y)); 842 | planarView->SetPixelOffset(pixelOffset); 843 | 844 | planarView->SetMatrices(viewMatrix, projection); 845 | planarView->UpdateCache(); 846 | 847 | m_ThirdPersonCamera.SetView(*planarView); 848 | 849 | if (topologyChanged) 850 | { 851 | *std::static_pointer_cast(m_ViewPrevious) = *std::static_pointer_cast(m_View); 852 | } 853 | } 854 | 855 | return topologyChanged; 856 | } 857 | 858 | void CreateRenderPasses(bool& exposureResetRequired) 859 | { 860 | uint32_t motionVectorStencilMask = 0x01; 861 | 862 | ForwardShadingPass::CreateParameters ForwardParams; 863 | ForwardParams.trackLiveness = false; 864 | m_ForwardPass = std::make_unique(GetDevice(), m_CommonPasses); 865 | m_ForwardPass->Init(*m_ShaderFactory, ForwardParams); 866 | 867 | DepthPass::CreateParameters DepthParams; 868 | DepthParams.trackLiveness = false; 869 | m_DepthPrePass = std::make_unique(GetDevice(), m_CommonPasses); 870 | m_DepthPrePass->Init(*m_ShaderFactory, DepthParams); 871 | 872 | GBufferFillPass::CreateParameters GBufferParams; 873 | GBufferParams.enableMotionVectors = true; 874 | GBufferParams.stencilWriteMask = motionVectorStencilMask; 875 | m_GBufferPass = std::make_unique(GetDevice(), m_CommonPasses); 876 | m_GBufferPass->Init(*m_ShaderFactory, GBufferParams); 877 | 878 | GBufferParams.enableMotionVectors = false; 879 | m_MaterialIDPass = std::make_unique(GetDevice(), m_CommonPasses); 880 | m_MaterialIDPass->Init(*m_ShaderFactory, GBufferParams); 881 | 882 | m_PixelReadbackPass = std::make_unique(GetDevice(), m_ShaderFactory, m_RenderTargets->MaterialIDs, nvrhi::Format::RGBA32_UINT); 883 | 884 | m_DeferredLightingPass = std::make_unique(GetDevice(), m_CommonPasses); 885 | m_DeferredLightingPass->Init(m_ShaderFactory); 886 | 887 | m_SkyPass = std::make_unique(GetDevice(), m_ShaderFactory, m_CommonPasses, m_RenderTargets->ForwardFramebuffer, *m_View); 888 | 889 | { 890 | TemporalAntiAliasingPass::CreateParameters taaParams; 891 | taaParams.sourceDepth = m_RenderTargets->Depth; 892 | taaParams.motionVectors = m_RenderTargets->MotionVectors; 893 | taaParams.unresolvedColor = m_RenderTargets->HdrColor; 894 | taaParams.resolvedColor = m_RenderTargets->ResolvedColor; 895 | taaParams.feedback1 = m_RenderTargets->TemporalFeedback1; 896 | taaParams.feedback2 = m_RenderTargets->TemporalFeedback2; 897 | taaParams.motionVectorStencilMask = motionVectorStencilMask; 898 | taaParams.useCatmullRomFilter = true; 899 | 900 | m_TemporalAntiAliasingPass = std::make_unique(GetDevice(), m_ShaderFactory, m_CommonPasses, *m_View, taaParams); 901 | } 902 | 903 | if (m_RenderTargets->GetSampleCount() == 1) 904 | { 905 | m_SsaoPass = std::make_unique(GetDevice(), m_ShaderFactory, m_CommonPasses, m_RenderTargets->Depth, m_RenderTargets->GBufferNormals, m_RenderTargets->AmbientOcclusion); 906 | } 907 | 908 | m_LightProbePass = std::make_shared(GetDevice(), m_ShaderFactory, m_CommonPasses); 909 | 910 | nvrhi::BufferHandle exposureBuffer = nullptr; 911 | if (m_ToneMappingPass) 912 | exposureBuffer = m_ToneMappingPass->GetExposureBuffer(); 913 | else 914 | exposureResetRequired = true; 915 | 916 | ToneMappingPass::CreateParameters toneMappingParams; 917 | toneMappingParams.exposureBufferOverride = exposureBuffer; 918 | m_ToneMappingPass = std::make_unique(GetDevice(), m_ShaderFactory, m_CommonPasses, m_RenderTargets->LdrFramebuffer, *m_View, toneMappingParams); 919 | 920 | m_BloomPass = std::make_unique(GetDevice(), m_ShaderFactory, m_CommonPasses, m_RenderTargets->ResolvedFramebuffer, *m_View); 921 | 922 | m_PreviousViewsValid = false; 923 | 924 | InitNASDataPass(); 925 | InitShadingRatePass(); 926 | InitVRSRateVisPass(); 927 | InitShadingRateSmoothPass(); 928 | } 929 | 930 | // NAS-related functions begin here 931 | // Creating required pipeline state and resources for NAS 932 | void InitNASDataPass() 933 | { 934 | m_NASDataPass.Shader = m_ShaderFactory->CreateShader("app/ComputeNASData", "main_cs", nullptr, nvrhi::ShaderType::Compute); 935 | if (!m_NASDataPass.Shader) 936 | { 937 | log::fatal("Cannot compile VRS rate shader"); 938 | } 939 | 940 | nvrhi::BindingLayoutDesc layoutDesc; 941 | layoutDesc.visibility = nvrhi::ShaderType::Compute; 942 | layoutDesc.bindings = { 943 | nvrhi::BindingLayoutItem::VolatileConstantBuffer(0), 944 | nvrhi::BindingLayoutItem::Texture_UAV(0), 945 | nvrhi::BindingLayoutItem::Texture_SRV(0) 946 | }; 947 | m_NASDataPass.BindingLayout = GetDevice()->createBindingLayout(layoutDesc); 948 | 949 | nvrhi::BufferDesc constantBufferDesc; 950 | constantBufferDesc.byteSize = sizeof(ComputeNASDataConstants); 951 | constantBufferDesc.debugName = "ComputeNASDataConstants"; 952 | constantBufferDesc.isConstantBuffer = true; 953 | constantBufferDesc.isVolatile = true; 954 | constantBufferDesc.maxVersions = engine::c_MaxRenderPassConstantBufferVersions; 955 | m_NASDataPass.ConstantBuffer = GetDevice()->createBuffer(constantBufferDesc); 956 | 957 | nvrhi::BindingSetDesc bindingSetDesc; 958 | bindingSetDesc.bindings = { 959 | nvrhi::BindingSetItem::ConstantBuffer(0, m_NASDataPass.ConstantBuffer), 960 | nvrhi::BindingSetItem::Texture_UAV(0, m_RenderTargets->m_NASDataSurface, nvrhi::Format::RG16_FLOAT), 961 | nvrhi::BindingSetItem::Texture_SRV(0, m_RenderTargets->LdrColor, nvrhi::Format::SRGBA8_UNORM) // TODO: should bind it as RGB? 962 | }; 963 | m_NASDataPass.BindingSet = GetDevice()->createBindingSet(bindingSetDesc, m_NASDataPass.BindingLayout); 964 | 965 | nvrhi::ComputePipelineDesc psoDesc = {}; 966 | psoDesc.CS = m_NASDataPass.Shader; 967 | psoDesc.bindingLayouts = { m_NASDataPass.BindingLayout }; 968 | 969 | m_NASDataPass.Pipeline = GetDevice()->createComputePipeline(psoDesc); 970 | } 971 | 972 | void InitShadingRatePass() 973 | { 974 | m_ShadingRatePass.Shader = m_ShaderFactory->CreateShader("app/ComputeShadingRate", "main_cs", nullptr, nvrhi::ShaderType::Compute); 975 | if (!m_ShadingRatePass.Shader) 976 | { 977 | log::fatal("Cannot compile VRS rate shader"); 978 | } 979 | 980 | nvrhi::BindingLayoutDesc layoutDesc; 981 | layoutDesc.visibility = nvrhi::ShaderType::Compute; 982 | layoutDesc.bindings = { 983 | nvrhi::BindingLayoutItem::VolatileConstantBuffer(0), 984 | nvrhi::BindingLayoutItem::Sampler(0), 985 | nvrhi::BindingLayoutItem::Texture_UAV(0), 986 | nvrhi::BindingLayoutItem::Texture_SRV(0), 987 | nvrhi::BindingLayoutItem::Texture_SRV(1) 988 | }; 989 | m_ShadingRatePass.BindingLayout = GetDevice()->createBindingLayout(layoutDesc); 990 | 991 | nvrhi::BufferDesc constantBufferDesc; 992 | constantBufferDesc.byteSize = sizeof(AdaptiveShadingConstants); 993 | constantBufferDesc.debugName = "NASRatePassConstants"; 994 | constantBufferDesc.isConstantBuffer = true; 995 | constantBufferDesc.isVolatile = true; 996 | constantBufferDesc.maxVersions = engine::c_MaxRenderPassConstantBufferVersions; 997 | m_ShadingRatePass.ConstantBuffer = GetDevice()->createBuffer(constantBufferDesc); 998 | 999 | nvrhi::SamplerDesc samplerDesc; 1000 | samplerDesc.setAddressU(nvrhi::SamplerAddressMode::Wrap).setAddressV(nvrhi::SamplerAddressMode::Wrap).setAddressW(nvrhi::SamplerAddressMode::Border); 1001 | samplerDesc.borderColor = nvrhi::Color(0.0f); 1002 | m_BilinearSampler = GetDevice()->createSampler(samplerDesc); 1003 | 1004 | nvrhi::BindingSetDesc bindingSetDesc; 1005 | bindingSetDesc.bindings = { 1006 | nvrhi::BindingSetItem::ConstantBuffer(0, m_ShadingRatePass.ConstantBuffer), 1007 | nvrhi::BindingSetItem::Sampler(0, m_BilinearSampler), 1008 | nvrhi::BindingSetItem::Texture_UAV(0, m_RenderTargets->m_VRSRateSurface), 1009 | nvrhi::BindingSetItem::Texture_SRV(0, m_RenderTargets->Depth), 1010 | nvrhi::BindingSetItem::Texture_SRV(1, m_RenderTargets->m_NASDataSurface) 1011 | }; 1012 | m_ShadingRatePass.BindingSet = GetDevice()->createBindingSet(bindingSetDesc, m_ShadingRatePass.BindingLayout); 1013 | 1014 | nvrhi::ComputePipelineDesc psoDesc = {}; 1015 | psoDesc.CS = m_ShadingRatePass.Shader; 1016 | psoDesc.bindingLayouts = { m_ShadingRatePass.BindingLayout }; 1017 | 1018 | m_ShadingRatePass.Pipeline = GetDevice()->createComputePipeline(psoDesc); 1019 | 1020 | } 1021 | 1022 | void InitShadingRateSmoothPass() 1023 | { 1024 | m_ShadingRateSmoothPass.Shader = m_ShaderFactory->CreateShader("app/SmoothShadingRate", "main_cs", nullptr, nvrhi::ShaderType::Compute); 1025 | if (!m_ShadingRateSmoothPass.Shader) 1026 | { 1027 | log::fatal("Cannot compile VRS rate shader"); 1028 | } 1029 | 1030 | nvrhi::BindingLayoutDesc layoutDesc; 1031 | layoutDesc.visibility = nvrhi::ShaderType::Compute; 1032 | layoutDesc.bindings = { 1033 | nvrhi::BindingLayoutItem::Texture_UAV(0), 1034 | }; 1035 | m_ShadingRateSmoothPass.BindingLayout = GetDevice()->createBindingLayout(layoutDesc); 1036 | 1037 | nvrhi::BindingSetDesc bindingSetDesc; 1038 | bindingSetDesc.bindings = { 1039 | nvrhi::BindingSetItem::Texture_UAV(0, m_RenderTargets->m_VRSRateSurface), 1040 | }; 1041 | m_ShadingRateSmoothPass.BindingSet = GetDevice()->createBindingSet(bindingSetDesc, m_ShadingRateSmoothPass.BindingLayout); 1042 | 1043 | nvrhi::ComputePipelineDesc psoDesc = {}; 1044 | psoDesc.CS = m_ShadingRateSmoothPass.Shader; 1045 | psoDesc.bindingLayouts = { m_ShadingRateSmoothPass.BindingLayout }; 1046 | 1047 | m_ShadingRateSmoothPass.Pipeline = GetDevice()->createComputePipeline(psoDesc); 1048 | 1049 | } 1050 | 1051 | // Shading passes to calculate shading rate surface 1052 | void ComputeNASData() 1053 | { 1054 | ComputeNASDataConstants NASDataPassConstants = {}; 1055 | NASDataPassConstants.brightnessSensitivity = m_ui.NASBrightnessSensitivity; 1056 | m_CommandList->writeBuffer(m_NASDataPass.ConstantBuffer, &NASDataPassConstants, sizeof(NASDataPassConstants)); 1057 | 1058 | nvrhi::ComputeState state; 1059 | state.pipeline = m_NASDataPass.Pipeline; 1060 | state.bindings = { m_NASDataPass.BindingSet }; 1061 | m_CommandList->setComputeState(state); 1062 | 1063 | // Dispatch call to generate the VRS surface 1064 | m_CommandList->dispatch(m_RenderTargets->m_VRSSurfaceSize.x, m_RenderTargets->m_VRSSurfaceSize.y, 1); 1065 | } 1066 | 1067 | void ComputeVRSRateSurface() 1068 | { 1069 | const IView* view = m_View->GetChildView(ViewType::PLANAR, 0); // TODO: support multiple views (VR) 1070 | const IView* viewPrevious = m_ViewPrevious->GetChildView(ViewType::PLANAR, 0); 1071 | 1072 | nvrhi::ViewportState viewportState = view->GetViewportState(); 1073 | nvrhi::ViewportState prevViewportState = viewPrevious->GetViewportState(); 1074 | 1075 | // This pass only works for planar, single-viewport views 1076 | assert(viewportState.viewports.size() == 1 && prevViewportState.viewports.size() == 1); 1077 | 1078 | const nvrhi::Viewport& prevViewport = prevViewportState.viewports[0]; 1079 | 1080 | AdaptiveShadingConstants ASRatePassConstants = {}; 1081 | affine3 viewReprojection = inverse(view->GetViewMatrix()) * viewPrevious->GetViewMatrix(); 1082 | ASRatePassConstants.reprojectionMatrix = inverse(view->GetProjectionMatrix(false)) * affineToHomogeneous(viewReprojection) * viewPrevious->GetProjectionMatrix(false); 1083 | ASRatePassConstants.previousViewOrigin = uint2(uint(floorf(prevViewport.minX)), uint(floorf(prevViewport.minY))); 1084 | ASRatePassConstants.previousViewSize = uint2(uint(floorf(prevViewport.width())), uint(floorf(prevViewport.height()))); 1085 | ASRatePassConstants.sourceTextureSizeInv = float2(1.f / m_RenderTargets->GetSize().x, 1.f / m_RenderTargets->GetSize().y); 1086 | ASRatePassConstants.errorSensitivity = m_ui.NASErrorSensitivity; 1087 | ASRatePassConstants.motionSensitivity = m_ui.NASMotionSensitivity; 1088 | 1089 | m_CommandList->writeBuffer(m_ShadingRatePass.ConstantBuffer, &ASRatePassConstants, sizeof(ASRatePassConstants)); 1090 | 1091 | nvrhi::ComputeState state; 1092 | state.pipeline = m_ShadingRatePass.Pipeline; 1093 | state.bindings = { m_ShadingRatePass.BindingSet }; 1094 | m_CommandList->setComputeState(state); 1095 | 1096 | // Dispatch call to generate the VRS surface 1097 | m_CommandList->dispatch(m_RenderTargets->m_VRSSurfaceSize.x, m_RenderTargets->m_VRSSurfaceSize.y, 1); 1098 | } 1099 | 1100 | void SmoothVRSRateSurface() 1101 | { 1102 | nvrhi::ComputeState state; 1103 | state.pipeline = m_ShadingRateSmoothPass.Pipeline; 1104 | state.bindings = { m_ShadingRateSmoothPass.BindingSet }; 1105 | m_CommandList->setComputeState(state); 1106 | 1107 | // Dispatch call to smooth the VRS surface 1108 | m_CommandList->dispatch((m_RenderTargets->m_VRSSurfaceSize.x + 15) / 16, (m_RenderTargets->m_VRSSurfaceSize.y + 15) / 16, 1); 1109 | } 1110 | 1111 | // special pass to visualize/debug the shading rate surface 1112 | void InitVRSRateVisPass() 1113 | { 1114 | m_VRSRateVisPass.VS = m_ShaderFactory->CreateShader("app/ShadingRateVis", "main_vs", nullptr, nvrhi::ShaderType::Vertex); 1115 | m_VRSRateVisPass.PS = m_ShaderFactory->CreateShader("app/ShadingRateVis", "main_ps", nullptr, nvrhi::ShaderType::Pixel); 1116 | 1117 | nvrhi::IFramebuffer* framebuffer = GetDeviceManager()->GetCurrentFramebuffer(); 1118 | m_VRSRateVisPass.Framebuffer = framebuffer; 1119 | 1120 | nvrhi::BindingLayoutDesc layoutDesc; 1121 | 1122 | layoutDesc.visibility = nvrhi::ShaderType::Pixel; 1123 | layoutDesc.bindings = { 1124 | nvrhi::BindingLayoutItem::Texture_SRV(0), 1125 | nvrhi::BindingLayoutItem::Texture_SRV(1) 1126 | }; 1127 | 1128 | m_VRSRateVisPass.BindingLayout = GetDevice()->createBindingLayout(layoutDesc); 1129 | 1130 | nvrhi::BindingSetDesc bindingDesc; 1131 | 1132 | bindingDesc.bindings = { 1133 | nvrhi::BindingSetItem::Texture_SRV(0, m_RenderTargets->m_VRSRateSurface, nvrhi::Format::R8_UINT), 1134 | nvrhi::BindingSetItem::Texture_SRV(1, m_RenderTargets->MotionVectors, nvrhi::Format::RG16_FLOAT) 1135 | }; 1136 | 1137 | m_VRSRateVisPass.BindingSet = GetDevice()->createBindingSet(bindingDesc, m_VRSRateVisPass.BindingLayout); 1138 | 1139 | nvrhi::GraphicsPipelineDesc psoDesc; 1140 | psoDesc.bindingLayouts = { m_VRSRateVisPass.BindingLayout }; 1141 | psoDesc.VS = m_VRSRateVisPass.VS; 1142 | psoDesc.PS = m_VRSRateVisPass.PS; 1143 | psoDesc.primType = nvrhi::PrimitiveType::TriangleStrip; 1144 | psoDesc.renderState.rasterState.cullMode = nvrhi::RasterCullMode::None; 1145 | psoDesc.renderState.depthStencilState.depthTestEnable = false; 1146 | psoDesc.renderState.depthStencilState.depthWriteEnable = false; 1147 | psoDesc.renderState.depthStencilState.stencilEnable = false; 1148 | psoDesc.renderState.blendState.targets[0].blendEnable = true; 1149 | psoDesc.renderState.blendState.targets[0].srcBlend = nvrhi::BlendFactor::SrcAlpha; 1150 | psoDesc.renderState.blendState.targets[0].destBlend = nvrhi::BlendFactor::InvSrcAlpha; 1151 | psoDesc.renderState.blendState.targets[0].blendOp = nvrhi::BlendOp::Add; 1152 | psoDesc.renderState.blendState.targets[0].srcBlendAlpha = nvrhi::BlendFactor::One; 1153 | psoDesc.renderState.blendState.targets[0].destBlendAlpha = nvrhi::BlendFactor::Zero; 1154 | psoDesc.renderState.blendState.targets[0].blendOpAlpha = nvrhi::BlendOp::Add; 1155 | 1156 | m_VRSRateVisPass.Pipeline = GetDevice()->createGraphicsPipeline(psoDesc, framebuffer); 1157 | } 1158 | 1159 | void RenderVRSRateVisualization(nvrhi::IFramebuffer* framebuffer) 1160 | { 1161 | nvrhi::FramebufferInfo const& fbInfo = framebuffer->getFramebufferInfo(); 1162 | nvrhi::Viewport viewport = nvrhi::Viewport(float(fbInfo.width), float(fbInfo.height)); 1163 | 1164 | nvrhi::GraphicsState state; 1165 | state.pipeline = m_VRSRateVisPass.Pipeline; 1166 | state.framebuffer = framebuffer; 1167 | state.bindings = { m_VRSRateVisPass.BindingSet }; 1168 | state.viewport.addViewport(viewport); 1169 | state.viewport.addScissorRect(nvrhi::Rect(viewport)); 1170 | 1171 | m_CommandList->setGraphicsState(state); 1172 | 1173 | nvrhi::DrawArguments args; 1174 | args.instanceCount = 1; 1175 | args.vertexCount = 4; 1176 | m_CommandList->draw(args); 1177 | } 1178 | // NAS-specific functions end here 1179 | 1180 | virtual void RenderSplashScreen(nvrhi::IFramebuffer* framebuffer) override 1181 | { 1182 | nvrhi::ITexture* framebufferTexture = framebuffer->getDesc().colorAttachments[0].texture; 1183 | m_CommandList->open(); 1184 | m_CommandList->clearTextureFloat(framebufferTexture, nvrhi::AllSubresources, nvrhi::Color(0.f)); 1185 | m_CommandList->close(); 1186 | GetDevice()->executeCommandList(m_CommandList); 1187 | GetDeviceManager()->SetVsyncEnabled(true); 1188 | } 1189 | 1190 | virtual void RenderScene(nvrhi::IFramebuffer* framebuffer) override 1191 | { 1192 | GetDevice()->resetTimerQuery(m_tqDepthPrePass); 1193 | GetDevice()->resetTimerQuery(m_tqForwardOpaque); 1194 | GetDevice()->resetTimerQuery(m_tqForwardSky); 1195 | GetDevice()->resetTimerQuery(m_tqForwardTransparent); 1196 | GetDevice()->resetTimerQuery(m_tqMotionVector); 1197 | 1198 | int windowWidth, windowHeight; 1199 | GetDeviceManager()->GetWindowDimensions(windowWidth, windowHeight); 1200 | nvrhi::Viewport windowViewport = nvrhi::Viewport(float(windowWidth), float(windowHeight)); 1201 | nvrhi::Viewport renderViewport = windowViewport; 1202 | 1203 | m_Scene->RefreshSceneGraph(GetFrameIndex()); 1204 | 1205 | bool exposureResetRequired = false; 1206 | 1207 | { 1208 | uint width = windowWidth; 1209 | uint height = windowHeight; 1210 | 1211 | uint sampleCount = 1; 1212 | switch (m_ui.AntiAliasingMode) 1213 | { 1214 | case AntiAliasingMode::MSAA_2X: sampleCount = 2; break; 1215 | case AntiAliasingMode::MSAA_4X: sampleCount = 4; break; 1216 | case AntiAliasingMode::MSAA_8X: sampleCount = 8; break; 1217 | default:; 1218 | } 1219 | 1220 | bool needNewPasses = false; 1221 | 1222 | if (!m_RenderTargets || m_RenderTargets->IsUpdateRequired(uint2(width, height), sampleCount)) 1223 | { 1224 | m_RenderTargets = nullptr; 1225 | m_BindingCache.Clear(); 1226 | m_RenderTargets = std::make_unique(); 1227 | m_RenderTargets->Init(GetDevice(), uint2(width, height), sampleCount, true, true); 1228 | 1229 | needNewPasses = true; 1230 | } 1231 | 1232 | if (SetupView()) 1233 | { 1234 | needNewPasses = true; 1235 | } 1236 | 1237 | if (m_ui.ShaderReloadRequested) 1238 | { 1239 | m_ShaderFactory->ClearCache(); 1240 | needNewPasses = true; 1241 | } 1242 | 1243 | if(needNewPasses) 1244 | { 1245 | CreateRenderPasses(exposureResetRequired); 1246 | } 1247 | 1248 | m_ui.ShaderReloadRequested = false; 1249 | } 1250 | 1251 | m_CommandList->open(); 1252 | 1253 | m_Scene->RefreshBuffers(m_CommandList, GetFrameIndex()); 1254 | 1255 | nvrhi::ITexture* framebufferTexture = framebuffer->getDesc().colorAttachments[0].texture; 1256 | m_CommandList->clearTextureFloat(framebufferTexture, nvrhi::AllSubresources, nvrhi::Color(0.f)); 1257 | 1258 | m_AmbientTop = m_ui.AmbientIntensity * m_ui.SkyParams.skyColor * m_ui.SkyParams.brightness; 1259 | m_AmbientBottom = m_ui.AmbientIntensity * m_ui.SkyParams.groundColor * m_ui.SkyParams.brightness; 1260 | if (m_ui.EnableShadows) 1261 | { 1262 | m_SunLight->shadowMap = m_ShadowMap; 1263 | box3 sceneBounds = m_Scene->GetSceneGraph()->GetRootNode()->GetGlobalBoundingBox(); 1264 | 1265 | frustum projectionFrustum = m_View->GetProjectionFrustum(); 1266 | const float maxShadowDistance = 100.f; 1267 | 1268 | dm::affine3 viewMatrixInv = m_View->GetChildView(ViewType::PLANAR, 0)->GetInverseViewMatrix(); 1269 | 1270 | float zRange = length(sceneBounds.diagonal()) * 0.5f; 1271 | m_ShadowMap->SetupForPlanarViewStable(*m_SunLight, projectionFrustum, viewMatrixInv, maxShadowDistance, zRange, zRange, m_ui.CsmExponent); 1272 | 1273 | m_ShadowMap->Clear(m_CommandList); 1274 | 1275 | DepthPass::Context context; 1276 | 1277 | RenderCompositeView(m_CommandList, 1278 | &m_ShadowMap->GetView(), nullptr, 1279 | *m_ShadowFramebuffer, 1280 | m_Scene->GetSceneGraph()->GetRootNode(), 1281 | *m_OpaqueDrawStrategy, 1282 | *m_ShadowDepthPass, 1283 | context, 1284 | "ShadowMap", 1285 | m_ui.EnableMaterialEvents); 1286 | } 1287 | else 1288 | { 1289 | m_SunLight->shadowMap = nullptr; 1290 | } 1291 | 1292 | std::vector> lightProbes; 1293 | if (m_ui.EnableLightProbe) 1294 | { 1295 | for (auto probe : m_LightProbes) 1296 | { 1297 | if (probe->enabled) 1298 | { 1299 | probe->diffuseScale = m_ui.LightProbeDiffuseScale; 1300 | probe->specularScale = m_ui.LightProbeSpecularScale; 1301 | lightProbes.push_back(probe); 1302 | } 1303 | } 1304 | } 1305 | 1306 | m_RenderTargets->Clear(m_CommandList); 1307 | 1308 | DepthPass::Context depthPrePassContext; 1309 | 1310 | m_CommandList->beginTimerQuery(m_tqDepthPrePass); 1311 | RenderCompositeView(m_CommandList, 1312 | m_View.get(), m_ViewPrevious.get(), 1313 | *m_RenderTargets->DepthPrePassFramebuffer, 1314 | m_Scene->GetSceneGraph()->GetRootNode(), 1315 | *m_OpaqueDrawStrategy, 1316 | *m_DepthPrePass, 1317 | depthPrePassContext, 1318 | "DepthOnly", 1319 | m_ui.EnableMaterialEvents); 1320 | m_CommandList->endTimerQuery(m_tqDepthPrePass); 1321 | 1322 | m_CommandList->beginTimerQuery(m_tqMotionVector); 1323 | if (m_PreviousViewsValid) 1324 | { 1325 | m_TemporalAntiAliasingPass->RenderMotionVectors(m_CommandList, *m_View, *m_ViewPrevious); 1326 | } 1327 | m_CommandList->endTimerQuery(m_tqMotionVector); 1328 | 1329 | // After motion vectors are ready, we can compute the VRS shading rate surface 1330 | if (m_ui.EnableNAS) 1331 | { 1332 | ComputeNASData(); 1333 | ComputeVRSRateSurface(); 1334 | if (m_ui.EnableShadingRateSurfaceSmoothing) 1335 | { 1336 | SmoothVRSRateSurface(); 1337 | } 1338 | } 1339 | 1340 | if (exposureResetRequired) 1341 | m_ToneMappingPass->ResetExposure(m_CommandList, 0.5f); 1342 | 1343 | ForwardShadingPass::Context forwardContext; 1344 | 1345 | if (!m_ui.UseDeferredShading || m_ui.EnableTranslucency) 1346 | { 1347 | m_ForwardPass->PrepareLights(forwardContext, m_CommandList, m_Scene->GetSceneGraph()->GetLights(), m_AmbientTop, m_AmbientBottom, lightProbes); 1348 | } 1349 | 1350 | // Enable the VRS rate surface, all future draw calls will be affected by the VRS rates 1351 | if (!IsStereo()) 1352 | { 1353 | std::shared_ptr planarView = std::dynamic_pointer_cast(m_View); 1354 | if (m_ui.EnableNAS) 1355 | { 1356 | planarView->SetVariableRateShadingState(nvrhi::VariableRateShadingState().setEnabled(true).setShadingRate(nvrhi::VariableShadingRate::e1x1).setImageCombiner(nvrhi::ShadingRateCombiner::Override)); 1357 | } 1358 | else 1359 | { 1360 | planarView->SetVariableRateShadingState(nvrhi::VariableRateShadingState().setEnabled(false)); 1361 | } 1362 | } 1363 | 1364 | m_CommandList->beginTimerQuery(m_tqForwardOpaque); 1365 | if (m_ui.UseDeferredShading) 1366 | { 1367 | GBufferFillPass::Context gbufferContext; 1368 | 1369 | RenderCompositeView(m_CommandList, 1370 | m_View.get(), m_ViewPrevious.get(), 1371 | *m_RenderTargets->GBufferFramebuffer, 1372 | m_Scene->GetSceneGraph()->GetRootNode(), 1373 | *m_OpaqueDrawStrategy, 1374 | *m_GBufferPass, 1375 | gbufferContext, 1376 | "GBufferFill", 1377 | m_ui.EnableMaterialEvents); 1378 | 1379 | nvrhi::ITexture* ambientOcclusionTarget = nullptr; 1380 | if (m_ui.EnableSsao && m_SsaoPass) 1381 | { 1382 | m_SsaoPass->Render(m_CommandList, m_ui.SsaoParameters, *m_View); 1383 | ambientOcclusionTarget = m_RenderTargets->AmbientOcclusion; 1384 | } 1385 | 1386 | DeferredLightingPass::Inputs deferredInputs; 1387 | deferredInputs.SetGBuffer(*m_RenderTargets); 1388 | deferredInputs.ambientOcclusion = m_ui.EnableSsao ? m_RenderTargets->AmbientOcclusion : nullptr; 1389 | deferredInputs.ambientColorTop = m_AmbientTop; 1390 | deferredInputs.ambientColorBottom = m_AmbientBottom; 1391 | deferredInputs.lights = &m_Scene->GetSceneGraph()->GetLights(); 1392 | deferredInputs.lightProbes = m_ui.EnableLightProbe ? &m_LightProbes : nullptr; 1393 | deferredInputs.output = m_RenderTargets->HdrColor; 1394 | 1395 | m_DeferredLightingPass->Render(m_CommandList, *m_View, deferredInputs); 1396 | } 1397 | else 1398 | { 1399 | RenderCompositeView(m_CommandList, 1400 | m_View.get(), m_ViewPrevious.get(), 1401 | *m_RenderTargets->ForwardFramebuffer, 1402 | m_Scene->GetSceneGraph()->GetRootNode(), 1403 | *m_OpaqueDrawStrategy, 1404 | *m_ForwardPass, 1405 | forwardContext, 1406 | "ForwardOpaque", 1407 | m_ui.EnableMaterialEvents); 1408 | } 1409 | m_CommandList->endTimerQuery(m_tqForwardOpaque); 1410 | 1411 | // Disable VRS rate surface, future draw calls will run at full rate. For NAS, we want VRS to affect main forward rendering pass only. 1412 | if (m_ui.EnableNAS && !IsStereo()) 1413 | { 1414 | //UnbindVRSRateSurface(); 1415 | std::shared_ptr planarView = std::dynamic_pointer_cast(m_View); 1416 | planarView->SetVariableRateShadingState(nvrhi::VariableRateShadingState().setEnabled(false)); 1417 | } 1418 | 1419 | if (m_Pick) 1420 | { 1421 | m_CommandList->clearTextureUInt(m_RenderTargets->MaterialIDs, nvrhi::AllSubresources, 0xffff); 1422 | 1423 | MaterialIDPass::Context materialIdContext; 1424 | 1425 | RenderCompositeView(m_CommandList, 1426 | m_View.get(), m_ViewPrevious.get(), 1427 | *m_RenderTargets->MaterialIDFramebuffer, 1428 | m_Scene->GetSceneGraph()->GetRootNode(), 1429 | *m_OpaqueDrawStrategy, 1430 | *m_MaterialIDPass, 1431 | materialIdContext, 1432 | "MaterialID"); 1433 | 1434 | if (m_ui.EnableTranslucency) 1435 | { 1436 | RenderCompositeView(m_CommandList, 1437 | m_View.get(), m_ViewPrevious.get(), 1438 | *m_RenderTargets->MaterialIDFramebuffer, 1439 | m_Scene->GetSceneGraph()->GetRootNode(), 1440 | *m_TransparentDrawStrategy, 1441 | *m_MaterialIDPass, 1442 | materialIdContext, 1443 | "MaterialID - Translucent"); 1444 | } 1445 | 1446 | m_PixelReadbackPass->Capture(m_CommandList, m_PickPosition); 1447 | } 1448 | 1449 | m_CommandList->beginTimerQuery(m_tqForwardSky); 1450 | if (m_EnvironmentMapPass && !m_ui.EnableProceduralSky) 1451 | m_EnvironmentMapPass->Render(m_CommandList, *m_View); 1452 | else 1453 | m_SkyPass->Render(m_CommandList, *m_View, *m_SunLight, m_ui.SkyParams); 1454 | m_CommandList->endTimerQuery(m_tqForwardSky); 1455 | 1456 | if (m_ui.EnableTranslucency) 1457 | { 1458 | RenderCompositeView(m_CommandList, 1459 | m_View.get(), m_ViewPrevious.get(), 1460 | *m_RenderTargets->ForwardFramebuffer, 1461 | m_Scene->GetSceneGraph()->GetRootNode(), 1462 | *m_TransparentDrawStrategy, 1463 | *m_ForwardPass, 1464 | forwardContext, 1465 | "ForwardTransparent", 1466 | m_ui.EnableMaterialEvents); 1467 | } 1468 | 1469 | nvrhi::ITexture* finalHdrColor = m_RenderTargets->HdrColor; 1470 | 1471 | if (m_ui.AntiAliasingMode == AntiAliasingMode::TEMPORAL) 1472 | { 1473 | if (m_PreviousViewsValid) 1474 | { 1475 | m_TemporalAntiAliasingPass->RenderMotionVectors(m_CommandList, *m_View, *m_ViewPrevious); 1476 | } 1477 | 1478 | m_TemporalAntiAliasingPass->TemporalResolve(m_CommandList, m_ui.TemporalAntiAliasingParams, m_PreviousViewsValid, *m_View, m_PreviousViewsValid ? *m_ViewPrevious : *m_View); 1479 | 1480 | finalHdrColor = m_RenderTargets->ResolvedColor; 1481 | 1482 | if (m_ui.EnableBloom) 1483 | { 1484 | m_BloomPass->Render(m_CommandList, m_RenderTargets->ResolvedFramebuffer, *m_View, m_RenderTargets->ResolvedColor, m_ui.BloomSigma, m_ui.BloomAlpha); 1485 | } 1486 | m_PreviousViewsValid = true; 1487 | } 1488 | else 1489 | { 1490 | std::shared_ptr finalHdrFramebuffer = m_RenderTargets->HdrFramebuffer; 1491 | 1492 | if (m_RenderTargets->GetSampleCount() > 1) 1493 | { 1494 | m_CommandList->resolveTexture(m_RenderTargets->ResolvedColor, nvrhi::AllSubresources, m_RenderTargets->HdrColor, nvrhi::AllSubresources); 1495 | finalHdrColor = m_RenderTargets->ResolvedColor; 1496 | finalHdrFramebuffer = m_RenderTargets->ResolvedFramebuffer; 1497 | } 1498 | 1499 | if (m_ui.EnableBloom) 1500 | { 1501 | m_BloomPass->Render(m_CommandList, finalHdrFramebuffer, *m_View, finalHdrColor, m_ui.BloomSigma, m_ui.BloomAlpha); 1502 | } 1503 | 1504 | m_PreviousViewsValid = false; 1505 | } 1506 | 1507 | auto toneMappingParams = m_ui.ToneMappingParams; 1508 | if (exposureResetRequired) 1509 | { 1510 | toneMappingParams.eyeAdaptationSpeedUp = 0.f; 1511 | toneMappingParams.eyeAdaptationSpeedDown = 0.f; 1512 | } 1513 | m_ToneMappingPass->SimpleRender(m_CommandList, toneMappingParams, *m_View, finalHdrColor); 1514 | 1515 | m_CommonPasses->BlitTexture(m_CommandList, framebuffer, m_RenderTargets->LdrColor, &m_BindingCache); 1516 | 1517 | if (m_ui.EnableNAS && m_ui.EnableShadingRateVis) 1518 | { 1519 | RenderVRSRateVisualization(framebuffer); 1520 | } 1521 | 1522 | if (m_ui.DisplayShadowMap) 1523 | { 1524 | for (int cascade = 0; cascade < 4; cascade++) 1525 | { 1526 | nvrhi::Viewport viewport = nvrhi::Viewport( 1527 | 10.f + 266.f * cascade, 1528 | 266.f * (1 + cascade), 1529 | windowViewport.maxY - 266.f, 1530 | windowViewport.maxY - 10.f, 0.f, 1.f 1531 | ); 1532 | 1533 | engine::BlitParameters blitParams; 1534 | blitParams.targetFramebuffer = framebuffer; 1535 | blitParams.targetViewport = viewport; 1536 | blitParams.sourceTexture = m_ShadowMap->GetTexture(); 1537 | blitParams.sourceArraySlice = cascade; 1538 | m_CommonPasses->BlitTexture(m_CommandList, blitParams, &m_BindingCache); 1539 | } 1540 | } 1541 | 1542 | m_CommandList->close(); 1543 | GetDevice()->executeCommandList(m_CommandList); 1544 | 1545 | if (!m_ui.ScreenshotFileName.empty()) 1546 | { 1547 | SaveTextureToFile(GetDevice(), m_CommonPasses.get(), framebufferTexture, nvrhi::ResourceStates::RenderTarget, m_ui.ScreenshotFileName.c_str()); 1548 | m_ui.ScreenshotFileName = ""; 1549 | } 1550 | 1551 | if (m_Pick) 1552 | { 1553 | m_Pick = false; 1554 | GetDevice()->waitForIdle(); 1555 | uint4 pixelValue = m_PixelReadbackPass->ReadUInts(); 1556 | m_ui.SelectedMaterial = nullptr; 1557 | m_ui.SelectedNode = nullptr; 1558 | 1559 | for (const auto& material : m_Scene->GetSceneGraph()->GetMaterials()) 1560 | { 1561 | if (material->materialID == int(pixelValue.x)) 1562 | { 1563 | m_ui.SelectedMaterial = material; 1564 | break; 1565 | } 1566 | } 1567 | 1568 | for (const auto& instance : m_Scene->GetSceneGraph()->GetMeshInstances()) 1569 | { 1570 | if (instance->GetInstanceIndex() == int(pixelValue.y)) 1571 | { 1572 | m_ui.SelectedNode = instance->GetNodeSharedPtr(); 1573 | break; 1574 | } 1575 | } 1576 | 1577 | if (m_ui.SelectedNode) 1578 | { 1579 | log::info("Picked node: %s", m_ui.SelectedNode->GetPath().generic_string().c_str()); 1580 | PointThirdPersonCameraAt(m_ui.SelectedNode); 1581 | } 1582 | else 1583 | { 1584 | PointThirdPersonCameraAt(m_Scene->GetSceneGraph()->GetRootNode()); 1585 | } 1586 | } 1587 | 1588 | m_TemporalAntiAliasingPass->AdvanceFrame(); 1589 | std::swap(m_View, m_ViewPrevious); 1590 | 1591 | GetDeviceManager()->SetVsyncEnabled(m_ui.EnableVsync); 1592 | } 1593 | 1594 | std::shared_ptr GetShaderFactory() 1595 | { 1596 | return m_ShaderFactory; 1597 | } 1598 | 1599 | std::vector>& GetLightProbes() 1600 | { 1601 | return m_LightProbes; 1602 | } 1603 | 1604 | void CreateLightProbes(uint32_t numProbes) 1605 | { 1606 | nvrhi::DeviceHandle device = GetDeviceManager()->GetDevice(); 1607 | 1608 | uint32_t diffuseMapSize = 256; 1609 | uint32_t diffuseMapMipLevels = 1; 1610 | uint32_t specularMapSize = 512; 1611 | uint32_t specularMapMipLevels = 8; 1612 | 1613 | nvrhi::TextureDesc cubemapDesc; 1614 | 1615 | cubemapDesc.arraySize = 6 * numProbes; 1616 | cubemapDesc.dimension = nvrhi::TextureDimension::TextureCubeArray; 1617 | cubemapDesc.isRenderTarget = true; 1618 | cubemapDesc.keepInitialState = true; 1619 | 1620 | cubemapDesc.width = diffuseMapSize; 1621 | cubemapDesc.height = diffuseMapSize; 1622 | cubemapDesc.mipLevels = diffuseMapMipLevels; 1623 | cubemapDesc.format = nvrhi::Format::RGBA16_FLOAT; 1624 | cubemapDesc.initialState = nvrhi::ResourceStates::ShaderResource; 1625 | cubemapDesc.keepInitialState = true; 1626 | 1627 | m_LightProbeDiffuseTexture = device->createTexture(cubemapDesc); 1628 | 1629 | cubemapDesc.width = specularMapSize; 1630 | cubemapDesc.height = specularMapSize; 1631 | cubemapDesc.mipLevels = specularMapMipLevels; 1632 | cubemapDesc.format = nvrhi::Format::RGBA16_FLOAT; 1633 | cubemapDesc.initialState = nvrhi::ResourceStates::ShaderResource; 1634 | cubemapDesc.keepInitialState = true; 1635 | 1636 | m_LightProbeSpecularTexture = device->createTexture(cubemapDesc); 1637 | 1638 | m_LightProbes.clear(); 1639 | 1640 | for (uint32_t i = 0; i < numProbes; i++) 1641 | { 1642 | std::shared_ptr probe = std::make_shared(); 1643 | 1644 | probe->name = std::to_string(i + 1); 1645 | probe->diffuseMap = m_LightProbeDiffuseTexture; 1646 | probe->specularMap = m_LightProbeSpecularTexture; 1647 | probe->diffuseArrayIndex = i; 1648 | probe->specularArrayIndex = i; 1649 | probe->bounds = frustum::empty(); 1650 | probe->enabled = false; 1651 | 1652 | m_LightProbes.push_back(probe); 1653 | } 1654 | } 1655 | 1656 | void RenderLightProbe(LightProbe& probe) 1657 | { 1658 | nvrhi::DeviceHandle device = GetDeviceManager()->GetDevice(); 1659 | 1660 | uint32_t environmentMapSize = 1024; 1661 | uint32_t environmentMapMipLevels = 8; 1662 | 1663 | nvrhi::TextureDesc cubemapDesc; 1664 | cubemapDesc.arraySize = 6; 1665 | cubemapDesc.width = environmentMapSize; 1666 | cubemapDesc.height = environmentMapSize; 1667 | cubemapDesc.mipLevels = environmentMapMipLevels; 1668 | cubemapDesc.dimension = nvrhi::TextureDimension::TextureCube; 1669 | cubemapDesc.isRenderTarget = true; 1670 | cubemapDesc.format = nvrhi::Format::RGBA16_FLOAT; 1671 | cubemapDesc.initialState = nvrhi::ResourceStates::RenderTarget; 1672 | cubemapDesc.keepInitialState = true; 1673 | cubemapDesc.clearValue = nvrhi::Color(0.f); 1674 | cubemapDesc.useClearValue = true; 1675 | 1676 | nvrhi::TextureHandle colorTexture = device->createTexture(cubemapDesc); 1677 | 1678 | cubemapDesc.mipLevels = 1; 1679 | cubemapDesc.format = nvrhi::Format::D24S8; 1680 | cubemapDesc.isTypeless = true; 1681 | cubemapDesc.initialState = nvrhi::ResourceStates::DepthWrite; 1682 | 1683 | nvrhi::TextureHandle depthTexture = device->createTexture(cubemapDesc); 1684 | 1685 | std::shared_ptr framebuffer = std::make_shared(device); 1686 | framebuffer->RenderTargets = { colorTexture }; 1687 | framebuffer->DepthTarget = depthTexture; 1688 | 1689 | CubemapView view; 1690 | view.SetArrayViewports(environmentMapSize, 0); 1691 | const float nearPlane = 0.1f; 1692 | const float cullDistance = 100.f; 1693 | float3 probePosition = GetActiveCamera().GetPosition(); 1694 | if (m_ui.ActiveSceneCamera) 1695 | probePosition = m_ui.ActiveSceneCamera->GetWorldToViewMatrix().m_translation; 1696 | 1697 | view.SetTransform(dm::translation(-probePosition), nearPlane, cullDistance); 1698 | view.UpdateCache(); 1699 | 1700 | std::shared_ptr skyPass = std::make_shared(device, m_ShaderFactory, m_CommonPasses, framebuffer, view); 1701 | 1702 | ForwardShadingPass::CreateParameters ForwardParams; 1703 | ForwardParams.singlePassCubemap = GetDevice()->queryFeatureSupport(nvrhi::Feature::FastGeometryShader); 1704 | std::shared_ptr forwardPass = std::make_shared(device, m_CommonPasses); 1705 | forwardPass->Init(*m_ShaderFactory, ForwardParams); 1706 | 1707 | nvrhi::CommandListHandle commandList = device->createCommandList(); 1708 | commandList->open(); 1709 | commandList->clearTextureFloat(colorTexture, nvrhi::AllSubresources, nvrhi::Color(0.f)); 1710 | commandList->clearDepthStencilTexture(depthTexture, nvrhi::AllSubresources, true, 0.f, true, 0); 1711 | 1712 | box3 sceneBounds = m_Scene->GetSceneGraph()->GetRootNode()->GetGlobalBoundingBox(); 1713 | float zRange = length(sceneBounds.diagonal()) * 0.5f; 1714 | m_ShadowMap->SetupForCubemapView(*m_SunLight, view.GetViewOrigin(), cullDistance, zRange, zRange, m_ui.CsmExponent); 1715 | m_ShadowMap->Clear(commandList); 1716 | 1717 | DepthPass::Context shadowContext; 1718 | 1719 | RenderCompositeView(commandList, 1720 | &m_ShadowMap->GetView(), nullptr, 1721 | *m_ShadowFramebuffer, 1722 | m_Scene->GetSceneGraph()->GetRootNode(), 1723 | *m_OpaqueDrawStrategy, 1724 | *m_ShadowDepthPass, 1725 | shadowContext, 1726 | "ShadowMap"); 1727 | 1728 | ForwardShadingPass::Context forwardContext; 1729 | 1730 | std::vector> lightProbes; 1731 | forwardPass->PrepareLights(forwardContext, commandList, m_Scene->GetSceneGraph()->GetLights(), m_AmbientTop, m_AmbientBottom, lightProbes); 1732 | 1733 | RenderCompositeView(commandList, 1734 | &view, nullptr, 1735 | *framebuffer, 1736 | m_Scene->GetSceneGraph()->GetRootNode(), 1737 | *m_OpaqueDrawStrategy, 1738 | *forwardPass, 1739 | forwardContext, 1740 | "ForwardOpaque"); 1741 | 1742 | skyPass->Render(commandList, view, *m_SunLight, m_ui.SkyParams); 1743 | 1744 | RenderCompositeView(commandList, 1745 | &view, nullptr, 1746 | *framebuffer, 1747 | m_Scene->GetSceneGraph()->GetRootNode(), 1748 | *m_TransparentDrawStrategy, 1749 | *forwardPass, 1750 | forwardContext, 1751 | "ForwardTransparent"); 1752 | 1753 | m_LightProbePass->GenerateCubemapMips(commandList, colorTexture, 0, 0, environmentMapMipLevels - 1); 1754 | 1755 | m_LightProbePass->RenderDiffuseMap(commandList, colorTexture, nvrhi::AllSubresources, probe.diffuseMap, probe.diffuseArrayIndex * 6, 0); 1756 | 1757 | uint32_t specularMapMipLevels = probe.specularMap->getDesc().mipLevels; 1758 | for (uint32_t mipLevel = 0; mipLevel < specularMapMipLevels; mipLevel++) 1759 | { 1760 | float roughness = powf(float(mipLevel) / float(specularMapMipLevels - 1), 2.0f); 1761 | m_LightProbePass->RenderSpecularMap(commandList, roughness, colorTexture, nvrhi::AllSubresources, probe.specularMap, probe.specularArrayIndex * 6, mipLevel); 1762 | } 1763 | 1764 | m_LightProbePass->RenderEnvironmentBrdfTexture(commandList); 1765 | 1766 | commandList->close(); 1767 | device->executeCommandList(commandList); 1768 | device->waitForIdle(); 1769 | device->runGarbageCollection(); 1770 | 1771 | probe.environmentBrdf = m_LightProbePass->GetEnvironmentBrdfTexture(); 1772 | box3 bounds = box3(probePosition, probePosition).grow(10.f); 1773 | probe.bounds = frustum::fromBox(bounds); 1774 | probe.enabled = true; 1775 | } 1776 | }; 1777 | 1778 | class UIRenderer : public ImGui_Renderer 1779 | { 1780 | private: 1781 | std::shared_ptr m_app; 1782 | 1783 | ImFont* m_FontOpenSans = nullptr; 1784 | ImFont* m_FontDroidMono = nullptr; 1785 | 1786 | std::unique_ptr m_console; 1787 | std::shared_ptr m_SelectedLight; 1788 | 1789 | UIData& m_ui; 1790 | nvrhi::CommandListHandle m_CommandList; 1791 | 1792 | public: 1793 | UIRenderer(DeviceManager* deviceManager, std::shared_ptr app, UIData& ui) 1794 | : ImGui_Renderer(deviceManager) 1795 | , m_app(app) 1796 | , m_ui(ui) 1797 | { 1798 | m_CommandList = GetDevice()->createCommandList(); 1799 | 1800 | m_FontOpenSans = this->LoadFont(*(app->GetRootFs()), "/media/fonts/OpenSans/OpenSans-Regular.ttf", 17.f); 1801 | m_FontDroidMono = this->LoadFont(*(app->GetRootFs()), "/media/fonts/DroidSans/DroidSans-Mono.ttf", 14.f); 1802 | 1803 | ImGui_Console::Options opts; 1804 | opts.font = m_FontDroidMono; 1805 | auto interpreter = std::make_shared(); 1806 | //m_console = std::make_unique(interpreter,opts); 1807 | 1808 | ImGui::GetIO().IniFilename = nullptr; 1809 | } 1810 | 1811 | protected: 1812 | virtual void buildUI(void) override 1813 | { 1814 | if (!m_ui.ShowUI) 1815 | return; 1816 | 1817 | const auto& io = ImGui::GetIO(); 1818 | 1819 | int width, height; 1820 | GetDeviceManager()->GetWindowDimensions(width, height); 1821 | 1822 | if (m_app->IsSceneLoading()) 1823 | { 1824 | BeginFullScreenWindow(); 1825 | 1826 | char messageBuffer[256]; 1827 | const auto& stats = Scene::GetLoadingStats(); 1828 | snprintf(messageBuffer, std::size(messageBuffer), "Loading scene %s, please wait...\nObjects: %d/%d, Textures: %d/%d", 1829 | m_app->GetCurrentSceneName().c_str(), stats.ObjectsLoaded.load(), stats.ObjectsTotal.load(), m_app->GetTextureCache()->GetNumberOfLoadedTextures(), m_app->GetTextureCache()->GetNumberOfRequestedTextures()); 1830 | 1831 | DrawScreenCenteredText(messageBuffer); 1832 | 1833 | EndFullScreenWindow(); 1834 | 1835 | return; 1836 | } 1837 | 1838 | if (m_ui.ShowConsole && m_console) 1839 | { 1840 | m_console->Render(&m_ui.ShowConsole); 1841 | } 1842 | 1843 | ImGui::SetNextWindowPos(ImVec2(10.f, 10.f), 0); 1844 | ImGui::Begin("Settings", 0, ImGuiWindowFlags_AlwaysAutoResize); 1845 | ImGui::Text("Renderer: %s", GetDeviceManager()->GetRendererString()); 1846 | double frameTime = GetDeviceManager()->GetAverageFrameTimeSeconds(); 1847 | if (frameTime > 0.0) 1848 | ImGui::Text("%.3f ms/frame (%.1f FPS)", frameTime * 1e3, 1.0 / frameTime); 1849 | ImGui::Text("DepthPrePass %.1f ms", GetDeviceManager()->GetDevice()->getTimerQueryTime(m_app->m_tqDepthPrePass) * 1e3); 1850 | ImGui::Text("Forward %.1f ms", GetDeviceManager()->GetDevice()->getTimerQueryTime(m_app->m_tqForwardOpaque) * 1e3); 1851 | ImGui::Text("MVec %.1f ms", GetDeviceManager()->GetDevice()->getTimerQueryTime(m_app->m_tqMotionVector) * 1e3); 1852 | ImGui::Text("Sky %.1f ms", GetDeviceManager()->GetDevice()->getTimerQueryTime(m_app->m_tqForwardSky) * 1e3); 1853 | ImGui::Text("Transp %.1f ms", GetDeviceManager()->GetDevice()->getTimerQueryTime(m_app->m_tqForwardTransparent) * 1e3); 1854 | 1855 | const std::string currentScene = m_app->GetCurrentSceneName(); 1856 | if (ImGui::BeginCombo("Scene", currentScene.c_str())) 1857 | { 1858 | const std::vector& scenes = m_app->GetAvailableScenes(); 1859 | for (const std::string& scene : scenes) 1860 | { 1861 | bool is_selected = scene == currentScene; 1862 | if (ImGui::Selectable(scene.c_str(), is_selected)) 1863 | m_app->SetCurrentSceneName(scene); 1864 | if (is_selected) 1865 | ImGui::SetItemDefaultFocus(); 1866 | } 1867 | ImGui::EndCombo(); 1868 | } 1869 | 1870 | if (ImGui::Button("Reload Shaders")) 1871 | m_ui.ShaderReloadRequested = true; 1872 | 1873 | ImGui::Checkbox("VSync", &m_ui.EnableVsync); 1874 | ImGui::Checkbox("Deferred Shading", &m_ui.UseDeferredShading); 1875 | if (m_ui.AntiAliasingMode >= AntiAliasingMode::MSAA_2X) 1876 | m_ui.UseDeferredShading = false; // Deferred shading doesn't work with MSAA 1877 | ImGui::Checkbox("Stereo", &m_ui.Stereo); 1878 | ImGui::Checkbox("Animations", &m_ui.EnableAnimations); 1879 | 1880 | if (ImGui::BeginCombo("Camera (T)", m_ui.ActiveSceneCamera ? m_ui.ActiveSceneCamera->GetName().c_str() 1881 | : m_ui.UseThirdPersonCamera ? "Third-Person" : "First-Person")) 1882 | { 1883 | if (ImGui::Selectable("First-Person", !m_ui.ActiveSceneCamera && !m_ui.UseThirdPersonCamera)) 1884 | { 1885 | m_ui.ActiveSceneCamera.reset(); 1886 | m_ui.UseThirdPersonCamera = false; 1887 | } 1888 | if (ImGui::Selectable("Third-Person", !m_ui.ActiveSceneCamera && m_ui.UseThirdPersonCamera)) 1889 | { 1890 | m_ui.ActiveSceneCamera.reset(); 1891 | m_ui.UseThirdPersonCamera = true; 1892 | m_app->CopyActiveCameraToFirstPerson(); 1893 | } 1894 | for (const auto& camera : m_app->GetScene()->GetSceneGraph()->GetCameras()) 1895 | { 1896 | if (ImGui::Selectable(camera->GetName().c_str(), m_ui.ActiveSceneCamera == camera)) 1897 | { 1898 | m_ui.ActiveSceneCamera = camera; 1899 | m_app->CopyActiveCameraToFirstPerson(); 1900 | } 1901 | } 1902 | ImGui::EndCombo(); 1903 | } 1904 | 1905 | ImGui::Combo("AA Mode", (int*)&m_ui.AntiAliasingMode, "None\0TemporalAA\0MSAA 2x\0MSAA 4x\0MSAA 8x\0"); 1906 | ImGui::Combo("TAA Camera Jitter", (int*)&m_ui.TemporalAntiAliasingJitter, "MSAA\0Halton\0R2\0White Noise\0"); 1907 | 1908 | ImGui::SliderFloat("Ambient Intensity", &m_ui.AmbientIntensity, 0.f, 1.f); 1909 | 1910 | ImGui::Checkbox("Enable Light Probe", &m_ui.EnableLightProbe); 1911 | if (m_ui.EnableLightProbe && ImGui::CollapsingHeader("Light Probe")) 1912 | { 1913 | ImGui::DragFloat("Diffuse Scale", &m_ui.LightProbeDiffuseScale, 0.01f, 0.0f, 10.0f); 1914 | ImGui::DragFloat("Specular Scale", &m_ui.LightProbeSpecularScale, 0.01f, 0.0f, 10.0f); 1915 | } 1916 | 1917 | ImGui::Checkbox("Enable Procedural Sky", &m_ui.EnableProceduralSky); 1918 | if (m_ui.EnableProceduralSky && ImGui::CollapsingHeader("Sky Parameters")) 1919 | { 1920 | ImGui::SliderFloat("Brightness", &m_ui.SkyParams.brightness, 0.f, 1.f); 1921 | ImGui::SliderFloat("Glow Size", &m_ui.SkyParams.glowSize, 0.f, 90.f); 1922 | ImGui::SliderFloat("Glow Sharpness", &m_ui.SkyParams.glowSharpness, 1.f, 10.f); 1923 | ImGui::SliderFloat("Glow Intensity", &m_ui.SkyParams.glowIntensity, 0.f, 1.f); 1924 | ImGui::SliderFloat("Horizon Size", &m_ui.SkyParams.horizonSize, 0.f, 90.f); 1925 | } 1926 | ImGui::Checkbox("Enable SSAO", &m_ui.EnableSsao); 1927 | ImGui::Checkbox("Enable Bloom", &m_ui.EnableBloom); 1928 | ImGui::DragFloat("Bloom Sigma", &m_ui.BloomSigma, 0.01f, 0.1f, 100.f); 1929 | ImGui::DragFloat("Bloom Alpha", &m_ui.BloomAlpha, 0.01f, 0.01f, 1.0f); 1930 | ImGui::Checkbox("Enable Shadows", &m_ui.EnableShadows); 1931 | ImGui::Checkbox("Enable Translucency", &m_ui.EnableTranslucency); 1932 | 1933 | ImGui::Separator(); 1934 | ImGui::Checkbox("Temporal AA Clamping", &m_ui.TemporalAntiAliasingParams.enableHistoryClamping); 1935 | ImGui::Checkbox("Material Events", &m_ui.EnableMaterialEvents); 1936 | ImGui::Separator(); 1937 | 1938 | ImGui::Separator(); 1939 | ImGui::Checkbox("Enable NAS", &m_ui.EnableNAS); 1940 | ImGui::Checkbox("Enable Shading Rate Vis", &m_ui.EnableShadingRateVis); 1941 | ImGui::Checkbox("Enable SR Surface Smoothing", &m_ui.EnableShadingRateSurfaceSmoothing); 1942 | ImGui::DragFloat("Error Sensitivity", &m_ui.NASErrorSensitivity, 0.001f, 0.001f, 0.2f); 1943 | ImGui::DragFloat("Brightness Sensitivity", &m_ui.NASBrightnessSensitivity, 0.01f, 0.01f, 0.2f); 1944 | ImGui::DragFloat("Motion Sensitivity", &m_ui.NASMotionSensitivity, 0.05f, 0.00f, 2.f); 1945 | ImGui::Separator(); 1946 | 1947 | const auto& lights = m_app->GetScene()->GetSceneGraph()->GetLights(); 1948 | 1949 | if (!lights.empty() && ImGui::CollapsingHeader("Lights")) 1950 | { 1951 | if (ImGui::BeginCombo("Select Light", m_SelectedLight ? m_SelectedLight->GetName().c_str() : "(None)")) 1952 | { 1953 | for (const auto& light : lights) 1954 | { 1955 | bool selected = m_SelectedLight == light; 1956 | ImGui::Selectable(light->GetName().c_str(), &selected); 1957 | if (selected) 1958 | { 1959 | m_SelectedLight = light; 1960 | ImGui::SetItemDefaultFocus(); 1961 | } 1962 | } 1963 | ImGui::EndCombo(); 1964 | } 1965 | 1966 | if (m_SelectedLight) 1967 | { 1968 | app::LightEditor(*m_SelectedLight); 1969 | } 1970 | } 1971 | 1972 | ImGui::TextUnformatted("Render Light Probe: "); 1973 | uint32_t probeIndex = 1; 1974 | for (auto probe : m_app->GetLightProbes()) 1975 | { 1976 | ImGui::SameLine(); 1977 | if (ImGui::Button(probe->name.c_str())) 1978 | { 1979 | m_app->RenderLightProbe(*probe); 1980 | } 1981 | } 1982 | 1983 | if (ImGui::Button("Screenshot")) 1984 | { 1985 | std::string fileName; 1986 | if (FileDialog(false, "BMP files\0*.bmp\0All files\0*.*\0\0", fileName)) 1987 | { 1988 | m_ui.ScreenshotFileName = fileName; 1989 | } 1990 | } 1991 | 1992 | ImGui::End(); 1993 | 1994 | auto material = m_ui.SelectedMaterial; 1995 | if (material) 1996 | { 1997 | ImGui::SetNextWindowPos(ImVec2(float(width) - 10.f, 10.f), 0, ImVec2(1.f, 0.f)); 1998 | ImGui::Begin("Material Editor"); 1999 | ImGui::Text("Material %d: %s", material->materialID, material->name.c_str()); 2000 | 2001 | MaterialDomain previousDomain = material->domain; 2002 | material->dirty = donut::app::MaterialEditor(material.get(), true); 2003 | 2004 | if (previousDomain != material->domain) 2005 | m_app->GetScene()->GetSceneGraph()->GetRootNode()->InvalidateContent(); 2006 | 2007 | ImGui::End(); 2008 | } 2009 | 2010 | if (m_ui.AntiAliasingMode != AntiAliasingMode::NONE && m_ui.AntiAliasingMode != AntiAliasingMode::TEMPORAL) 2011 | m_ui.UseDeferredShading = false; 2012 | 2013 | if (!m_ui.UseDeferredShading) 2014 | m_ui.EnableSsao = false; 2015 | } 2016 | }; 2017 | 2018 | bool ProcessCommandLine(int argc, const char* const* argv, DeviceCreationParameters& deviceParams, std::string& sceneName) 2019 | { 2020 | for (int i = 1; i < argc; i++) 2021 | { 2022 | if (!strcmp(argv[i], "-width")) 2023 | { 2024 | deviceParams.backBufferWidth = std::stoi(argv[++i]); 2025 | } 2026 | else if (!strcmp(argv[i], "-height")) 2027 | { 2028 | deviceParams.backBufferHeight = std::stoi(argv[++i]); 2029 | } 2030 | else if (!strcmp(argv[i], "-fullscreen")) 2031 | { 2032 | deviceParams.startFullscreen = true; 2033 | } 2034 | else if (!strcmp(argv[i], "-debug")) 2035 | { 2036 | deviceParams.enableDebugRuntime = true; 2037 | deviceParams.enableNvrhiValidationLayer = true; 2038 | } 2039 | else if (!strcmp(argv[i], "-no-vsync")) 2040 | { 2041 | deviceParams.vsyncEnabled = false; 2042 | } 2043 | else if (!strcmp(argv[i], "-print-graph")) 2044 | { 2045 | g_PrintSceneGraph = true; 2046 | } 2047 | else if (argv[i][0] != '-') 2048 | { 2049 | sceneName = argv[i]; 2050 | } 2051 | } 2052 | 2053 | return true; 2054 | } 2055 | 2056 | #ifdef _WIN32 2057 | int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) 2058 | { 2059 | nvrhi::GraphicsAPI api = app::GetGraphicsAPIFromCommandLine(__argc, __argv); 2060 | #else // _WIN32 2061 | int main(int __argc, const char* const* __argv) 2062 | { 2063 | nvrhi::GraphicsAPI api = nvrhi::GraphicsAPI::VULKAN; 2064 | #endif // _WIN32 2065 | 2066 | DeviceCreationParameters deviceParams; 2067 | 2068 | // deviceParams.adapter = VrSystem::GetRequiredAdapter(); 2069 | deviceParams.backBufferWidth = 1920; 2070 | deviceParams.backBufferHeight = 1080; 2071 | deviceParams.swapChainSampleCount = 1; 2072 | deviceParams.swapChainBufferCount = 2; 2073 | deviceParams.startFullscreen = false; 2074 | deviceParams.vsyncEnabled = true; 2075 | 2076 | std::string sceneName; 2077 | if (!ProcessCommandLine(__argc, __argv, deviceParams, sceneName)) 2078 | { 2079 | log::error("Failed to process the command line."); 2080 | return 1; 2081 | } 2082 | 2083 | DeviceManager* deviceManager = DeviceManager::Create(api); 2084 | const char* apiString = nvrhi::utils::GraphicsAPIToString(deviceManager->GetGraphicsAPI()); 2085 | 2086 | std::string windowTitle = "Donut Feature Demo (" + std::string(apiString) + ")"; 2087 | 2088 | if (!deviceManager->CreateWindowDeviceAndSwapChain(deviceParams, windowTitle.c_str())) 2089 | { 2090 | log::error("Cannot initialize a %s graphics device with the requested parameters", apiString); 2091 | return 1; 2092 | } 2093 | 2094 | { 2095 | UIData uiData; 2096 | 2097 | std::shared_ptr demo = std::make_shared(deviceManager, uiData, sceneName); 2098 | std::shared_ptr gui = std::make_shared(deviceManager, demo, uiData); 2099 | 2100 | gui->Init(demo->GetShaderFactory()); 2101 | 2102 | deviceManager->AddRenderPassToBack(demo.get()); 2103 | deviceManager->AddRenderPassToBack(gui.get()); 2104 | 2105 | deviceManager->RunMessageLoop(); 2106 | } 2107 | 2108 | deviceManager->Shutdown(); 2109 | #ifdef _DEBUG 2110 | deviceManager->ReportLiveObjects(); 2111 | #endif 2112 | delete deviceManager; 2113 | 2114 | return 0; 2115 | } 2116 | -------------------------------------------------------------------------------- /adaptive_shading/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a 5 | # copy of this software and associated documentation files (the "Software"), 6 | # to deal in the Software without restriction, including without limitation 7 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | # and/or sell copies of the Software, and to permit persons to whom the 9 | # Software is furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | # DEALINGS IN THE SOFTWARE. 21 | 22 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/bin") 23 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") 24 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") 25 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") 26 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") 27 | set(DONUT_SHADERS_OUTPUT_DIR "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/shaders/framework") 28 | 29 | include(../donut_examples/donut/compileshaders.cmake) 30 | file(GLOB shaders "*.hlsl") 31 | file(GLOB sources "*.cpp" "*.h") 32 | 33 | set(project adaptive_shading) 34 | set(folder "Examples/Adaptive Shading") 35 | 36 | donut_compile_shaders( 37 | TARGET ${project}_shaders 38 | CONFIG ${CMAKE_CURRENT_SOURCE_DIR}/shaders.cfg 39 | SOURCES ${shaders} 40 | FOLDER ${folder} 41 | DXIL ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/shaders/${project}/dxil 42 | SPIRV_DXC ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/shaders/${project}/spirv 43 | ) 44 | 45 | add_executable(${project} WIN32 ${sources}) 46 | target_link_libraries(${project} donut_render donut_app donut_engine) 47 | add_dependencies(${project} ${project}_shaders) 48 | set_target_properties(${project} PROPERTIES FOLDER ${folder}) 49 | 50 | if (MSVC) 51 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3 /MP") 52 | endif() 53 | -------------------------------------------------------------------------------- /adaptive_shading/ComputeNASData.hlsl: -------------------------------------------------------------------------------- 1 | RWTexture2D nasDataSurface : register(u0); 2 | Texture2D prevFrameColors : register(t0); 3 | 4 | #include "Compute_cb.h" 5 | 6 | cbuffer ComputeNasDataPassCB : register(b0) 7 | { 8 | ComputeNASDataConstants ComputeNASDataParams; 9 | }; 10 | 11 | float RgbToLuminance(float3 color) 12 | { 13 | return dot(color, float3(0.299, 0.587, 0.114)); 14 | } 15 | 16 | // Use a single wave threadgroup to leverage wave intrinsics 17 | [numthreads(8, 4, 1)] 18 | void main_cs(uint3 DispatchThreadID : SV_DispatchThreadID, uint3 GroupThreadID : SV_GroupThreadID, uint3 GroupID : SV_GroupID) 19 | { 20 | // Block of 8x4 threads (each thread is a block of 2x4 pixels) 21 | // Each block is responsible for loading data from a 16x16 pixel tile 22 | uint2 localID = GroupThreadID.xy; 23 | localID <<= uint2(1, 2); 24 | 25 | // Tile global location 26 | uint2 tileOffset = GroupID.xy << 4; 27 | 28 | // Global block coordinates 29 | int3 blockBaseCoord = int3(tileOffset + localID, 0); 30 | 31 | // Fetch color (final post-AA) data 32 | // l0.x l0.y 33 | // l0.z l0.w l2.x 34 | // l1.x l1.y 35 | // l1.z l1.w l2.y 36 | // l2.z 37 | float4 l0; 38 | l0.x = RgbToLuminance(prevFrameColors.Load(blockBaseCoord, int2(0, 0)).xyz); 39 | l0.y = RgbToLuminance(prevFrameColors.Load(blockBaseCoord, int2(1, 0)).xyz); 40 | l0.z = RgbToLuminance(prevFrameColors.Load(blockBaseCoord, int2(0, 1)).xyz); 41 | l0.w = RgbToLuminance(prevFrameColors.Load(blockBaseCoord, int2(1, 1)).xyz); 42 | 43 | float4 l1; 44 | l1.x = RgbToLuminance(prevFrameColors.Load(blockBaseCoord, int2(0, 2)).xyz); 45 | l1.y = RgbToLuminance(prevFrameColors.Load(blockBaseCoord, int2(1, 2)).xyz); 46 | l1.z = RgbToLuminance(prevFrameColors.Load(blockBaseCoord, int2(0, 3)).xyz); 47 | l1.w = RgbToLuminance(prevFrameColors.Load(blockBaseCoord, int2(1, 3)).xyz); 48 | 49 | float3 l2; 50 | l2.x = RgbToLuminance(prevFrameColors.Load(blockBaseCoord, int2(2, 1)).xyz); 51 | l2.y = RgbToLuminance(prevFrameColors.Load(blockBaseCoord, int2(2, 3)).xyz); 52 | l2.z = RgbToLuminance(prevFrameColors.Load(blockBaseCoord, int2(1, 4)).xyz); 53 | 54 | // Derivatives X 55 | float4 a = float4(l0.y, l2.x, l1.y, l2.y); 56 | float4 b = float4(l0.x, l0.w, l1.x, l1.w); 57 | float4 dx = abs(a - b); 58 | 59 | // Derivatives Y 60 | a = float4(l0.z, l1.y, l1.z, l2.z); 61 | b = float4(l0.x, l0.w, l1.x, l1.w); 62 | float4 dy = abs(a - b); 63 | 64 | // Compute block average luma (8 total samples) 65 | float4 sumAB = l0 + l1; 66 | float avgLuma = (sumAB.x + sumAB.y + sumAB.z + sumAB.w) / 8; 67 | avgLuma = WaveActiveSum(avgLuma) / WaveGetLaneCount() + ComputeNASDataParams.brightnessSensitivity; 68 | 69 | // Compute maximum partial derivative of all 16x16 pixels (256 total) 70 | // one thread works on 2x4 pixels, one wave has 32 threads, 2x4x32 = 256 71 | // this approach is more "sensitive" to individual outliers in a tile, since it takes the max instead of the average 72 | float maxDx = max(max(dx.x, dx.y), max(dx.z, dx.w)); 73 | float maxDy = max(max(dy.x, dy.y), max(dy.z, dy.w)); 74 | float errX = WaveActiveMax(maxDx); 75 | float errY = WaveActiveMax(maxDy); 76 | 77 | /* 78 | // Alternative: compute block error using L2 norm; this is the original approach from the paper 79 | // (Note: errorSensitivity threshold needs to be reduced to get similar quality) 80 | float sumDxSq = dot(dx * dx, float4(1.0, 1.0, 1.0, 1.0)) / 4.0; 81 | float sumDySq = dot(dy * dy, float4(1.0, 1.0, 1.0, 1.0)) / 4.0; 82 | float errX = sqrt(WaveActiveSum(sumDxSq) / WaveGetLaneCount()); 83 | float errY = sqrt(WaveActiveSum(sumDySq) / WaveGetLaneCount()); 84 | */ 85 | 86 | if (all(GroupThreadID.xy == 0)) 87 | { 88 | nasDataSurface[GroupID.xy] = float2(errX, errY) / abs(avgLuma); 89 | } 90 | } -------------------------------------------------------------------------------- /adaptive_shading/ComputeShadingRate.hlsl: -------------------------------------------------------------------------------- 1 | #pragma pack_matrix(row_major) 2 | 3 | #include "Compute_cb.h" 4 | 5 | cbuffer ShadingRatePassCB : register(b0) 6 | { 7 | AdaptiveShadingConstants ShadingRatePassParams; 8 | }; 9 | 10 | 11 | RWTexture2D vrsSurface : register(u0); 12 | Texture2D gBufferDepth : register(t0); 13 | Texture2D nasDataSurface : register(t1); 14 | SamplerState s_Sampler : register(s0); 15 | 16 | groupshared uint groupMinDepth; 17 | 18 | #define TILE_SIZE 16 19 | 20 | [numthreads(8, 4, 1)] 21 | void main_cs(uint3 DispatchThreadID : SV_DispatchThreadID, uint3 GroupThreadID : SV_GroupThreadID, uint3 GroupID : SV_GroupID) 22 | { 23 | uint screenWidth, screenHeight; 24 | gBufferDepth.GetDimensions(screenWidth, screenHeight); 25 | 26 | if (all(GroupThreadID.xy == 0)) 27 | { 28 | groupMinDepth = asuint(1.0f); 29 | } 30 | GroupMemoryBarrierWithGroupSync(); 31 | 32 | // Block of 8x4 threads (each thread is a block of 2x4 pixels) 33 | // Each block is responsible for loading data from a 16x16 pixel tile 34 | uint2 localID = GroupThreadID.xy; 35 | localID <<= uint2(1, 2); 36 | 37 | // Tile global location 38 | uint2 tileOffset = GroupID.xy << 4; 39 | 40 | // Global block coordinates 41 | int3 blockBaseCoord = int3(tileOffset + localID, 0); 42 | 43 | // Sample depth in the 2x4 block of each thread 44 | // sparsely sampling only four of the eight samples 45 | float4 depth; 46 | depth.x = gBufferDepth.Load(blockBaseCoord, int2(0, 0)).x; 47 | depth.y = gBufferDepth.Load(blockBaseCoord, int2(1, 1)).x; 48 | depth.z = gBufferDepth.Load(blockBaseCoord, int2(0, 2)).x; 49 | depth.w = gBufferDepth.Load(blockBaseCoord, int2(1, 3)).x; 50 | 51 | // Reduction: find block minimum depth (corresponding to largest motion in block) 52 | depth.xy = min(depth.xy, depth.zw); 53 | depth.x = min(depth.x, depth.y); 54 | InterlockedMin(groupMinDepth, asuint(depth.x)); 55 | 56 | GroupMemoryBarrierWithGroupSync(); 57 | 58 | if (all(GroupThreadID.xy == 0)) 59 | { 60 | // Compute motion vector by reconstructing and reprojecting clipPos of the tile center 61 | // currWindowPos assumes only a single view, safe for non-stereo cases 62 | float2 currWindowPos = (GroupID.xy + 0.5) * TILE_SIZE; 63 | float2 currUv = currWindowPos * ShadingRatePassParams.sourceTextureSizeInv; 64 | 65 | float4 clipPos; 66 | clipPos.x = currUv.x * 2 - 1; 67 | clipPos.y = 1 - currUv.y * 2; 68 | clipPos.z = asfloat(groupMinDepth); 69 | clipPos.w = 1; 70 | 71 | float2 mVec = float2(0, 0); 72 | float4 prevClipPos = mul(clipPos, ShadingRatePassParams.reprojectionMatrix); 73 | 74 | float2 prevWindowPos = currWindowPos; 75 | 76 | if (prevClipPos.w > 0) 77 | { 78 | prevClipPos.xyz /= prevClipPos.w; 79 | float2 prevUV; 80 | prevUV.x = 0.5 + prevClipPos.x * 0.5; 81 | prevUV.y = 0.5 - prevClipPos.y * 0.5; 82 | 83 | prevWindowPos = prevUV * ShadingRatePassParams.previousViewSize + ShadingRatePassParams.previousViewOrigin; 84 | mVec = prevWindowPos.xy - currWindowPos.xy; 85 | } 86 | 87 | mVec = abs(mVec) * ShadingRatePassParams.motionSensitivity; 88 | 89 | // Error scalers (equations from the I3D 2019 paper) 90 | // bhv for half rate, bqv for quarter rate 91 | float2 bhv = pow(1.0 / (1 + pow(1.05 * mVec, 3.1)), 0.35); 92 | float2 bqv = 2.13 * pow(1.0 / (1 + pow(0.55 * mVec, 2.41)), 0.49); 93 | 94 | // Sample block error data from NAS data pass and apply the error scalars 95 | float2 diff = nasDataSurface.SampleLevel(s_Sampler, prevWindowPos * ShadingRatePassParams.sourceTextureSizeInv, 0).rg; 96 | float2 diff2 = diff * bhv; 97 | float2 diff4 = diff * bqv; 98 | 99 | float threshold = ShadingRatePassParams.errorSensitivity; 100 | 101 | /* 102 | D3D12_SHADING_RATE_1X1 = 0, // 0b0000 103 | D3D12_SHADING_RATE_1X2 = 0x1, // 0b0001 104 | D3D12_SHADING_RATE_2X1 = 0x4, // 0b0100 105 | D3D12_SHADING_RATE_2X2 = 0x5, // 0b0101 106 | D3D12_SHADING_RATE_2X4 = 0x6, // 0b0110 107 | D3D12_SHADING_RATE_4X2 = 0x9, // 0b1001 108 | D3D12_SHADING_RATE_4X4 = 0xa // 0b1010 109 | */ 110 | 111 | // Compute block shading rate based on if the error computation goes over the threshold 112 | // shading rates in D3D are purposely designed to be able to combined, e.g. 2x1 | 1x2 = 2x2 113 | uint ShadingRate = 0; 114 | ShadingRate |= ((diff2.x >= threshold) ? 0 : ((diff4.x > threshold) ? 0x4 : 0x8)); 115 | ShadingRate |= ((diff2.y >= threshold) ? 0 : ((diff4.y > threshold) ? 0x1 : 0x2)); 116 | 117 | // Disable 4x4 shading rate (low quality, limited perf gain) 118 | if (ShadingRate == 0xa) 119 | { 120 | ShadingRate = (diff2.x > diff2.y) ? 0x6 : 0x9; // use 2x4 or 4x2 based on directional gradient 121 | } 122 | // Disable 4x1 or 1x4 shading rate (unsupported) 123 | else if (ShadingRate == 0x8) 124 | { 125 | ShadingRate = 0x4; 126 | } 127 | else if (ShadingRate == 0x2) 128 | { 129 | ShadingRate = 0x1; 130 | } 131 | 132 | vrsSurface[GroupID.xy] = ShadingRate; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /adaptive_shading/Compute_cb.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef COMPUTE_CB_H 3 | #define COMPUTE_CB_H 4 | 5 | struct ComputeNASDataConstants 6 | { 7 | float brightnessSensitivity; 8 | }; 9 | 10 | struct AdaptiveShadingConstants 11 | { 12 | float4x4 reprojectionMatrix; 13 | uint2 previousViewOrigin; 14 | uint2 previousViewSize; 15 | float2 sourceTextureSizeInv; 16 | float errorSensitivity; 17 | float motionSensitivity; 18 | }; 19 | 20 | #endif // COMPUTE_CB_H -------------------------------------------------------------------------------- /adaptive_shading/ShadingRateVis.hlsl: -------------------------------------------------------------------------------- 1 | 2 | enum { 3 | D3D12_SHADING_RATE_1X1 = 0, 4 | D3D12_SHADING_RATE_1X2 = 0x1, 5 | D3D12_SHADING_RATE_2X1 = 0x4, 6 | D3D12_SHADING_RATE_2X2 = 0x5, 7 | D3D12_SHADING_RATE_2X4 = 0x6, 8 | D3D12_SHADING_RATE_4X2 = 0x9, 9 | D3D12_SHADING_RATE_4X4 = 0xa 10 | }; 11 | 12 | Texture2D vrsSurface : register(t0); 13 | Texture2D nasData : register(t1); // for debug vis 14 | 15 | #define TILE_SIZE 16 16 | 17 | void main_vs( 18 | in uint iVertex : SV_VertexID, 19 | out float4 o_posClip : SV_Position) 20 | { 21 | int u = iVertex & 1; 22 | int v = (iVertex >> 1) & 1; 23 | 24 | o_posClip = float4(u * 2 - 1, 1 - v * 2, 0, 1); 25 | } 26 | 27 | 28 | void main_ps( 29 | in float4 pos : SV_Position, 30 | out float4 o_rgba : SV_Target) 31 | { 32 | uint2 xy = uint2(pos.xy); 33 | uint2 xyGrid = xy % TILE_SIZE; 34 | uint shadingRate = vrsSurface.Load(uint3(xy / TILE_SIZE, 0)); 35 | 36 | float4 overlay = float4(0.0, 0.0, 0.0, 0.0); 37 | 38 | if (shadingRate == D3D12_SHADING_RATE_1X2 || shadingRate == D3D12_SHADING_RATE_2X1) 39 | overlay = float4(0.0, 0.0, 1.0, 0.3); 40 | else if (shadingRate == D3D12_SHADING_RATE_2X2) 41 | overlay = float4(0.0, 1.0, 0.0, 0.3); 42 | else if (shadingRate == D3D12_SHADING_RATE_2X4 || shadingRate == D3D12_SHADING_RATE_4X2) 43 | overlay = float4(0.8, 0.8, 0.0, 0.3); 44 | else if (shadingRate == D3D12_SHADING_RATE_4X4) 45 | overlay = float4(0.8, 0.0, 0.0, 0.3); 46 | 47 | // White dot if a tile is transposed 48 | if (shadingRate == D3D12_SHADING_RATE_1X2 || shadingRate == D3D12_SHADING_RATE_2X4) 49 | { 50 | if (all(xyGrid > 2) && all(xyGrid < 6)) 51 | overlay = float4(1.0, 1.0, 1.0, 1.0); 52 | } 53 | 54 | // Tile borders 55 | if (xyGrid.x == 15 || xyGrid.y == 15) 56 | overlay = float4(0.0, 0.0, 0.0, 0.5); 57 | 58 | o_rgba = overlay; 59 | } -------------------------------------------------------------------------------- /adaptive_shading/SmoothShadingRate.hlsl: -------------------------------------------------------------------------------- 1 | 2 | RWTexture2D vrsSurface : register(u0); 3 | 4 | #define TILE_SIZE 16 5 | 6 | [numthreads(16, 16, 1)] 7 | void main_cs(uint3 DispatchThreadID : SV_DispatchThreadID, uint3 GroupThreadID : SV_GroupThreadID, uint3 GroupID : SV_GroupID) 8 | { 9 | uint surfaceWidth, surfaceHeight; 10 | vrsSurface.GetDimensions(surfaceWidth, surfaceHeight); 11 | 12 | // out-of-bounds check 13 | if (DispatchThreadID.x >= surfaceWidth || DispatchThreadID.y >= surfaceHeight) 14 | return; 15 | 16 | uint centerSR = vrsSurface.Load(DispatchThreadID.xy).x; 17 | 18 | // Check all tiles that contain 4x shading rate in either X or Y 19 | if (centerSR & 0xa) 20 | { 21 | bool x1 = false, y1 = false; 22 | uint SR; 23 | // check if any of the 4 immediate neighboring tiles has 1x rate 24 | # define TestOffset(X, Y) \ 25 | SR = vrsSurface.Load(DispatchThreadID.xy + int2(X, Y)); \ 26 | x1 |= ((SR & 0x3) == 0); y1 |= ((SR & 0xc) == 0); 27 | 28 | TestOffset(-1, 0); 29 | TestOffset( 0, -1); 30 | TestOffset( 0, 1); 31 | TestOffset( 1, 0); 32 | 33 | // if an neighboring tile has 1x rate and current tile is 4x in X 34 | if (x1 && (centerSR & 0x8)) 35 | { 36 | centerSR ^= 0xc; // increase the X shading rate from 4x to 2x 37 | } 38 | // if an neighboring tile has 1x rate and current tile is 4x in Y 39 | if (y1 && (centerSR & 0x2)) 40 | { 41 | centerSR ^= 0x3; // increase the Y shading rate from 4x to 2x 42 | } 43 | } 44 | 45 | vrsSurface[DispatchThreadID.xy] = centerSR; 46 | 47 | } 48 | -------------------------------------------------------------------------------- /adaptive_shading/shaders.cfg: -------------------------------------------------------------------------------- 1 | ComputeNASData.hlsl -T cs_6_0 -E main_cs 2 | ComputeShadingRate.hlsl -T cs_6_0 -E main_cs 3 | SmoothShadingRate.hlsl -T cs_6_0 -E main_cs 4 | ShadingRateVis.hlsl -T ps_6_0 -E main_ps 5 | ShadingRateVis.hlsl -T vs_6_0 -E main_vs --------------------------------------------------------------------------------